find($companyId); return (float) data_get($company?->settings, 'labor_rate', 400); } /** * Add a labor line to a WO. Fixed-price labors set total directly via * hours=1 × price_per_hour=fixed_price. Returns the created work line. */ public function addLabor(WorkOrder $wo, Labor $labor, bool $withParts = true): WorkOrderWork { $rate = $this->hourlyRate($wo->company_id); return DB::transaction(function () use ($wo, $labor, $withParts, $rate) { if ($labor->pricing_mode === 'fixed') { $hours = 1; $pricePerHour = (float) $labor->fixed_price; } else { $hours = (float) $labor->hours ?: 1; $pricePerHour = $rate; } $work = WorkOrderWork::create([ 'work_order_id' => $wo->id, 'labor_id' => $labor->id, 'name' => $labor->name_ro, 'hours' => $hours, 'price_per_hour' => $pricePerHour, 'status' => 'todo', ]); if ($withParts) { foreach ($labor->laborParts as $lp) { $part = $lp->part; if (! $part) continue; $this->addPart($wo, $part, (float) $lp->qty, $lp->unit ?: $part->unit); } } return $work; }); } public function addPart(WorkOrder $wo, Part $part, float $qty, ?string $unit = null): WorkOrderPart { return WorkOrderPart::create([ 'work_order_id' => $wo->id, 'part_id' => $part->id, 'name' => $part->name, 'article' => $part->article, 'brand' => $part->brand, 'qty' => $qty, 'unit' => $unit ?: $part->unit ?: 'buc', 'buy_price' => (float) $part->buy_price, 'sell_price' => (float) $part->sell_price, 'status' => 'needed', ]); } /** * Apply a full template to a WO: every labor + part item becomes a WO line. * Labor items are added WITHOUT their own default parts (template is explicit). * * @return array{labor:int, parts:int} */ public function applyTemplate(WorkOrder $wo, ServiceTemplate $template): array { return DB::transaction(function () use ($wo, $template) { $laborCount = 0; $partCount = 0; foreach ($template->items as $item) { if ($item->kind === 'labor') { if ($item->labor_id && ($labor = Labor::find($item->labor_id))) { $work = $this->addLabor($wo, $labor, withParts: false); if ($item->hours) { $work->hours = (float) $item->hours; $work->save(); } } else { // Free-text labor line from snapshot. WorkOrderWork::create([ 'work_order_id' => $wo->id, 'name' => $item->name, 'hours' => (float) ($item->hours ?: 1), 'price_per_hour' => $this->hourlyRate($wo->company_id), 'status' => 'todo', ]); } $laborCount++; } elseif ($item->kind === 'part') { if ($item->part_id && ($part = Part::find($item->part_id))) { $this->addPart($wo, $part, (float) ($item->qty ?: 1)); } else { WorkOrderPart::create([ 'work_order_id' => $wo->id, 'name' => $item->name, 'qty' => (float) ($item->qty ?: 1), 'unit' => 'buc', 'sell_price' => 0, 'status' => 'needed', ]); } $partCount++; } } $wo->recalcTotal(); return ['labor' => $laborCount, 'parts' => $partCount]; }); } }