transferService = $transferService; } /** * 建立叫貨單(含明細) */ public function create(array $data, array $items, int $userId): StoreRequisition { return DB::transaction(function () use ($data, $items, $userId) { $requisition = StoreRequisition::create([ 'store_warehouse_id' => $data['store_warehouse_id'], 'status' => 'draft', 'remark' => $data['remark'] ?? null, 'created_by' => $userId, ]); foreach ($items as $item) { $requisition->items()->create([ 'product_id' => $item['product_id'], 'requested_qty' => $item['requested_qty'], 'remark' => $item['remark'] ?? null, ]); } return $requisition->load('items'); }); } /** * 更新叫貨單(僅限 draft / rejected 狀態) */ public function update(StoreRequisition $requisition, array $data, array $items): StoreRequisition { if (!in_array($requisition->status, ['draft', 'rejected'])) { throw ValidationException::withMessages([ 'status' => '僅能編輯草稿或被駁回的叫貨單', ]); } return DB::transaction(function () use ($requisition, $data, $items) { $requisition->update([ 'store_warehouse_id' => $data['store_warehouse_id'], 'remark' => $data['remark'] ?? null, 'reject_reason' => null, // 清除駁回原因 ]); // 重建明細 $requisition->items()->delete(); foreach ($items as $item) { $requisition->items()->create([ 'product_id' => $item['product_id'], 'requested_qty' => $item['requested_qty'], 'remark' => $item['remark'] ?? null, ]); } return $requisition->load('items'); }); } /** * 提交審核(draft → pending) */ public function submit(StoreRequisition $requisition, int $userId): StoreRequisition { if ($requisition->status !== 'draft' && $requisition->status !== 'rejected') { throw ValidationException::withMessages([ 'status' => '僅能提交草稿或被駁回的叫貨單', ]); } if ($requisition->items()->count() === 0) { throw ValidationException::withMessages([ 'items' => '叫貨單必須至少有一項商品', ]); } $requisition->update([ 'status' => 'pending', 'submitted_at' => now(), 'reject_reason' => null, ]); // 通知有審核權限的使用者 $this->notifyApprovers($requisition, 'submitted', $userId); return $requisition; } /** * 核准叫貨單(pending → approved),選擇供貨倉庫並自動產生調撥單 */ public function approve(StoreRequisition $requisition, array $data, int $userId): StoreRequisition { if ($requisition->status !== 'pending') { throw ValidationException::withMessages([ 'status' => '僅能核准待審核的叫貨單', ]); } return DB::transaction(function () use ($requisition, $data, $userId) { // 更新核准數量 if (isset($data['items'])) { foreach ($data['items'] as $itemData) { StoreRequisitionItem::where('id', $itemData['id']) ->where('store_requisition_id', $requisition->id) ->update(['approved_qty' => $itemData['approved_qty']]); } } // 產生調撥單(供貨倉庫 → 門市倉庫) $transferOrder = $this->transferService->createOrder( fromWarehouseId: $data['supply_warehouse_id'], toWarehouseId: $requisition->store_warehouse_id, remarks: "由叫貨單 {$requisition->doc_no} 自動產生", userId: $userId, ); // 將核准的明細寫入調撥單 $requisition->load('items'); $transferItems = []; foreach ($requisition->items as $item) { $qty = $item->approved_qty ?? $item->requested_qty; if ($qty > 0) { $transferItems[] = [ 'product_id' => $item->product_id, 'quantity' => $qty, ]; } } if (!empty($transferItems)) { $this->transferService->updateItems($transferOrder, $transferItems); } // 更新叫貨單狀態 $requisition->update([ 'status' => 'approved', 'supply_warehouse_id' => $data['supply_warehouse_id'], 'approved_by' => $userId, 'approved_at' => now(), 'transfer_order_id' => $transferOrder->id, ]); // 通知申請人 $this->notifyCreator($requisition, 'approved', $userId); return $requisition->load(['items', 'transferOrder']); }); } /** * 駁回叫貨單(pending → rejected) */ public function reject(StoreRequisition $requisition, string $reason, int $userId): StoreRequisition { if ($requisition->status !== 'pending') { throw ValidationException::withMessages([ 'status' => '僅能駁回待審核的叫貨單', ]); } $requisition->update([ 'status' => 'rejected', 'reject_reason' => $reason, 'approved_by' => $userId, 'approved_at' => now(), ]); // 通知申請人 $this->notifyCreator($requisition, 'rejected', $userId); return $requisition; } /** * 取消叫貨單 */ public function cancel(StoreRequisition $requisition): StoreRequisition { if (!in_array($requisition->status, ['draft', 'pending'])) { throw ValidationException::withMessages([ 'status' => '僅能取消草稿或待審核的叫貨單', ]); } $requisition->update(['status' => 'cancelled']); return $requisition; } /** * 通知有審核權限的使用者 */ protected function notifyApprovers(StoreRequisition $requisition, string $action, int $actorId): void { $actor = User::find($actorId); $actorName = $actor?->name ?? 'System'; // 找出有 store_requisitions.approve 權限的使用者 $approvers = User::permission('store_requisitions.approve')->get(); foreach ($approvers as $approver) { if ($approver->id !== $actorId) { $approver->notify(new StoreRequisitionNotification($requisition, $action, $actorName)); } } } /** * 通知叫貨單申請人 */ protected function notifyCreator(StoreRequisition $requisition, string $action, int $actorId): void { $actor = User::find($actorId); $actorName = $actor?->name ?? 'System'; $creator = User::find($requisition->created_by); if ($creator && $creator->id !== $actorId) { $creator->notify(new StoreRequisitionNotification($requisition, $action, $actorName)); } } }