diff --git a/app/Classes/General/Eloquent/Filters/BelongsToSupplierId.php b/app/Classes/General/Eloquent/Filters/BelongsToSupplierId.php new file mode 100644 index 00000000..63536412 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/BelongsToSupplierId.php @@ -0,0 +1,24 @@ +whereHas('owner', function ($q) use ($value) { + $q->whereHas('transactions', function ($q2) use ($value) { + $q2->where('type', TransactionType::BILL)->where('issuer', $value); + }); + }); + } +} diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreateBillGroupPaymentTransactionLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateBillGroupPaymentTransactionLogic.php index 5c1ae1bf..92a6343d 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/CreateBillGroupPaymentTransactionLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateBillGroupPaymentTransactionLogic.php @@ -68,6 +68,12 @@ class CreateBillGroupPaymentTransactionLogic extends AbstractControllerLogic $billGroupPayment = $this->calculatesBillGroupPaymentAmount->execute($billGroup); $outstanding_amount = $billGroupPayment['outstanding_amount']; + if ($billGroupPayment['outstanding_amount'] == 0) { + if ($billGroup->transactions()->whereIn('status', [ApprovalStatus::PENDING_SUBMISSION, ApprovalStatus::PENDING_VERIFICATION, ApprovalStatus::APPROVED, ApprovalStatus::COMPLETED])->count() !== 0) { + throw new MalformedRequestException('Invalid bill group, payment transaction already exist.'); + } + } + $payAmount = floatval(str_replace(',', '', $request->input('payAmount'))); if($payAmount > round($outstanding_amount, 2)) throw new MalformedRequestException('Your payment must not be greater than '. $outstanding_amount .'.'); diff --git a/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierBillGroupLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierBillGroupLogic.php index f89af62c..b67db312 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierBillGroupLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/CreateSupplierBillGroupLogic.php @@ -21,9 +21,11 @@ use App\Classes\Modules\Documents\Services\CreatesDocument; use App\Classes\Modules\Documents\DataTransferObjects\DocumentObject; use App\Classes\Modules\Transactions\DataTransferObjects\TransactionObject; use App\Classes\Modules\Transactions\Services\CreatesTransaction; +use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; use App\Classes\ValueObjects\Constants\PaymentMethodType; use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\BillGroup; +use App\Models\Transaction; class CreateSupplierBillGroupLogic extends AbstractControllerLogic { @@ -57,6 +59,9 @@ class CreateSupplierBillGroupLogic extends AbstractControllerLogic /** @var UpdateGroupLogic */ private $updateGroupLogic; + /** @var UpdatesTransactionStatus */ + private $updatesTransactionStatus; + /** * CreateSupplierBillGroupLogic constructor. @@ -66,8 +71,9 @@ class CreateSupplierBillGroupLogic extends AbstractControllerLogic * @param CreatesFiles $createsFile * @param GeneratesTransactionBillNumber $generatesTransactionBillNumber * @param UpdateGroupLogic $updateGroupLogic + * @param UpdatesTransactionStatus $updatesTransactionStatus */ - public function __construct(FetchesCompany $fetchesCompany, CreatesTransaction $createsTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber, UpdateGroupLogic $updateGroupLogic) + public function __construct(FetchesCompany $fetchesCompany, CreatesTransaction $createsTransaction, CreatesDocument $createsDocument, CreatesFiles $createsFile, GeneratesTransactionBillNumber $generatesTransactionBillNumber, UpdateGroupLogic $updateGroupLogic, UpdatesTransactionStatus $updatesTransactionStatus) { $this->fetchesCompany = $fetchesCompany; $this->createsTransaction = $createsTransaction; @@ -75,6 +81,7 @@ class CreateSupplierBillGroupLogic extends AbstractControllerLogic $this->createsFile = $createsFile; $this->generatesTransactionBillNumber = $generatesTransactionBillNumber; $this->updateGroupLogic = $updateGroupLogic; + $this->updatesTransactionStatus = $updatesTransactionStatus; } public function logic(Request $request) : JsonResponse @@ -83,6 +90,19 @@ class CreateSupplierBillGroupLogic extends AbstractControllerLogic $supplier = $this->fetchesCompany->execute(['id' => $request->route('id')]); $payments = $request->input('payments'); + $supplierRefunds = $request->input('supplierRefunds'); + + foreach ($supplierRefunds as $supplierRefund) { + $refund = Transaction::find($supplierRefund['id']); + + if ($refund->owner->transactions()->where('type', TransactionType::BILL)->first()->issuer !== $supplier->id) { + throw new MalformedRequestException('The supplier refund and bill group does not belongs to same supplier.'); + } + + if ($refund->type !== TransactionType::SUPPLIER_REFUND) { + throw new MalformedRequestException('Only transaction type supplier refund can be used for bill refund.'); + } + } $amount = 0; $original_amount = 0; @@ -129,6 +149,41 @@ class CreateSupplierBillGroupLogic extends AbstractControllerLogic $billGroup->groups()->sync($payment['id'], false); } + //create bill refund + foreach ($supplierRefunds as $supplierRefund) { + $refund = Transaction::find($supplierRefund['id']); + $deductedRefunds = $refund->transactions()->where('type', TransactionType::BILL_REFUND)->where('status', ApprovalStatus::APPROVED)->get(); + $refundDeductableAmount = $refund->amount - $deductedRefunds->sum('amount'); + $refundDeductableOriginalAmount = $refund->original_amount - $deductedRefunds->sum('original_amount'); + + $amount -= round($refundDeductableAmount, 2); + $original_amount -= round($refundDeductableOriginalAmount, 2); + + if ($amount > 0) { + $deductedRefundAmount = $refundDeductableAmount; + $deductedRefundOriginalAmount = $refundDeductableOriginalAmount; + + $this->updatesTransactionStatus->execute($refund, ApprovalStatus::COMPLETED); + } + + if ($amount < 0) { + $deductedRefundAmount = $refundDeductableAmount + $amount; + $deductedRefundOriginalAmount = $refundDeductableOriginalAmount + $original_amount; + } + + $billNumber = $this->generatesTransactionBillNumber->execute('BRFD-'); + + $object = new TransactionObject($billNumber, TransactionType::BILL_REFUND, $supplier->id, 1, + 1, PaymentMethodType::CASH, + $deductedRefundAmount, $deductedRefundOriginalAmount, $refund->currency_id, + $refund->original_currency_id, $deductedRefundOriginalAmount / $deductedRefundAmount, + 0, 0, null, ApprovalStatus::APPROVED, []); + + $transaction = $this->createsTransaction->execute($refund, $object); + + $billGroup->billRefunds()->sync($transaction->id, false); + } + return $this->response([]); } } diff --git a/app/Classes/Modules/Transactions/ControllersLogic/DeleteBillGroupLogic.php b/app/Classes/Modules/Transactions/ControllersLogic/DeleteBillGroupLogic.php index 56464409..1122c111 100644 --- a/app/Classes/Modules/Transactions/ControllersLogic/DeleteBillGroupLogic.php +++ b/app/Classes/Modules/Transactions/ControllersLogic/DeleteBillGroupLogic.php @@ -5,6 +5,8 @@ namespace App\Classes\Modules\Transactions\ControllersLogic; use App\Classes\General\Abstracts\AbstractControllerLogic; use App\Classes\Modules\Transactions\Services\FetchesBillGroup; use App\Classes\Modules\Transactions\Services\DeletesTransaction; +use App\Classes\Modules\Transactions\Services\UpdatesTransactionStatus; +use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Http\Resources\BillGroupResource; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -28,15 +30,20 @@ class DeleteBillGroupLogic extends AbstractControllerLogic /** @var DeletesTransaction */ private $deletesTransaction; + /** @var UpdatesTransactionStatus */ + private $updatesTransactionStatus; + /** * DeleteBillGroupLogic constructor. * @param FetchesBillGroup $fetchesBillGroup * @param DeletesTransaction $deletesTransaction + * @param UpdatesTransactionStatus $updatesTransactionStatus */ - public function __construct(FetchesBillGroup $fetchesBillGroup, DeletesTransaction $deletesTransaction) + public function __construct(FetchesBillGroup $fetchesBillGroup, DeletesTransaction $deletesTransaction, UpdatesTransactionStatus $updatesTransactionStatus) { $this->fetchesBillGroup = $fetchesBillGroup; $this->deletesTransaction = $deletesTransaction; + $this->updatesTransactionStatus = $updatesTransactionStatus; } /** @@ -59,7 +66,15 @@ class DeleteBillGroupLogic extends AbstractControllerLogic foreach($groups as $group) { $billGroup->groups()->detach($group->id); } + + $billRefunds = $billGroup->billRefunds()->get(); + foreach($billRefunds as $billRefund) { + $billGroup->billRefunds()->detach($billRefund->id); + $this->deletesTransaction->execute($billRefund); + $this->updatesTransactionStatus->execute($billRefund->owner, ApprovalStatus::APPROVED); + } + $billGroup->delete(); return $this->resourceResponse(new BillGroupResource($billGroup)); diff --git a/app/Classes/Modules/Transactions/Services/CalculatesBillGroupPaymentAmount.php b/app/Classes/Modules/Transactions/Services/CalculatesBillGroupPaymentAmount.php index 812f8a3c..1a130b41 100644 --- a/app/Classes/Modules/Transactions/Services/CalculatesBillGroupPaymentAmount.php +++ b/app/Classes/Modules/Transactions/Services/CalculatesBillGroupPaymentAmount.php @@ -8,11 +8,13 @@ use App\Models\BillGroup; class CalculatesBillGroupPaymentAmount { public function execute(BillGroup $billGroup){ + $bill_refund_amount = floatval($billGroup->billRefunds->sum('amount')); $floating_amount = floatval($billGroup->transactions()->whereIn('status', [ApprovalStatus::PENDING_SUBMISSION, ApprovalStatus::PENDING_VERIFICATION])->sum('amount')); $paid_amount = floatval($billGroup->transactions()->where('status', ApprovalStatus::APPROVED)->sum('amount')); - $outstanding_amount = $billGroup->amount - $paid_amount - $floating_amount + $billGroup->service_charge; + $outstanding_amount = $billGroup->amount - $bill_refund_amount - $paid_amount - $floating_amount + $billGroup->service_charge; return [ + 'bill_refund_amount' => $bill_refund_amount, 'floating_amount' => $floating_amount, 'paid_amount' => $paid_amount, 'outstanding_amount' => $outstanding_amount, diff --git a/app/Classes/ValueObjects/Constants/TransactionType.php b/app/Classes/ValueObjects/Constants/TransactionType.php index fbab3119..e14930b0 100644 --- a/app/Classes/ValueObjects/Constants/TransactionType.php +++ b/app/Classes/ValueObjects/Constants/TransactionType.php @@ -33,4 +33,9 @@ final class TransactionType { public const CASH_BACK = 13; public const SUPPLIER_PAYMENT = 14; + + public const SUPPLIER_REFUND = 15; + + public const BILL_REFUND = 16; + } diff --git a/app/Console/Commands/ExpiredRefundedBookingCommand.php b/app/Console/Commands/ExpiredRefundedBookingCommand.php index db7e00a7..0fcbd87b 100644 --- a/app/Console/Commands/ExpiredRefundedBookingCommand.php +++ b/app/Console/Commands/ExpiredRefundedBookingCommand.php @@ -151,6 +151,22 @@ class ExpiredRefundedBookingCommand extends Command $transaction = $this->createsTransaction->execute($bookingPayment, $object); } + + if ($bookingInWhiteForm) { + $refund = $bookingPayment->transactions()->supplierRefunds()->where('amount', $transaction->amount)->where('status', ApprovalStatus::APPROVED)->first(); + + if (!$refund) { + $billNumber = $this->generatesTransactionBillNumber->execute('SRFD-'); + + $object = new TransactionObject($billNumber, TransactionType::SUPPLIER_REFUND, 1, $booking->company->id, + 1, PaymentMethodType::CASH, + $transaction->amount, $transaction->amount * $bookingPayment->currency_rate, 1, + $bookingPayment->original_currency_id, $bookingPayment->currency_rate, + 0, 0, null, ApprovalStatus::APPROVED, [], $bookingPayment->bill_no); + + $transaction = $this->createsTransaction->execute($bookingPayment, $object); + } + } } else { Log::info("Credit note transaction id: {$transaction->id}, booking marking not found, the payment reference is: {$transaction->payment_reference}"); } diff --git a/app/Http/Resources/BillGroupResource.php b/app/Http/Resources/BillGroupResource.php index 6f61aeb7..23c8050e 100644 --- a/app/Http/Resources/BillGroupResource.php +++ b/app/Http/Resources/BillGroupResource.php @@ -18,6 +18,7 @@ class BillGroupResource extends JsonResource public function toArray($request) { $billGroupPayment = (App()->make(CalculatesBillGroupPaymentAmount::class))->execute($this->resource); + $bill_refund_amount = $billGroupPayment['bill_refund_amount']; $floating_amount = $billGroupPayment['floating_amount']; $paid_amount = $billGroupPayment['paid_amount']; $outstanding_amount = $billGroupPayment['outstanding_amount']; @@ -37,6 +38,7 @@ class BillGroupResource extends JsonResource 'currency_rate' => (float) $this->currency_rate, 'status' => $this->status, 'groups' => GroupResource::collection($this->groups), + 'bill_refund_amount' => $bill_refund_amount, 'floating_amount' => $floating_amount, 'paid_amount' => $paid_amount, 'outstanding_amount' => $outstanding_amount, @@ -58,6 +60,23 @@ class BillGroupResource extends JsonResource 'updated_at' => Carbon::parse($transaction->updated_at)->format('d-m-Y h:i:s A'), ]; }), + 'bill_refunds' => $this->billRefunds->map(function ($transaction) { + return [ + 'id' => $transaction->id, + 'type' => (int) $transaction->type, + 'bill_no' => $transaction->bill_no, + 'payment_method' => (float) $transaction->payment_method, + 'amount' => (double) $transaction->amount, + 'original_amount' => (double) $transaction->original_amount, + 'currency' => new CurrencyResource($transaction->currency), + 'original_currency' => new CurrencyResource($transaction->original_currency), + 'service_charge' => (double) $transaction->service_charge, + 'tax' => (double) $transaction->tax, + 'status' => (int) $transaction->status, + 'statusText' => ApprovalStatus::APPROVAL_STATUS_ID[(int) $transaction->status], + 'updated_at' => Carbon::parse($transaction->updated_at)->format('d-m-Y h:i:s A'), + ]; + }), ]; } } diff --git a/app/Http/Resources/TransactionResource.php b/app/Http/Resources/TransactionResource.php index 09967f4c..b431c854 100644 --- a/app/Http/Resources/TransactionResource.php +++ b/app/Http/Resources/TransactionResource.php @@ -3,6 +3,7 @@ namespace App\Http\Resources; use App\Classes\Modules\Bookings\Services\CalculatesBookingRefundAmount; +use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Booking; use Carbon\Carbon; @@ -19,7 +20,7 @@ class TransactionResource extends JsonResource public function toArray($request) { - $booking = in_array((int)$this->type, [TransactionType::BILL, TransactionType::REFUND])? $this->owner->owner : $this->owner; + $booking = in_array((int)$this->type, [TransactionType::BILL, TransactionType::REFUND, TransactionType::SUPPLIER_REFUND])? $this->owner->owner : $this->owner; $days = $this->created_at->endOfDay()->addWeekdays($booking->service_id === 3 ? 3 : 1); return [ @@ -32,8 +33,8 @@ class TransactionResource extends JsonResource 'recipient_bank_account' => new BankResource($booking->bank), 'issuer_name' => $this->issuerCompany->name, 'issuer_id' => $this->issuerCompany->id, - 'amount' => (double) $this->amount, - 'original_amount' => (double) $this->original_amount, + 'amount' => (double) ($this->type === TransactionType::SUPPLIER_REFUND ? $this->amount - $this->transactions()->where('type', TransactionType::BILL_REFUND)->where('status', ApprovalStatus::APPROVED)->sum('amount') : $this->amount), + 'original_amount' => (double) ($this->type === TransactionType::SUPPLIER_REFUND ? $this->original_amount - $this->transactions()->where('type', TransactionType::BILL_REFUND)->where('status', ApprovalStatus::APPROVED)->sum('original_amount') : $this->original_amount), 'currency' => new CurrencyResource($this->currency), 'original_currency' => new CurrencyResource($this->original_currency), 'service_charge' => (double) $this->service_charge, diff --git a/app/Models/BillGroup.php b/app/Models/BillGroup.php index 6986ee52..9270d8e8 100644 --- a/app/Models/BillGroup.php +++ b/app/Models/BillGroup.php @@ -29,6 +29,14 @@ class BillGroup extends Model implements Documentable, Transactionable return $this->MorphMany(Transaction::class, 'owner'); } + use HasRelationships; + use \Staudenmeir\EloquentHasManyDeep\HasTableAlias; + + public function billRefunds() + { + return $this->belongsToMany(Transaction::class, BillGroupRefund::class); + } + /** * @return MorphMany */ diff --git a/app/Models/BillGroupRefund.php b/app/Models/BillGroupRefund.php new file mode 100644 index 00000000..aa430873 --- /dev/null +++ b/app/Models/BillGroupRefund.php @@ -0,0 +1,27 @@ +BelongsTo(BillGroup::class, 'bill_group_id', 'id'); + } + + /** + * @return BelongsTo + */ + public function transaction(): BelongsTo + { + return $this->BelongsTo(Transaction::class, 'transaction_id', 'id'); + } +} diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 185fdb36..4161632b 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -186,6 +186,20 @@ class Transaction extends AbstractModel implements Documentable, Transactionable return $query->where('type', TransactionType::REFUND); } + /** + * @param Builder $query + * @param string $payment_reference + * @return Builder + */ + public function scopeSupplierRefunds(Builder $query, ?string $payment_reference = NULL) + { + if($payment_reference){ + $query->where('payment_reference', $payment_reference); + } + + return $query->where('type', TransactionType::SUPPLIER_REFUND); + } + /** * @param Builder $query diff --git a/database/migrations/2024_02_22_222130_create_bill_group_refunds_table.php b/database/migrations/2024_02_22_222130_create_bill_group_refunds_table.php new file mode 100644 index 00000000..f7b278b1 --- /dev/null +++ b/database/migrations/2024_02_22_222130_create_bill_group_refunds_table.php @@ -0,0 +1,22 @@ +id(); + $table->foreignId('bill_group_id')->unsigned()->on('bill_groups'); + $table->foreignId('transaction_id')->unsigned()->on('transactions'); + }); + } + + public function down() + { + Schema::dropIfExists('bill_group_refunds'); + } +} diff --git a/resources/assets/vue/components/bookings/elements/BillGroupComponent.vue b/resources/assets/vue/components/bookings/elements/BillGroupComponent.vue index 42727055..e93eb1ad 100644 --- a/resources/assets/vue/components/bookings/elements/BillGroupComponent.vue +++ b/resources/assets/vue/components/bookings/elements/BillGroupComponent.vue @@ -58,6 +58,42 @@ +