523 lines
25 KiB
PHP
523 lines
25 KiB
PHP
<?php
|
||
|
||
namespace Modules\IndustrialEngineering\Domain;
|
||
|
||
/**
|
||
* Canonical registry of every Industrial Engineering entity, its table,
|
||
* bounded context, and cross-module integration boundaries.
|
||
*
|
||
* This is the single source of truth for the digital-thread domain model.
|
||
* Migrations in Database/Migrations must stay aligned with this registry.
|
||
*/
|
||
final class IndustrialDomainRegistry
|
||
{
|
||
/**
|
||
* @return array<string, array{
|
||
* table: string,
|
||
* model: class-string|null,
|
||
* context: string,
|
||
* aggregate_root: bool,
|
||
* description: string
|
||
* }>
|
||
*/
|
||
public static function entities(): array
|
||
{
|
||
return [
|
||
// ── Master Data ──────────────────────────────────────────────
|
||
'manufacturer' => [
|
||
'table' => 'ie_manufacturers',
|
||
'model' => \Modules\IndustrialEngineering\Models\Manufacturer::class,
|
||
'context' => BoundedContext::MASTER_DATA,
|
||
'aggregate_root' => true,
|
||
'description' => 'Approved manufacturer / OEM catalog entry.',
|
||
],
|
||
'supplier' => [
|
||
'table' => 'ie_suppliers',
|
||
'model' => \Modules\IndustrialEngineering\Models\Supplier::class,
|
||
'context' => BoundedContext::MASTER_DATA,
|
||
'aggregate_root' => true,
|
||
'description' => 'Approved vendor; may link to contacts or intercompany business.',
|
||
],
|
||
'item_master' => [
|
||
'table' => 'ie_item_masters',
|
||
'model' => \Modules\IndustrialEngineering\Models\ItemMaster::class,
|
||
'context' => BoundedContext::MASTER_DATA,
|
||
'aggregate_root' => true,
|
||
'description' => 'Canonical industrial item (part, assembly, device, etc.).',
|
||
],
|
||
'item_revision' => [
|
||
'table' => 'ie_item_revisions',
|
||
'model' => \Modules\IndustrialEngineering\Models\ItemRevision::class,
|
||
'context' => BoundedContext::MASTER_DATA,
|
||
'aggregate_root' => false,
|
||
'description' => 'Versioned engineering definition of an item master.',
|
||
],
|
||
'supplier_part_number' => [
|
||
'table' => 'ie_supplier_part_numbers',
|
||
'model' => \Modules\IndustrialEngineering\Models\SupplierPartNumber::class,
|
||
'context' => BoundedContext::MASTER_DATA,
|
||
'aggregate_root' => false,
|
||
'description' => 'AVL/AML supplier part number and commercial terms.',
|
||
],
|
||
'part_equivalent' => [
|
||
'table' => 'ie_part_equivalents',
|
||
'model' => \Modules\IndustrialEngineering\Models\PartEquivalent::class,
|
||
'context' => BoundedContext::MASTER_DATA,
|
||
'aggregate_root' => false,
|
||
'description' => 'Approved alternate items (form-fit-function, functional, upgrade).',
|
||
],
|
||
'item_classification' => [
|
||
'table' => 'ie_item_classifications',
|
||
'model' => \Modules\IndustrialEngineering\Models\ItemClassification::class,
|
||
'context' => BoundedContext::MASTER_DATA,
|
||
'aggregate_root' => false,
|
||
'description' => 'Taxonomy mapping to industrial category seeders.',
|
||
],
|
||
|
||
// ── BOM & Configuration ──────────────────────────────────────
|
||
'product_platform' => [
|
||
'table' => 'ie_product_platforms',
|
||
'model' => \Modules\IndustrialEngineering\Models\ProductPlatform::class,
|
||
'context' => BoundedContext::BOM_CONFIGURATION,
|
||
'aggregate_root' => true,
|
||
'description' => 'Product / production-line family.',
|
||
],
|
||
'line_template' => [
|
||
'table' => 'ie_line_templates',
|
||
'model' => \Modules\IndustrialEngineering\Models\LineTemplate::class,
|
||
'context' => BoundedContext::BOM_CONFIGURATION,
|
||
'aggregate_root' => false,
|
||
'description' => 'Configurable line or machine template within a platform.',
|
||
],
|
||
'line_revision' => [
|
||
'table' => 'ie_line_revisions',
|
||
'model' => \Modules\IndustrialEngineering\Models\LineRevision::class,
|
||
'context' => BoundedContext::BOM_CONFIGURATION,
|
||
'aggregate_root' => true,
|
||
'description' => 'Official approved/released version of a line template.',
|
||
],
|
||
'bom' => [
|
||
'table' => 'ie_boms',
|
||
'model' => \Modules\IndustrialEngineering\Models\Bom::class,
|
||
'context' => BoundedContext::BOM_CONFIGURATION,
|
||
'aggregate_root' => true,
|
||
'description' => 'EBOM, MBOM, or SBOM header (distinct but linked by line revision).',
|
||
],
|
||
'bom_line' => [
|
||
'table' => 'ie_bom_lines',
|
||
'model' => \Modules\IndustrialEngineering\Models\BomLine::class,
|
||
'context' => BoundedContext::BOM_CONFIGURATION,
|
||
'aggregate_root' => false,
|
||
'description' => 'Multi-level BOM line with alternates, costing, and routing refs.',
|
||
],
|
||
'bom_line_alternate' => [
|
||
'table' => 'ie_bom_line_alternates',
|
||
'model' => \Modules\IndustrialEngineering\Models\BomLineAlternate::class,
|
||
'context' => BoundedContext::BOM_CONFIGURATION,
|
||
'aggregate_root' => false,
|
||
'description' => 'Approved substitute within an alternate group.',
|
||
],
|
||
'bom_change_request' => [
|
||
'table' => 'ie_bom_change_requests',
|
||
'model' => \Modules\IndustrialEngineering\Models\BomChangeRequest::class,
|
||
'context' => BoundedContext::BOM_CONFIGURATION,
|
||
'aggregate_root' => true,
|
||
'description' => 'ECR – engineering change request.',
|
||
],
|
||
'bom_change_order' => [
|
||
'table' => 'ie_bom_change_orders',
|
||
'model' => \Modules\IndustrialEngineering\Models\BomChangeOrder::class,
|
||
'context' => BoundedContext::BOM_CONFIGURATION,
|
||
'aggregate_root' => true,
|
||
'description' => 'ECO – approved implementation of a change request.',
|
||
],
|
||
'bom_effectivity' => [
|
||
'table' => 'ie_bom_effectivities',
|
||
'model' => \Modules\IndustrialEngineering\Models\BomEffectivity::class,
|
||
'context' => BoundedContext::BOM_CONFIGURATION,
|
||
'aggregate_root' => false,
|
||
'description' => 'Effectivity rules (date, serial, customer, project, revision).',
|
||
],
|
||
|
||
// ── Costing & Release ────────────────────────────────────────
|
||
'work_center' => [
|
||
'table' => 'ie_work_centers',
|
||
'model' => \Modules\IndustrialEngineering\Models\WorkCenter::class,
|
||
'context' => BoundedContext::COSTING_RELEASE,
|
||
'aggregate_root' => true,
|
||
'description' => 'Machine/labor/overhead rates for routing.',
|
||
],
|
||
'routing_operation' => [
|
||
'table' => 'ie_routing_operations',
|
||
'model' => \Modules\IndustrialEngineering\Models\RoutingOperation::class,
|
||
'context' => BoundedContext::COSTING_RELEASE,
|
||
'aggregate_root' => false,
|
||
'description' => 'Sequence of operations for a line revision.',
|
||
],
|
||
'cost_scenario' => [
|
||
'table' => 'ie_cost_scenarios',
|
||
'model' => \Modules\IndustrialEngineering\Models\CostScenario::class,
|
||
'context' => BoundedContext::COSTING_RELEASE,
|
||
'aggregate_root' => true,
|
||
'description' => 'What-if or baseline cost rollup for a BOM/line revision.',
|
||
],
|
||
'cost_scenario_line' => [
|
||
'table' => 'ie_cost_scenario_lines',
|
||
'model' => \Modules\IndustrialEngineering\Models\CostScenarioLine::class,
|
||
'context' => BoundedContext::COSTING_RELEASE,
|
||
'aggregate_root' => false,
|
||
'description' => 'Per-line cost with optional alternate selection.',
|
||
],
|
||
'release_package' => [
|
||
'table' => 'ie_release_packages',
|
||
'model' => \Modules\IndustrialEngineering\Models\ReleasePackage::class,
|
||
'context' => BoundedContext::COSTING_RELEASE,
|
||
'aggregate_root' => true,
|
||
'description' => 'Locked MBOM/routing/cost snapshot approved for production.',
|
||
],
|
||
'release_signoff' => [
|
||
'table' => 'ie_release_signoffs',
|
||
'model' => \Modules\IndustrialEngineering\Models\ReleaseSignoff::class,
|
||
'context' => BoundedContext::COSTING_RELEASE,
|
||
'aggregate_root' => false,
|
||
'description' => 'Multi-discipline approval on a release package.',
|
||
],
|
||
'manufacturing_work_order' => [
|
||
'table' => 'ie_manufacturing_work_orders',
|
||
'model' => \Modules\IndustrialEngineering\Models\ManufacturingWorkOrder::class,
|
||
'context' => BoundedContext::COSTING_RELEASE,
|
||
'aggregate_root' => true,
|
||
'description' => 'Shop-floor work order with frozen BOM/routing snapshot.',
|
||
],
|
||
|
||
// ── Serialization & As-Built ─────────────────────────────────
|
||
'serial_registry' => [
|
||
'table' => 'ie_serial_registry',
|
||
'model' => \Modules\IndustrialEngineering\Models\SerialRegistry::class,
|
||
'context' => BoundedContext::SERIALIZATION,
|
||
'aggregate_root' => true,
|
||
'description' => 'Global serial/lot registry for units and components.',
|
||
],
|
||
'serialized_unit' => [
|
||
'table' => 'ie_serialized_units',
|
||
'model' => \Modules\IndustrialEngineering\Models\SerializedUnit::class,
|
||
'context' => BoundedContext::SERIALIZATION,
|
||
'aggregate_root' => true,
|
||
'description' => 'As-built unit built from a release package / work order.',
|
||
],
|
||
'serialized_unit_component' => [
|
||
'table' => 'ie_serialized_unit_components',
|
||
'model' => \Modules\IndustrialEngineering\Models\SerializedUnitComponent::class,
|
||
'context' => BoundedContext::SERIALIZATION,
|
||
'aggregate_root' => false,
|
||
'description' => 'Installed component genealogy (serial, brand, warranty).',
|
||
],
|
||
'build_genealogy_event' => [
|
||
'table' => 'ie_build_genealogy_events',
|
||
'model' => \Modules\IndustrialEngineering\Models\BuildGenealogyEvent::class,
|
||
'context' => BoundedContext::SERIALIZATION,
|
||
'aggregate_root' => false,
|
||
'description' => 'Immutable audit event during build or QC.',
|
||
],
|
||
|
||
// ── Installed Base ───────────────────────────────────────────
|
||
'customer_installed_asset' => [
|
||
'table' => 'ie_customer_installed_assets',
|
||
'model' => \Modules\IndustrialEngineering\Models\CustomerInstalledAsset::class,
|
||
'context' => BoundedContext::INSTALLED_BASE,
|
||
'aggregate_root' => true,
|
||
'description' => 'As-sold / as-installed customer line or device.',
|
||
],
|
||
'customer_asset_warranty' => [
|
||
'table' => 'ie_customer_asset_warranties',
|
||
'model' => \Modules\IndustrialEngineering\Models\CustomerAssetWarranty::class,
|
||
'context' => BoundedContext::INSTALLED_BASE,
|
||
'aggregate_root' => false,
|
||
'description' => 'Warranty coverage at asset or component level.',
|
||
],
|
||
'commissioning_record' => [
|
||
'table' => 'ie_commissioning_records',
|
||
'model' => \Modules\IndustrialEngineering\Models\CommissioningRecord::class,
|
||
'context' => BoundedContext::INSTALLED_BASE,
|
||
'aggregate_root' => true,
|
||
'description' => 'Commissioning checklist and handover documents.',
|
||
],
|
||
'installation_task' => [
|
||
'table' => 'ie_installation_tasks',
|
||
'model' => \Modules\IndustrialEngineering\Models\InstallationTask::class,
|
||
'context' => BoundedContext::INSTALLED_BASE,
|
||
'aggregate_root' => false,
|
||
'description' => 'Install/commission/training task on an installed asset.',
|
||
],
|
||
|
||
// ── Sourcing ─────────────────────────────────────────────────
|
||
'intercompany_stock_visibility' => [
|
||
'table' => 'ie_intercompany_stock_visibility',
|
||
'model' => \Modules\IndustrialEngineering\Models\IntercompanyStockVisibility::class,
|
||
'context' => BoundedContext::SOURCING,
|
||
'aggregate_root' => false,
|
||
'description' => 'Cached cross-company stock availability for an item.',
|
||
],
|
||
'intercompany_transfer_request' => [
|
||
'table' => 'ie_intercompany_transfer_requests',
|
||
'model' => \Modules\IndustrialEngineering\Models\IntercompanyTransferRequest::class,
|
||
'context' => BoundedContext::SOURCING,
|
||
'aggregate_root' => true,
|
||
'description' => 'Internal transfer request between holding companies.',
|
||
],
|
||
'supplier_sourcing_case' => [
|
||
'table' => 'ie_supplier_sourcing_cases',
|
||
'model' => \Modules\IndustrialEngineering\Models\SupplierSourcingCase::class,
|
||
'context' => BoundedContext::SOURCING,
|
||
'aggregate_root' => true,
|
||
'description' => 'Sourcing case when part not available in holding.',
|
||
],
|
||
'vendor_quote_comparison' => [
|
||
'table' => 'ie_vendor_quote_comparisons',
|
||
'model' => \Modules\IndustrialEngineering\Models\VendorQuoteComparison::class,
|
||
'context' => BoundedContext::SOURCING,
|
||
'aggregate_root' => false,
|
||
'description' => 'Vendor quotes ranked for a sourcing case.',
|
||
],
|
||
'procurement_task' => [
|
||
'table' => 'ie_procurement_tasks',
|
||
'model' => \Modules\IndustrialEngineering\Models\ProcurementTask::class,
|
||
'context' => BoundedContext::SOURCING,
|
||
'aggregate_root' => false,
|
||
'description' => 'Actionable procurement step (quote, PR, PO, transfer).',
|
||
],
|
||
|
||
// ── Competitive Research ─────────────────────────────────────
|
||
'research_project' => [
|
||
'table' => 'ie_research_projects',
|
||
'model' => \Modules\IndustrialEngineering\Models\ResearchProject::class,
|
||
'context' => BoundedContext::COMPETITIVE_RESEARCH,
|
||
'aggregate_root' => true,
|
||
'description' => 'R&D or competitive intelligence project.',
|
||
],
|
||
'competitive_product' => [
|
||
'table' => 'ie_competitive_products',
|
||
'model' => \Modules\IndustrialEngineering\Models\CompetitiveProduct::class,
|
||
'context' => BoundedContext::COMPETITIVE_RESEARCH,
|
||
'aggregate_root' => true,
|
||
'description' => 'Competitor product under study.',
|
||
],
|
||
'competitive_product_structure' => [
|
||
'table' => 'ie_competitive_product_structures',
|
||
'model' => \Modules\IndustrialEngineering\Models\CompetitiveProductStructure::class,
|
||
'context' => BoundedContext::COMPETITIVE_RESEARCH,
|
||
'aggregate_root' => false,
|
||
'description' => 'Multi-level teardown structure of a competitor product.',
|
||
],
|
||
'competitive_benchmark' => [
|
||
'table' => 'ie_competitive_benchmarks',
|
||
'model' => \Modules\IndustrialEngineering\Models\CompetitiveBenchmark::class,
|
||
'context' => BoundedContext::COMPETITIVE_RESEARCH,
|
||
'aggregate_root' => false,
|
||
'description' => 'Metric comparison (cost, quality, lead time) vs our line.',
|
||
],
|
||
'engineering_finding' => [
|
||
'table' => 'ie_engineering_findings',
|
||
'model' => \Modules\IndustrialEngineering\Models\EngineeringFinding::class,
|
||
'context' => BoundedContext::COMPETITIVE_RESEARCH,
|
||
'aggregate_root' => false,
|
||
'description' => 'Finding linked to research, ECR, or cost scenario.',
|
||
],
|
||
'lessons_learned' => [
|
||
'table' => 'ie_lessons_learned',
|
||
'model' => \Modules\IndustrialEngineering\Models\LessonsLearned::class,
|
||
'context' => BoundedContext::COMPETITIVE_RESEARCH,
|
||
'aggregate_root' => false,
|
||
'description' => 'Captured lesson from research or field installation.',
|
||
],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* External tables owned by other modules; IE must not duplicate their data.
|
||
*
|
||
* @return array<string, array{
|
||
* module: string,
|
||
* table: string,
|
||
* mode: string,
|
||
* bridge: string|null,
|
||
* fk_columns: array<string, string>
|
||
* }>
|
||
*/
|
||
public static function externalBoundaries(): array
|
||
{
|
||
return [
|
||
'erp_product' => [
|
||
'module' => 'Core',
|
||
'table' => 'products',
|
||
'mode' => IntegrationMode::READ_REFERENCE,
|
||
'bridge' => null,
|
||
'fk_columns' => ['ie_item_masters.product_id' => 'products.id'],
|
||
],
|
||
'erp_variation' => [
|
||
'module' => 'Core',
|
||
'table' => 'variations',
|
||
'mode' => IntegrationMode::READ_REFERENCE,
|
||
'bridge' => null,
|
||
'fk_columns' => ['ie_item_masters.variation_id' => 'variations.id'],
|
||
],
|
||
'erp_contact' => [
|
||
'module' => 'Core',
|
||
'table' => 'contacts',
|
||
'mode' => IntegrationMode::READ_REFERENCE,
|
||
'bridge' => null,
|
||
'fk_columns' => [
|
||
'ie_suppliers.contact_id' => 'contacts.id',
|
||
'ie_customer_installed_assets.customer_id' => 'contacts.id',
|
||
'ie_bom_effectivities.customer_id' => 'contacts.id',
|
||
],
|
||
],
|
||
'erp_transaction' => [
|
||
'module' => 'Core',
|
||
'table' => 'transactions',
|
||
'mode' => IntegrationMode::READ_REFERENCE,
|
||
'bridge' => 'IntegrationBridgeService::linkSaleToInstalledAsset',
|
||
'fk_columns' => [
|
||
'ie_manufacturing_work_orders.production_transaction_id' => 'transactions.id',
|
||
'ie_serialized_units.production_transaction_id' => 'transactions.id',
|
||
'ie_customer_installed_assets.sell_transaction_id' => 'transactions.id',
|
||
'ie_procurement_tasks.purchase_requisition_id' => 'transactions.id',
|
||
],
|
||
],
|
||
'erp_sell_line' => [
|
||
'module' => 'Core',
|
||
'table' => 'transaction_sell_lines',
|
||
'mode' => IntegrationMode::READ_REFERENCE,
|
||
'bridge' => 'IntegrationBridgeService::linkSaleToInstalledAsset',
|
||
'fk_columns' => [
|
||
'ie_customer_installed_assets.sell_line_id' => 'transaction_sell_lines.id',
|
||
],
|
||
],
|
||
'erp_project' => [
|
||
'module' => 'Project',
|
||
'table' => 'pjt_projects',
|
||
'mode' => IntegrationMode::READ_REFERENCE,
|
||
'bridge' => null,
|
||
'fk_columns' => [
|
||
'ie_customer_installed_assets.project_id' => 'pjt_projects.id',
|
||
'ie_research_projects.project_id' => 'pjt_projects.id',
|
||
'ie_bom_effectivities.project_id' => 'pjt_projects.id',
|
||
],
|
||
],
|
||
'maintenance_equipment' => [
|
||
'module' => 'Maintenance',
|
||
'table' => 'maintenance_equipment',
|
||
'mode' => IntegrationMode::WRITE_THROUGH_BRIDGE,
|
||
'bridge' => 'IntegrationBridgeService::syncToMaintenanceEquipment',
|
||
'fk_columns' => [
|
||
'ie_customer_installed_assets.maintenance_equipment_id' => 'maintenance_equipment.id',
|
||
],
|
||
],
|
||
'maintenance_equipment_parts' => [
|
||
'module' => 'Maintenance',
|
||
'table' => 'maintenance_equipment_parts',
|
||
'mode' => IntegrationMode::WRITE_THROUGH_BRIDGE,
|
||
'bridge' => 'IntegrationBridgeService::syncComponentsToEquipmentParts',
|
||
'fk_columns' => [],
|
||
],
|
||
'maintenance_part_catalogs' => [
|
||
'module' => 'Maintenance',
|
||
'table' => 'maintenance_part_catalogs',
|
||
'mode' => IntegrationMode::WRITE_THROUGH_BRIDGE,
|
||
'bridge' => 'IntegrationBridgeService::syncComponentsToEquipmentParts',
|
||
'fk_columns' => [],
|
||
],
|
||
'mfg_recipe' => [
|
||
'module' => 'Manufacturing',
|
||
'table' => 'mfg_recipes',
|
||
'mode' => IntegrationMode::IMPORT_BRIDGE,
|
||
'bridge' => 'IntegrationBridgeService::importMfgRecipeToMbom',
|
||
'fk_columns' => [],
|
||
],
|
||
'asset_exchange_listing' => [
|
||
'module' => 'AssetExchange',
|
||
'table' => 'aex_listings',
|
||
'mode' => IntegrationMode::READ_REFERENCE,
|
||
'bridge' => 'AssetExchangeIeBridgeService::resolveBomForListing',
|
||
'fk_columns' => [
|
||
'aex_listings.ie_line_template_id' => 'ie_line_templates.id',
|
||
'aex_listings.ie_line_revision_id' => 'ie_line_revisions.id',
|
||
'aex_listings.ie_bom_id' => 'ie_boms.id',
|
||
'aex_listings.ie_installed_asset_id' => 'ie_customer_installed_assets.id',
|
||
'aex_listings.maintenance_equipment_id' => 'maintenance_equipment.id',
|
||
],
|
||
],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Digital-thread flow: which contexts feed the next stage.
|
||
*
|
||
* @return array<string, array<int, string>>
|
||
*/
|
||
public static function contextFlow(): array
|
||
{
|
||
return [
|
||
BoundedContext::MASTER_DATA => [BoundedContext::BOM_CONFIGURATION],
|
||
BoundedContext::BOM_CONFIGURATION => [BoundedContext::COSTING_RELEASE],
|
||
BoundedContext::COSTING_RELEASE => [BoundedContext::SERIALIZATION],
|
||
BoundedContext::SERIALIZATION => [BoundedContext::INSTALLED_BASE],
|
||
BoundedContext::INSTALLED_BASE => [BoundedContext::SOURCING],
|
||
BoundedContext::COMPETITIVE_RESEARCH => [
|
||
BoundedContext::BOM_CONFIGURATION,
|
||
BoundedContext::COSTING_RELEASE,
|
||
],
|
||
];
|
||
}
|
||
|
||
public static function tablePrefix(): string
|
||
{
|
||
return 'ie_';
|
||
}
|
||
|
||
public static function moduleName(): string
|
||
{
|
||
return 'IndustrialEngineering';
|
||
}
|
||
|
||
/**
|
||
* @return array<int, string>
|
||
*/
|
||
public static function tablesForContext(string $context): array
|
||
{
|
||
return array_values(array_map(
|
||
fn (array $entity) => $entity['table'],
|
||
array_filter(self::entities(), fn (array $e) => $e['context'] === $context)
|
||
));
|
||
}
|
||
|
||
public static function entityForTable(string $table): ?array
|
||
{
|
||
foreach (self::entities() as $key => $entity) {
|
||
if ($entity['table'] === $table) {
|
||
return array_merge(['key' => $key], $entity);
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* @return array<int, string>
|
||
*/
|
||
public static function allTables(): array
|
||
{
|
||
return array_values(array_map(fn (array $e) => $e['table'], self::entities()));
|
||
}
|
||
|
||
/**
|
||
* @return array<int, class-string>
|
||
*/
|
||
public static function allModelClasses(): array
|
||
{
|
||
return array_values(array_filter(
|
||
array_map(fn (array $e) => $e['model'], self::entities())
|
||
));
|
||
}
|
||
}
|