chore(receivables): remove legacy-era receivables sources

This commit is contained in:
Darko Gjorgjijoski
2026-08-21 09:38:43 +02:00
parent c7130e1e17
commit d593cccccf
22 changed files with 0 additions and 1713 deletions
@@ -1,99 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Controllers\Company;
use App\Domains\Receivables\Http\Requests\PaymentMethodRequest;
use App\Domains\Receivables\Http\Resources\PaymentMethodResource;
use App\Domains\Receivables\Models\PaymentMethod;
use App\Platform\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class PaymentMethodsController extends Controller
{
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index(Request $request)
{
$this->authorize('viewAny', PaymentMethod::class);
$limit = $request->has('limit') ? $request->limit : 5;
$paymentMethods = PaymentMethod::applyFilters($request->all())
->where('type', PaymentMethod::TYPE_GENERAL)
->whereCompany()
->latest()
->paginateData($limit);
return PaymentMethodResource::collection($paymentMethods);
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return Response
*/
public function store(PaymentMethodRequest $request)
{
$this->authorize('create', PaymentMethod::class);
$paymentMethod = PaymentMethod::create($request->getPaymentMethodPayload());
return new PaymentMethodResource($paymentMethod);
}
/**
* Display the specified resource.
*
* @return Response
*/
public function show(PaymentMethod $paymentMethod)
{
$this->authorize('view', $paymentMethod);
return new PaymentMethodResource($paymentMethod);
}
/**
* Update the specified resource in storage.
*
* @param Request $request
* @return Response
*/
public function update(PaymentMethodRequest $request, PaymentMethod $paymentMethod)
{
$this->authorize('update', $paymentMethod);
$paymentMethod->update($request->getPaymentMethodPayload());
return new PaymentMethodResource($paymentMethod);
}
/**
* Remove the specified resource from storage.
*
* @return Response
*/
public function destroy(PaymentMethod $paymentMethod)
{
$this->authorize('delete', $paymentMethod);
if ($paymentMethod->payments()->exists()) {
return respondJson('payments_attached', 'Payments Attached.');
}
if ($paymentMethod->expenses()->exists()) {
return respondJson('expenses_attached', 'Expenses Attached.');
}
$paymentMethod->delete();
return response()->json([
'success' => 'Payment method deleted successfully',
]);
}
}
@@ -1,145 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Controllers\Company;
use App\Domains\Receivables\Application\PaymentAllocationService;
use App\Domains\Receivables\Application\PaymentService;
use App\Domains\Receivables\Http\Requests\DeletePaymentsRequest;
use App\Domains\Receivables\Http\Requests\PaymentRequest;
use App\Domains\Receivables\Http\Requests\ReplacePaymentAllocationsRequest;
use App\Domains\Receivables\Http\Requests\SendPaymentRequest;
use App\Domains\Receivables\Http\Resources\PaymentResource;
use App\Domains\Receivables\Models\Payment;
use App\Platform\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Mail\Markdown;
class PaymentsController extends Controller
{
public function __construct(
private readonly PaymentAllocationService $paymentAllocationService,
private readonly PaymentService $paymentService,
) {}
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index(Request $request)
{
$this->authorize('viewAny', Payment::class);
$limit = $request->has('limit') ? $request->limit : 10;
$payments = Payment::with(['allocations.invoice'])
->whereCompany()
->join('customers', 'customers.id', '=', 'payments.customer_id')
->leftJoin('payment_methods', 'payment_methods.id', '=', 'payments.payment_method_id')
->applyFilters($request->all())
->select('payments.*', 'customers.name', 'payment_methods.name as payment_mode')
->latest()
->paginateData($limit);
return PaymentResource::collection($payments)
->additional(['meta' => [
'payment_total_count' => Payment::whereCompany()->count(),
]]);
}
/**
* Store a newly created resource in storage.
*
* @param Request $request
* @return Response
*/
public function store(PaymentRequest $request)
{
$this->authorize('create', Payment::class);
$payment = $this->paymentService->create(
attributes: $request->getPaymentPayload(),
allocations: $request->validated('allocations') ?? [],
customFields: $this->customFields($request),
);
return new PaymentResource($payment);
}
public function show(Request $request, Payment $payment)
{
$this->authorize('view', $payment);
return new PaymentResource($payment->load(['allocations.invoice']));
}
public function update(PaymentRequest $request, Payment $payment)
{
$this->authorize('update', $payment);
$payment = $this->paymentService->update(
payment: $payment,
attributes: $request->getPaymentPayload(),
replaceAllocations: $request->exists('allocations'),
allocations: $request->validated('allocations') ?? [],
customFields: $this->customFields($request),
);
return new PaymentResource($payment);
}
public function replaceAllocations(ReplacePaymentAllocationsRequest $request, Payment $payment)
{
$this->authorize('update', $payment);
abort_unless((int) $payment->company_id === (int) $request->header('company'), 404);
$payment = $this->paymentAllocationService->replace($payment, $request->validated('allocations'));
return new PaymentResource($payment->load(['allocations.invoice']));
}
public function delete(DeletePaymentsRequest $request)
{
$this->authorize('delete multiple payments');
$ids = Payment::whereCompany()
->whereIn('id', $request->ids)
->pluck('id');
$this->paymentService->delete($ids);
return response()->json([
'success' => true,
]);
}
public function send(SendPaymentRequest $request, Payment $payment)
{
$this->authorize('send payment', $payment);
$response = $this->paymentService->send($payment, $request->all());
return response()->json($response);
}
public function sendPreview(Request $request, Payment $payment)
{
$this->authorize('send payment', $payment);
$markdown = new Markdown(view(), config('mail.markdown'));
$data = $this->paymentService->sendPaymentData($payment, $request->all());
$data['url'] = $payment->paymentPdfUrl;
return $markdown->render('emails.send.payment', ['data' => $data]);
}
private function customFields(PaymentRequest $request): ?iterable
{
$customFields = $request->input('customFields');
return is_iterable($customFields) ? $customFields : null;
}
}
@@ -1,23 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Controllers\CustomerPortal;
use App\Domains\Accounts\Models\Company;
use App\Domains\Receivables\Http\Resources\CustomerPortal\PaymentMethodResource;
use App\Domains\Receivables\Models\PaymentMethod;
use App\Platform\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class PaymentMethodController extends Controller
{
/**
* Handle the incoming request.
*
* @return Response
*/
public function __invoke(Request $request, Company $company)
{
return PaymentMethodResource::collection(PaymentMethod::where('company_id', $company->id)->get());
}
}
@@ -1,61 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Controllers\CustomerPortal;
use App\Domains\Accounts\Models\Company;
use App\Domains\Receivables\Http\Resources\CustomerPortal\PaymentResource;
use App\Domains\Receivables\Models\Payment;
use App\Platform\Http\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
class PaymentsController extends Controller
{
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index(Request $request)
{
$limit = $request->has('limit') ? $request->limit : 10;
$payments = Payment::with(['customer', 'allocations.invoice', 'paymentMethod', 'creator'])
->whereCustomer(Auth::guard('customer')->id())
->applyFilters($request->only([
'payment_number',
'payment_method_id',
'orderByField',
'orderBy',
]))
->select('payments.*')
->latest()
->paginateData($limit);
return PaymentResource::collection($payments)
->additional(['meta' => [
'paymentTotalCount' => Payment::whereCustomer(Auth::guard('customer')->id())->count(),
]]);
}
/**
* Display the specified resource.
*
* @param Payment $payment
* @return Response
*/
public function show(Company $company, $id)
{
$payment = $company->payments()
->whereCustomer(Auth::guard('customer')->id())
->where('id', $id)
->first();
if (! $payment) {
return response()->json(['error' => 'payment_not_found'], 404);
}
return new PaymentResource($payment->load(['allocations.invoice']));
}
}
@@ -1,30 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Controllers;
use App\Domains\Receivables\Http\Resources\PaymentResource;
use App\Domains\Receivables\Models\Payment;
use App\Platform\Http\Controller;
use App\Platform\Mail\Models\EmailLog;
use Illuminate\Http\Request;
class PublicPaymentController extends Controller
{
public function getPdf(EmailLog $emailLog, Request $request)
{
$payment = $emailLog->mailable;
abort_unless($payment instanceof Payment, 404);
abort_if($emailLog->isExpired(), 403, 'Link Expired.');
return $payment->getGeneratedPDFOrStream('payment');
}
public function getPayment(EmailLog $emailLog)
{
$payment = $emailLog->mailable;
abort_unless($payment instanceof Payment, 404);
abort_if($emailLog->isExpired(), 403, 'Link Expired.');
return new PaymentResource($payment);
}
}
@@ -1,33 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class DeletePaymentsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'ids' => [
'required',
],
'ids.*' => [
'required',
Rule::exists('payments', 'id'),
],
];
}
}
@@ -1,53 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Requests;
use App\Domains\Receivables\Models\PaymentMethod;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class PaymentMethodRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
$data = [
'name' => [
'required',
Rule::unique('payment_methods')
->where('company_id', $this->header('company')),
],
];
if ($this->getMethod() == 'PUT') {
$data['name'] = [
'required',
Rule::unique('payment_methods')
->ignore($this->route('payment_method'), 'id')
->where('company_id', $this->header('company')),
];
}
return $data;
}
public function getPaymentMethodPayload()
{
return collect($this->validated())
->merge([
'company_id' => $this->header('company'),
'type' => PaymentMethod::TYPE_GENERAL,
])
->toArray();
}
}
@@ -1,116 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Requests;
use App\Domains\Accounts\Models\CompanySetting;
use App\Domains\Contacts\Models\Customer;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Validator;
class PaymentRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
$rules = [
'payment_date' => [
'required',
],
'customer_id' => [
'required',
Rule::exists('customers', 'id')->where('company_id', $this->header('company')),
],
'exchange_rate' => [
'nullable',
'numeric',
'gt:0',
],
'amount' => ['required', 'integer', 'min:1'],
'payment_number' => [
'required',
Rule::unique('payments')->where('company_id', $this->header('company')),
],
'allocations' => ['sometimes', 'array'],
'allocations.*.invoice_id' => ['required', 'integer', 'distinct'],
'allocations.*.amount' => ['required', 'integer', 'min:1'],
'payment_method_id' => [
'nullable',
],
'notes' => [
'nullable',
],
];
if ($this->isMethod('PUT')) {
$rules['payment_number'] = [
'required',
Rule::unique('payments')
->ignore($this->route('payment')->id)
->where('company_id', $this->header('company')),
];
}
$companyCurrency = CompanySetting::getSetting('currency', $this->header('company'));
$customer = Customer::find($this->customer_id);
if ($customer && $companyCurrency) {
if ((string) $customer->currency_id !== $companyCurrency) {
$rules['exchange_rate'] = [
'required',
'numeric',
'gt:0',
];
}
}
return $rules;
}
/**
* Reject the retired field without advertising it in the generated API
* schema. Payment-to-invoice links now exist only inside allocations.
*/
public function withValidator(Validator $validator): void
{
$validator->after(function (Validator $validator): void {
if ($this->exists('invoice_id')) {
$validator->errors()->add(
'invoice_id',
__('validation.prohibited', ['attribute' => 'invoice id'])
);
}
});
}
public function getPaymentPayload()
{
$company_currency = CompanySetting::getSetting('currency', $this->header('company'));
$currency = Customer::find($this->customer_id)->currency_id;
$exchange_rate = (string) $company_currency !== (string) $currency
? (float) $this->exchange_rate
: 1;
return collect($this->validated())
->except('allocations')
->merge([
'creator_id' => $this->user()->id,
'company_id' => $this->header('company'),
'exchange_rate' => $exchange_rate,
'base_amount' => (int) round($this->amount * $exchange_rate),
'currency_id' => $currency,
])
->toArray();
}
}
@@ -1,43 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class SendPaymentRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'subject' => [
'required',
],
'body' => [
'required',
],
'from' => [
'required',
],
'to' => [
'required',
],
'cc' => [
'nullable',
],
'bcc' => [
'nullable',
],
];
}
}
@@ -1,27 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Resources\CustomerPortal;
use App\Domains\Accounts\Http\Resources\CustomerPortal\CompanyResource;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentMethodResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'company_id' => $this->company_id,
'company' => $this->when($this->company()->exists(), function () {
return new CompanyResource($this->company);
}),
];
}
}
@@ -1,79 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Resources\CustomerPortal;
use App\Domains\Accounts\Http\Resources\CustomerPortal\CompanyResource;
use App\Domains\Contacts\Http\Resources\CustomerPortal\CustomerResource;
use App\Domains\Metadata\Http\Resources\CustomerPortal\CustomFieldValueResource;
use App\Domains\Money\Http\Resources\CustomerPortal\CurrencyResource;
use App\Domains\Sales\Http\Resources\CustomerPortal\InvoiceResource;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
*/
public function toArray($request): array
{
$allocations = $this->relationLoaded('allocations')
? $this->allocations
: $this->allocations()->with('invoice')->get();
$allocatedAmount = (int) $allocations->sum('amount');
$baseAllocatedAmount = (int) $allocations->sum('base_amount');
$baseAmount = $this->base_amount === null
? (int) round($this->amount * ($this->exchange_rate ?: 1))
: (int) $this->base_amount;
return [
'id' => $this->id,
'payment_number' => $this->payment_number,
'payment_date' => $this->payment_date,
'notes' => $this->notes,
'amount' => $this->amount,
'unique_hash' => $this->unique_hash,
'company_id' => $this->company_id,
'payment_method_id' => $this->payment_method_id,
'customer_id' => $this->customer_id,
'exchange_rate' => $this->exchange_rate,
'base_amount' => $baseAmount,
'allocations' => $allocations->map(fn ($allocation) => [
'id' => $allocation->id,
'invoice_id' => $allocation->invoice_id,
'amount' => $allocation->amount,
'base_amount' => $allocation->base_amount,
'invoice' => $allocation->invoice ? new InvoiceResource($allocation->invoice) : null,
]),
'allocated_amount' => $allocatedAmount,
'unallocated_amount' => (int) ((int) $this->amount - $allocatedAmount),
'base_allocated_amount' => $baseAllocatedAmount,
'base_unallocated_amount' => (int) ($baseAmount - $baseAllocatedAmount),
'currency_id' => $this->currency_id,
'transaction_id' => $this->transaction_id,
'formatted_created_at' => $this->formattedCreatedAt,
'formatted_payment_date' => $this->formattedPaymentDate,
'payment_pdf_url' => $this->paymentPdfUrl,
'customer' => $this->when($this->customer()->exists(), function () {
return new CustomerResource($this->customer);
}),
'payment_method' => $this->when($this->paymentMethod()->exists(), function () {
return new PaymentMethodResource($this->paymentMethod);
}),
'fields' => $this->when($this->fields()->exists(), function () {
return CustomFieldValueResource::collection($this->fields);
}),
'company' => $this->when($this->company()->exists(), function () {
return new CompanyResource($this->company);
}),
'currency' => $this->when($this->currency()->exists(), function () {
return new CurrencyResource($this->currency);
}),
'transaction' => $this->when($this->transaction()->exists(), function () {
return new TransactionResource($this->transaction);
}),
];
}
}
@@ -1,36 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Resources\CustomerPortal;
use App\Domains\Accounts\Http\Resources\CustomerPortal\CompanyResource;
use App\Domains\Sales\Http\Resources\CustomerPortal\InvoiceResource;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TransactionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
* @return array|Arrayable|\JsonSerializable
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'transaction_id' => $this->transaction_id,
'type' => $this->type,
'status' => $this->status,
'transaction_date' => $this->transaction_date,
'invoice_id' => $this->invoice_id,
'invoice' => $this->when($this->invoice()->exists(), function () {
return new InvoiceResource($this->invoice);
}),
'company' => $this->when($this->company()->exists(), function () {
return new CompanyResource($this->company);
}),
];
}
}
@@ -1,28 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Resources;
use App\Domains\Accounts\Http\Resources\CompanyResource;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentMethodResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'company_id' => $this->company_id,
'type' => $this->type,
'company' => $this->when($this->company()->exists(), function () {
return new CompanyResource($this->company);
}),
];
}
}
@@ -1,81 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Resources;
use App\Domains\Accounts\Http\Resources\CompanyResource;
use App\Domains\Contacts\Http\Resources\CustomerResource;
use App\Domains\Metadata\Http\Resources\CustomFieldValueResource;
use App\Domains\Money\Http\Resources\CurrencyResource;
use App\Domains\Sales\Http\Resources\InvoiceResource;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PaymentResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
*/
public function toArray($request): array
{
$allocations = $this->relationLoaded('allocations')
? $this->allocations
: $this->allocations()->with('invoice')->get();
$allocatedAmount = (int) $allocations->sum('amount');
$baseAllocatedAmount = (int) $allocations->sum('base_amount');
$baseAmount = $this->base_amount === null
? (int) round($this->amount * ($this->exchange_rate ?: 1))
: (int) $this->base_amount;
return [
'id' => $this->id,
'payment_number' => $this->payment_number,
'payment_date' => $this->payment_date,
'notes' => $this->getNotes(),
'amount' => $this->amount,
'unique_hash' => $this->unique_hash,
'company_id' => $this->company_id,
'payment_method_id' => $this->payment_method_id,
'creator_id' => $this->creator_id,
'customer_id' => $this->customer_id,
'exchange_rate' => $this->exchange_rate,
'base_amount' => $baseAmount,
'allocations' => $allocations->map(fn ($allocation) => [
'id' => $allocation->id,
'invoice_id' => $allocation->invoice_id,
'amount' => $allocation->amount,
'base_amount' => $allocation->base_amount,
'invoice' => $allocation->invoice ? new InvoiceResource($allocation->invoice) : null,
]),
'allocated_amount' => $allocatedAmount,
'unallocated_amount' => (int) ((int) $this->amount - $allocatedAmount),
'base_allocated_amount' => $baseAllocatedAmount,
'base_unallocated_amount' => (int) ($baseAmount - $baseAllocatedAmount),
'currency_id' => $this->currency_id,
'transaction_id' => $this->transaction_id,
'sequence_number' => $this->sequence_number,
'formatted_created_at' => $this->formattedCreatedAt,
'formatted_payment_date' => $this->formattedPaymentDate,
'payment_pdf_url' => $this->paymentPdfUrl,
'customer' => $this->when($this->customer()->exists(), function () {
return new CustomerResource($this->customer);
}),
'payment_method' => $this->when($this->paymentMethod()->exists(), function () {
return new PaymentMethodResource($this->paymentMethod);
}),
'fields' => $this->when($this->fields()->exists(), function () {
return CustomFieldValueResource::collection($this->fields);
}),
'company' => $this->when($this->company()->exists(), function () {
return new CompanyResource($this->company);
}),
'currency' => $this->when($this->currency()->exists(), function () {
return new CurrencyResource($this->currency);
}),
'transaction' => $this->when($this->transaction()->exists(), function () {
return new TransactionResource($this->transaction);
}),
];
}
}
@@ -1,36 +0,0 @@
<?php
namespace App\Domains\Receivables\Http\Resources;
use App\Domains\Accounts\Http\Resources\CompanyResource;
use App\Domains\Sales\Http\Resources\InvoiceResource;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class TransactionResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
* @return array|Arrayable|\JsonSerializable
*/
public function toArray($request): array
{
return [
'id' => $this->id,
'transaction_id' => $this->transaction_id,
'type' => $this->type,
'status' => $this->status,
'transaction_date' => $this->transaction_date,
'invoice_id' => $this->invoice_id,
'invoice' => $this->when($this->invoice()->exists(), function () {
return new InvoiceResource($this->invoice);
}),
'company' => $this->when($this->company()->exists(), function () {
return new CompanyResource($this->company);
}),
];
}
}
@@ -1,42 +0,0 @@
<?php
namespace App\Domains\Receivables\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class GeneratePaymentPdfJob implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
public $payment;
public $deleteExistingFile;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($payment, $deleteExistingFile = false)
{
$this->payment = $payment;
$this->deleteExistingFile = $deleteExistingFile;
}
/**
* Execute the job.
*/
public function handle(): int
{
$this->payment->generatePDF('payment', $this->payment->payment_number, $this->deleteExistingFile);
return 0;
}
}
@@ -1,60 +0,0 @@
<?php
namespace App\Domains\Receivables\Mail;
use App\Domains\Receivables\Models\Payment;
use App\Platform\Mail\Contracts\EmailLogWriter;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class SendPaymentMail extends Mailable
{
use Queueable;
use SerializesModels;
public $data = [];
/**
* Create a new message instance.
*
* @return void
*/
public function __construct($data)
{
$this->data = $data;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$payment = Payment::findOrFail($this->data['payment']['id']);
$token = app(EmailLogWriter::class)->record($payment, [
'from' => $this->data['from'],
'to' => $this->data['to'],
'cc' => $this->data['cc'] ?? null,
'bcc' => $this->data['bcc'] ?? null,
'subject' => $this->data['subject'],
'body' => $this->data['body'],
]);
$this->data['url'] = route('payment', ['email_log' => $token]);
$mailContent = $this->from($this->data['from'], config('mail.from.name'))
->subject($this->data['subject'])
->markdown('emails.send.payment', ['data', $this->data]);
if ($this->data['attach']['data']) {
$mailContent->attachData(
$this->data['attach']['data']->output(),
$this->data['payment']['payment_number'].'.pdf'
);
}
return $mailContent;
}
}
-296
View File
@@ -1,296 +0,0 @@
<?php
namespace App\Domains\Receivables\Models;
use App\Domains\Accounts\Models\Company;
use App\Domains\Accounts\Models\CompanySetting;
use App\Domains\Accounts\Models\User;
use App\Domains\Contacts\Models\Customer;
use App\Domains\Metadata\Concerns\HasCustomFields;
use App\Domains\Money\Models\Currency;
use App\Domains\Receivables\Contracts\PaymentPdfDataProvider;
use App\Domains\Receivables\Jobs\GeneratePaymentPdfJob;
use App\Domains\Sales\Models\Invoice;
use App\Platform\Mail\Models\EmailLog;
use App\Platform\Pdf\Concerns\GeneratesPdf;
use App\Platform\Pdf\Rendering\PdfHtmlSanitizer;
use App\Support\SafeOrderBy;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Facades\DB;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
class Payment extends Model implements HasMedia
{
protected $table = 'payments';
use GeneratesPdf;
use HasCustomFields;
use HasFactory;
use InteractsWithMedia;
protected $dates = ['created_at', 'updated_at', 'payment_date'];
protected $guarded = ['id'];
protected $appends = [
'formattedCreatedAt',
'formattedPaymentDate',
'paymentPdfUrl',
];
protected function casts(): array
{
return [
'notes' => 'string',
'exchange_rate' => 'float',
];
}
protected static function booted()
{
static::created(function ($payment) {
DB::afterCommit(fn () => GeneratePaymentPdfJob::dispatch($payment)->afterCommit());
});
static::updated(function ($payment) {
DB::afterCommit(fn () => GeneratePaymentPdfJob::dispatch($payment, true)->afterCommit());
});
}
public function setSettingsAttribute($value)
{
if ($value) {
$this->attributes['settings'] = json_encode($value);
}
}
public function getFormattedCreatedAtAttribute($value)
{
$dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id);
return Carbon::parse($this->created_at)->translatedFormat($dateFormat);
}
public function getFormattedPaymentDateAttribute($value)
{
$dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id);
return Carbon::parse($this->payment_date)->translatedFormat($dateFormat);
}
public function getPaymentPdfUrlAttribute()
{
return url('/payments/pdf/'.$this->unique_hash);
}
public function transaction(): BelongsTo
{
return $this->belongsTo(Transaction::class);
}
public function emailLogs(): MorphMany
{
return $this->morphMany(EmailLog::class, 'mailable');
}
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class, 'customer_id');
}
public function company(): BelongsTo
{
return $this->belongsTo(Company::class);
}
public function allocations(): HasMany
{
return $this->hasMany(PaymentAllocation::class);
}
public function invoices(): BelongsToMany
{
return $this->belongsToMany(Invoice::class, 'payment_allocations')
->withPivot(['amount', 'base_amount'])
->withTimestamps();
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'creator_id');
}
public function currency(): BelongsTo
{
return $this->belongsTo(Currency::class);
}
public function paymentMethod(): BelongsTo
{
return $this->belongsTo(PaymentMethod::class);
}
public function scopeWhereSearch($query, $search)
{
foreach (explode(' ', $search) as $term) {
$query->whereHas('customer', function ($query) use ($term) {
$query->where('name', 'LIKE', '%'.$term.'%')
->orWhere('contact_name', 'LIKE', '%'.$term.'%')
->orWhere('company_name', 'LIKE', '%'.$term.'%');
});
}
}
public function scopePaymentNumber($query, $paymentNumber)
{
return $query->where('payments.payment_number', 'LIKE', '%'.$paymentNumber.'%');
}
public function scopePaymentMethod($query, $paymentMethodId)
{
return $query->where('payments.payment_method_id', $paymentMethodId);
}
public function scopePaginateData($query, $limit)
{
if ($limit == 'all') {
return $query->get();
}
return $query->paginate($limit);
}
public function scopeApplyFilters($query, array $filters)
{
$filters = collect($filters);
if ($filters->get('search')) {
$query->whereSearch($filters->get('search'));
}
if ($filters->get('payment_number')) {
$query->paymentNumber($filters->get('payment_number'));
}
if ($filters->get('payment_id')) {
$query->wherePayment($filters->get('payment_id'));
}
if ($filters->get('payment_method_id')) {
$query->paymentMethod($filters->get('payment_method_id'));
}
if ($filters->get('customer_id')) {
$query->whereCustomer($filters->get('customer_id'));
}
if ($filters->get('from_date') && $filters->get('to_date')) {
$start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date'));
$end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date'));
$query->paymentsBetween($start, $end);
}
if ($filters->get('orderByField') || $filters->get('orderBy')) {
$field = $filters->get('orderByField') ? $filters->get('orderByField') : 'sequence_number';
$orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'desc';
$query->whereOrder($field, $orderBy);
}
}
public function scopePaymentsBetween($query, $start, $end)
{
return $query->whereBetween(
'payments.payment_date',
[$start->format('Y-m-d'), $end->format('Y-m-d')]
);
}
public function scopeWhereOrder($query, $orderByField, $orderBy)
{
SafeOrderBy::apply($query, $orderByField, $orderBy);
}
public function scopeWherePayment($query, $payment_id)
{
$query->orWhere('id', $payment_id);
}
public function scopeWhereCompany($query)
{
$query->where('payments.company_id', request()->header('company'));
}
public function scopeWhereCustomer($query, $customer_id)
{
$query->where('payments.customer_id', $customer_id);
}
public function getPDFData(): mixed
{
return app(PaymentPdfDataProvider::class)->getPdfData($this);
}
public function getCompanyAddress(): string|false
{
if ($this->company && (! $this->company->address()->exists())) {
return false;
}
$format = CompanySetting::getSetting('payment_company_address_format', $this->company_id);
return $this->getFormattedString($format);
}
public function getCustomerBillingAddress(): string|false
{
if ($this->customer && (! $this->customer->billingAddress()->exists())) {
return false;
}
$format = CompanySetting::getSetting('payment_from_customer_address_format', $this->company_id);
return $this->getFormattedString($format);
}
public function getEmailAttachmentSetting(): bool
{
$paymentAsAttachment = CompanySetting::getSetting('payment_email_attachment', $this->company_id);
if ($paymentAsAttachment == 'NO') {
return false;
}
return true;
}
public function getNotes(): string
{
return PdfHtmlSanitizer::sanitize($this->getFormattedString($this->notes));
}
public function getEmailBody(string $body): string
{
$values = array_merge($this->getFieldsArray(), $this->getExtraFields());
$body = strtr($body, $values);
return preg_replace('/{(.*?)}/', '', $body);
}
public function getExtraFields(): array
{
return [
'{PAYMENT_DATE}' => $this->formattedPaymentDate,
'{PAYMENT_MODE}' => $this->paymentMethod ? $this->paymentMethod->name : null,
'{PAYMENT_NUMBER}' => $this->payment_number,
'{PAYMENT_AMOUNT}' => format_money_pdf($this->amount, $this->customer->currency),
];
}
}
@@ -1,110 +0,0 @@
<?php
namespace App\Domains\Receivables\Models;
use App\Domains\Accounts\Models\Company;
use App\Domains\Purchases\Models\Expense;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class PaymentMethod extends Model
{
protected $table = 'payment_methods';
use HasFactory;
protected $guarded = [
'id',
];
public const TYPE_GENERAL = 'GENERAL';
public const TYPE_MODULE = 'MODULE';
protected function casts(): array
{
return [
'settings' => 'array',
'use_test_env' => 'boolean',
];
}
public function setSettingsAttribute($value)
{
$this->attributes['settings'] = json_encode($value);
}
public function payments(): HasMany
{
return $this->hasMany(Payment::class);
}
public function expenses(): HasMany
{
return $this->hasMany(Expense::class);
}
public function company(): BelongsTo
{
return $this->belongsTo(Company::class);
}
public function scopeWhereCompanyId($query, $id)
{
$query->where('company_id', $id);
}
public function scopeWhereCompany($query)
{
$query->where('company_id', request()->header('company'));
}
public function scopeWherePaymentMethod($query, $payment_id)
{
$query->orWhere('id', $payment_id);
}
public function scopeWhereSearch($query, $search)
{
$query->where('name', 'LIKE', '%'.$search.'%');
}
public function scopeApplyFilters($query, array $filters)
{
$filters = collect($filters);
if ($filters->get('method_id')) {
$query->wherePaymentMethod($filters->get('method_id'));
}
if ($filters->get('company_id')) {
$query->whereCompany($filters->get('company_id'));
}
if ($filters->get('search')) {
$query->whereSearch($filters->get('search'));
}
}
public function scopePaginateData($query, $limit)
{
if ($limit == 'all') {
return $query->get();
}
return $query->paginate($limit);
}
/**
* Retrieve the settings array for a payment method by its ID.
*/
public static function getSettings(int $id): mixed
{
$settings = PaymentMethod::find($id)
->settings;
return $settings;
}
}
@@ -1,64 +0,0 @@
<?php
namespace App\Domains\Receivables\Models;
use App\Domains\Accounts\Models\Company;
use App\Domains\Accounts\Models\CompanySetting;
use App\Domains\Sales\Models\Invoice;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Transaction extends Model
{
protected $table = 'transactions';
use HasFactory;
protected $guarded = [
'id',
];
protected $dates = [
'transaction_date',
];
public const FAILED = 'FAILED';
public const SUCCESS = 'SUCCESS';
public function payments(): HasMany
{
return $this->hasMany(Payment::class);
}
public function invoice(): BelongsTo
{
return $this->belongsTo(Invoice::class);
}
public function company(): BelongsTo
{
return $this->belongsTo(Company::class);
}
/**
* Check if a completed transaction's public link has expired based on the
* company's link expiry settings (link_expiry_days and automatically_expire_public_links).
*/
public function isExpired(): bool
{
$linkExpiryDays = (int) CompanySetting::getSetting('link_expiry_days', $this->company_id);
$checkExpiryLinks = CompanySetting::getSetting('automatically_expire_public_links', $this->company_id);
$expiryDate = $this->updated_at->addDays($linkExpiryDays);
if ($checkExpiryLinks == 'YES' && $this->status == self::SUCCESS && Carbon::now()->format('Y-m-d') > $expiryDate->format('Y-m-d')) {
return true;
}
return false;
}
}
@@ -1,112 +0,0 @@
<?php
namespace App\Domains\Receivables\Policies;
use App\Domains\Accounts\Models\User;
use App\Domains\Receivables\Models\Payment;
use App\Domains\Receivables\Models\PaymentMethod;
use Illuminate\Auth\Access\HandlesAuthorization;
use Silber\Bouncer\BouncerFacade;
class PaymentMethodPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* @return mixed
*/
public function viewAny(User $user): bool
{
if (BouncerFacade::can('view-payment', Payment::class)) {
return true;
}
return false;
}
/**
* Determine whether the user can view the model.
*
* @return mixed
*/
public function view(User $user, PaymentMethod $paymentMethod): bool
{
if (BouncerFacade::can('view-payment', Payment::class) && $user->hasCompany($paymentMethod->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can create models.
*
* @return mixed
*/
public function create(User $user): bool
{
if (BouncerFacade::can('view-payment', Payment::class)) {
return true;
}
return false;
}
/**
* Determine whether the user can update the model.
*
* @return mixed
*/
public function update(User $user, PaymentMethod $paymentMethod): bool
{
if (BouncerFacade::can('view-payment', Payment::class) && $user->hasCompany($paymentMethod->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can delete the model.
*
* @return mixed
*/
public function delete(User $user, PaymentMethod $paymentMethod): bool
{
if (BouncerFacade::can('view-payment', Payment::class) && $user->hasCompany($paymentMethod->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can restore the model.
*
* @return mixed
*/
public function restore(User $user, PaymentMethod $paymentMethod): bool
{
if (BouncerFacade::can('view-payment', Payment::class) && $user->hasCompany($paymentMethod->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can permanently delete the model.
*
* @return mixed
*/
public function forceDelete(User $user, PaymentMethod $paymentMethod): bool
{
if (BouncerFacade::can('view-payment', Payment::class) && $user->hasCompany($paymentMethod->company_id)) {
return true;
}
return false;
}
}
@@ -1,139 +0,0 @@
<?php
namespace App\Domains\Receivables\Policies;
use App\Domains\Accounts\Models\User;
use App\Domains\Receivables\Models\Payment;
use Illuminate\Auth\Access\HandlesAuthorization;
use Silber\Bouncer\BouncerFacade;
class PaymentPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* @return mixed
*/
public function viewAny(User $user): bool
{
if (BouncerFacade::can('view-payment', Payment::class)) {
return true;
}
return false;
}
/**
* Determine whether the user can view the model.
*
* @return mixed
*/
public function view(User $user, Payment $payment): bool
{
if (BouncerFacade::can('view-payment', $payment) && $user->hasCompany($payment->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can create models.
*
* @return mixed
*/
public function create(User $user): bool
{
if (BouncerFacade::can('create-payment', Payment::class)) {
return true;
}
return false;
}
/**
* Determine whether the user can update the model.
*
* @return mixed
*/
public function update(User $user, Payment $payment): bool
{
if (BouncerFacade::can('edit-payment', $payment) && $user->hasCompany($payment->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can delete the model.
*
* @return mixed
*/
public function delete(User $user, Payment $payment): bool
{
if (BouncerFacade::can('delete-payment', $payment) && $user->hasCompany($payment->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can restore the model.
*
* @return mixed
*/
public function restore(User $user, Payment $payment): bool
{
if (BouncerFacade::can('delete-payment', $payment) && $user->hasCompany($payment->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can permanently delete the model.
*
* @return mixed
*/
public function forceDelete(User $user, Payment $payment): bool
{
if (BouncerFacade::can('delete-payment', $payment) && $user->hasCompany($payment->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can send email of the model.
*
* @return mixed
*/
public function send(User $user, Payment $payment)
{
if (BouncerFacade::can('send-payment', $payment) && $user->hasCompany($payment->company_id)) {
return true;
}
return false;
}
/**
* Determine whether the user can delete models.
*
* @return mixed
*/
public function deleteMultiple(User $user)
{
if (BouncerFacade::can('delete-payment', Payment::class)) {
return true;
}
return false;
}
}