liveCosting->analyze($bom, $plannedQty); $leafParts = collect($analysis['leaf_lines']); $unlinked = $leafParts->filter(fn ($l) => empty($l['variation_id'])); $linked = $leafParts->filter(fn ($l) => ! empty($l['variation_id'])); $assemblies = $leafParts->filter(fn ($l) => in_array($l['item_type'], ['assembly', 'subassembly', 'device'], true)); $blockers = []; if ($leafParts->isEmpty()) { $blockers[] = __('industrialengineering::lang.gate_no_bom_leaves'); } $warnings = []; foreach ($unlinked as $row) { $warnings[] = __('industrialengineering::lang.gate_unlinked_item', [ 'code' => $row['item_code'], 'name' => $row['item_name'], ]); } return [ 'can_run_mrp' => empty($blockers) && $unlinked->isEmpty(), 'can_run_mrp_with_warnings' => empty($blockers) && $unlinked->isNotEmpty(), 'blockers' => $blockers, 'warnings' => $warnings, 'stats' => [ 'total_leaves' => $leafParts->count(), 'erp_linked' => $linked->count(), 'unlinked' => $unlinked->count(), 'assemblies' => $assemblies->count(), 'shortage_count' => count($analysis['shortages']), ], 'analysis' => $analysis, ]; } public function assertMrpAllowed(Bom $bom, float $plannedQty = 1, bool $force = false): void { $gate = $this->assessBom($bom, $plannedQty); if (! empty($gate['blockers'])) { throw new \RuntimeException(implode("\n", $gate['blockers'])); } if (! empty($gate['warnings']) && ! $force) { throw new \RuntimeException( __('industrialengineering::lang.gate_mrp_blocked_unlinked')."\n". implode("\n", array_slice($gate['warnings'], 0, 5)). (count($gate['warnings']) > 5 ? "\n..." : '') ); } } }