diff --git a/app/Domains/Academic/Integration/Mappers/FacultyDataMapper.php b/app/Domains/Academic/Integration/Mappers/FacultyDataMapper.php index 1ec57b3..a52fe94 100644 --- a/app/Domains/Academic/Integration/Mappers/FacultyDataMapper.php +++ b/app/Domains/Academic/Integration/Mappers/FacultyDataMapper.php @@ -7,7 +7,7 @@ use App\Domains\Academic\Actions\Curriculum\ModifyFacultyDetails; use App\Domains\Academic\DTOs\Curriculum\EstablishFacultyDTO; use App\Domains\Academic\DTOs\Curriculum\ModifyFacultyDetailsDTO; use App\Domains\Academic\Models\Faculty; -use App\Domains\System\Support\Integration\DataPayloadMapper; +use App\Domains\System\Support\Contract\DataPayloadMapper; use Illuminate\Database\Eloquent\Model; class FacultyDataMapper implements DataPayloadMapper diff --git a/app/Domains/Academic/Integration/Mappers/StudyProgramDataMapper.php b/app/Domains/Academic/Integration/Mappers/StudyProgramDataMapper.php index 01f98ff..f5d3af5 100644 --- a/app/Domains/Academic/Integration/Mappers/StudyProgramDataMapper.php +++ b/app/Domains/Academic/Integration/Mappers/StudyProgramDataMapper.php @@ -9,7 +9,7 @@ use App\Domains\Academic\DTOs\Curriculum\RegisterStudyProgramDTO; use App\Domains\Academic\Models\Faculty; use App\Domains\Academic\Models\StudyProgram; use App\Domains\System\Enums\LifecycleStatus; -use App\Domains\System\Support\Integration\DataPayloadMapper; +use App\Domains\System\Support\Contract\DataPayloadMapper; use Illuminate\Database\Eloquent\Model; class StudyProgramDataMapper implements DataPayloadMapper diff --git a/app/Domains/Academic/Integration/Mappers/TermDataMapper.php b/app/Domains/Academic/Integration/Mappers/TermDataMapper.php index 8999852..90ed671 100644 --- a/app/Domains/Academic/Integration/Mappers/TermDataMapper.php +++ b/app/Domains/Academic/Integration/Mappers/TermDataMapper.php @@ -7,7 +7,7 @@ use App\Domains\Academic\Actions\Calendar\ModifyTermDate; use App\Domains\Academic\DTOs\Calendar\InitializeAcademicTermDTO; use App\Domains\Academic\DTOs\Calendar\ModifyTermDateDTO; use App\Domains\Academic\Models\Term; -use App\Domains\System\Support\Integration\DataPayloadMapper; +use App\Domains\System\Support\Contract\DataPayloadMapper; use Carbon\CarbonImmutable; use Illuminate\Database\Eloquent\Model; diff --git a/app/Domains/Academic/Providers/AcademicServiceProvider.php b/app/Domains/Academic/Providers/AcademicServiceProvider.php index 9db0e07..0e54b64 100644 --- a/app/Domains/Academic/Providers/AcademicServiceProvider.php +++ b/app/Domains/Academic/Providers/AcademicServiceProvider.php @@ -6,7 +6,7 @@ use App\Domains\Academic\Integration\Mappers\FacultyDataMapper; use App\Domains\Academic\Integration\Mappers\StudyProgramDataMapper; use App\Domains\Academic\Integration\Mappers\TermDataMapper; use App\Domains\Channel\Enums\Api\ApiType; -use App\Domains\Channel\Support\Mapper\ApiMapperRegistry; +use App\Domains\Channel\Support\Registry\ApiMapperRegistry; use App\Domains\System\Traits\Provider\RegistersDomainEvents; use Illuminate\Support\ServiceProvider; diff --git a/app/Domains/Academic/Support/Generator/AcademicProductGenerator.php b/app/Domains/Academic/Support/Generator/AcademicProductGenerator.php new file mode 100644 index 0000000..cb3f2a5 --- /dev/null +++ b/app/Domains/Academic/Support/Generator/AcademicProductGenerator.php @@ -0,0 +1,60 @@ +loadMissing([ + 'admissionSchedule.admissionTrack', + 'admissionSchedule.term', + 'academicProgram', + 'feeType', + ]); + + $code = sprintf( + '%s-%s-%s-%s', + str($rate->admissionSchedule->admissionTrack->name)->initials(), + 'AC'.$rate->academicProgram->code, + 'FT'.$rate->feeType->code, + $rate->admissionSchedule->term->year_study + ); + + $name = sprintf( + '%s %s %s %s', + $rate->admissionSchedule->admissionTrack->name, + $rate->academicProgram->name, + $rate->feeType->name, + $rate->admissionSchedule->term->year_study + ); + + return new ProductIdentifierDTO( + code: $code, + name: $name, + feeRateType: $rate->getMorphClass(), + feeRateId: $rate->id + ); + } +} diff --git a/app/Domains/Admission/Actions/AdmissionPricing/SetUpAdmissionFeeRates.php b/app/Domains/Admission/Actions/AdmissionPricing/SetUpAdmissionFeeRates.php index cf9343c..2aae407 100644 --- a/app/Domains/Admission/Actions/AdmissionPricing/SetUpAdmissionFeeRates.php +++ b/app/Domains/Admission/Actions/AdmissionPricing/SetUpAdmissionFeeRates.php @@ -2,12 +2,17 @@ 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; class SetUpAdmissionFeeRates { + public function __construct(protected AcademicProductGenerator $generator) + { + } + public function execute(AdmissionFeeRatesDTO $dto): void { $rate = FeeRate::updateOrCreate([ @@ -19,8 +24,8 @@ class SetUpAdmissionFeeRates 'amount' => $dto->amount, ]); - $name = $rate->generateProductName(); + $product = $this->generator->generate($rate); - AdmissionFeeRateWasSetUp::dispatch(productName: $name); + AdmissionFeeRateWasSetUp::dispatch($product); } } diff --git a/app/Domains/Admission/Events/Pricing/AdmissionFeeRateWasSetUp.php b/app/Domains/Admission/Events/Pricing/AdmissionFeeRateWasSetUp.php index 0256add..b275262 100644 --- a/app/Domains/Admission/Events/Pricing/AdmissionFeeRateWasSetUp.php +++ b/app/Domains/Admission/Events/Pricing/AdmissionFeeRateWasSetUp.php @@ -10,6 +10,6 @@ class AdmissionFeeRateWasSetUp use Dispatchable, SerializesModels; public function __construct( - public string $productName + public object $product ) {} } diff --git a/app/Domains/Admission/Integration/Mappers/AcademicProgramDataMapper.php b/app/Domains/Admission/Integration/Mappers/AcademicProgramDataMapper.php index ba6c638..1ec0bc9 100644 --- a/app/Domains/Admission/Integration/Mappers/AcademicProgramDataMapper.php +++ b/app/Domains/Admission/Integration/Mappers/AcademicProgramDataMapper.php @@ -6,7 +6,7 @@ use App\Domains\Admission\Actions\StudyModeProvisioning\DefineAcademicProgram; use App\Domains\Admission\DTOs\StudyModeProvisioning\DefineAcademicProgramDTO; use App\Domains\Admission\Models\AcademicProgram; use App\Domains\System\Enums\LifecycleStatus; -use App\Domains\System\Support\Integration\DataPayloadMapper; +use App\Domains\System\Support\Contract\DataPayloadMapper; use Illuminate\Database\Eloquent\Model; class AcademicProgramDataMapper implements DataPayloadMapper diff --git a/app/Domains/Admission/Integration/Mappers/AdmissionScheduleDataMapper.php b/app/Domains/Admission/Integration/Mappers/AdmissionScheduleDataMapper.php index 5b1b21b..2543680 100644 --- a/app/Domains/Admission/Integration/Mappers/AdmissionScheduleDataMapper.php +++ b/app/Domains/Admission/Integration/Mappers/AdmissionScheduleDataMapper.php @@ -9,7 +9,7 @@ use App\Domains\Admission\DTOs\IntakeScheduling\AdjustAdmissionScheduleDurationD use App\Domains\Admission\DTOs\IntakeScheduling\DefineAdmissionScheduleDTO; use App\Domains\Admission\Models\AdmissionSchedule; use App\Domains\Admission\Models\AdmissionTrack; -use App\Domains\System\Support\Integration\DataPayloadMapper; +use App\Domains\System\Support\Contract\DataPayloadMapper; use Carbon\CarbonImmutable; use Illuminate\Database\Eloquent\Model; diff --git a/app/Domains/Admission/Integration/Mappers/AdmissionTrackDataMapper.php b/app/Domains/Admission/Integration/Mappers/AdmissionTrackDataMapper.php index fb7d698..920ad5e 100644 --- a/app/Domains/Admission/Integration/Mappers/AdmissionTrackDataMapper.php +++ b/app/Domains/Admission/Integration/Mappers/AdmissionTrackDataMapper.php @@ -6,7 +6,7 @@ use App\Domains\Admission\Actions\IntakeSourcing\InitializeAdmissionTrack; use App\Domains\Admission\DTOs\IntakeSourcing\InitializeAdmissionTrackDTO; use App\Domains\Admission\Models\AdmissionTrack; use App\Domains\System\Enums\LifecycleStatus; -use App\Domains\System\Support\Integration\DataPayloadMapper; +use App\Domains\System\Support\Contract\DataPayloadMapper; use Illuminate\Database\Eloquent\Model; class AdmissionTrackDataMapper implements DataPayloadMapper diff --git a/app/Domains/Admission/Integration/Mappers/FeeRateDataMapper.php b/app/Domains/Admission/Integration/Mappers/FeeRateDataMapper.php index a902cab..cde91c4 100644 --- a/app/Domains/Admission/Integration/Mappers/FeeRateDataMapper.php +++ b/app/Domains/Admission/Integration/Mappers/FeeRateDataMapper.php @@ -10,7 +10,7 @@ use App\Domains\Admission\Models\AdmissionSchedule; use App\Domains\Admission\Models\FeeRate; use App\Domains\Admission\Models\FeeType; use Illuminate\Database\Eloquent\Model; -use App\Domains\System\Support\Integration\DataPayloadMapper; +use App\Domains\System\Support\Contract\DataPayloadMapper; class FeeRateDataMapper implements DataPayloadMapper { diff --git a/app/Domains/Admission/Integration/Mappers/FeeTypeDataMapper.php b/app/Domains/Admission/Integration/Mappers/FeeTypeDataMapper.php index 70c892b..f4a697b 100644 --- a/app/Domains/Admission/Integration/Mappers/FeeTypeDataMapper.php +++ b/app/Domains/Admission/Integration/Mappers/FeeTypeDataMapper.php @@ -6,7 +6,7 @@ use App\Domains\Admission\Actions\AdmissionPricing\DefineFeeType; use App\Domains\Admission\DTOs\AdmissionPricing\FeeTypeDTO; use App\Domains\Admission\Enums\BillingCycle; use App\Domains\Admission\Models\FeeType; -use App\Domains\System\Support\Integration\DataPayloadMapper; +use App\Domains\System\Support\Contract\DataPayloadMapper; use Illuminate\Database\Eloquent\Model; class FeeTypeDataMapper implements DataPayloadMapper diff --git a/app/Domains/Admission/Providers/AdmissionServiceProvider.php b/app/Domains/Admission/Providers/AdmissionServiceProvider.php index c915bd9..95a5e5e 100644 --- a/app/Domains/Admission/Providers/AdmissionServiceProvider.php +++ b/app/Domains/Admission/Providers/AdmissionServiceProvider.php @@ -8,7 +8,7 @@ use App\Domains\Admission\Integration\Mappers\AdmissionTrackDataMapper; use App\Domains\Admission\Integration\Mappers\FeeRateDataMapper; use App\Domains\Admission\Integration\Mappers\FeeTypeDataMapper; use App\Domains\Channel\Enums\Api\ApiType; -use App\Domains\Channel\Support\Mapper\ApiMapperRegistry; +use App\Domains\Channel\Support\Registry\ApiMapperRegistry; use App\Domains\System\Traits\Provider\RegistersDomainEvents; use Illuminate\Support\ServiceProvider; diff --git a/app/Domains/Billing/Actions/Catalog/ProvisionProductFromFeeRate.php b/app/Domains/Billing/Actions/Catalog/ProvisionProductFromFeeRate.php new file mode 100644 index 0000000..bfed9c9 --- /dev/null +++ b/app/Domains/Billing/Actions/Catalog/ProvisionProductFromFeeRate.php @@ -0,0 +1,11 @@ + $rules + */ + public function __construct( + public string $name, + public string $type, + public string $value, + public array $rules, + ) {} +} diff --git a/app/Domains/Billing/DTOs/Invoicing/DraftInvoiceDTO.php b/app/Domains/Billing/DTOs/Invoicing/DraftInvoiceDTO.php new file mode 100644 index 0000000..e692606 --- /dev/null +++ b/app/Domains/Billing/DTOs/Invoicing/DraftInvoiceDTO.php @@ -0,0 +1,14 @@ +'; + case GREATER_OR_EQUAL = '>='; + case LESS = '<'; + case LESS_OR_EQUAL = '<='; + case EQUAL = '='; +} diff --git a/app/Domains/Billing/Enums/Discounts/DiscountType.php b/app/Domains/Billing/Enums/Discounts/DiscountType.php new file mode 100644 index 0000000..b03cbef --- /dev/null +++ b/app/Domains/Billing/Enums/Discounts/DiscountType.php @@ -0,0 +1,9 @@ +belongsTo(ChartOfAccount::class); + } +} diff --git a/app/Domains/Billing/Models/DiscountSnapshot.php b/app/Domains/Billing/Models/DiscountSnapshot.php new file mode 100644 index 0000000..073d1c9 --- /dev/null +++ b/app/Domains/Billing/Models/DiscountSnapshot.php @@ -0,0 +1,46 @@ +belongsTo(ChartOfAccount::class); + } + + public function discount(): BelongsTo + { + return $this->belongsTo(Discount::class); + } + + public function invoice(): BelongsTo + { + return $this->belongsTo(Invoice::class); + } + + public function invoiceItem(): BelongsTo + { + return $this->belongsTo(InvoiceItem::class); + } +} diff --git a/app/Domains/Billing/Models/Invoice.php b/app/Domains/Billing/Models/Invoice.php new file mode 100644 index 0000000..7f8ecba --- /dev/null +++ b/app/Domains/Billing/Models/Invoice.php @@ -0,0 +1,27 @@ +belongsTo(Product::class); + } + + public function chartOfAccount(): BelongsTo + { + return $this->belongsTo(ChartOfAccount::class); + } +} diff --git a/app/Domains/Billing/Models/Product.php b/app/Domains/Billing/Models/Product.php new file mode 100644 index 0000000..8938ce9 --- /dev/null +++ b/app/Domains/Billing/Models/Product.php @@ -0,0 +1,32 @@ +belongsTo(ChartOfAccount::class); + } + + public function pricing(): MorphTo + { + return $this->morphTo(); + } +} diff --git a/app/Domains/Billing/Providers/BillingServiceProvider.php b/app/Domains/Billing/Providers/BillingServiceProvider.php new file mode 100644 index 0000000..2966867 --- /dev/null +++ b/app/Domains/Billing/Providers/BillingServiceProvider.php @@ -0,0 +1,23 @@ +app->register(RelationshipServiceProvider::class); + } + + public function boot(): void + { + $this->registerEvents(); + } +} diff --git a/app/Domains/Billing/Providers/RelationshipServiceProvider.php b/app/Domains/Billing/Providers/RelationshipServiceProvider.php new file mode 100644 index 0000000..b27e582 --- /dev/null +++ b/app/Domains/Billing/Providers/RelationshipServiceProvider.php @@ -0,0 +1,38 @@ + + * $order->belongsTo(Customer::class, 'customer_id') + * ); + */ + public function boot(): void + { + Invoice::resolveRelationUsing('client', function (Invoice $invoice) { + return $invoice->belongsTo(Client::class, 'client_id'); + }); + Client::resolveRelationUsing('invoices', function (Client $client) { + return $client->hasMany(Invoice::class, 'client_id'); + }); + } +} diff --git a/app/Domains/Billing/Support/Contract/ProductIdentifierGenerator.php b/app/Domains/Billing/Support/Contract/ProductIdentifierGenerator.php new file mode 100644 index 0000000..98ee6ce --- /dev/null +++ b/app/Domains/Billing/Support/Contract/ProductIdentifierGenerator.php @@ -0,0 +1,27 @@ + $this->copy() ->tz(config('app.display_timezone', 'UTC'))); + + + Str::macro('initials', function (string $name, int $limit = 2) { + $words = explode(' ', trim($name)); + + $initials = collect($words)->map(function ($word) { + return substr($word, 0, 1); + })->take($limit)->implode(''); + + return strtoupper($initials); + }); } } diff --git a/app/Domains/System/Support/Integration/DataPayloadMapper.php b/app/Domains/System/Support/Contract/DataPayloadMapper.php similarity index 93% rename from app/Domains/System/Support/Integration/DataPayloadMapper.php rename to app/Domains/System/Support/Contract/DataPayloadMapper.php index b29c829..f490887 100644 --- a/app/Domains/System/Support/Integration/DataPayloadMapper.php +++ b/app/Domains/System/Support/Contract/DataPayloadMapper.php @@ -1,6 +1,6 @@ + */ +class DiscountFactory extends Factory +{ + protected $model = Discount::class; + + public function definition(): array + { + return []; + } +} \ No newline at end of file diff --git a/database/factories/Billing/DiscountRulesFactory.php b/database/factories/Billing/DiscountRulesFactory.php new file mode 100644 index 0000000..c2acf86 --- /dev/null +++ b/database/factories/Billing/DiscountRulesFactory.php @@ -0,0 +1,19 @@ + + */ +class DiscountRulesFactory extends Factory +{ + protected $model = DiscountRules::class; + + public function definition(): array + { + return []; + } +} \ No newline at end of file diff --git a/database/factories/Billing/DiscountSnapshotFactory.php b/database/factories/Billing/DiscountSnapshotFactory.php new file mode 100644 index 0000000..8bcc64f --- /dev/null +++ b/database/factories/Billing/DiscountSnapshotFactory.php @@ -0,0 +1,19 @@ + + */ +class DiscountSnapshotFactory extends Factory +{ + protected $model = DiscountSnapshot::class; + + public function definition(): array + { + return []; + } +} \ No newline at end of file diff --git a/database/factories/Finance/InvoiceFactory.php b/database/factories/Billing/InvoiceFactory.php similarity index 75% rename from database/factories/Finance/InvoiceFactory.php rename to database/factories/Billing/InvoiceFactory.php index 1906a3d..a4dd910 100644 --- a/database/factories/Finance/InvoiceFactory.php +++ b/database/factories/Billing/InvoiceFactory.php @@ -1,8 +1,8 @@ + */ +class InvoiceItemFactory extends Factory +{ + protected $model = InvoiceItem::class; + + public function definition(): array + { + return []; + } +} \ No newline at end of file diff --git a/database/factories/Billing/ProductFactory.php b/database/factories/Billing/ProductFactory.php new file mode 100644 index 0000000..d45411c --- /dev/null +++ b/database/factories/Billing/ProductFactory.php @@ -0,0 +1,19 @@ + + */ +class ProductFactory extends Factory +{ + protected $model = Product::class; + + public function definition(): array + { + return []; + } +} \ No newline at end of file diff --git a/database/factories/Finance/PaymentFactory.php b/database/factories/Finance/PaymentFactory.php deleted file mode 100644 index d3b562d..0000000 --- a/database/factories/Finance/PaymentFactory.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -class PaymentFactory extends Factory -{ - protected $model = Payment::class; - - public function definition(): array - { - return []; - } -} diff --git a/database/factories/System/FileFactory.php b/database/factories/System/FileFactory.php deleted file mode 100644 index 4ef537b..0000000 --- a/database/factories/System/FileFactory.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -class FileFactory extends Factory -{ - protected $model = File::class; - - public function definition(): array - { - return []; - } -} diff --git a/database/migrations/2026_07_06_035952_create_payments_table.php b/database/migrations/2026_07_06_035952_create_payments_table.php deleted file mode 100644 index 0eb4f73..0000000 --- a/database/migrations/2026_07_06_035952_create_payments_table.php +++ /dev/null @@ -1,35 +0,0 @@ -id(); - $table->ulid(); - $table->integer('virtual_account'); - $table->string('bank_ref_id'); - $table->string('bank'); - $table->string('nominal'); - $table->date('valid_until'); - $table->string('status'); - $table->foreignId('invoice_id')->constrained()->restrictOnDelete(); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('payments'); - } -}; diff --git a/database/migrations/2026_07_15_071124_create_student_invoice_reports_table.php b/database/migrations/2026_07_15_071124_create_student_invoice_reports_table.php deleted file mode 100644 index 10b9e71..0000000 --- a/database/migrations/2026_07_15_071124_create_student_invoice_reports_table.php +++ /dev/null @@ -1,43 +0,0 @@ -id(); - $table->foreignId('invoice_id') - ->constrained('invoices') - ->restrictOnDelete(); - $table->string('invoice_name'); - $table->string('student_name'); - $table->string('faculty_id'); - $table->string('faculty_name'); - $table->string('study_program_id'); - $table->string('study_program_name'); - $table->string('term_id'); - $table->string('term_name'); - $table->integer('amount'); - $table->integer('paid_amount'); - $table->dateTime('issued_at'); - $table->dateTime('paid_at'); - $table->string('status'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('student_invoice_reports'); - } -}; diff --git a/database/migrations/2026_07_15_071129_create_invoice_reports_table.php b/database/migrations/2026_07_15_071129_create_invoice_reports_table.php deleted file mode 100644 index c4bf66c..0000000 --- a/database/migrations/2026_07_15_071129_create_invoice_reports_table.php +++ /dev/null @@ -1,38 +0,0 @@ -id(); - $table->foreignId('invoice_id') - ->constrained('invoices') - ->restrictOnDelete(); - $table->string('invoice_name'); - $table->string('customer_name'); - $table->string('customer_id'); - $table->integer('amount'); - $table->integer('paid_amount'); - $table->dateTime('issued_at'); - $table->dateTime('paid_at'); - $table->string('status'); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('invoice_reports'); - } -}; diff --git a/database/migrations/2026_07_06_034148_create_chart_of_accounts_table.php b/database/migrations/2026_09_08_022815_create_chart_of_accounts_table.php similarity index 78% rename from database/migrations/2026_07_06_034148_create_chart_of_accounts_table.php rename to database/migrations/2026_09_08_022815_create_chart_of_accounts_table.php index d2342a9..56be674 100644 --- a/database/migrations/2026_07_06_034148_create_chart_of_accounts_table.php +++ b/database/migrations/2026_09_08_022815_create_chart_of_accounts_table.php @@ -14,13 +14,11 @@ return new class extends Migration Schema::create('chart_of_accounts', function (Blueprint $table) { $table->id(); $table->ulid(); - $table->string('code'); + $table->string('code')->unique(); $table->string('name'); $table->string('classification'); - $table->string('status'); - $table->unsignedBigInteger('parent_id') - ->nullable() - ->index(); + $table->string('normal_balance'); + $table->foreignId('parent_id')->nullable(); $table->timestamps(); }); } diff --git a/database/migrations/2026_09_08_022816_create_products_table.php b/database/migrations/2026_09_08_022816_create_products_table.php new file mode 100644 index 0000000..68eab07 --- /dev/null +++ b/database/migrations/2026_09_08_022816_create_products_table.php @@ -0,0 +1,31 @@ +id(); + $table->string('code')->unique(); + $table->string('name'); + $table->morphs('pricing'); + $table->foreignId('chart_of_account_id')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('products'); + } +}; diff --git a/database/migrations/2026_07_06_035903_create_invoices_table.php b/database/migrations/2026_09_08_022817_create_invoices_table.php similarity index 71% rename from database/migrations/2026_07_06_035903_create_invoices_table.php rename to database/migrations/2026_09_08_022817_create_invoices_table.php index fe1a2a8..d035f32 100644 --- a/database/migrations/2026_07_06_035903_create_invoices_table.php +++ b/database/migrations/2026_09_08_022817_create_invoices_table.php @@ -13,17 +13,16 @@ return new class extends Migration { Schema::create('invoices', function (Blueprint $table) { $table->id(); - $table->ulid(); + $table->string('code')->unique(); $table->string('name'); - $table->string('detail'); + $table->json('client_detail'); + $table->string('client_ref_id'); $table->string('type'); - $table->string('amount'); + $table->integer('gross_amount'); + $table->integer('discount_amount'); + $table->integer('net_amount'); $table->string('status'); - $table->string('client_ref_id'); - $table->string('client_id')->index(); - $table->foreignId('chart_of_account_id') - ->constrained() - ->restrictOnDelete(); + $table->foreignId('client_id'); $table->timestamps(); }); } diff --git a/database/migrations/2026_09_08_022818_create_invoice_items_table.php b/database/migrations/2026_09_08_022818_create_invoice_items_table.php new file mode 100644 index 0000000..5ebea72 --- /dev/null +++ b/database/migrations/2026_09_08_022818_create_invoice_items_table.php @@ -0,0 +1,37 @@ +id(); + $table->string('code'); + $table->string('name'); + $table->integer('quantity'); + $table->integer('unit_price'); + $table->integer('gross_amount'); + $table->integer('discount_amount'); + $table->integer('net_amount'); + $table->foreignId('invoice_id')->constrained(); + $table->foreignId('product_id')->constrained(); + $table->foreignId('chart_of_account_id')->constrained(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('invoice_items'); + } +}; diff --git a/database/migrations/2026_07_13_045042_create_product_mappings_table.php b/database/migrations/2026_09_08_022819_create_discounts_table.php similarity index 65% rename from database/migrations/2026_07_13_045042_create_product_mappings_table.php rename to database/migrations/2026_09_08_022819_create_discounts_table.php index 9e09670..fe268bc 100644 --- a/database/migrations/2026_07_13_045042_create_product_mappings_table.php +++ b/database/migrations/2026_09_08_022819_create_discounts_table.php @@ -11,12 +11,14 @@ return new class extends Migration */ public function up(): void { - Schema::create('product_mappings', function (Blueprint $table) { + Schema::create('discounts', function (Blueprint $table) { $table->id(); - $table->string('code'); $table->string('name'); + $table->string('type'); + $table->integer('value'); + $table->json('rules')->default('{}'); + $table->boolean('is_active'); $table->foreignId('chart_of_account_id')->constrained(); - $table->unsignedBigInteger('client_id')->index()->nullable(); $table->timestamps(); }); } @@ -26,6 +28,6 @@ return new class extends Migration */ public function down(): void { - Schema::dropIfExists('product_mappings'); + Schema::dropIfExists('discounts'); } }; diff --git a/database/migrations/2026_09_08_022821_create_discount_snapshots_table.php b/database/migrations/2026_09_08_022821_create_discount_snapshots_table.php new file mode 100644 index 0000000..56fc32c --- /dev/null +++ b/database/migrations/2026_09_08_022821_create_discount_snapshots_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('name'); + $table->string('type'); + $table->integer('value'); + $table->integer('amount'); + $table->foreignId('chart_of_account_id')->constrained(); + $table->foreignId('discount_id')->constrained(); + $table->foreignId('invoice_id')->constrained(); + $table->foreignId('invoice_item_id')->nullable()->constrained()->nullOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('discount_snapshots'); + } +}; diff --git a/resources/views/components/container/accordion-item.blade.php b/resources/views/components/container/accordion-item.blade.php index 273b54a..8366e5f 100644 --- a/resources/views/components/container/accordion-item.blade.php +++ b/resources/views/components/container/accordion-item.blade.php @@ -1,5 +1,8 @@ @props(['itemId' => md5(microtime(true)), 'header' => 'Accordion', 'content' => null]) + @aware(['id' => md5(microtime(true))]) +@php($class = 'accordion-button collapsed bg-body-tertiary py-3 px-4 fw-semibold text-body shadow-none d-flex align-items-center gap-3') +