From 817bc77ab23c0479c246e83831047f30f5d31f95 Mon Sep 17 00:00:00 2001 From: Syarif Ubaidillah Date: Sat, 26 Sep 2026 10:15:01 +0700 Subject: [PATCH] feat: refactor generators, enhance event handling, and improve API and product mappings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace `AcademicProductGenerator` with `AdmissionProductGenerator` for clearer domain alignment. - Update `SetUpAdmissionFeeRates` constructor and event listeners for `AdmissionFeeRateWasSetUp`. - Introduce `ProvisionProductIdentifier` action for streamlined product provisioning. - Enhance API synchronization logic with dynamic endpoint handling. - Simplify DTO structures and standardize naming (e.g., `feeRateType` → `pricingType`). - Add UI/UX updates to endpoint modal, including real-time validation and form simplifications. --- .../SetUpAdmissionFeeRates.php | 4 +- .../Generator/AdmissionProductGenerator.php} | 19 ++- .../Catalog/ProvisionProductFromFeeRate.php | 11 -- .../Catalog/ProvisionProductIdentifier.php | 25 +++ .../ProductIdentifierDTO.php | 10 +- .../DTOs/Catalog/ProvisionProductDTO.php | 13 -- .../DTOs/Invoicing/DraftInvoiceDTO.php | 14 -- .../Listeners/Catalog/ProductGenerated.php | 21 +++ app/Domains/Billing/Models/Product.php | 2 + .../Providers/BillingServiceProvider.php | 8 +- .../Contract/ProductIdentifierGenerator.php | 2 +- .../Channel/Actions/Api/AssignEndpointUrl.php | 17 +- app/Domains/Channel/Enums/Api/ApiType.php | 2 +- lang/en/domains/channel/enum.php | 1 + lang/id/domains/channel/enum.php | 1 + resources/scss/app.scss | 4 + .../⚡detail-view/detail-view.php | 2 +- .../⚡api-form-modal/api-form-modal.blade.php | 148 ++++++++++++------ .../⚡api-form-modal/api-form-modal.php | 27 ++-- 19 files changed, 197 insertions(+), 134 deletions(-) rename app/Domains/{Academic/Support/Generator/AcademicProductGenerator.php => Admission/Support/Generator/AdmissionProductGenerator.php} (72%) delete mode 100644 app/Domains/Billing/Actions/Catalog/ProvisionProductFromFeeRate.php create mode 100644 app/Domains/Billing/Actions/Catalog/ProvisionProductIdentifier.php rename app/Domains/Billing/DTOs/{Products => Catalog}/ProductIdentifierDTO.php (67%) delete mode 100644 app/Domains/Billing/DTOs/Catalog/ProvisionProductDTO.php delete mode 100644 app/Domains/Billing/DTOs/Invoicing/DraftInvoiceDTO.php create mode 100644 app/Domains/Billing/Listeners/Catalog/ProductGenerated.php diff --git a/app/Domains/Admission/Actions/AdmissionPricing/SetUpAdmissionFeeRates.php b/app/Domains/Admission/Actions/AdmissionPricing/SetUpAdmissionFeeRates.php index 2aae407..9a4c010 100644 --- a/app/Domains/Admission/Actions/AdmissionPricing/SetUpAdmissionFeeRates.php +++ b/app/Domains/Admission/Actions/AdmissionPricing/SetUpAdmissionFeeRates.php @@ -2,14 +2,14 @@ namespace App\Domains\Admission\Actions\AdmissionPricing; -use App\Domains\Academic\Support\Generator\AcademicProductGenerator; use App\Domains\Admission\DTOs\AdmissionPricing\AdmissionFeeRatesDTO; use App\Domains\Admission\Events\Pricing\AdmissionFeeRateWasSetUp; use App\Domains\Admission\Models\FeeRate; +use App\Domains\Admission\Support\Generator\AdmissionProductGenerator; class SetUpAdmissionFeeRates { - public function __construct(protected AcademicProductGenerator $generator) + public function __construct(protected AdmissionProductGenerator $generator) { } diff --git a/app/Domains/Academic/Support/Generator/AcademicProductGenerator.php b/app/Domains/Admission/Support/Generator/AdmissionProductGenerator.php similarity index 72% rename from app/Domains/Academic/Support/Generator/AcademicProductGenerator.php rename to app/Domains/Admission/Support/Generator/AdmissionProductGenerator.php index cb3f2a5..4ee959e 100644 --- a/app/Domains/Academic/Support/Generator/AcademicProductGenerator.php +++ b/app/Domains/Admission/Support/Generator/AdmissionProductGenerator.php @@ -1,12 +1,12 @@ admissionSchedule->admissionTrack->name)->initials(), - 'AC'.$rate->academicProgram->code, + 'AP'.$rate->academicProgram->code, + $rate->studyProgram->code, 'FT'.$rate->feeType->code, $rate->admissionSchedule->term->year_study ); $name = sprintf( - '%s %s %s %s', + '%s %s %s %s %s', $rate->admissionSchedule->admissionTrack->name, $rate->academicProgram->name, + $rate->studyProgram->name, $rate->feeType->name, $rate->admissionSchedule->term->year_study ); @@ -53,8 +56,8 @@ class AcademicProductGenerator implements ProductIdentifierGenerator return new ProductIdentifierDTO( code: $code, name: $name, - feeRateType: $rate->getMorphClass(), - feeRateId: $rate->id + pricingType: $rate->getMorphClass(), + pricingId: $rate->id ); } } diff --git a/app/Domains/Billing/Actions/Catalog/ProvisionProductFromFeeRate.php b/app/Domains/Billing/Actions/Catalog/ProvisionProductFromFeeRate.php deleted file mode 100644 index bfed9c9..0000000 --- a/app/Domains/Billing/Actions/Catalog/ProvisionProductFromFeeRate.php +++ /dev/null @@ -1,11 +0,0 @@ -pricingId) + ->where('pricing_type', $identifierDTO->pricingType) + ->exists(); + + if (!$isExists) { + Product::create([ + 'code' => $identifierDTO->code, + 'name' => $identifierDTO->name, + 'pricing_id' => $identifierDTO->pricingId, + 'pricing_type' => $identifierDTO->pricingType, + ]); + } + } +} diff --git a/app/Domains/Billing/DTOs/Products/ProductIdentifierDTO.php b/app/Domains/Billing/DTOs/Catalog/ProductIdentifierDTO.php similarity index 67% rename from app/Domains/Billing/DTOs/Products/ProductIdentifierDTO.php rename to app/Domains/Billing/DTOs/Catalog/ProductIdentifierDTO.php index bc6fcbd..1b9c77e 100644 --- a/app/Domains/Billing/DTOs/Products/ProductIdentifierDTO.php +++ b/app/Domains/Billing/DTOs/Catalog/ProductIdentifierDTO.php @@ -1,6 +1,6 @@ product instanceof ProductIdentifierDTO) + { + $this->provisionProductIdentifier->execute($event->product); + } + } +} diff --git a/app/Domains/Billing/Models/Product.php b/app/Domains/Billing/Models/Product.php index 8938ce9..8b8f079 100644 --- a/app/Domains/Billing/Models/Product.php +++ b/app/Domains/Billing/Models/Product.php @@ -13,6 +13,8 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; #[Fillable([ 'code', 'name', + 'pricing_type', + 'pricing_id', 'chart_of_account_id' ])] #[UseFactory(ProductFactory::class)] diff --git a/app/Domains/Billing/Providers/BillingServiceProvider.php b/app/Domains/Billing/Providers/BillingServiceProvider.php index 2966867..fe0e512 100644 --- a/app/Domains/Billing/Providers/BillingServiceProvider.php +++ b/app/Domains/Billing/Providers/BillingServiceProvider.php @@ -2,6 +2,8 @@ namespace App\Domains\Billing\Providers; +use App\Domains\Admission\Events\Pricing\AdmissionFeeRateWasSetUp; +use App\Domains\Billing\Listeners\Catalog\ProductGenerated; use App\Domains\System\Traits\Provider\RegistersDomainEvents; use Illuminate\Support\ServiceProvider; @@ -9,7 +11,11 @@ class BillingServiceProvider extends ServiceProvider { use RegistersDomainEvents; - protected array $listen = []; + protected array $listen = [ + AdmissionFeeRateWasSetUp::class => [ + ProductGenerated::class + ] + ]; public function register(): void { diff --git a/app/Domains/Billing/Support/Contract/ProductIdentifierGenerator.php b/app/Domains/Billing/Support/Contract/ProductIdentifierGenerator.php index 98ee6ce..407b08c 100644 --- a/app/Domains/Billing/Support/Contract/ProductIdentifierGenerator.php +++ b/app/Domains/Billing/Support/Contract/ProductIdentifierGenerator.php @@ -2,7 +2,7 @@ namespace App\Domains\Billing\Support\Contract; -use App\Domains\Billing\DTOs\Products\ProductIdentifierDTO; +use App\Domains\Billing\DTOs\Catalog\ProductIdentifierDTO; /** * Contract interface for generating product codes and display names. diff --git a/app/Domains/Channel/Actions/Api/AssignEndpointUrl.php b/app/Domains/Channel/Actions/Api/AssignEndpointUrl.php index 9978a98..aeb15f4 100644 --- a/app/Domains/Channel/Actions/Api/AssignEndpointUrl.php +++ b/app/Domains/Channel/Actions/Api/AssignEndpointUrl.php @@ -9,22 +9,17 @@ use Exception; class AssignEndpointUrl { /** - * @param EndpointUrlDTO[] $endpointToSave + * @param EndpointUrlDTO $endpoint * * @throws Exception */ - public function execute(array $endpointToSave): void + public function execute(EndpointUrlDTO $endpoint): void { - $validateArrayDTO = array_map(fn ($endpoint) => $endpoint instanceof EndpointUrlDTO, $endpointToSave); - if (in_array(false, $validateArrayDTO)) { - throw new Exception(sprintf('Invalid parameter. to use this action, the $endpointToSave must be an array of %s', EndpointUrlDTO::class)); - } - - $toSave = array_map(fn (EndpointUrlDTO $endpoint) => [ - 'client_id' => $endpoint->clientId, + ApiEndpoint::updateOrCreate([ 'key' => $endpoint->field, + ], [ + 'client_id' => $endpoint->clientId, 'value' => $endpoint->endpointUrl, - ], $endpointToSave); - ApiEndpoint::upsert($toSave, ['client_id', 'key']); + ]); } } diff --git a/app/Domains/Channel/Enums/Api/ApiType.php b/app/Domains/Channel/Enums/Api/ApiType.php index 805be0d..839eb2b 100644 --- a/app/Domains/Channel/Enums/Api/ApiType.php +++ b/app/Domains/Channel/Enums/Api/ApiType.php @@ -15,9 +15,9 @@ enum ApiType: string implements HasLabel case STUDY_PROGRAM = 'study_program'; case TERM = 'term'; case ADMISSION_TRACK = 'admission_track'; - case ADMISSION_SCHEDULE = 'admission_schedule'; case ACADEMIC_PROGRAM = 'academic_program'; case FEE_TYPE = 'fee_type'; + case ADMISSION_SCHEDULE = 'admission_schedule'; case FEE_RATE = 'fee_rate'; public function mapper(): DataPayloadMapper diff --git a/lang/en/domains/channel/enum.php b/lang/en/domains/channel/enum.php index df697bf..61877f9 100644 --- a/lang/en/domains/channel/enum.php +++ b/lang/en/domains/channel/enum.php @@ -12,6 +12,7 @@ return [ ApiType::ADMISSION_SCHEDULE->value => 'Admission Schedule', ApiType::ACADEMIC_PROGRAM->value => 'Academic Program', ApiType::FEE_TYPE->value => 'Fee Type', + ApiType::FEE_RATE->value => 'Fee Rate', ], 'auth_type' => [ AuthType::NONE->value => 'None', diff --git a/lang/id/domains/channel/enum.php b/lang/id/domains/channel/enum.php index e36da81..51e1105 100644 --- a/lang/id/domains/channel/enum.php +++ b/lang/id/domains/channel/enum.php @@ -12,6 +12,7 @@ return [ ApiType::ADMISSION_SCHEDULE->value => 'Jadwal Pendaftaran', ApiType::ACADEMIC_PROGRAM->value => 'Program Akademik', ApiType::FEE_TYPE->value => 'Jenis Biaya', + ApiType::FEE_RATE->value => 'Rate Biaya', ], 'auth_type' => [ AuthType::NONE->value => 'Tidak Ada', diff --git a/resources/scss/app.scss b/resources/scss/app.scss index c4a1fd5..893f453 100644 --- a/resources/scss/app.scss +++ b/resources/scss/app.scss @@ -2,6 +2,10 @@ $enable-deprecation-messages: false ); +svg { + pointer-events: none; +} + body { background-color: var(--cui-tertiary-bg); } diff --git a/resources/views/pages/admission/admission-schedule/⚡detail-view/detail-view.php b/resources/views/pages/admission/admission-schedule/⚡detail-view/detail-view.php index fe15a71..a9cb0a9 100644 --- a/resources/views/pages/admission/admission-schedule/⚡detail-view/detail-view.php +++ b/resources/views/pages/admission/admission-schedule/⚡detail-view/detail-view.php @@ -40,7 +40,7 @@ class extends Component public function admissionSchedule(): AdmissionSchedule { return AdmissionSchedule::with(['admissionTrack', 'term']) - ->where('ulid', $this->id)->first(); + ->where('ulid', $this->id)->firstOrFail(); } #[Computed] diff --git a/resources/views/pages/channel/client/⚡api-form-modal/api-form-modal.blade.php b/resources/views/pages/channel/client/⚡api-form-modal/api-form-modal.blade.php index 9f68001..e2110ce 100644 --- a/resources/views/pages/channel/client/⚡api-form-modal/api-form-modal.blade.php +++ b/resources/views/pages/channel/client/⚡api-form-modal/api-form-modal.blade.php @@ -1,25 +1,22 @@ - +
- @foreach($this->endpoints as $key => $endpoint)
-
- -
+
+ +
- - - + + + - {{ $endpoint['label'] }} - + {{ $endpoint['label'] }} +
+
- + @isset($endpoint['client_id']) + + @endif
-
-
-
- -
+ +
+ +
+ + {{ __('domains/channel/field.client.name') }} + + + {{ $this->clients->firstWhere('ulid', $endpoint['client_id'] ?? null)?->name ?? '-' }} + +
-
- -
+ +
+ + {{ __('domains/channel/field.client.endpoint_url') }} + + + {{ $endpoint['value'] ?? '-' }} +
@@ -81,9 +82,60 @@ @script @endscript diff --git a/resources/views/pages/channel/client/⚡api-form-modal/api-form-modal.php b/resources/views/pages/channel/client/⚡api-form-modal/api-form-modal.php index e933548..159802a 100644 --- a/resources/views/pages/channel/client/⚡api-form-modal/api-form-modal.php +++ b/resources/views/pages/channel/client/⚡api-form-modal/api-form-modal.php @@ -26,8 +26,9 @@ new class extends Component public string $mode = 'view'; #[Validate([ - 'field.*.client_id' => 'required', - 'field.*.value' => 'required', + 'field.type' => 'required', + 'field.client_id' => 'required', + 'field.value' => 'required', ])] public array $field = []; @@ -40,7 +41,7 @@ new class extends Component public function hide(): void { - $this->reset('id', 'mode'); + $this->reset('mode'); } public function editMode(): void @@ -70,10 +71,6 @@ new class extends Component $endpoints[$item->value] = [ 'label' => $item->label(), 'value' => $value, - ]; - $this->field[$item->value] = [ - 'type' => $item->value, - 'value' => $value, 'client_id' => $client_id, ]; } @@ -91,17 +88,11 @@ new class extends Component { $this->validate(); - $clients = $this->clients->pluck('id', 'ulid'); - $endpointToSave = []; - foreach ($this->field as $key => $item) { - $endpointToSave[$key] = new EndpointUrlDTO( - clientId: $clients[$item['client_id']], - endpointUrl: $item['value'], - field: ApiType::tryFrom($item['type']) - ); - } - - $assign->execute($endpointToSave); + $assign->execute(new EndpointUrlDTO( + clientId: $this->clients->where('ulid', $this->field['client_id'])->first()->id, + endpointUrl: $this->field['value'], + field: ApiType::tryFrom($this->field['type']) + )); $this->mode = 'view';