ultimatepos/Modules/IndustrialEngineering/Services/ReleaseService.php

138 lines
4.9 KiB
PHP

<?php
namespace Modules\IndustrialEngineering\Services;
use Illuminate\Support\Facades\DB;
use Modules\IndustrialEngineering\Models\Bom;
use Modules\IndustrialEngineering\Models\CostScenario;
use Modules\IndustrialEngineering\Models\LineRevision;
use Modules\IndustrialEngineering\Models\ManufacturingWorkOrder;
use Modules\IndustrialEngineering\Models\ReleasePackage;
use Modules\IndustrialEngineering\Models\ReleaseSignoff;
class ReleaseService
{
public function __construct(
protected BomService $bomService
) {
}
public function createReleasePackage(
int $businessId,
LineRevision $revision,
Bom $mbom,
?CostScenario $costScenario,
string $packageCode,
string $name
): ReleasePackage {
$routing = $revision->routingOperations()->with('workCenter')->get()->map(fn ($op) => [
'operation_code' => $op->operation_code,
'name' => $op->name,
'sequence' => $op->sequence,
'work_center' => $op->workCenter?->name,
'setup_hours' => (float) $op->setup_time_hours,
'run_hours' => (float) $op->run_time_hours,
])->values()->all();
return ReleasePackage::create([
'business_id' => $businessId,
'line_revision_id' => $revision->id,
'mbom_id' => $mbom->id,
'cost_scenario_id' => $costScenario?->id,
'package_code' => $packageCode,
'name' => $name,
'status' => 'draft',
'mbom_snapshot' => $this->bomService->snapshot($mbom),
'routing_snapshot' => $routing,
'cost_snapshot' => $costScenario ? [
'scenario_id' => $costScenario->id,
'total_cost' => (float) $costScenario->total_cost,
'material' => (float) $costScenario->total_material_cost,
'labor' => (float) $costScenario->total_labor_cost,
'machine' => (float) $costScenario->total_machine_cost,
] : null,
]);
}
public function requestSignoffs(ReleasePackage $package, array $roles = ['engineering', 'manufacturing', 'quality']): void
{
foreach ($roles as $role) {
ReleaseSignoff::firstOrCreate(
['release_package_id' => $package->id, 'signoff_role' => $role],
['business_id' => $package->business_id, 'status' => 'pending']
);
}
$package->update(['status' => 'pending_approval']);
}
public function signoff(ReleasePackage $package, string $role, int $userId, string $status = 'approved', ?string $notes = null): void
{
ReleaseSignoff::where('release_package_id', $package->id)
->where('signoff_role', $role)
->update([
'signer_id' => $userId,
'signer_name' => auth()->user()?->user_full_name ?? auth()->user()?->username,
'status' => $status,
'signed_at' => now(),
'notes' => $notes,
]);
}
public function release(ReleasePackage $package, int $userId): ReleasePackage
{
$pending = ReleaseSignoff::where('release_package_id', $package->id)
->where('status', '!=', 'approved')
->exists();
if ($pending) {
throw new \RuntimeException('All signoffs must be approved before release.');
}
return DB::transaction(function () use ($package, $userId) {
$package->update([
'status' => 'released',
'released_by' => $userId,
'released_at' => now(),
]);
$package->lineRevision->update([
'status' => 'released',
'released_by' => $userId,
'released_at' => now(),
]);
if ($package->mbom) {
$package->mbom->update([
'status' => 'released',
'released_by' => $userId,
'released_at' => now(),
]);
}
return $package->fresh();
});
}
public function createWorkOrder(
ReleasePackage $package,
string $workOrderNumber,
float $plannedQty = 1,
?int $locationId = null
): ManufacturingWorkOrder {
if ($package->status !== 'released') {
throw new \RuntimeException('Release package must be released before creating work orders.');
}
return ManufacturingWorkOrder::create([
'business_id' => $package->business_id,
'release_package_id' => $package->id,
'location_id' => $locationId,
'work_order_number' => $workOrderNumber,
'planned_quantity' => $plannedQty,
'status' => 'planned',
'bom_snapshot' => $package->mbom_snapshot,
'routing_snapshot' => $package->routing_snapshot,
]);
}
}