Browse Source

feat: add Billing domain with models, DTOs, actions, and runtime relationship support; refactor mappers and event-driven logic

- Introduced Billing-specific models (Invoice, Product, Discount) and DTOs for catalog, discount, and invoicing functionalities.
- Implemented runtime relationships using RelationshipServiceProvider.
- Refactored `DataPayloadMapper` to `DataPayloadMapper` under `Contract` for improved consistency.
- Added `AcademicProductGenerator` for dynamic product identifiers.
- Streamlined API registry and mapper dependencies across domains.
- Enhanced `AdmissionFeeRateWasSetUp` event to dispatch enriched product data.
master
Syarif Ubaidillah 1 month ago
parent
commit
a39fe46272
  1. 2
      app/Domains/Academic/Integration/Mappers/FacultyDataMapper.php
  2. 2
      app/Domains/Academic/Integration/Mappers/StudyProgramDataMapper.php
  3. 2
      app/Domains/Academic/Integration/Mappers/TermDataMapper.php
  4. 2
      app/Domains/Academic/Providers/AcademicServiceProvider.php
  5. 60
      app/Domains/Academic/Support/Generator/AcademicProductGenerator.php
  6. 9
      app/Domains/Admission/Actions/AdmissionPricing/SetUpAdmissionFeeRates.php
  7. 2
      app/Domains/Admission/Events/Pricing/AdmissionFeeRateWasSetUp.php
  8. 2
      app/Domains/Admission/Integration/Mappers/AcademicProgramDataMapper.php
  9. 2
      app/Domains/Admission/Integration/Mappers/AdmissionScheduleDataMapper.php
  10. 2
      app/Domains/Admission/Integration/Mappers/AdmissionTrackDataMapper.php
  11. 2
      app/Domains/Admission/Integration/Mappers/FeeRateDataMapper.php
  12. 2
      app/Domains/Admission/Integration/Mappers/FeeTypeDataMapper.php
  13. 2
      app/Domains/Admission/Providers/AdmissionServiceProvider.php
  14. 11
      app/Domains/Billing/Actions/Catalog/ProvisionProductFromFeeRate.php
  15. 11
      app/Domains/Billing/Actions/Discounts/DefineDiscount.php
  16. 11
      app/Domains/Billing/Actions/Invoicing/DraftInvoice.php
  17. 13
      app/Domains/Billing/DTOs/Catalog/ProvisionProductDTO.php
  18. 19
      app/Domains/Billing/DTOs/Discounts/DefineDiscountDTO.php
  19. 14
      app/Domains/Billing/DTOs/Invoicing/DraftInvoiceDTO.php
  20. 24
      app/Domains/Billing/DTOs/Products/ProductIdentifierDTO.php
  21. 9
      app/Domains/Billing/Enums/Catalog/AccountBalance.php
  22. 12
      app/Domains/Billing/Enums/Catalog/AccountClassification.php
  23. 12
      app/Domains/Billing/Enums/Discounts/DiscountOperator.php
  24. 9
      app/Domains/Billing/Enums/Discounts/DiscountType.php
  25. 11
      app/Domains/Billing/Enums/Invoicing/InvoiceStatus.php
  26. 10
      app/Domains/Billing/Enums/Invoicing/InvoiceType.php
  27. 21
      app/Domains/Billing/Models/ChartOfAccount.php
  28. 29
      app/Domains/Billing/Models/Discount.php
  29. 46
      app/Domains/Billing/Models/DiscountSnapshot.php
  30. 27
      app/Domains/Billing/Models/Invoice.php
  31. 38
      app/Domains/Billing/Models/InvoiceItem.php
  32. 32
      app/Domains/Billing/Models/Product.php
  33. 23
      app/Domains/Billing/Providers/BillingServiceProvider.php
  34. 38
      app/Domains/Billing/Providers/RelationshipServiceProvider.php
  35. 27
      app/Domains/Billing/Support/Contract/ProductIdentifierGenerator.php
  36. 2
      app/Domains/Channel/Actions/Api/SynchronizeWithApi.php
  37. 4
      app/Domains/Channel/Enums/Api/ApiType.php
  38. 4
      app/Domains/Channel/Support/Registry/ApiMapperRegistry.php
  39. 2
      app/Domains/Identity/Integration/Mappers/UserDataMapper.php
  40. 11
      app/Domains/Payment/Listeners/VirtualAccount/InitializeVirtualAccountForPublishedInvoice.php
  41. 2
      app/Domains/System/Actions/Integration/RunGenericImportPipeline.php
  42. 12
      app/Domains/System/Providers/SystemServiceProvider.php
  43. 2
      app/Domains/System/Support/Contract/DataPayloadMapper.php
  44. 3
      app/Providers/AppServiceProvider.php
  45. 3
      bootstrap/providers.php
  46. 4
      database/factories/Billing/ChartOfAccountFactory.php
  47. 19
      database/factories/Billing/DiscountFactory.php
  48. 19
      database/factories/Billing/DiscountRulesFactory.php
  49. 19
      database/factories/Billing/DiscountSnapshotFactory.php
  50. 4
      database/factories/Billing/InvoiceFactory.php
  51. 19
      database/factories/Billing/InvoiceItemFactory.php
  52. 19
      database/factories/Billing/ProductFactory.php
  53. 19
      database/factories/Finance/PaymentFactory.php
  54. 19
      database/factories/System/FileFactory.php
  55. 35
      database/migrations/2026_07_06_035952_create_payments_table.php
  56. 43
      database/migrations/2026_07_15_071124_create_student_invoice_reports_table.php
  57. 38
      database/migrations/2026_07_15_071129_create_invoice_reports_table.php
  58. 8
      database/migrations/2026_09_08_022815_create_chart_of_accounts_table.php
  59. 31
      database/migrations/2026_09_08_022816_create_products_table.php
  60. 15
      database/migrations/2026_09_08_022817_create_invoices_table.php
  61. 37
      database/migrations/2026_09_08_022818_create_invoice_items_table.php
  62. 10
      database/migrations/2026_09_08_022819_create_discounts_table.php
  63. 35
      database/migrations/2026_09_08_022821_create_discount_snapshots_table.php
  64. 7
      resources/views/components/container/accordion-item.blade.php
  65. 6
      resources/views/components/container/accordion.blade.php
  66. 34
      resources/views/pages/admission/admission-schedule/⚡detail-view/detail-view.blade.php
  67. 2
      resources/views/pages/admission/admission-schedule/⚡detail-view/detail-view.php
  68. 2
      tests/Unit/Domains/System/Actions/Integration/RunGenericImportPipelineTest.php

2
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

2
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

2
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;

2
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;

60
app/Domains/Academic/Support/Generator/AcademicProductGenerator.php

@ -0,0 +1,60 @@
<?php
namespace App\Domains\Academic\Support\Generator;
use App\Domains\Admission\Models\FeeRate;
use App\Domains\Billing\DTOs\Products\ProductIdentifierDTO;
use App\Domains\Billing\Support\Contract\ProductIdentifierGenerator;
class AcademicProductGenerator implements ProductIdentifierGenerator
{
/**
* @param object $source
* @return bool
*/
public function supports(object $source): bool
{
return $source instanceof FeeRate;
}
/**
* @param object $source
* @return ProductIdentifierDTO
*/
public function generate(object $source): ProductIdentifierDTO
{
/** @var FeeRate $rate */
$rate = $source;
$rate->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
);
}
}

9
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);
}
}

2
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
) {}
}

2
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

2
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;

2
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

2
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
{

2
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

2
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;

11
app/Domains/Billing/Actions/Catalog/ProvisionProductFromFeeRate.php

@ -0,0 +1,11 @@
<?php
namespace App\Domains\Billing\Actions\Catalog;
class ProvisionProductFromFeeRate
{
public function execute(): void
{
//
}
}

11
app/Domains/Billing/Actions/Discounts/DefineDiscount.php

@ -0,0 +1,11 @@
<?php
namespace App\Domains\Billing\Actions\Discounts;
class DefineDiscount
{
public function execute(): void
{
//
}
}

11
app/Domains/Billing/Actions/Invoicing/DraftInvoice.php

@ -0,0 +1,11 @@
<?php
namespace App\Domains\Billing\Actions\Invoicing;
class DraftInvoice
{
public function execute(): void
{
//
}
}

13
app/Domains/Billing/DTOs/Catalog/ProvisionProductDTO.php

@ -0,0 +1,13 @@
<?php
namespace App\Domains\Billing\DTOs\Catalog;
readonly class ProvisionProductDTO
{
public function __construct(
public string $name,
public string $code,
public string $pricingType,
public string $pricingId,
) {}
}

19
app/Domains/Billing/DTOs/Discounts/DefineDiscountDTO.php

@ -0,0 +1,19 @@
<?php
namespace App\Domains\Billing\DTOs\Discounts;
readonly class DefineDiscountDTO
{
/**
* @param string $name
* @param string $type
* @param string $value
* @param array<int, array{context: string, condition_type: string, operator: string, value: string}> $rules
*/
public function __construct(
public string $name,
public string $type,
public string $value,
public array $rules,
) {}
}

14
app/Domains/Billing/DTOs/Invoicing/DraftInvoiceDTO.php

@ -0,0 +1,14 @@
<?php
namespace App\Domains\Billing\DTOs\Invoicing;
readonly class DraftInvoiceDTO
{
public function __construct(
public string $name,
public string $clientId,
public string $clientRefId,
public array $items,
public array $metadata,
) {}
}

24
app/Domains/Billing/DTOs/Products/ProductIdentifierDTO.php

@ -0,0 +1,24 @@
<?php
namespace App\Domains\Billing\DTOs\Products;
/**
* Readonly Data Transfer Object containing generated product credentials.
*/
readonly class ProductIdentifierDTO
{
/**
* Construct the identifier result instance.
*
* @param string $code Short deterministic business code (e.g. PMDK-UKT-PRK-SI-25G).
* @param string $name Full descriptive name for UI and invoice documents.
* @param string $feeRateType relation map
* @param string|int $feeRateId relation identifier to fee rate
*/
public function __construct(
public string $code,
public string $name,
public string $feeRateType,
public string|int $feeRateId
) {}
}

9
app/Domains/Billing/Enums/Catalog/AccountBalance.php

@ -0,0 +1,9 @@
<?php
namespace App\Domains\Billing\Enums\Catalog;
enum AccountBalance: string
{
case CREDIT = 'credit';
case DEBIT = 'debit';
}

12
app/Domains/Billing/Enums/Catalog/AccountClassification.php

@ -0,0 +1,12 @@
<?php
namespace App\Domains\Billing\Enums\Catalog;
enum AccountClassification: string
{
case LIABILITY = 'liability';
case ASSET = 'asset';
case EQUITY = 'equity';
case REVENUE = 'revenue';
case EXPENSE = 'expense';
}

12
app/Domains/Billing/Enums/Discounts/DiscountOperator.php

@ -0,0 +1,12 @@
<?php
namespace App\Domains\Billing\Enums\Discounts;
enum DiscountOperator: string
{
case GREATER = '>';
case GREATER_OR_EQUAL = '>=';
case LESS = '<';
case LESS_OR_EQUAL = '<=';
case EQUAL = '=';
}

9
app/Domains/Billing/Enums/Discounts/DiscountType.php

@ -0,0 +1,9 @@
<?php
namespace App\Domains\Billing\Enums\Discounts;
enum DiscountType: string
{
case PERCENTAGE = 'percentage';
case FIXED = 'fixed';
}

11
app/Domains/Billing/Enums/Invoicing/InvoiceStatus.php

@ -0,0 +1,11 @@
<?php
namespace App\Domains\Billing\Enums\Invoicing;
enum InvoiceStatus: string
{
case PENDING = 'pending';
case PARTIALLY_PAID = 'partially_paid';
case PAID = 'paid';
case VOID = 'void';
}

10
app/Domains/Billing/Enums/Invoicing/InvoiceType.php

@ -0,0 +1,10 @@
<?php
namespace App\Domains\Billing\Enums\Invoicing;
enum InvoiceType: string
{
case STUDENT_INVOICE = 'student_invoice';
case VENDOR_BILL = 'vendor_bill';
case CREDIT_NOTE = 'credit_note';
}

21
app/Domains/Billing/Models/ChartOfAccount.php

@ -0,0 +1,21 @@
<?php
namespace App\Domains\Billing\Models;
use Database\Factories\Billing\ChartOfAccountFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Illuminate\Database\Eloquent\Model;
#[Fillable([
'code',
'name',
'classification',
'normal_balance'
])]
#[UseFactory(ChartOfAccountFactory::class)]
class ChartOfAccount extends Model
{
use HasFactory;
}

29
app/Domains/Billing/Models/Discount.php

@ -0,0 +1,29 @@
<?php
namespace App\Domains\Billing\Models;
use Database\Factories\Billing\DiscountFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
#[Fillable([
'name',
'type',
'value',
'rules',
'is_active',
'chart_of_account_id'
])]
#[UseFactory(DiscountFactory::class)]
class Discount extends Model
{
use HasFactory;
public function chartOfAccount(): BelongsTo
{
return $this->belongsTo(ChartOfAccount::class);
}
}

46
app/Domains/Billing/Models/DiscountSnapshot.php

@ -0,0 +1,46 @@
<?php
namespace App\Domains\Billing\Models;
use Database\Factories\Billing\DiscountSnapshotFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
#[Fillable([
'name',
'type',
'value',
'amount',
'chart_of_account_id',
'discount_id',
'invoice_id',
'invoice_item_id'
])]
#[UseFactory(DiscountSnapshotFactory::class)]
class DiscountSnapshot extends Model
{
use HasFactory;
public function chartOfAccount(): BelongsTo
{
return $this->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);
}
}

27
app/Domains/Billing/Models/Invoice.php

@ -0,0 +1,27 @@
<?php
namespace App\Domains\Billing\Models;
use Database\Factories\Billing\InvoiceFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Illuminate\Database\Eloquent\Model;
#[Fillable([
'code',
'name',
'client_detail',
'client_ref_id',
'type',
'gross_amount',
'discount_amount',
'net_amount',
'status',
'client_id'
])]
#[UseFactory(InvoiceFactory::class)]
class Invoice extends Model
{
use HasFactory;
}

38
app/Domains/Billing/Models/InvoiceItem.php

@ -0,0 +1,38 @@
<?php
namespace App\Domains\Billing\Models;
use Database\Factories\Billing\InvoiceItemFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
#[Fillable([
'code',
'name',
'quantity',
'unit_price',
'gross_amount',
'discount_amount',
'net_amount',
'invoice_id',
'product_id',
'chart_of_account_id'
])]
#[UseFactory(InvoiceItemFactory::class)]
class InvoiceItem extends Model
{
use HasFactory;
public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
public function chartOfAccount(): BelongsTo
{
return $this->belongsTo(ChartOfAccount::class);
}
}

32
app/Domains/Billing/Models/Product.php

@ -0,0 +1,32 @@
<?php
namespace App\Domains\Billing\Models;
use Database\Factories\Billing\ProductFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Attributes\UseFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\MorphTo;
#[Fillable([
'code',
'name',
'chart_of_account_id'
])]
#[UseFactory(ProductFactory::class)]
class Product extends Model
{
use HasFactory;
public function chartOfAccount(): BelongsTo
{
return $this->belongsTo(ChartOfAccount::class);
}
public function pricing(): MorphTo
{
return $this->morphTo();
}
}

23
app/Domains/Billing/Providers/BillingServiceProvider.php

@ -0,0 +1,23 @@
<?php
namespace App\Domains\Billing\Providers;
use App\Domains\System\Traits\Provider\RegistersDomainEvents;
use Illuminate\Support\ServiceProvider;
class BillingServiceProvider extends ServiceProvider
{
use RegistersDomainEvents;
protected array $listen = [];
public function register(): void
{
$this->app->register(RelationshipServiceProvider::class);
}
public function boot(): void
{
$this->registerEvents();
}
}

38
app/Domains/Billing/Providers/RelationshipServiceProvider.php

@ -0,0 +1,38 @@
<?php
namespace App\Domains\Billing\Providers;
use App\Domains\Billing\Models\Invoice;
use App\Domains\Channel\Models\Client;
use Illuminate\Support\ServiceProvider;
class RelationshipServiceProvider extends ServiceProvider
{
/**
* Boot cross-domain runtime relationships.
*
* This provider is strictly dedicated to runtime, decoupled cross-domain
* relationship definitions. Use Model::resolveRelationUsing() here to wire
* explicit polymorphic or foreign-key macros between models that live in
* different domains — without introducing a hard compile-time dependency
* between those domains.
*
* Do NOT add route registrations, bindings, event listeners, or any other
* bootstrapping logic here. Keep this file focused on relationships only.
*
* Example:
*
* Order::resolveRelationUsing('customer', fn (Order $order) =>
* $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');
});
}
}

27
app/Domains/Billing/Support/Contract/ProductIdentifierGenerator.php

@ -0,0 +1,27 @@
<?php
namespace App\Domains\Billing\Support\Contract;
use App\Domains\Billing\DTOs\Products\ProductIdentifierDTO;
/**
* Contract interface for generating product codes and display names.
*/
interface ProductIdentifierGenerator
{
/**
* Determine if this generator supports the given pricing source entity.
*
* @param object $source The pricing source model instance (e.g., FeeRate, RentalRate).
* @return bool True if supported, false otherwise.
*/
public function supports(object $source): bool;
/**
* Generate product code and descriptive name from a pricing source entity.
*
* @param object $source The pricing source model containing context attributes.
* @return ProductIdentifierDTO Typed DTO holding generated code and name.
*/
public function generate(object $source): ProductIdentifierDTO;
}

2
app/Domains/Channel/Actions/Api/SynchronizeWithApi.php

@ -4,7 +4,7 @@ namespace App\Domains\Channel\Actions\Api;
use App\Domains\Channel\Enums\Api\ApiType;
use App\Domains\Channel\Models\ApiEndpoint;
use App\Domains\Channel\Support\Mapper\ApiMapperRegistry;
use App\Domains\Channel\Support\Registry\ApiMapperRegistry;
use App\Domains\System\Actions\Integration\RunGenericImportPipeline;
use Exception;
use Illuminate\Http\Client\ConnectionException;

4
app/Domains/Channel/Enums/Api/ApiType.php

@ -2,8 +2,8 @@
namespace App\Domains\Channel\Enums\Api;
use App\Domains\Channel\Support\Mapper\ApiMapperRegistry;
use App\Domains\System\Support\Integration\DataPayloadMapper;
use App\Domains\Channel\Support\Registry\ApiMapperRegistry;
use App\Domains\System\Support\Contract\DataPayloadMapper;
use App\UI\Enums\Concerns\InteractsWithLabels;
use App\UI\Enums\Contracts\HasLabel;

4
app/Domains/Channel/Support/Mapper/ApiMapperRegistry.php → app/Domains/Channel/Support/Registry/ApiMapperRegistry.php

@ -1,9 +1,9 @@
<?php
namespace App\Domains\Channel\Support\Mapper;
namespace App\Domains\Channel\Support\Registry;
use App\Domains\Channel\Enums\Api\ApiType;
use App\Domains\System\Support\Integration\DataPayloadMapper;
use App\Domains\System\Support\Contract\DataPayloadMapper;
use InvalidArgumentException;
class ApiMapperRegistry

2
app/Domains/Identity/Integration/Mappers/UserDataMapper.php

@ -9,7 +9,7 @@ use App\Domains\Identity\DTOs\Onboarding\ProvisionUserDTO;
use App\Domains\Identity\Enums\RoleType;
use App\Domains\Identity\Events\Integration\UserImportWasProcessed;
use App\Domains\Identity\Models\User;
use App\Domains\System\Support\Integration\DataPayloadMapper;
use App\Domains\System\Support\Contract\DataPayloadMapper;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Throwable;

11
app/Domains/Payment/Listeners/VirtualAccount/InitializeVirtualAccountForPublishedInvoice.php

@ -0,0 +1,11 @@
<?php
namespace App\Domains\Payment\Listeners\VirtualAccount;
class InitializeVirtualAccountForPublishedInvoice
{
public function handle(object $event): void
{
//
}
}

2
app/Domains/System/Actions/Integration/RunGenericImportPipeline.php

@ -2,7 +2,7 @@
namespace App\Domains\System\Actions\Integration;
use App\Domains\System\Support\Integration\DataPayloadMapper;
use App\Domains\System\Support\Contract\DataPayloadMapper;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

12
app/Domains/System/Providers/SystemServiceProvider.php

@ -12,6 +12,7 @@ use App\Domains\System\Queries\GetSystemSettings;
use App\Domains\System\Traits\Provider\RegistersDomainEvents;
use Illuminate\Support\Carbon;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
class SystemServiceProvider extends ServiceProvider
{
@ -47,5 +48,16 @@ class SystemServiceProvider extends ServiceProvider
Carbon::macro('toUserTz', fn () => $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);
});
}
}

2
app/Domains/System/Support/Integration/DataPayloadMapper.php → app/Domains/System/Support/Contract/DataPayloadMapper.php

@ -1,6 +1,6 @@
<?php
namespace App\Domains\System\Support\Integration;
namespace App\Domains\System\Support\Contract;
use Illuminate\Database\Eloquent\Model;

3
app/Providers/AppServiceProvider.php

@ -2,7 +2,10 @@
namespace App\Providers;
use Event;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\ServiceProvider;
use ReflectionClass;
class AppServiceProvider extends ServiceProvider
{

3
bootstrap/providers.php

@ -36,4 +36,7 @@ return [
// Domain: Academic
AcademicServiceProvider::class,
// Domain: Billing
App\Domains\Billing\Providers\BillingServiceProvider::class,
];

4
database/factories/Finance/ChartOfAccountFactory.php → database/factories/Billing/ChartOfAccountFactory.php

@ -1,8 +1,8 @@
<?php
namespace Database\Factories\Finance;
namespace Database\Factories\Billing;
use App\Domains\Finance\Models\ChartOfAccount;
use App\Domains\Billing\Models\ChartOfAccount;
use Illuminate\Database\Eloquent\Factories\Factory;
/**

19
database/factories/Billing/DiscountFactory.php

@ -0,0 +1,19 @@
<?php
namespace Database\Factories\Billing;
use App\Domains\Billing\Models\Discount;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Discount>
*/
class DiscountFactory extends Factory
{
protected $model = Discount::class;
public function definition(): array
{
return [];
}
}

19
database/factories/Billing/DiscountRulesFactory.php

@ -0,0 +1,19 @@
<?php
namespace Database\Factories\Billing;
use App\Domains\Billing\Models\DiscountRules;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<DiscountRules>
*/
class DiscountRulesFactory extends Factory
{
protected $model = DiscountRules::class;
public function definition(): array
{
return [];
}
}

19
database/factories/Billing/DiscountSnapshotFactory.php

@ -0,0 +1,19 @@
<?php
namespace Database\Factories\Billing;
use App\Domains\Billing\Models\DiscountSnapshot;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<DiscountSnapshot>
*/
class DiscountSnapshotFactory extends Factory
{
protected $model = DiscountSnapshot::class;
public function definition(): array
{
return [];
}
}

4
database/factories/Finance/InvoiceFactory.php → database/factories/Billing/InvoiceFactory.php

@ -1,8 +1,8 @@
<?php
namespace Database\Factories\Finance;
namespace Database\Factories\Billing;
use App\Domains\Finance\Models\Invoice;
use App\Domains\Billing\Models\Invoice;
use Illuminate\Database\Eloquent\Factories\Factory;
/**

19
database/factories/Billing/InvoiceItemFactory.php

@ -0,0 +1,19 @@
<?php
namespace Database\Factories\Billing;
use App\Domains\Billing\Models\InvoiceItem;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<InvoiceItem>
*/
class InvoiceItemFactory extends Factory
{
protected $model = InvoiceItem::class;
public function definition(): array
{
return [];
}
}

19
database/factories/Billing/ProductFactory.php

@ -0,0 +1,19 @@
<?php
namespace Database\Factories\Billing;
use App\Domains\Billing\Models\Product;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Product>
*/
class ProductFactory extends Factory
{
protected $model = Product::class;
public function definition(): array
{
return [];
}
}

19
database/factories/Finance/PaymentFactory.php

@ -1,19 +0,0 @@
<?php
namespace Database\Factories\Finance;
use App\Domains\Finance\Models\Payment;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Payment>
*/
class PaymentFactory extends Factory
{
protected $model = Payment::class;
public function definition(): array
{
return [];
}
}

19
database/factories/System/FileFactory.php

@ -1,19 +0,0 @@
<?php
namespace Database\Factories\System;
use App\Domains\System\Models\File;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<File>
*/
class FileFactory extends Factory
{
protected $model = File::class;
public function definition(): array
{
return [];
}
}

35
database/migrations/2026_07_06_035952_create_payments_table.php

@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('payments', function (Blueprint $table) {
$table->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');
}
};

43
database/migrations/2026_07_15_071124_create_student_invoice_reports_table.php

@ -1,43 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('student_invoice_reports', function (Blueprint $table) {
$table->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');
}
};

38
database/migrations/2026_07_15_071129_create_invoice_reports_table.php

@ -1,38 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('invoice_reports', function (Blueprint $table) {
$table->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');
}
};

8
database/migrations/2026_07_06_034148_create_chart_of_accounts_table.php → 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();
});
}

31
database/migrations/2026_09_08_022816_create_products_table.php

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->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');
}
};

15
database/migrations/2026_07_06_035903_create_invoices_table.php → 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();
});
}

37
database/migrations/2026_09_08_022818_create_invoice_items_table.php

@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('invoice_items', function (Blueprint $table) {
$table->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');
}
};

10
database/migrations/2026_07_13_045042_create_product_mappings_table.php → 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');
}
};

35
database/migrations/2026_09_08_022821_create_discount_snapshots_table.php

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('discount_snapshots', function (Blueprint $table) {
$table->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');
}
};

7
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')
<div class="accordion-item" {{ $attributes }}>
<h2 class="accordion-header">
<button
@ -7,9 +10,9 @@
data-coreui-toggle="collapse"
data-coreui-target="#{{ $itemId }}"
@if (! is_string($header))
{{ $header->attributes->merge(['class' => 'accordion-button collapsed']) }}
{{ $header->attributes->merge(['class' => $class]) }}
@else
class="accordion-button collapsed"
class="{{ $class }}"
@endif
wire:ignore.self
aria-expanded="false"

6
resources/views/components/container/accordion.blade.php

@ -1,3 +1,5 @@
@props(['id' => md5(microtime(true))])
@props(['id' => 'accordion-' . md5(microtime(true))])
<div class="accordion" id="{{ $id }}">{{ $slot }}</div>
<div {{ $attributes->merge(['class' => 'accordion accordion-flush d-flex flex-column', 'id' => $id]) }} wire:ignore.self>
{{ $slot }}
</div>

34
resources/views/pages/admission/admission-schedule/⚡detail-view/detail-view.blade.php

@ -72,31 +72,19 @@
<x-button type="button" theme="primary" size="sm" icon="tabler-stack-pull" :label="__('ui/button.publish')" class="px-3 fw-medium" />
</x-slot:actions>
<div class="accordion accordion-flush d-flex flex-column gap-2" id="fee-detail" wire:ignore.self>
<x-container.accordion id="fee-detail">
@foreach ($this->studyPrograms as $study)
<div class="accordion-item border border-body-tertiary rounded-3 overflow-hidden shadow-sm">
<!-- Accordion Header -->
<h2 class="accordion-header">
<button class="accordion-button collapsed bg-body-tertiary py-3 px-4 d-flex align-items-center gap-3"
type="button"
data-coreui-toggle="collapse"
data-coreui-target="#study-{{ $study->ulid }}"
aria-expanded="false"
aria-controls="study-{{ $study->ulid }}">
<div class="d-flex align-items-center gap-2">
<x-container.accordion-item item-id="study-{{ $study->ulid }}" wire:key="study-item-{{ $study->id }}">
<x-slot:header class="d-flex align-items-center gap-2">
<x-dynamic-component
component="badge"
:attributes="new Illuminate\View\ComponentAttributeBag($this->setStudyProgramBadgeAttribute($study->id))"
/>
<span class="fw-bold text-body fs-6">{{ $study->name }}</span>
</div>
</button>
</h2>
</x-slot:header>
<!-- Accordion Body -->
<div id="study-{{ $study->ulid }}" class="accordion-collapse collapse" data-coreui-parent="#fee-detail">
<div class="accordion-body p-0">
<!-- Scrollable Fee Table -->
<x-slot:content>
<div class="table-responsive" style="max-height: 450px;">
<table class="table table-hover align-middle mb-0 text-nowrap small" style="table-layout: fixed; min-width: {{ 200 + ($this->feeTypes->count() * 160) }}px;">
<thead class="position-sticky top-0 z-2 shadow-sm">
@ -106,7 +94,6 @@
<span title="{{ __('domains/admission/field.academic_program.name') }}">
{{ __('domains/admission/field.academic_program.name') }}
</span>
</th>
<!-- Dynamic Fee Columns -->
@foreach ($this->feeTypes as $fee)
<th class="text-end py-2.5 text-uppercase fs-7 fw-semibold text-body-secondary pe-3" style="width: 160px;">
@ -125,12 +112,12 @@
@foreach ($this->feeTypes as $fee)
@php($lookupKey = "{$study->id}_{$program->id}_{$fee->id}")
@php($feeRate = isset($this->feeRates[$lookupKey]) ? $this->feeRates[$lookupKey] : new Money(0))
<td wire:key="{{ $lookupKey }}" class="text-end pe-3 py-2">
<td wire:key="cell-{{ $lookupKey }}" class="text-end pe-3 py-2">
<x-badge
data-fee-type-id="{{ $fee->ulid }}"
data-program-id="{{ $program->ulid }}"
data-study-program-id="{{ $study->ulid }}"
data-fee-type="{{ $feeRate }}"
data-fee-type="{{ $feeRate->toFloat() }}"
class="cursor-pointer px-3 py-1.5 font-monospace w-100 text-end justify-content-end border shadow-2xs transition-all hover-opacity-80"
x-on:click="$js.ask"
:label="$feeRate"
@ -143,11 +130,10 @@
</tbody>
</table>
</div>
</div>
</div>
</div>
</x-slot:content>
</x-container.accordion-item>
@endforeach
</div>
</x-container.accordion>
</x-card>
</div>
</div>

2
resources/views/pages/admission/admission-schedule/⚡detail-view/detail-view.php

@ -20,7 +20,7 @@ use Livewire\Attributes\Locked;
use Livewire\Component;
new #[Layout('components.layouts.app')]
#[Seo(title: 'domains/admission/seo.admission_schedule-detail.title', description: 'domains/admission/seo.admission_schedule-detail.description', keywords: 'domains/admission/seo.admission_schedule-detail.keywords')]
#[Seo(title: 'domains/admission/seo.admission_schedule_detail.title', description: 'domains/admission/seo.admission_schedule_detail.description', keywords: 'domains/admission/seo.admission_schedule-detail.keywords')]
#[LayoutData(header: 'domains/admission/seo.admission_schedule.title', breadcrumbs: ['ui/menu.dashboard' => 'dashboard', 'domains/admission/seo.admission_schedule.title' => 'admission-schedules.index', '{admissionTrack.name} {term.name}' => ''], context: 'admissionSchedule')]
class extends Component
{

2
tests/Unit/Domains/System/Actions/Integration/RunGenericImportPipelineTest.php

@ -3,7 +3,7 @@
namespace Tests\Unit\Domains\System\Actions\Integration;
use App\Domains\System\Actions\Integration\RunGenericImportPipeline;
use App\Domains\System\Support\Integration\DataPayloadMapper;
use App\Domains\System\Support\Contract\DataPayloadMapper;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Schema;

Loading…
Cancel
Save