From c2eefe806a7905da0398a7714dd31c96cfa248f9 Mon Sep 17 00:00:00 2001 From: Dillon Ngo Date: Wed, 19 Nov 2025 07:13:15 +0800 Subject: [PATCH] Period Lock - prototype (2nd demo based on PM feedback) --- .../Eloquent/Filters/KeyValuePairIn.php | 23 +++++ .../ControllersLogic/CreateLockLogic.php | 65 ++++++++++++++ .../ControllersLogic/FetchLockLogic.php | 60 +++++++++++++ .../ControllersLogic/ListLocksLogic.php | 62 +++++++++++++ .../ControllersLogic/UpdateLockLogic.php | 87 +++++++++++++++++++ .../Settings/Services/FetchesKeyValuePair.php | 34 ++++++++ .../Settings/Services/ListsKeyValuePairs.php | 34 ++++++++ app/Classes/ValueObjects/Constants/KVPKey.php | 6 ++ .../Settings/CreateLockController.php | 19 ++++ .../Settings/FetchLockController.php | 20 +++++ .../Settings/ListLocksController.php | 20 +++++ .../Settings/UpdateLockController.php | 20 +++++ app/Http/Resources/BillGroupResource.php | 8 ++ app/Http/Resources/GroupResource.php | 16 +++- app/Http/Resources/LockResource.php | 24 +++++ ...make_owner_nullable_in_key_value_pairs.php | 34 ++++++++ database/seeds/KeyValuePairSeeder.php | 42 +++++++++ .../bookings/elements/BillGroupComponent.vue | 12 +-- .../bookings/elements/PaymentComponent.vue | 16 +++- .../elements/TransactionGroupComponent.vue | 17 +++- .../settings/elements/ComponentLock.vue | 58 +++++++++++++ .../sections/LockSectionComponent.vue | 39 +++++++++ .../pages/dashboards/admin_lock.blade.php | 8 ++ .../views/pages/supplier-payments.blade.php | 7 +- routes/api.php | 2 + routes/setting.php | 14 +++ routes/web.php | 5 ++ 27 files changed, 737 insertions(+), 15 deletions(-) create mode 100644 app/Classes/General/Eloquent/Filters/KeyValuePairIn.php create mode 100644 app/Classes/Modules/Settings/ControllersLogic/CreateLockLogic.php create mode 100644 app/Classes/Modules/Settings/ControllersLogic/FetchLockLogic.php create mode 100644 app/Classes/Modules/Settings/ControllersLogic/ListLocksLogic.php create mode 100644 app/Classes/Modules/Settings/ControllersLogic/UpdateLockLogic.php create mode 100644 app/Classes/Modules/Settings/Services/FetchesKeyValuePair.php create mode 100644 app/Classes/Modules/Settings/Services/ListsKeyValuePairs.php create mode 100644 app/Http/Controllers/Settings/CreateLockController.php create mode 100644 app/Http/Controllers/Settings/FetchLockController.php create mode 100644 app/Http/Controllers/Settings/ListLocksController.php create mode 100644 app/Http/Controllers/Settings/UpdateLockController.php create mode 100644 app/Http/Resources/LockResource.php create mode 100644 database/migrations/2025_11_15_133905_make_owner_nullable_in_key_value_pairs.php create mode 100644 database/seeds/KeyValuePairSeeder.php create mode 100644 resources/assets/vue/components/settings/elements/ComponentLock.vue create mode 100644 resources/assets/vue/components/settings/sections/LockSectionComponent.vue create mode 100644 resources/views/pages/dashboards/admin_lock.blade.php create mode 100644 routes/setting.php diff --git a/app/Classes/General/Eloquent/Filters/KeyValuePairIn.php b/app/Classes/General/Eloquent/Filters/KeyValuePairIn.php new file mode 100644 index 00000000..845a3203 --- /dev/null +++ b/app/Classes/General/Eloquent/Filters/KeyValuePairIn.php @@ -0,0 +1,23 @@ +whereNull('owner_type') + ->whereNull('owner_id') + ->whereIn('key', [$value]); + } +} diff --git a/app/Classes/Modules/Settings/ControllersLogic/CreateLockLogic.php b/app/Classes/Modules/Settings/ControllersLogic/CreateLockLogic.php new file mode 100644 index 00000000..0dd83853 --- /dev/null +++ b/app/Classes/Modules/Settings/ControllersLogic/CreateLockLogic.php @@ -0,0 +1,65 @@ + 'Lock Created', + 'message' => 'You have successfully created a lock' + ]; + } + + // /** @var CanCreateBooking */ + // private $canCreateBooking; + + /** @var CreatesKeyValuePair */ + private $createsKeyValuePair; + + /** + * CreateLockLogic constructor. + * @param CanCreateBooking $canCreateBooking + * @param CreatesKeyValuePair $createsKeyValuePair + */ + public function __construct(CanCreateBooking $canCreateBooking, CreatesKeyValuePair $createsKeyValuePair) + { + // $this->canCreateBooking = $canCreateBooking; + $this->createsKeyValuePair = $createsKeyValuePair; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function logic(Request $request) : JsonResponse + { + // $password = $request->input('password'); + // if($password !== 'prototype'){ + // throw new AccessForbiddenException('Incorrect Password'); + // } + + $keyValuePairObject = new KeyValuePairObject( + KVPKey::UI_SUPPLIER_PAID_CURRENCY_ORDER_TAB_IS_LOCKED, + 0 + ); + $this->createsKeyValuePair->execute(null, $keyValuePairObject); + return $this->response([]); + } +} diff --git a/app/Classes/Modules/Settings/ControllersLogic/FetchLockLogic.php b/app/Classes/Modules/Settings/ControllersLogic/FetchLockLogic.php new file mode 100644 index 00000000..a9961331 --- /dev/null +++ b/app/Classes/Modules/Settings/ControllersLogic/FetchLockLogic.php @@ -0,0 +1,60 @@ + 'Retrieved Lock', + 'message' => 'You have successfully retrieved a Lock' + ]; + } + + // /** @var CanFetchBooking */ + // private $canFetchBooking; + + /** @var FetchesKeyValuePair */ + private $fetchesKeyValuePair; + + /** + * FetchLockLogic constructor. + * @param CanFetchBooking $canFetchBooking + * @param FetchesKeyValuePair $fetchesKeyValuePair + */ + public function __construct(CanFetchBooking $canFetchBooking, FetchesBooking $fetchesKeyValuePair) + { + // $this->canFetchBooking = $canFetchBooking; + $this->fetchesKeyValuePair = $fetchesKeyValuePair; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function logic(Request $request) : JsonResponse + { + // $this->canFetchBooking->passes(); + + // $query = $this->fetchesKeyValuePair->execute(['marking' => $request->route('marking'), 'with_transactions' => true]); + + // return $this->resourceResponse(new LockResource($query)); + + return $this->response([]); + } +} diff --git a/app/Classes/Modules/Settings/ControllersLogic/ListLocksLogic.php b/app/Classes/Modules/Settings/ControllersLogic/ListLocksLogic.php new file mode 100644 index 00000000..dc5de052 --- /dev/null +++ b/app/Classes/Modules/Settings/ControllersLogic/ListLocksLogic.php @@ -0,0 +1,62 @@ + 'Retrieved Locks', + 'message' => 'You have successfully retrieved a list of Locks' + ]; + } + + // /** @var CanListServiceTypes */ + // private $canListServiceTypes; + + /** @var ListsKeyValuePairs */ + private $listsKeyValuePairs; + + /** + * ListLocksLogic constructor. + * @param CanListServiceTypes $canListServiceTypes + * @param ListsKeyValuePairs $listsKeyValuePairs + */ + public function __construct(CanListServiceTypes $canListServiceTypes, ListsKeyValuePairs $listsKeyValuePairs) + { + // $this->canListServiceTypes = $canListServiceTypes; + $this->listsKeyValuePairs = $listsKeyValuePairs; + } + + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function logic(Request $request) : JsonResponse + { + // $this->canListServiceTypes->passes(); + + $query = $this->listsKeyValuePairs->execute($this->listsKeyValuePairs->deserializeFilters($request->input('filters'))); + + return $this->collectionResponse(LockResource::collection($query)); + + } + +} diff --git a/app/Classes/Modules/Settings/ControllersLogic/UpdateLockLogic.php b/app/Classes/Modules/Settings/ControllersLogic/UpdateLockLogic.php new file mode 100644 index 00000000..c131ea62 --- /dev/null +++ b/app/Classes/Modules/Settings/ControllersLogic/UpdateLockLogic.php @@ -0,0 +1,87 @@ + 'Updated Lock Status', + 'message' => 'You have successfully updated the Lock status' + ]; + } + + // /** @var CanUpdateServiceType */ + // private $canUpdateServiceType; + + /** @var CreatesKeyValuePair */ + private $createsKeyValuePair; + + /** @var UpdatesKeyValuePair */ + private $updatesKeyValuePair; + + /** + * UpdateLockLogic constructor. + * @param CanUpdateServiceType $canUpdateServiceType + * @param CreatesKeyValuePair $createsKeyValuePair + * @param UpdatesKeyValuePair $updatesKeyValuePair + */ + public function __construct(CanUpdateServiceType $canUpdateServiceType, CreatesKeyValuePair $createsKeyValuePair, UpdatesKeyValuePair $updatesKeyValuePair) + { + // $this->canUpdateServiceType = $canUpdateServiceType; + $this->createsKeyValuePair = $createsKeyValuePair; + $this->updatesKeyValuePair = $updatesKeyValuePair; + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \App\Classes\Exceptions\AccessForbiddenException + * @throws \App\Classes\Exceptions\MalformedRequestException + * @throws \App\Classes\Exceptions\RequestValidationException + */ + public function logic(Request $request) : JsonResponse + { + $key = $request->input('key'); + $kvp = KeyValuePair::whereNull('owner_type') + ->whereNull('owner_id') + ->where('key', $key) + ->first(); + $value = ($kvp->value === "1") ? "0" : "1"; + $this->updateOrCreateKeyValuePair($key, $value); + return $this->resourceResponse(new LockResource($kvp)); + + } + + private function updateOrCreateKeyValuePair($key, $value) + { + $keyValuePairObject = new KeyValuePairObject($key, $value); + $kvp = KeyValuePair::whereNull('owner_type') + ->whereNull('owner_id') + ->where('key', $key) + ->first(); + + if ($kvp) { + $this->updatesKeyValuePair->execute($kvp, $keyValuePairObject); + } else { + $this->createsKeyValuePair->execute(null, $keyValuePairObject); + } + } + +} diff --git a/app/Classes/Modules/Settings/Services/FetchesKeyValuePair.php b/app/Classes/Modules/Settings/Services/FetchesKeyValuePair.php new file mode 100644 index 00000000..0ba19b84 --- /dev/null +++ b/app/Classes/Modules/Settings/Services/FetchesKeyValuePair.php @@ -0,0 +1,34 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + public function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/Modules/Settings/Services/ListsKeyValuePairs.php b/app/Classes/Modules/Settings/Services/ListsKeyValuePairs.php new file mode 100644 index 00000000..4abd5086 --- /dev/null +++ b/app/Classes/Modules/Settings/Services/ListsKeyValuePairs.php @@ -0,0 +1,34 @@ +repository = $repository; + } + + + /** + * @return Builder + */ + function getRepository(): Builder + { + return $this->repository->newQuery(); + } +} diff --git a/app/Classes/ValueObjects/Constants/KVPKey.php b/app/Classes/ValueObjects/Constants/KVPKey.php index f49e6f3f..df7a65de 100644 --- a/app/Classes/ValueObjects/Constants/KVPKey.php +++ b/app/Classes/ValueObjects/Constants/KVPKey.php @@ -26,4 +26,10 @@ class KVPKey public const BOOKING_IS_LOCKED = 'BOOKING_IS_LOCKED'; + public const UI_SUPPLIER_PAID_CURRENCY_ORDER_TAB_IS_LOCKED = 'UI_SUPPLIER_PAID_CURRENCY_ORDER_TAB_IS_LOCKED'; + + public const UI_PAYMENTS_COMPLETE_CURRENCY_ORDERS_IS_LOCKED = 'UI_PAYMENTS_COMPLETE_CURRENCY_ORDERS_IS_LOCKED'; + + public const UI_PAYMENTS_OPEN_CURRENCY_ORDERS_IS_LOCKED = 'UI_PAYMENTS_OPEN_CURRENCY_ORDERS_IS_LOCKED'; + } diff --git a/app/Http/Controllers/Settings/CreateLockController.php b/app/Http/Controllers/Settings/CreateLockController.php new file mode 100644 index 00000000..89f13f31 --- /dev/null +++ b/app/Http/Controllers/Settings/CreateLockController.php @@ -0,0 +1,19 @@ +execute($request); + } +} diff --git a/app/Http/Controllers/Settings/FetchLockController.php b/app/Http/Controllers/Settings/FetchLockController.php new file mode 100644 index 00000000..c8ed73e4 --- /dev/null +++ b/app/Http/Controllers/Settings/FetchLockController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Settings/ListLocksController.php b/app/Http/Controllers/Settings/ListLocksController.php new file mode 100644 index 00000000..4da3f7cb --- /dev/null +++ b/app/Http/Controllers/Settings/ListLocksController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Controllers/Settings/UpdateLockController.php b/app/Http/Controllers/Settings/UpdateLockController.php new file mode 100644 index 00000000..bee069dc --- /dev/null +++ b/app/Http/Controllers/Settings/UpdateLockController.php @@ -0,0 +1,20 @@ +execute($request); + } + +} diff --git a/app/Http/Resources/BillGroupResource.php b/app/Http/Resources/BillGroupResource.php index 748dc392..19c3f0af 100644 --- a/app/Http/Resources/BillGroupResource.php +++ b/app/Http/Resources/BillGroupResource.php @@ -4,6 +4,8 @@ namespace App\Http\Resources; use App\Classes\Modules\Transactions\Services\CalculatesBillGroupPaymentAmount; use App\Classes\ValueObjects\Constants\ApprovalStatus; +use App\Classes\ValueObjects\Constants\KVPKey; +use App\Models\KeyValuePair; use Carbon\Carbon; use Illuminate\Http\Resources\Json\JsonResource; @@ -23,6 +25,11 @@ class BillGroupResource extends JsonResource $paid_amount = $billGroupPayment['paid_amount']; $outstanding_amount = $billGroupPayment['outstanding_amount']; + $ui_supplier_paid_currency_tab_is_locked = ($this->status === 2 || $this->status === 3) + ? KeyValuePair::whereNull('owner_type')->whereNull('owner_id') + ->whereIn('key', [KVPKey::UI_SUPPLIER_PAID_CURRENCY_ORDER_TAB_IS_LOCKED])->where('value', 1)->first() + : null; + return [ 'id' => $this->id, 'reference' => $this->reference, @@ -78,6 +85,7 @@ class BillGroupResource extends JsonResource 'updated_at' => Carbon::parse($transaction->updated_at)->format('d-m-Y h:i:s A'), ]; }), + 'ui_supplier_paid_currency_tab_is_locked' => $ui_supplier_paid_currency_tab_is_locked ? 1 : 0, ]; } } diff --git a/app/Http/Resources/GroupResource.php b/app/Http/Resources/GroupResource.php index fdddd195..b92da2bc 100644 --- a/app/Http/Resources/GroupResource.php +++ b/app/Http/Resources/GroupResource.php @@ -4,8 +4,10 @@ namespace App\Http\Resources; use App\Classes\ValueObjects\Constants\ApprovalStatus; use App\Classes\ValueObjects\Constants\DocumentType; +use App\Classes\ValueObjects\Constants\KVPKey; use App\Classes\ValueObjects\Constants\TransactionType; use App\Models\Booking; +use App\Models\KeyValuePair; use App\Models\Transaction; use Carbon\Carbon; use Illuminate\Database\Eloquent\Collection; @@ -32,6 +34,16 @@ class GroupResource extends JsonResource if(!$this->issuerCompany){ dd($this->id); } + + $ui_payments_complete_currecy_orders_is_locked = ($this->status === 3) + ? KeyValuePair::whereNull('owner_type')->whereNull('owner_id') + ->whereIn('key', [KVPKey::UI_PAYMENTS_COMPLETE_CURRENCY_ORDERS_IS_LOCKED])->where('value', 1)->first() + : null; + $ui_payments_open_currecy_orders_is_locked = ($this->status === 0 || $this->status === 1 || $this->status === 2) + ? KeyValuePair::whereNull('owner_type')->whereNull('owner_id') + ->whereIn('key', [KVPKey::UI_PAYMENTS_OPEN_CURRENCY_ORDERS_IS_LOCKED])->where('value', 1)->first() + : null; + return [ 'id' => $this->id, 'original_amount' => (float) $this->original_amount, @@ -55,7 +67,9 @@ class GroupResource extends JsonResource 'documents' => [ 'currency_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::CURRENCY_VENDOR_ORDER)->first()), 'purchase_order' => new DocumentResource($this->documents()->where('document_type', DocumentType::BULK_PURCHASE_ORDER)->first()) - ] + ], + 'ui_payments_complete_currecy_orders_is_locked' => $ui_payments_complete_currecy_orders_is_locked ? 1 : 0, + 'ui_payments_open_currecy_orders_is_locked' => $ui_payments_open_currecy_orders_is_locked ? 1 : 0, ]; } } diff --git a/app/Http/Resources/LockResource.php b/app/Http/Resources/LockResource.php new file mode 100644 index 00000000..b4915f36 --- /dev/null +++ b/app/Http/Resources/LockResource.php @@ -0,0 +1,24 @@ + $this->id, + 'key' => $this->key, + 'value' => $this->value, + ]; + } +} diff --git a/database/migrations/2025_11_15_133905_make_owner_nullable_in_key_value_pairs.php b/database/migrations/2025_11_15_133905_make_owner_nullable_in_key_value_pairs.php new file mode 100644 index 00000000..6149bf30 --- /dev/null +++ b/database/migrations/2025_11_15_133905_make_owner_nullable_in_key_value_pairs.php @@ -0,0 +1,34 @@ +string('owner_type')->nullable()->change(); + $table->unsignedBigInteger('owner_id')->nullable()->change(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('key_value_pairs', function (Blueprint $table) { + $table->string('owner_type')->nullable(false)->change(); + $table->unsignedBigInteger('owner_id')->nullable(false)->change(); + }); + } +} diff --git a/database/seeds/KeyValuePairSeeder.php b/database/seeds/KeyValuePairSeeder.php new file mode 100644 index 00000000..034721b5 --- /dev/null +++ b/database/seeds/KeyValuePairSeeder.php @@ -0,0 +1,42 @@ + KVPKey::UI_PAYMENTS_COMPLETE_CURRENCY_ORDERS_IS_LOCKED, + 'value' => 0, + ], + [ + 'key' => KVPKey::UI_PAYMENTS_OPEN_CURRENCY_ORDERS_IS_LOCKED, + 'value' => 0, + ], + [ + 'key' => KVPKey::UI_SUPPLIER_PAID_CURRENCY_ORDER_TAB_IS_LOCKED, + 'value' => 0, + ], + ]; + + foreach ($data as $item) { + KeyValuePair::updateOrCreate( + [ + 'owner_type' => null, + 'owner_id' => null, + 'key' => $item['key'], + ], + [ + 'value' => $item['value'], + ] + ); + } + } +} diff --git a/resources/assets/vue/components/bookings/elements/BillGroupComponent.vue b/resources/assets/vue/components/bookings/elements/BillGroupComponent.vue index 6bde1a01..cd5b62e4 100644 --- a/resources/assets/vue/components/bookings/elements/BillGroupComponent.vue +++ b/resources/assets/vue/components/bookings/elements/BillGroupComponent.vue @@ -51,10 +51,10 @@
-
- Cancel +
+ {{ item.ui_supplier_paid_currency_tab_is_locked ? 'Cancel Disabled' : 'Cancel' }}
- +
- +
@@ -195,7 +195,7 @@
- +
@@ -261,7 +261,7 @@
- + diff --git a/resources/assets/vue/components/bookings/elements/PaymentComponent.vue b/resources/assets/vue/components/bookings/elements/PaymentComponent.vue index 02f9a953..ea8eccb6 100644 --- a/resources/assets/vue/components/bookings/elements/PaymentComponent.vue +++ b/resources/assets/vue/components/bookings/elements/PaymentComponent.vue @@ -62,13 +62,18 @@
Complete Currency Orders + + +