class BomItem { final int lineNo; final int componentProductId; final double qtyPer; final String? uom; final double? wastagePercent; final bool isOptional; final String? substituteGroup; final int? suggestedWarehouseId; const BomItem({ required this.lineNo, required this.componentProductId, required this.qtyPer, this.uom, this.wastagePercent, this.isOptional = false, this.substituteGroup, this.suggestedWarehouseId, }); factory BomItem.fromJson(Map json) { return BomItem( lineNo: (json['line_no'] ?? json['lineNo']) as int, componentProductId: (json['component_product_id'] ?? json['componentProductId']) as int, qtyPer: double.tryParse(json['qty_per']?.toString() ?? '0') ?? 0, uom: json['uom'] as String?, wastagePercent: json['wastage_percent'] != null ? double.tryParse(json['wastage_percent'].toString()) : null, isOptional: (json['is_optional'] ?? false) as bool, substituteGroup: json['substitute_group'] as String?, suggestedWarehouseId: json['suggested_warehouse_id'] as int?, ); } Map toJson() { return { 'line_no': lineNo, 'component_product_id': componentProductId, 'qty_per': qtyPer, 'uom': uom, 'wastage_percent': wastagePercent, 'is_optional': isOptional, 'substitute_group': substituteGroup, 'suggested_warehouse_id': suggestedWarehouseId, }; } } class BomOutput { final int lineNo; final int outputProductId; final double ratio; final String? uom; final String? outputProductName; final String? outputProductCode; final double? ratioMainUnit; final String? mainUnit; const BomOutput({ required this.lineNo, required this.outputProductId, required this.ratio, this.uom, this.outputProductName, this.outputProductCode, this.ratioMainUnit, this.mainUnit, }); factory BomOutput.fromJson(Map json) { return BomOutput( lineNo: (json['line_no'] ?? json['lineNo']) as int, outputProductId: (json['output_product_id'] ?? json['outputProductId']) as int, ratio: double.tryParse(json['ratio']?.toString() ?? '0') ?? 0, uom: json['uom'] as String?, outputProductName: json['output_product_name'] as String?, outputProductCode: json['output_product_code'] as String?, ratioMainUnit: json['ratio_main_unit'] != null ? double.tryParse(json['ratio_main_unit'].toString()) : null, mainUnit: json['main_unit'] as String?, ); } Map toJson() { return { 'line_no': lineNo, 'output_product_id': outputProductId, 'ratio': ratio, 'uom': uom, if (outputProductName != null) 'output_product_name': outputProductName, if (outputProductCode != null) 'output_product_code': outputProductCode, if (ratioMainUnit != null) 'ratio_main_unit': ratioMainUnit, if (mainUnit != null) 'main_unit': mainUnit, }; } } class BomOperation { final int lineNo; final String operationName; final double? costFixed; final double? costPerUnit; final String? costUom; final String? workCenter; const BomOperation({ required this.lineNo, required this.operationName, this.costFixed, this.costPerUnit, this.costUom, this.workCenter, }); factory BomOperation.fromJson(Map json) { return BomOperation( lineNo: (json['line_no'] ?? json['lineNo']) as int, operationName: (json['operation_name'] ?? json['operationName'] ?? '') as String, costFixed: json['cost_fixed'] != null ? double.tryParse(json['cost_fixed'].toString()) : null, costPerUnit: json['cost_per_unit'] != null ? double.tryParse(json['cost_per_unit'].toString()) : null, costUom: json['cost_uom'] as String?, workCenter: json['work_center'] as String?, ); } Map toJson() { return { 'line_no': lineNo, 'operation_name': operationName, 'cost_fixed': costFixed, 'cost_per_unit': costPerUnit, 'cost_uom': costUom, 'work_center': workCenter, }; } } class ProductBOM { final int? id; final int businessId; final int productId; final String version; final String name; final bool isDefault; final String? effectiveFrom; final String? effectiveTo; final double? yieldPercent; final double? wastagePercent; final String status; final String? notes; final DateTime? createdAt; final DateTime? updatedAt; final List items; final List outputs; final List operations; const ProductBOM({ this.id, required this.businessId, required this.productId, required this.version, required this.name, this.isDefault = false, this.effectiveFrom, this.effectiveTo, this.yieldPercent, this.wastagePercent, this.status = 'draft', this.notes, this.createdAt, this.updatedAt, this.items = const [], this.outputs = const [], this.operations = const [], }); factory ProductBOM.fromJson(Map json) { final items = (json['items'] as List? ?? const []) .map((e) => BomItem.fromJson(Map.from(e as Map))) .toList(); final outputs = (json['outputs'] as List? ?? const []) .map((e) => BomOutput.fromJson(Map.from(e as Map))) .toList(); final operations = (json['operations'] as List? ?? const []) .map((e) => BomOperation.fromJson(Map.from(e as Map))) .toList(); return ProductBOM( id: json['id'] as int?, businessId: (json['business_id'] ?? json['businessId']) as int, productId: (json['product_id'] ?? json['productId']) as int, version: (json['version'] ?? '') as String, name: (json['name'] ?? '') as String, isDefault: (json['is_default'] ?? json['isDefault'] ?? false) as bool, effectiveFrom: json['effective_from'] as String?, effectiveTo: json['effective_to'] as String?, yieldPercent: json['yield_percent'] != null ? double.tryParse(json['yield_percent'].toString()) : null, wastagePercent: json['wastage_percent'] != null ? double.tryParse(json['wastage_percent'].toString()) : null, status: (json['status'] ?? 'draft') as String, notes: json['notes'] as String?, createdAt: json['created_at'] != null ? DateTime.tryParse(json['created_at'].toString()) : null, updatedAt: json['updated_at'] != null ? DateTime.tryParse(json['updated_at'].toString()) : null, items: items, outputs: outputs, operations: operations, ); } Map toJson() { return { 'id': id, 'business_id': businessId, 'product_id': productId, 'version': version, 'name': name, 'is_default': isDefault, 'effective_from': effectiveFrom, 'effective_to': effectiveTo, 'yield_percent': yieldPercent, 'wastage_percent': wastagePercent, 'status': status, 'notes': notes, 'created_at': createdAt?.toIso8601String(), 'updated_at': updatedAt?.toIso8601String(), 'items': items.map((e) => e.toJson()).toList(), 'outputs': outputs.map((e) => e.toJson()).toList(), 'operations': operations.map((e) => e.toJson()).toList(), }; } } class BomExplosionItem { final int componentProductId; final double requiredQty; final String? uom; final int? suggestedWarehouseId; final bool isOptional; final String? substituteGroup; final String? componentProductName; final String? componentProductCode; final String? componentProductMainUnit; final double? requiredQtyMainUnit; final String? mainUnit; const BomExplosionItem({ required this.componentProductId, required this.requiredQty, this.uom, this.suggestedWarehouseId, this.isOptional = false, this.substituteGroup, this.componentProductName, this.componentProductCode, this.componentProductMainUnit, this.requiredQtyMainUnit, this.mainUnit, }); factory BomExplosionItem.fromJson(Map json) { return BomExplosionItem( componentProductId: (json['component_product_id'] ?? json['componentProductId']) as int, requiredQty: double.tryParse(json['required_qty']?.toString() ?? '0') ?? 0, uom: json['uom'] as String?, suggestedWarehouseId: json['suggested_warehouse_id'] as int?, isOptional: (json['is_optional'] ?? false) as bool, substituteGroup: json['substitute_group'] as String?, componentProductName: json['component_product_name'] as String?, componentProductCode: json['component_product_code'] as String?, componentProductMainUnit: json['component_product_main_unit'] as String?, requiredQtyMainUnit: json['required_qty_main_unit'] != null ? double.tryParse(json['required_qty_main_unit'].toString()) : null, mainUnit: json['main_unit'] as String?, ); } } class BomExplosionResult { final List items; final List outputs; const BomExplosionResult({this.items = const [], this.outputs = const []}); factory BomExplosionResult.fromJson(Map json) { final items = (json['items'] as List? ?? const []) .map((e) => BomExplosionItem.fromJson(Map.from(e as Map))) .toList(); final outputs = (json['outputs'] as List? ?? const []) .map((e) => BomOutput.fromJson(Map.from(e as Map))) .toList(); return BomExplosionResult(items: items, outputs: outputs); } }