diff --git a/app/Domains/Academic/Queries/Selection/SelectFacultyQuery.php b/app/Domains/Academic/Queries/Selection/SelectFacultyQuery.php new file mode 100644 index 0000000..af43e26 --- /dev/null +++ b/app/Domains/Academic/Queries/Selection/SelectFacultyQuery.php @@ -0,0 +1,15 @@ + Faculty::all(['id', 'name']) + ->pluck('name', 'id')); + } +} diff --git a/app/Domains/Academic/Queries/Selection/SelectStudyProgramQuery.php b/app/Domains/Academic/Queries/Selection/SelectStudyProgramQuery.php new file mode 100644 index 0000000..7ad8986 --- /dev/null +++ b/app/Domains/Academic/Queries/Selection/SelectStudyProgramQuery.php @@ -0,0 +1,21 @@ + StudyProgram::select('id', 'name') + ->where('faculty_id', $context['faculty']) + ->get() + ->pluck('name', 'id')); + } +} diff --git a/app/Domains/Academic/Queries/Selection/SelectTermQuery.php b/app/Domains/Academic/Queries/Selection/SelectTermQuery.php new file mode 100644 index 0000000..10b3a5a --- /dev/null +++ b/app/Domains/Academic/Queries/Selection/SelectTermQuery.php @@ -0,0 +1,15 @@ + Term::all(['id', 'name']) + ->pluck('name', 'id')); + } +} diff --git a/app/Domains/Admission/Actions/IntakeScheduling/DefineAdmissionSchedule.php b/app/Domains/Admission/Actions/IntakeScheduling/DefineAdmissionSchedule.php index f1a5578..75c2434 100644 --- a/app/Domains/Admission/Actions/IntakeScheduling/DefineAdmissionSchedule.php +++ b/app/Domains/Admission/Actions/IntakeScheduling/DefineAdmissionSchedule.php @@ -4,6 +4,7 @@ namespace App\Domains\Admission\Actions\IntakeScheduling; use App\Domains\Admission\DTOs\IntakeScheduling\DefineAdmissionScheduleDTO; use App\Domains\Admission\Enums\AdmissionStatus; +use App\Domains\Admission\Events\IntakeScheduling\AdmissionScheduleDefined; use App\Domains\Admission\Models\AdmissionSchedule; class DefineAdmissionSchedule diff --git a/app/Domains/Admission/DTOs/IntakeScheduling/DefineAdmissionScheduleDTO.php b/app/Domains/Admission/DTOs/IntakeScheduling/DefineAdmissionScheduleDTO.php index 30d6450..04400d4 100644 --- a/app/Domains/Admission/DTOs/IntakeScheduling/DefineAdmissionScheduleDTO.php +++ b/app/Domains/Admission/DTOs/IntakeScheduling/DefineAdmissionScheduleDTO.php @@ -3,6 +3,7 @@ namespace App\Domains\Admission\DTOs\IntakeScheduling; use Carbon\CarbonImmutable; +use Illuminate\Contracts\Support\Arrayable; readonly class DefineAdmissionScheduleDTO { diff --git a/app/Domains/Admission/Queries/Selection/SelectFeeTypeQuery.php b/app/Domains/Admission/Queries/Selection/SelectFeeTypeQuery.php new file mode 100644 index 0000000..f1a6914 --- /dev/null +++ b/app/Domains/Admission/Queries/Selection/SelectFeeTypeQuery.php @@ -0,0 +1,13 @@ +update([ 'code' => $dto->code, - 'name' => $dto->name, 'classification' => $dto->classification, 'parent_id' => $dto->parentId, ]); diff --git a/app/Domains/Finance/Actions/LedgerConfiguration/SuspendAccountPostings.php b/app/Domains/Finance/Actions/LedgerConfiguration/SuspendAccountPostings.php index 3dde2dd..1886676 100644 --- a/app/Domains/Finance/Actions/LedgerConfiguration/SuspendAccountPostings.php +++ b/app/Domains/Finance/Actions/LedgerConfiguration/SuspendAccountPostings.php @@ -10,7 +10,7 @@ class SuspendAccountPostings public function execute(ChartOfAccount $coa): void { $coa->update([ - 'status' => LifecycleStatus::ARCHIVED, + 'status' => LifecycleStatus::INACTIVE, ]); } } diff --git a/app/Domains/Finance/Actions/PaymentProcessing/InitializeInvoice.php b/app/Domains/Finance/Actions/PaymentProcessing/InitializeInvoice.php index 97f0aff..6c18da3 100644 --- a/app/Domains/Finance/Actions/PaymentProcessing/InitializeInvoice.php +++ b/app/Domains/Finance/Actions/PaymentProcessing/InitializeInvoice.php @@ -2,10 +2,22 @@ namespace App\Domains\Finance\Actions\PaymentProcessing; +use App\Domains\Finance\DTOs\PaymentProcessing\IssueInvoiceDTO; +use App\Domains\Finance\Models\Invoice; + class InitializeInvoice { - public function execute(): void + public function execute(IssueInvoiceDTO $dto): void { - // + Invoice::create([ + 'name' => $dto->name, + 'detail' => $dto->detail->toArray(), + 'client_ref_id' => $dto->clientRefId, + 'type' => $dto->type, + 'amount' => $dto->amount, + 'status' => $dto->status, + 'client_id' => $dto->clientId, + 'chart_of_account_id' => $dto->coaId, + ]); } } diff --git a/app/Domains/Finance/Actions/ProductConfiguration/CreateProductMapping.php b/app/Domains/Finance/Actions/ProductConfiguration/CreateProductMapping.php deleted file mode 100644 index 0f1ddd1..0000000 --- a/app/Domains/Finance/Actions/ProductConfiguration/CreateProductMapping.php +++ /dev/null @@ -1,11 +0,0 @@ -update(['chart_of_account_id' => $coaId]); + } +} diff --git a/app/Domains/Finance/Actions/ProductConfiguration/RegisterProductMapping.php b/app/Domains/Finance/Actions/ProductConfiguration/RegisterProductMapping.php index 31d7b84..ad08969 100644 --- a/app/Domains/Finance/Actions/ProductConfiguration/RegisterProductMapping.php +++ b/app/Domains/Finance/Actions/ProductConfiguration/RegisterProductMapping.php @@ -2,10 +2,17 @@ namespace App\Domains\Finance\Actions\ProductConfiguration; +use App\Domains\Finance\DTOs\ProductConfiguration\RegisterProductMappingDTO; +use App\Domains\Finance\Models\ProductMapping; + class RegisterProductMapping { - public function execute(): void + public function execute(RegisterProductMappingDTO $dto): void { - // + ProductMapping::create([ + 'name' => $dto->name, + 'code' => $dto->code, + 'chart_of_account_id' => $dto->coaId + ]); } } diff --git a/app/Domains/Finance/DTOs/LedgerConfiguration/AdjustAccountClassificationDTO.php b/app/Domains/Finance/DTOs/LedgerConfiguration/AdjustAccountClassificationDTO.php index 228867c..6c23b30 100644 --- a/app/Domains/Finance/DTOs/LedgerConfiguration/AdjustAccountClassificationDTO.php +++ b/app/Domains/Finance/DTOs/LedgerConfiguration/AdjustAccountClassificationDTO.php @@ -8,7 +8,6 @@ readonly class AdjustAccountClassificationDTO { public function __construct( public string $code, - public string $name, public AccountClassification $classification, public ?int $parentId, ) {} diff --git a/app/Domains/Finance/DTOs/PaymentProcessing/Detail/InvoiceDetailDTO.php b/app/Domains/Finance/DTOs/PaymentProcessing/Detail/InvoiceDetailDTO.php new file mode 100644 index 0000000..96a5598 --- /dev/null +++ b/app/Domains/Finance/DTOs/PaymentProcessing/Detail/InvoiceDetailDTO.php @@ -0,0 +1,10 @@ + $this->studentId, + 'student_name' => $this->studentName, + 'faculty' => $this->faculty, + 'study_program' => $this->studyProgram, + 'term' => $this->term, + ]; + } + + public static function fromArray(array $data): self + { + return new self( + studentId: $data['student_id'], + studentName: $data['student_name'], + faculty: $data['faculty'], + studyProgram: $data['study_program'], + term: $data['term'], + ); + } +} diff --git a/app/Domains/Finance/DTOs/PaymentProcessing/IssueInvoiceDTO.php b/app/Domains/Finance/DTOs/PaymentProcessing/IssueInvoiceDTO.php index b3c7fab..d0db157 100644 --- a/app/Domains/Finance/DTOs/PaymentProcessing/IssueInvoiceDTO.php +++ b/app/Domains/Finance/DTOs/PaymentProcessing/IssueInvoiceDTO.php @@ -2,14 +2,20 @@ namespace App\Domains\Finance\DTOs\PaymentProcessing; +use App\Domains\Finance\DTOs\PaymentProcessing\Detail\InvoiceDetailDTO; +use App\Domains\Finance\Enums\InvoiceStatus; +use App\Domains\Finance\Enums\InvoiceType; + readonly class IssueInvoiceDTO { public function __construct( - public string $name, - public string $clientRefId, - public string $detail, - public string $type, - public string $status, - public string $clientId + public string $name, + public string $clientRefId, + public InvoiceDetailDTO $detail, + public InvoiceType $type, + public InvoiceStatus $status, + public int $amount, + public string $coaId, + public string $clientId ) {} } diff --git a/app/Domains/Finance/DTOs/ProductConfiguration/ModifyProductMappingDTO.php b/app/Domains/Finance/DTOs/ProductConfiguration/ModifyProductMappingDTO.php new file mode 100644 index 0000000..407c7fe --- /dev/null +++ b/app/Domains/Finance/DTOs/ProductConfiguration/ModifyProductMappingDTO.php @@ -0,0 +1,10 @@ + StudentInvoiceSchema::make(), + default => InvoiceSchema::make(true), + }; + } + + public function dtoTransform(array $data): InvoiceDetailDTO + { + return match ($this) { + self::STUDENT_INVOICE => StudentInvoiceDetailDTO::fromArray($data), + }; + } } diff --git a/app/Domains/Finance/Schemas/Invoice/StudentInvoiceSchema.php b/app/Domains/Finance/Schemas/Invoice/StudentInvoiceSchema.php new file mode 100644 index 0000000..bb7e9e1 --- /dev/null +++ b/app/Domains/Finance/Schemas/Invoice/StudentInvoiceSchema.php @@ -0,0 +1,66 @@ +addSchema( + key: 'student_id', + label: __('domains/finance/field.invoice.detail.student_id'), + schema: SettingSchema::make(InputType::TEXTLINE) + ->rules(['required']) + )->addSchema( + key: 'student_name', + label: __('domains/finance/field.invoice.detail.student_name'), + schema: SettingSchema::make(InputType::TEXTLINE) + ->rules(['required', 'string']) + )->addSchema( + key: 'faculty', + label: __('domains/finance/field.invoice.detail.faculty'), + schema: SettingSchema::make(InputType::SELECT) + ->rules(['required', 'string']) + ->select2() + ->live() + ->options(fn() => SelectFacultyQuery::fetch()) + )->addSchema( + key: 'study_program', + label: __('domains/finance/field.invoice.detail.study_program'), + schema: SettingSchema::make(InputType::SELECT) + ->rules(['required', 'string']) + ->select2() + ->dependsOn('faculty') + ->setContextKey('faculty') + ->options(fn ($context) => SelectStudyProgramQuery::fetch($context)) + )->addSchema( + key: 'term', + label: __('domains/finance/field.invoice.detail.term'), + schema: SettingSchema::make(InputType::SELECT) + ->rules(['required']) + ->options(fn() => SelectTermQuery::fetch()) + ->select2() + )->addSchema( + key: 'semester', + label: __('domains/finance/field.invoice.detail.semester'), + schema: SettingSchema::make(InputType::SELECT) + ->rules(['required']) + ->options(range(1, 13)) + ->select2() + )->addSchema( + key: 'fee_type', + label: __('domains/finance/field.invoice.detail.fee_type'), + schema: SettingSchema::make(InputType::SELECT) + ->rules(['required']) + ->select2() + ); + } +} diff --git a/app/Domains/Finance/Support/Schema/InvoiceSchema.php b/app/Domains/Finance/Support/Schema/InvoiceSchema.php new file mode 100644 index 0000000..c12328c --- /dev/null +++ b/app/Domains/Finance/Support/Schema/InvoiceSchema.php @@ -0,0 +1,67 @@ +attributes(['label' => $label]); + $uiSchema = array_merge($this->uiSchema, [[ + 'key' => $key, + 'schema' => $schema + ]]); + return new self($uiSchema, $this->coaVisibility); + } + + public function withContext(array $context): static + { + $this->context = $context; + return $this; + } + + public function withFormString(string $form): static + { + $this->form = $form; + return $this; + } + + public function getSchema(): array + { + $fields = []; + foreach($this->uiSchema as $field) { + /** @var SettingSchema $schema */ + $schema = $field['schema']; + $attributes['options'] =$schema->type->isSelect() ? $schema->resolveOptions($this->context) : ''; + + $fields[$field['key']] = (object) [ + 'attributes' => array_merge($schema->attributes, $attributes), + 'schema' => $schema, + ]; + } + + return $fields; + } + + public function isCoaVisible(): bool + { + return $this->coaVisibility; + } +} diff --git a/app/Domains/System/Enums/LifecycleStatus.php b/app/Domains/System/Enums/LifecycleStatus.php index b9df5a9..7d2984e 100644 --- a/app/Domains/System/Enums/LifecycleStatus.php +++ b/app/Domains/System/Enums/LifecycleStatus.php @@ -5,12 +5,21 @@ namespace App\Domains\System\Enums; use App\Domains\System\Traits\Enum\HasPredicateMethod; use App\UI\Enums\Concerns\InteractsWithLabels; use App\UI\Enums\Contracts\HasLabel; +use App\UI\Enums\Contracts\HasUiBadge; -enum LifecycleStatus: string implements HasLabel +enum LifecycleStatus: string implements HasLabel, HasUiBadge { use HasPredicateMethod; use InteractsWithLabels; case ACTIVE = 'active'; case INACTIVE = 'inactive'; + + public function variant(): string + { + return match ($this) { + self::ACTIVE => 'success', + self::INACTIVE => 'danger', + }; + } } diff --git a/app/Domains/System/Traits/Enum/HasPredicateMethod.php b/app/Domains/System/Traits/Enum/HasPredicateMethod.php index b8a8f26..72ca508 100644 --- a/app/Domains/System/Traits/Enum/HasPredicateMethod.php +++ b/app/Domains/System/Traits/Enum/HasPredicateMethod.php @@ -15,9 +15,9 @@ trait HasPredicateMethod { if (str_starts_with($method, 'is')) { $expectedCase = Str::substr($method, 2); - $expectedCase = Str::upper($expectedCase); + $expectedCase = Str::pascal($expectedCase); foreach ($this::cases() as $case) { - if ($case->name === $expectedCase || $case->value === Str::kebab($expectedCase)) { + if ($case->name === $expectedCase || Str::pascal($case->value) === $expectedCase) { return $this === $case; } } diff --git a/app/Http/DataTables/Finance/ChartOfAccountDataTable.php b/app/Http/DataTables/Finance/ChartOfAccountDataTable.php index 15e1c8b..9898a24 100644 --- a/app/Http/DataTables/Finance/ChartOfAccountDataTable.php +++ b/app/Http/DataTables/Finance/ChartOfAccountDataTable.php @@ -20,6 +20,11 @@ class ChartOfAccountDataTable extends DataTable public function dataTable(QueryBuilder $query): EloquentDataTable { return (new EloquentDataTable($query)) + ->editColumn('status', fn (ChartOfAccount $coa) => view('components.badge', [ + 'label' => $coa->status->label(), + 'variant' => $coa->status->variant(), + ])) + ->editColumn('classification', fn (ChartOfAccount $coa) => $coa->classification->label()) ->addColumn( 'action', fn ($coa) => view('components.datatables.action-button', [ diff --git a/app/Http/DataTables/Finance/ProductMappingDataTable.php b/app/Http/DataTables/Finance/ProductMappingDataTable.php index 2f10e96..035d0d0 100644 --- a/app/Http/DataTables/Finance/ProductMappingDataTable.php +++ b/app/Http/DataTables/Finance/ProductMappingDataTable.php @@ -25,7 +25,7 @@ class ProductMappingDataTable extends DataTable fn ($productMapping) => view('components.datatables.action-button', [ 'log' => true, 'edit' => [ - 'modal' => 'productmapping-form-modal', + 'modal' => 'product-mapping-form-modal', 'permission' => auth()->user()->can('update', $productMapping), ], 'delete' => [ @@ -91,7 +91,7 @@ class ProductMappingDataTable extends DataTable ]) ->buttons([ Button::make('add') - ->action('$("#productmapping-form-modal").modal("show");') + ->action('$("#product-mapping-form-modal").modal("show");') ->text(svg('tabler-plus', ['width' => 16, 'height' => 16])->toHtml()) ->addClass('btn-sm'), Button::make('reload') diff --git a/app/Livewire/Forms/Finance/ChartOfAccountForm.php b/app/Livewire/Forms/Finance/ChartOfAccountForm.php new file mode 100644 index 0000000..8b7bec1 --- /dev/null +++ b/app/Livewire/Forms/Finance/ChartOfAccountForm.php @@ -0,0 +1,26 @@ +type->schema()->getSchema(); + + $this->resetValidation(); + foreach ($formSchema as $key => $schema) { + /** @var SettingSchema $schema */ + $schema = $schema->schema; + if ($schema->depends == $prop) { + $this->detail[$key] = ''; + } + } + $this->validateOnly($prop); + } + + public function updatedType(): void + { + $this->reset('detail'); + + $schema = $this->type?->schema()?->getSchema() ?? []; + foreach ($schema as $key => $value) { + $this->detail[$key] = ''; + } + + $this->resetValidation(); + } + + public function rules(): array + { + $rules = []; + $detailSchema = $this->type + ?->schema() + ->getSchema() ?? []; + foreach ($detailSchema as $key => $schema) { + $rules = array_merge($rules, [ + 'detail.'.$key => $schema->schema->rules, + ]); + } + + return $rules; + } + + public function validationAttributes(): array + { + $attributes = []; + $detailSchema = $this->type + ?->schema() + ->getSchema() ?? []; + foreach ($detailSchema as $key => $schema) { + $attributes = array_merge($attributes, [ + 'detail.'.$key => $schema->schema->attributes['label'], + ]); + } + + return $attributes; + } + + public function toDto(): IssueInvoiceDTO + { + + return new IssueInvoiceDTO( + name: $this->name, + clientRefId: $this->client_id, + detail: $this->type->dtoTransform($this->detail), + type: $this->type, + status: InvoiceStatus::PENDING, + clientId: $this->client_id + ); + } +} diff --git a/app/Livewire/Forms/Finance/PaymentForm.php b/app/Livewire/Forms/Finance/PaymentForm.php new file mode 100644 index 0000000..2d0461f --- /dev/null +++ b/app/Livewire/Forms/Finance/PaymentForm.php @@ -0,0 +1,11 @@ +default = $default; + + return $this; + } + + public function rules(array $rules): static + { + $this->rules = $rules; + + return $this; + } + + public function options(array|Closure $options): static { - return new self($type, $rules); + $this->options = $options; + + return $this; } - public function default(mixed $default): self + public function attributes(array $attributes): static { - return new self($this->type, $this->rules, $default, $this->options, $this->attributes); + $this->attributes = array_merge($this->attributes, $attributes); + + return $this; } - public function rules(array $rules): self + public function setContextKey(array|string $contextKey): static { - return new self($this->type, $rules, $this->default, $this->options, $this->attributes); + $this->contextKey = array_merge($this->contextKey, (array) $contextKey); + return $this; + } + + public function withContext(array $context): static + { + $this->context = array_merge($this->context, $context); + + return $this; } - public function options(array $options): self + public function select2(array $config = []): static { - // If the array is simple (non-associative), we should try to localize the values if they are Enums - // But for now, we just pass it as is, or expect the caller to handle it. - // The common case is ['value' => 'Label'] - return new self($this->type, $this->rules, $this->default, $options, $this->attributes); + $this->attributes(['x-select2' => json_encode($config)]); + return $this; } - public function attributes(array $attributes): self + public function live(): static { - return new self($this->type, $this->rules, $this->default, $this->options, $attributes); + $this->live = true; + return $this; + } + + public function dependsOn(string $depends): static + { + $this->depends = $depends; + return $this; + } + + public function isLive(): bool + { + return $this->live; + } + + public function resolveOptions(array $extraContext = []): array|Collection + { + if ($this->options instanceof Closure) { + $mergedContext = array_merge($this->context, $extraContext); + + if(!empty($this->contextKey)) { + $temporaryContext = []; + foreach($this->contextKey as $key) { + $temporaryContext[$key] = !empty($mergedContext[$key]) ? $mergedContext[$key] : null; + } + $mergedContext = $temporaryContext; + } + + return ($this->options)($mergedContext); + } + + return $this->options; + } + + public function getAttributes(array $extraContext = []): array + { + $attributes = $this->attributes; + + if ($this->type->isSelect()) { + $attributes['options'] = $this->resolveOptions($extraContext); + } + + return $attributes; } } diff --git a/composer.lock b/composer.lock index a92f65f..31b6e41 100644 --- a/composer.lock +++ b/composer.lock @@ -133,16 +133,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.390.1", + "version": "3.390.4", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "c75d5f489113e3c140d5a37602375e4359b66a75" + "reference": "51115bd27065e978c2a4cf297f8280d8c39e83da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/c75d5f489113e3c140d5a37602375e4359b66a75", - "reference": "c75d5f489113e3c140d5a37602375e4359b66a75", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/51115bd27065e978c2a4cf297f8280d8c39e83da", + "reference": "51115bd27065e978c2a4cf297f8280d8c39e83da", "shasum": "" }, "require": { @@ -224,9 +224,9 @@ "support": { "forum": "https://github.com/aws/aws-sdk-php/discussions", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.390.1" + "source": "https://github.com/aws/aws-sdk-php/tree/3.390.4" }, - "time": "2026-07-31T02:53:14+00:00" + "time": "2026-08-04T18:52:58+00:00" }, { "name": "barryvdh/laravel-dompdf", @@ -1926,16 +1926,16 @@ }, { "name": "laravel/framework", - "version": "v13.23.0", + "version": "v13.24.0", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "92a707229148e57f08a249211c8a5a194159c619" + "reference": "6d481710375d2aa67656922ef760cdd2b18bcfe0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/92a707229148e57f08a249211c8a5a194159c619", - "reference": "92a707229148e57f08a249211c8a5a194159c619", + "url": "https://api.github.com/repos/laravel/framework/zipball/6d481710375d2aa67656922ef760cdd2b18bcfe0", + "reference": "6d481710375d2aa67656922ef760cdd2b18bcfe0", "shasum": "" }, "require": { @@ -1955,7 +1955,7 @@ "guzzlehttp/guzzle": "^7.8.2", "guzzlehttp/promises": "^2.0.3", "guzzlehttp/uri-template": "^1.0", - "laravel/prompts": "^0.3.0", + "laravel/prompts": "^0.3.11", "laravel/serializable-closure": "^2.0.10", "league/commonmark": "^2.8.1", "league/flysystem": "^3.25.1", @@ -2149,20 +2149,20 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2026-07-27T14:48:58+00:00" + "time": "2026-08-04T15:54:59+00:00" }, { "name": "laravel/prompts", - "version": "v0.3.21", + "version": "v0.3.22", "source": { "type": "git", "url": "https://github.com/laravel/prompts.git", - "reference": "7753c65c281c2550c7c183f14e18062073b7d821" + "reference": "02b89b39e8972a998db4d5d4ad4719239dd4aee4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/prompts/zipball/7753c65c281c2550c7c183f14e18062073b7d821", - "reference": "7753c65c281c2550c7c183f14e18062073b7d821", + "url": "https://api.github.com/repos/laravel/prompts/zipball/02b89b39e8972a998db4d5d4ad4719239dd4aee4", + "reference": "02b89b39e8972a998db4d5d4ad4719239dd4aee4", "shasum": "" }, "require": { @@ -2206,9 +2206,9 @@ "description": "Add beautiful and user-friendly forms to your command-line applications.", "support": { "issues": "https://github.com/laravel/prompts/issues", - "source": "https://github.com/laravel/prompts/tree/v0.3.21" + "source": "https://github.com/laravel/prompts/tree/v0.3.22" }, - "time": "2026-06-26T00:11:25+00:00" + "time": "2026-08-04T14:50:50+00:00" }, { "name": "laravel/sanctum", @@ -2405,16 +2405,16 @@ }, { "name": "league/commonmark", - "version": "2.8.3", + "version": "2.9.0", "source": { "type": "git", "url": "https://github.com/thephpleague/commonmark.git", - "reference": "1902f60f984235023acbe03db6ad614a37b3c3e7" + "reference": "5703d83ba3da3b2e356a5fedc848ed6d8ffb6529" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/1902f60f984235023acbe03db6ad614a37b3c3e7", - "reference": "1902f60f984235023acbe03db6ad614a37b3c3e7", + "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/5703d83ba3da3b2e356a5fedc848ed6d8ffb6529", + "reference": "5703d83ba3da3b2e356a5fedc848ed6d8ffb6529", "shasum": "" }, "require": { @@ -2451,7 +2451,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.9-dev" + "dev-main": "2.10-dev" } }, "autoload": { @@ -2508,7 +2508,7 @@ "type": "tidelift" } ], - "time": "2026-07-12T15:29:16+00:00" + "time": "2026-08-03T13:42:31+00:00" }, { "name": "league/config", @@ -3034,16 +3034,16 @@ }, { "name": "livewire/livewire", - "version": "v4.3.4", + "version": "v4.3.5", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "e6c8d631e9687fbdd5c2bb7be9d7a5d699cce0e2" + "reference": "7ef4b2a876c71744e86463079dd506b26eeab624" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/e6c8d631e9687fbdd5c2bb7be9d7a5d699cce0e2", - "reference": "e6c8d631e9687fbdd5c2bb7be9d7a5d699cce0e2", + "url": "https://api.github.com/repos/livewire/livewire/zipball/7ef4b2a876c71744e86463079dd506b26eeab624", + "reference": "7ef4b2a876c71744e86463079dd506b26eeab624", "shasum": "" }, "require": { @@ -3098,7 +3098,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v4.3.4" + "source": "https://github.com/livewire/livewire/tree/v4.3.5" }, "funding": [ { @@ -3106,7 +3106,7 @@ "type": "github" } ], - "time": "2026-07-31T00:19:18+00:00" + "time": "2026-08-03T04:09:44+00:00" }, { "name": "maatwebsite/excel", @@ -4027,16 +4027,16 @@ }, { "name": "openspout/openspout", - "version": "v5.8.0", + "version": "v5.10.1", "source": { "type": "git", "url": "https://github.com/openspout/openspout.git", - "reference": "1e1aad228e3e289c7e11d97b07496f2569121afc" + "reference": "5531f2e5bf1f6cfd6e5caa781a79a9282562c783" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/openspout/openspout/zipball/1e1aad228e3e289c7e11d97b07496f2569121afc", - "reference": "1e1aad228e3e289c7e11d97b07496f2569121afc", + "url": "https://api.github.com/repos/openspout/openspout/zipball/5531f2e5bf1f6cfd6e5caa781a79a9282562c783", + "reference": "5531f2e5bf1f6cfd6e5caa781a79a9282562c783", "shasum": "" }, "require": { @@ -4050,13 +4050,13 @@ "require-dev": { "ext-fileinfo": "*", "ext-zlib": "*", - "friendsofphp/php-cs-fixer": "^3.95.14", + "friendsofphp/php-cs-fixer": "^3.95.18", "infection/infection": "^0.34", "phpbench/phpbench": "^1.7.0", - "phpstan/phpstan": "^2.2.5", + "phpstan/phpstan": "^2.2.7", "phpstan/phpstan-phpunit": "^2.0.18", - "phpstan/phpstan-strict-rules": "^2.0.11", - "phpunit/phpunit": "^13.2.4" + "phpstan/phpstan-strict-rules": "^2.0.12", + "phpunit/phpunit": "^13.2.6" }, "suggest": { "ext-iconv": "To handle non UTF-8 CSV files (if \"php-mbstring\" is not already installed or is too limited)", @@ -4104,7 +4104,7 @@ ], "support": { "issues": "https://github.com/openspout/openspout/issues", - "source": "https://github.com/openspout/openspout/tree/v5.8.0" + "source": "https://github.com/openspout/openspout/tree/v5.10.1" }, "funding": [ { @@ -4116,7 +4116,7 @@ "type": "github" } ], - "time": "2026-07-15T07:34:50+00:00" + "time": "2026-08-03T12:12:33+00:00" }, { "name": "owen-it/laravel-auditing", @@ -9051,16 +9051,16 @@ }, { "name": "yajra/laravel-datatables-oracle", - "version": "v13.1.5", + "version": "v13.1.6", "source": { "type": "git", "url": "https://github.com/yajra/laravel-datatables.git", - "reference": "299c07a7dae380e565bd328104965b6e02286c2f" + "reference": "662cbca5d7c7cecd1e21b123eafda0485f0f033d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yajra/laravel-datatables/zipball/299c07a7dae380e565bd328104965b6e02286c2f", - "reference": "299c07a7dae380e565bd328104965b6e02286c2f", + "url": "https://api.github.com/repos/yajra/laravel-datatables/zipball/662cbca5d7c7cecd1e21b123eafda0485f0f033d", + "reference": "662cbca5d7c7cecd1e21b123eafda0485f0f033d", "shasum": "" }, "require": { @@ -9128,7 +9128,7 @@ ], "support": { "issues": "https://github.com/yajra/laravel-datatables/issues", - "source": "https://github.com/yajra/laravel-datatables/tree/v13.1.5" + "source": "https://github.com/yajra/laravel-datatables/tree/v13.1.6" }, "funding": [ { @@ -9136,22 +9136,22 @@ "type": "github" } ], - "time": "2026-07-03T01:53:46+00:00" + "time": "2026-07-31T03:18:01+00:00" } ], "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v4.4.0", + "version": "v4.4.1", "source": { "type": "git", "url": "https://github.com/fruitcake/laravel-debugbar.git", - "reference": "80ef956bda9e1a5824037d6f2cd06e73092e5634" + "reference": "389157fb616e5c5d19d16a88fd9bfcf3e3248e9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/80ef956bda9e1a5824037d6f2cd06e73092e5634", - "reference": "80ef956bda9e1a5824037d6f2cd06e73092e5634", + "url": "https://api.github.com/repos/fruitcake/laravel-debugbar/zipball/389157fb616e5c5d19d16a88fd9bfcf3e3248e9b", + "reference": "389157fb616e5c5d19d16a88fd9bfcf3e3248e9b", "shasum": "" }, "require": { @@ -9226,7 +9226,7 @@ ], "support": { "issues": "https://github.com/fruitcake/laravel-debugbar/issues", - "source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.4.0" + "source": "https://github.com/fruitcake/laravel-debugbar/tree/v4.4.1" }, "funding": [ { @@ -9238,7 +9238,7 @@ "type": "github" } ], - "time": "2026-07-04T08:30:57+00:00" + "time": "2026-08-03T06:23:16+00:00" }, { "name": "barryvdh/laravel-ide-helper", @@ -9971,16 +9971,16 @@ }, { "name": "laravel/boost", - "version": "v2.4.13", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/laravel/boost.git", - "reference": "f55e08f5afa89ac72f23f574175005b67878f466" + "reference": "f6b054dcbc0aacf1d187128edf7d917c6d99792a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/boost/zipball/f55e08f5afa89ac72f23f574175005b67878f466", - "reference": "f55e08f5afa89ac72f23f574175005b67878f466", + "url": "https://api.github.com/repos/laravel/boost/zipball/f6b054dcbc0aacf1d187128edf7d917c6d99792a", + "reference": "f6b054dcbc0aacf1d187128edf7d917c6d99792a", "shasum": "" }, "require": { @@ -9991,7 +9991,7 @@ "illuminate/support": "^11.45.3|^12.41.1|^13.0", "laravel/mcp": "^0.7.1|^0.8.0|^0.9.0", "laravel/prompts": "^0.3.10", - "laravel/roster": "^0.5.0", + "laravel/roster": "^1.0.0", "php": "^8.2" }, "require-dev": { @@ -10033,7 +10033,7 @@ "issues": "https://github.com/laravel/boost/issues", "source": "https://github.com/laravel/boost" }, - "time": "2026-07-17T14:28:57+00:00" + "time": "2026-08-04T21:10:39+00:00" }, { "name": "laravel/mcp", @@ -10191,16 +10191,16 @@ }, { "name": "laravel/pint", - "version": "v1.30.0", + "version": "v1.30.3", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "72a0540d1aa10b6c146bda2a22f3ae003123c0ea" + "reference": "19ca6de4ce07869f61f09863e37e81562ebc0a9b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/72a0540d1aa10b6c146bda2a22f3ae003123c0ea", - "reference": "72a0540d1aa10b6c146bda2a22f3ae003123c0ea", + "url": "https://api.github.com/repos/laravel/pint/zipball/19ca6de4ce07869f61f09863e37e81562ebc0a9b", + "reference": "19ca6de4ce07869f61f09863e37e81562ebc0a9b", "shasum": "" }, "require": { @@ -10212,7 +10212,7 @@ }, "require-dev": { "composer/semver": "^3.4.4", - "friendsofphp/php-cs-fixer": "^3.95.17", + "friendsofphp/php-cs-fixer": "^3.95.18", "illuminate/view": "^12.64.0", "larastan/larastan": "^3.10.0", "laravel-zero/framework": "^12.1.0", @@ -10257,36 +10257,37 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2026-07-28T20:48:56+00:00" + "time": "2026-08-02T01:28:21+00:00" }, { "name": "laravel/roster", - "version": "v0.5.1", + "version": "v1.0.0", "source": { "type": "git", "url": "https://github.com/laravel/roster.git", - "reference": "5089de7615f72f78e831590ff9d0435fed0102bb" + "reference": "89e518bd88ae98ff50f6082f6b517c8d8e8245fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/roster/zipball/5089de7615f72f78e831590ff9d0435fed0102bb", - "reference": "5089de7615f72f78e831590ff9d0435fed0102bb", + "url": "https://api.github.com/repos/laravel/roster/zipball/89e518bd88ae98ff50f6082f6b517c8d8e8245fa", + "reference": "89e518bd88ae98ff50f6082f6b517c8d8e8245fa", "shasum": "" }, "require": { + "composer/semver": "^3.0", "illuminate/console": "^11.0|^12.0|^13.0", "illuminate/contracts": "^11.0|^12.0|^13.0", - "illuminate/routing": "^11.0|^12.0|^13.0", "illuminate/support": "^11.0|^12.0|^13.0", "php": "^8.2", "symfony/yaml": "^7.2|^8.0" }, "require-dev": { - "laravel/pint": "^1.14", + "laravel/pint": "^1.29", "mockery/mockery": "^1.6", "orchestra/testbench": "^9.0|^10.0|^11.0", "pestphp/pest": "^3.0|^4.1", - "phpstan/phpstan": "^2.0" + "phpstan/phpstan": "^2.0", + "rector/rector": "^2.0" }, "type": "library", "extra": { @@ -10318,7 +10319,7 @@ "issues": "https://github.com/laravel/roster/issues", "source": "https://github.com/laravel/roster" }, - "time": "2026-03-05T07:58:43+00:00" + "time": "2026-07-18T17:53:15+00:00" }, { "name": "mockery/mockery", @@ -10561,39 +10562,39 @@ }, { "name": "pestphp/pest", - "version": "v4.7.5", + "version": "v4.7.8", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "5dc49a71d63602a9b98fed0f2017c4679ef9f8e0" + "reference": "5b2293f67adcf1b2320b33f521b94a692d18f360" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/5dc49a71d63602a9b98fed0f2017c4679ef9f8e0", - "reference": "5dc49a71d63602a9b98fed0f2017c4679ef9f8e0", + "url": "https://api.github.com/repos/pestphp/pest/zipball/5b2293f67adcf1b2320b33f521b94a692d18f360", + "reference": "5b2293f67adcf1b2320b33f521b94a692d18f360", "shasum": "" }, "require": { "brianium/paratest": "^7.20.0", "composer/xdebug-handler": "^3.0.5", - "nunomaduro/collision": "^8.9.4", + "nunomaduro/collision": "^8.9.5", "nunomaduro/termwind": "^2.4.0", "pestphp/pest-plugin": "^4.0.0", "pestphp/pest-plugin-arch": "^4.0.2", "pestphp/pest-plugin-mutate": "^4.0.1", "pestphp/pest-plugin-profanity": "^4.2.1", "php": "^8.3.0", - "phpunit/phpunit": "^12.5.30", + "phpunit/phpunit": "^12.5.33", "symfony/process": "^7.4.13|^8.1.0" }, "conflict": { "filp/whoops": "<2.18.3", - "phpunit/phpunit": ">12.5.30", + "phpunit/phpunit": ">12.5.33", "sebastian/exporter": "<7.0.0", "webmozart/assert": "<1.11.0" }, "require-dev": { - "mrpunyapal/peststan": "^0.2.11", + "mrpunyapal/peststan": "^0.2.12", "pestphp/pest-dev-tools": "^4.1.0", "pestphp/pest-plugin-browser": "^4.3.1", "pestphp/pest-plugin-type-coverage": "^4.0.4", @@ -10624,7 +10625,6 @@ "Pest\\Plugins\\Verbose", "Pest\\Plugins\\Version", "Pest\\Plugins\\Shard", - "Pest\\Plugins\\Tia", "Pest\\Plugins\\Parallel" ] }, @@ -10664,7 +10664,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v4.7.5" + "source": "https://github.com/pestphp/pest/tree/v4.7.8" }, "funding": [ { @@ -10676,7 +10676,7 @@ "type": "github" } ], - "time": "2026-07-06T17:06:29+00:00" + "time": "2026-08-03T20:49:44+00:00" }, { "name": "pestphp/pest-plugin", @@ -11880,24 +11880,24 @@ }, { "name": "phpunit/phpunit", - "version": "12.5.30", + "version": "12.5.33", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "900400a5b616d6fb306f9549f6da33ba615d3fbb" + "reference": "b98e028a26c5c5ba7e4a54be96ccf35f2914d184" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/900400a5b616d6fb306f9549f6da33ba615d3fbb", - "reference": "900400a5b616d6fb306f9549f6da33ba615d3fbb", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b98e028a26c5c5ba7e4a54be96ccf35f2914d184", + "reference": "b98e028a26c5c5ba7e4a54be96ccf35f2914d184", "shasum": "" }, "require": { "ext-dom": "*", + "ext-filter": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", - "ext-xml": "*", "ext-xmlwriter": "*", "myclabs/deep-copy": "^1.13.4", "phar-io/manifest": "^2.0.4", @@ -11958,7 +11958,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.30" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.33" }, "funding": [ { @@ -11966,7 +11966,7 @@ "type": "other" } ], - "time": "2026-06-15T13:12:30+00:00" + "time": "2026-07-28T13:58:09+00:00" }, { "name": "sebastian/cli-parser", diff --git a/lang/en/domains/finance/enum.php b/lang/en/domains/finance/enum.php index 4cbfdec..64de028 100644 --- a/lang/en/domains/finance/enum.php +++ b/lang/en/domains/finance/enum.php @@ -14,7 +14,7 @@ return [ InvoiceStatus::OVERDUE->value => 'Overdue', ], 'invoice_type' => [ - InvoiceType::CUSTOMER_INVOICE->value => 'Customer Invoice', + InvoiceType::STUDENT_INVOICE->value => 'Student Invoice', InvoiceType::VENDOR_BILL->value => 'Vendor Bill', InvoiceType::CREDIT_NOTE->value => 'Credit Note', ], diff --git a/lang/en/domains/finance/field.php b/lang/en/domains/finance/field.php index 368974c..d4e8160 100644 --- a/lang/en/domains/finance/field.php +++ b/lang/en/domains/finance/field.php @@ -6,13 +6,40 @@ return [ 'name' => 'Account Name', 'classification' => 'Classification', 'status' => 'Status', + 'parent' => 'Derived From' ], 'invoice' => [ - 'name' => 'Payer Name', + 'name' => 'Invoice Name', + 'code' => 'Invoice Code', 'type' => 'Invoice Type', 'amount' => 'Invoice Amount', 'client_name' => 'Client Name', 'status' => 'Status', + 'coa_name' => 'Account Name (COA)', + 'detail' => [ + // Detail for Student + 'student_name' => 'Student Name', + 'student_id' => 'Student ID', + 'faculty' => 'Faculty', + 'study_program' => 'Study Program', + 'term' => 'Study Term', + 'fee_type' => 'Fee Type', + 'semester' => 'Semester', + + // Vendor Bill + 'vendor_name' => 'Vendor Name', + 'vendor_id' => 'Vendor ID', + ] + ], + 'payment' => [ + 'virtual_account' => 'Virtual Account', + 'bank_ref_id' => 'Bank Reference Code', + 'bank_name' => 'Bank Name', + 'nominal' => 'Nominal', + 'validity' => 'Validity', + 'payment_date' => 'Payment Date', + 'direction' => 'Direction', + 'status' => 'Status', ], 'product_mapping' => [ 'code' => 'Product Code', diff --git a/lang/id/domains/finance/enum.php b/lang/id/domains/finance/enum.php index af8b78f..1a4c5b0 100644 --- a/lang/id/domains/finance/enum.php +++ b/lang/id/domains/finance/enum.php @@ -14,7 +14,7 @@ return [ InvoiceStatus::OVERDUE->value => 'Jatuh Tempo', ], 'invoice_type' => [ - InvoiceType::CUSTOMER_INVOICE->value => 'Faktur Pelanggan', + InvoiceType::STUDENT_INVOICE->value => 'Faktur Pelanggan', InvoiceType::VENDOR_BILL->value => 'Tagihan Vendor', InvoiceType::CREDIT_NOTE->value => 'Nota Kredit', ], diff --git a/lang/id/domains/finance/field.php b/lang/id/domains/finance/field.php index dc32980..b6e6e88 100644 --- a/lang/id/domains/finance/field.php +++ b/lang/id/domains/finance/field.php @@ -6,6 +6,7 @@ return [ 'name' => 'Nama Akun', 'classification' => 'Klasifikasi', 'status' => 'Status', + 'parent' => 'Turunan Dari' ], 'invoice' => [ 'name' => 'Nama Pembayar', diff --git a/resources/js/alpine/select2.js b/resources/js/alpine/select2.js index 4e8e9ef..043b8bd 100644 --- a/resources/js/alpine/select2.js +++ b/resources/js/alpine/select2.js @@ -1,77 +1,140 @@ - export default function alpineSelect2(Alpine) { - Alpine.directive('select2', function (el, { expression }, { evaluate, cleanup }) { - let config = evaluate(expression); - - if((window.jQuery||window.jquery||window.$) && (typeof window.$.fn.select2 !== 'undefined')) { - let select2Default = { - placeholder: config.placeholder || 'Select an option', - ajax: config.url ? { - url: config.url, - dataType: 'json', - delay: 250, - xhrFields: { - withCredentials: true - }, - data: function (params) { - return { - search: params.terms - } - }, - processResults: function (data) { - return { - results: data.data || data - } + Alpine.directive( + "select2", + function (el, { expression }, { evaluate, cleanup }) { + let config = evaluate(expression) || {}; + + if ( + (window.jQuery || window.jquery || window.$) && + typeof window.$.fn.select2 !== "undefined" + ) { + let modalContainer = el.closest(".modal"); + let select2Default = { + placeholder: config.placeholder || "Select an option", + dropdownParent: config.dropdownParent || modalContainer || el.parentElement, + allowClear: config.allowClear ?? true, + ajax: config.url + ? { + url: config.url, + dataType: "json", + delay: 250, + xhrFields: { + withCredentials: true, + }, + data: function (params) { + return { + search: params.terms, + }; + }, + processResults: function (data) { + return { + results: data.data || data, + }; + }, + } + : undefined, + }; + + const container = document.createElement("div"); + el.parentElement.insertBefore(container, el); + + // Keep wire:ignore on container so Livewire DOM diffing does not destroy Select2 UI + container.setAttribute("wire:ignore", ""); + container.appendChild(el); + + config = Object.assign(select2Default, config); + let $el = $(el).select2(config); + + const $select2Container = $el.next(".select2-container"); + + const modelName = + el.getAttribute("wire:model") || + el.getAttribute("wire:model.live") || + el.getAttribute("wire:model.blur") || + "select2"; + + let isUpdating = false; + + $el.on("change", (e) => { + if (isUpdating) return; + isUpdating = true; + el.dispatchEvent(new Event("change", { bubbles: true })); + isUpdating = false; + }); + + // Fix for allowClear: prevent dropdown from opening when clicking the 'x' + $el.on("select2:unselecting", function (e) { + $(this).data("unselecting", true); + }).on("select2:opening", function (e) { + if ($(this).data("unselecting")) { + $(this).removeData("unselecting"); + e.preventDefault(); } - } : undefined - } + }); - config = Object.assign(config, select2Default) - let $el = $(el).select2(config) - - const modelName = el.getAttribute('wire:model') || - el.getAttribute('wire:model.live') || - el.getAttribute('wire:model.blur') || 'select2-clear'; - - let isUpdating = false - - $el.on('change', (e) => { - if(isUpdating) return - isUpdating = true - el.dispatchEvent(new Event('change', {bubbles: true})) - isUpdating = false - }) - - // Fix for allowClear: prevent dropdown from opening when clicking the 'x' - $el.on('select2:unselecting', function (e) { - $(this).data('unselecting', true); - }).on('select2:opening', function (e) { - if ($(this).data('unselecting')) { - $(this).removeData('unselecting'); - e.preventDefault(); - } - }); + const event = (e) => { + $el.val(null).trigger("change", { bubbles: true }); + }; - const event = (e) => { - $el.val(null).trigger('change', {bubbles: true}) - } + window.addEventListener(`${modelName}-clear`, event); + + // Sync error state (is-invalid) from native select to Select2 container + const syncErrorState = () => { + if (el.classList.contains("is-invalid")) { + container.classList.add("is-invalid"); + $select2Container + .find(".select2-selection") + .addClass("is-invalid border-danger"); + } else { + container.classList.remove("is-invalid"); + $select2Container + .find(".select2-selection") + .removeClass("is-invalid border-danger"); + } + }; + + // Run initial check on load + syncErrorState(); + + // Observe attribute changes on the native false, ]) -@php - $name = $attributes->has('wire:model') ? $attributes->get('wire:model') : $name; -@endphp +@php($name = $attributes->whereStartsWith('wire:model')->first() ?? $name)
has('x-show')) x-show="{{ $attributes->get('x-show') }}" x-cloak @endif> @isset($label) diff --git a/resources/views/components/form/select.blade.php b/resources/views/components/form/select.blade.php index 794042f..bcf4ad3 100644 --- a/resources/views/components/form/select.blade.php +++ b/resources/views/components/form/select.blade.php @@ -6,26 +6,27 @@ 'noLabel' => false, ]) -@php - $name = $attributes->has('wire:model') ? $attributes->get('wire:model') : $name; -@endphp - -
has('x-select2')) wire:ignore @endif +@php($name = $attributes->whereStartsWith('wire:model')->first() ?? $name) +
has('x-select2')) x-data="{ + error: @js($errors->first($name)), + hasError: @js($errors->has($name)) +}" x-init="$watch('error', (val) => console.log(val))" @endif class="form-group" @if ($attributes->has('x-show')) x-show="{{ $attributes->get('x-show') }}" x-cloak @endif > @if (! $noLabel) @endif + + @if ($feedback) - + @elseif ($errors->has($name)) {{ $errors->first($name) }} @endif diff --git a/resources/views/components/form/textarea.blade.php b/resources/views/components/form/textarea.blade.php index 2fd52cc..42048d5 100644 --- a/resources/views/components/form/textarea.blade.php +++ b/resources/views/components/form/textarea.blade.php @@ -5,9 +5,7 @@ 'disabled' => false, ]) -@php - $name = $attributes->has('wire:model') ? $attributes->get('wire:model') : $name; -@endphp +@php($name = $attributes->whereStartsWith('wire:model')->first() ?? $name)
has('x-show')) x-show="{{ $attributes->get('x-show') }}" x-cloak @endif> diff --git a/resources/views/components/layouts/nav/topbar.blade.php b/resources/views/components/layouts/nav/topbar.blade.php index 8cd5958..06bcdff 100644 --- a/resources/views/components/layouts/nav/topbar.blade.php +++ b/resources/views/components/layouts/nav/topbar.blade.php @@ -77,7 +77,7 @@
- @isset($breadcrumb) + @isset($breadcrumbs)
@@ -98,7 +98,7 @@ style="width: 3rem; height: 3rem" role="status" >
-

{{ __('ui/loading') }}...

+

{{ __('ui/common.loading') }}...

diff --git a/resources/views/pages/finance/chart-of-account/⚡form-modal/form-modal.blade.php b/resources/views/pages/finance/chart-of-account/⚡form-modal/form-modal.blade.php index 9a5e557..b0e07a3 100644 --- a/resources/views/pages/finance/chart-of-account/⚡form-modal/form-modal.blade.php +++ b/resources/views/pages/finance/chart-of-account/⚡form-modal/form-modal.blade.php @@ -1,10 +1,47 @@ +@use(App\Domains\Finance\Enums\AccountClassification) +@use(App\Domains\System\Enums\LifecycleStatus)
- {{-- Form Fields --}} + + + + + + + + +
- - + +
diff --git a/resources/views/pages/finance/chart-of-account/⚡form-modal/form-modal.php b/resources/views/pages/finance/chart-of-account/⚡form-modal/form-modal.php index 2943090..6d0bc15 100644 --- a/resources/views/pages/finance/chart-of-account/⚡form-modal/form-modal.php +++ b/resources/views/pages/finance/chart-of-account/⚡form-modal/form-modal.php @@ -1,7 +1,15 @@ ', $props); + $this->{$props} = $value; + + $coa = $this->chartOfAccounts->where('ulid', $value)->first(); + if($coa) { + $this->form->code = $coa->code + 1; + $this->form->classification = $coa->classification; + } + } + } public function show(int|string $id): void { $this->id = $id; $this->mode = 'update'; - // $this->form->fill($this->model->only(['name'])); + $coa = $this->coa->only(['name', 'code', 'classification', 'status', 'parent_id']); + $coa['parent_id'] = $this->chartOfAccounts + ->where('id', $coa['parent_id']) + ->first() + ?->ulid; + $this->form->fill($coa); + } + + #[Computed] + public function coa(): ChartOfAccount + { + return $this->id ? ChartOfAccount::where('ulid', $this->id)->first() : new ChartOfAccount; + } + + #[Computed] + public function chartOfAccounts(): Collection + { + return ChartOfAccount::query() + ->select(['id', 'name', 'code', 'ulid', 'classification']) + ->get(); + } + + public function save(RegisterChartOfAccount $register, AdjustAccountClassification $adjust): void + { + $this->form->validate(); + + $parentId = $this->chartOfAccounts + ->where('ulid', $this->form->parent_id) + ->first() + ->id; + + if ($this->mode === 'create') { + $register->execute(new RegisterChartOfAccountDTO( + code: $this->form->code, + name: $this->form->name, + classification: $this->form->classification, + status: $this->form->status, + parentId: $parentId, + )); + } elseif ($this->mode == 'update') { + $adjust->execute($this->coa, new AdjustAccountClassificationDTO( + code: $this->form->code, + classification: $this->form->classification, + parentId: $parentId, + )); + } + + $this->dispatch('hide-chartofaccount-form-modal'); + $this->js("LaravelDataTables['chartofaccount-table'].ajax.reload(null, false)"); } public function hide(): void { - // $this->form->reset(); + $this->form->reset(); $this->reset('id', 'mode'); } }; diff --git a/resources/views/pages/finance/invoice/index.blade.php b/resources/views/pages/finance/invoice/index.blade.php index 5bd18f7..db1125d 100644 --- a/resources/views/pages/finance/invoice/index.blade.php +++ b/resources/views/pages/finance/invoice/index.blade.php @@ -7,6 +7,10 @@ translation="domains/finance/field.invoice." /> + @can('invoice.update') + + @endcan + @can('invoice.delete') @endcan diff --git a/resources/views/pages/finance/invoice/⚡form-modal/form-modal.blade.php b/resources/views/pages/finance/invoice/⚡form-modal/form-modal.blade.php index 769db9a..37ee373 100644 --- a/resources/views/pages/finance/invoice/⚡form-modal/form-modal.blade.php +++ b/resources/views/pages/finance/invoice/⚡form-modal/form-modal.blade.php @@ -1,10 +1,68 @@ - +@use(App\Domains\Finance\Enums\InvoiceType) +
- {{-- Form Fields --}} + + @forelse ($this->invoiceSchema as $key => $value) + @php($model = $value->schema?->isLive() ? 'wire:model.live' : 'wire:model') + + @empty +
+ +
+ @endforelse + + + + + @if ($this->form->type == null || $form->type?->schema()->isCoaVisible()) + + @endif
+ @push('scripts') + @vite(['resources/js/plugin/select2.js']) + @endpush + - - + +
diff --git a/resources/views/pages/finance/invoice/⚡form-modal/form-modal.php b/resources/views/pages/finance/invoice/⚡form-modal/form-modal.php index 2943090..eb2634c 100644 --- a/resources/views/pages/finance/invoice/⚡form-modal/form-modal.php +++ b/resources/views/pages/finance/invoice/⚡form-modal/form-modal.php @@ -1,8 +1,12 @@ form->fill($this->model->only(['name'])); } + #[Computed] + public function invoiceSchema(): array + { + + return $this->form + ->type + ?->schema() + ->withContext([ + 'faculty' => $this->form->detail['faculty'] ?? null, + ]) + ->withFormString('form.detail.') + ->getSchema() ?? []; + } + + public function save(InitializeInvoice $initializeInvoice): void + { + $this->form->validate(); + $initializeInvoice->execute($this->form->toDto()); + + $this->dispatch('hide-invoice-form-modal'); + $this->js("LaravelDataTables['invoice-table'].ajax.reload(null, false)"); + } + public function hide(): void { - // $this->form->reset(); + $this->form->reset(); $this->reset('id', 'mode'); + $this->resetErrorBag(); } }; diff --git a/resources/views/pages/finance/product-mapping/index.blade.php b/resources/views/pages/finance/product-mapping/index.blade.php index 93282de..f6f336b 100644 --- a/resources/views/pages/finance/product-mapping/index.blade.php +++ b/resources/views/pages/finance/product-mapping/index.blade.php @@ -7,6 +7,8 @@ translation="domains/finance/field.product-mapping." /> + + @can('product-mapping.delete') @endcan diff --git a/resources/views/pages/finance/product-mapping/⚡form-modal/form-modal.blade.php b/resources/views/pages/finance/product-mapping/⚡form-modal/form-modal.blade.php index 769db9a..29a88d3 100644 --- a/resources/views/pages/finance/product-mapping/⚡form-modal/form-modal.blade.php +++ b/resources/views/pages/finance/product-mapping/⚡form-modal/form-modal.blade.php @@ -1,8 +1,28 @@ - +
- {{-- Form Fields --}} + + +
+ @push('scripts') + @vite('resources/js/plugin/select2.js') + @endpush diff --git a/resources/views/pages/finance/product-mapping/⚡form-modal/form-modal.php b/resources/views/pages/finance/product-mapping/⚡form-modal/form-modal.php index 2943090..11a153e 100644 --- a/resources/views/pages/finance/product-mapping/⚡form-modal/form-modal.php +++ b/resources/views/pages/finance/product-mapping/⚡form-modal/form-modal.php @@ -1,7 +1,14 @@ form->fill($this->model->only(['name'])); } + #[Computed] + public function coaOptions(): Collection + { + return ChartOfAccount::query() + ->select(['id', 'name', 'code']) + ->orderBy('code') + ->get() + ->mapWithKeys(fn ($coa) => [$coa->id => "{$coa->code} - {$coa->name}"]); + } + + public function save(RegisterProductMapping $register, ModifyProductMapping $modify): void + { + $this->form->validate(); + if($this->mode == 'create') { + $register->execute(new RegisterProductMappingDTO( + name: $this->form->name, + code: $this->form->code, + coaId: $this->form->coa_id, + )); + } elseif($this->mode == 'update') { + $modify->execute($this->id, $this->form->coa_id); + } + } + public function hide(): void { // $this->form->reset(); + $this->dispatch('form.coa_id-clear'); $this->reset('id', 'mode'); } };