UI優化: 全系統狀態標籤 (StatusBadge) 統一化重構完成 (Phase 3 & 4)
This commit is contained in:
247
app/Modules/Inventory/Services/StoreRequisitionService.php
Normal file
247
app/Modules/Inventory/Services/StoreRequisitionService.php
Normal file
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Inventory\Services;
|
||||
|
||||
use App\Modules\Inventory\Models\StoreRequisition;
|
||||
use App\Modules\Inventory\Models\StoreRequisitionItem;
|
||||
use App\Modules\Inventory\Models\InventoryTransferOrder;
|
||||
use App\Modules\Inventory\Models\InventoryTransferItem;
|
||||
use App\Modules\Inventory\Notifications\StoreRequisitionNotification;
|
||||
use App\Modules\Core\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class StoreRequisitionService
|
||||
{
|
||||
protected TransferService $transferService;
|
||||
|
||||
public function __construct(TransferService $transferService)
|
||||
{
|
||||
$this->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']]);
|
||||
}
|
||||
}
|
||||
|
||||
// 查詢供貨倉庫是否有預設在途倉
|
||||
$supplyWarehouse = \App\Modules\Inventory\Models\Warehouse::find($data['supply_warehouse_id']);
|
||||
$defaultTransitId = $supplyWarehouse?->default_transit_warehouse_id;
|
||||
|
||||
// 產生調撥單(供貨倉庫 → 門市倉庫)
|
||||
$transferOrder = $this->transferService->createOrder(
|
||||
fromWarehouseId: $data['supply_warehouse_id'],
|
||||
toWarehouseId: $requisition->store_warehouse_id,
|
||||
remarks: "由叫貨單 {$requisition->doc_no} 自動產生",
|
||||
userId: $userId,
|
||||
transitWarehouseId: $defaultTransitId,
|
||||
);
|
||||
|
||||
// 將核准的明細寫入調撥單
|
||||
$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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,27 +14,32 @@ class TransferService
|
||||
/**
|
||||
* 建立調撥單草稿
|
||||
*/
|
||||
public function createOrder(int $fromWarehouseId, int $toWarehouseId, ?string $remarks, int $userId): InventoryTransferOrder
|
||||
public function createOrder(int $fromWarehouseId, int $toWarehouseId, ?string $remarks, int $userId, ?int $transitWarehouseId = null): InventoryTransferOrder
|
||||
{
|
||||
// 若未指定在途倉,嘗試使用來源倉庫的預設在途倉 (一次性設定)
|
||||
if (is_null($transitWarehouseId)) {
|
||||
$fromWarehouse = Warehouse::find($fromWarehouseId);
|
||||
if ($fromWarehouse && $fromWarehouse->default_transit_warehouse_id) {
|
||||
$transitWarehouseId = $fromWarehouse->default_transit_warehouse_id;
|
||||
}
|
||||
}
|
||||
|
||||
return InventoryTransferOrder::create([
|
||||
'from_warehouse_id' => $fromWarehouseId,
|
||||
'to_warehouse_id' => $toWarehouseId,
|
||||
'transit_warehouse_id' => $transitWarehouseId,
|
||||
'status' => 'draft',
|
||||
'remarks' => $remarks,
|
||||
'created_by' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新調撥單明細
|
||||
*/
|
||||
/**
|
||||
* 更新調撥單明細 (支援精確 Diff 與自動日誌整合)
|
||||
*/
|
||||
public function updateItems(InventoryTransferOrder $order, array $itemsData): bool
|
||||
{
|
||||
return DB::transaction(function () use ($order, $itemsData) {
|
||||
// 1. 準備舊資料索引 (Key: product_id . '_' . batch_number)
|
||||
$oldItemsMap = $order->items->mapWithKeys(function ($item) {
|
||||
$key = $item->product_id . '_' . ($item->batch_number ?? '');
|
||||
return [$key => $item];
|
||||
@@ -46,13 +51,7 @@ class TransferService
|
||||
'updated' => [],
|
||||
];
|
||||
|
||||
// 2. 處理新資料 (Deleted and Re-inserted currently for simplicity, but logic simulates update)
|
||||
// 為了保持 ID 當作外鍵的穩定性,最佳做法是 update 存在的,create 新的,delete 舊的。
|
||||
// 但考量現有邏輯是 delete all -> create all,我們維持原策略但優化 Diff 計算。
|
||||
|
||||
// 由於採用全刪重建,我們必須手動計算 Diff
|
||||
$order->items()->delete();
|
||||
|
||||
$newItemsKeys = [];
|
||||
|
||||
foreach ($itemsData as $data) {
|
||||
@@ -66,13 +65,10 @@ class TransferService
|
||||
'position' => $data['position'] ?? null,
|
||||
'notes' => $data['notes'] ?? null,
|
||||
]);
|
||||
// Eager load product for name
|
||||
$item->load('product');
|
||||
|
||||
// 比對邏輯
|
||||
if ($oldItemsMap->has($key)) {
|
||||
$oldItem = $oldItemsMap->get($key);
|
||||
// 檢查數值是否有變動
|
||||
if ((float)$oldItem->quantity !== (float)$data['quantity'] ||
|
||||
$oldItem->notes !== ($data['notes'] ?? null) ||
|
||||
$oldItem->position !== ($data['position'] ?? null)) {
|
||||
@@ -92,7 +88,6 @@ class TransferService
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// 新增 (使用者需求:顯示為更新,從 0 -> X)
|
||||
$diff['updated'][] = [
|
||||
'product_name' => $item->product->name,
|
||||
'old' => [
|
||||
@@ -107,7 +102,6 @@ class TransferService
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 處理被移除的項目
|
||||
foreach ($oldItemsMap as $key => $oldItem) {
|
||||
if (!in_array($key, $newItemsKeys)) {
|
||||
$diff['removed'][] = [
|
||||
@@ -120,7 +114,6 @@ class TransferService
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 將 Diff 注入到 Model 的暫存屬性中
|
||||
$hasChanged = !empty($diff['added']) || !empty($diff['removed']) || !empty($diff['updated']);
|
||||
if ($hasChanged) {
|
||||
$order->activityProperties['items_diff'] = $diff;
|
||||
@@ -131,16 +124,24 @@ class TransferService
|
||||
}
|
||||
|
||||
/**
|
||||
* 過帳 (Post) - 執行調撥 (直接扣除來源,增加目的)
|
||||
* 出貨 (Dispatch) - 根據是否有在途倉決定流程
|
||||
*
|
||||
* 有在途倉:來源倉扣除 → 在途倉增加,狀態改為 dispatched
|
||||
* 無在途倉:來源倉扣除 → 目的倉增加,狀態改為 completed(維持原有邏輯)
|
||||
*/
|
||||
public function post(InventoryTransferOrder $order, int $userId): void
|
||||
public function dispatch(InventoryTransferOrder $order, int $userId): void
|
||||
{
|
||||
// [IMPORTANT] 強制重新載入品項,因為在 Controller 中可能剛執行過 updateItems,導致記憶體中快取的 items 是舊的或空的
|
||||
$order->load('items.product');
|
||||
|
||||
DB::transaction(function () use ($order, $userId) {
|
||||
$fromWarehouse = $order->fromWarehouse;
|
||||
$toWarehouse = $order->toWarehouse;
|
||||
$hasTransit = !empty($order->transit_warehouse_id);
|
||||
|
||||
$targetWarehouseId = $hasTransit ? $order->transit_warehouse_id : $order->to_warehouse_id;
|
||||
$targetWarehouse = $hasTransit ? $order->transitWarehouse : $order->toWarehouse;
|
||||
|
||||
$outType = '調撥出庫';
|
||||
$inType = $hasTransit ? '在途入庫' : '調撥入庫';
|
||||
|
||||
foreach ($order->items as $item) {
|
||||
if ($item->quantity <= 0) continue;
|
||||
@@ -162,46 +163,41 @@ class TransferService
|
||||
$oldSourceQty = $sourceInventory->quantity;
|
||||
$newSourceQty = $oldSourceQty - $item->quantity;
|
||||
|
||||
// 儲存庫存快照
|
||||
$item->update(['snapshot_quantity' => $oldSourceQty]);
|
||||
|
||||
$sourceInventory->quantity = $newSourceQty;
|
||||
// 更新總值 (假設成本不變)
|
||||
$sourceInventory->total_value = $sourceInventory->quantity * $sourceInventory->unit_cost;
|
||||
$sourceInventory->save();
|
||||
|
||||
// 記錄來源交易
|
||||
$sourceInventory->transactions()->create([
|
||||
'type' => '調撥出庫',
|
||||
'type' => $outType,
|
||||
'quantity' => -$item->quantity,
|
||||
'unit_cost' => $sourceInventory->unit_cost,
|
||||
'balance_before' => $oldSourceQty,
|
||||
'balance_after' => $newSourceQty,
|
||||
'reason' => "調撥單 {$order->doc_no} 至 {$toWarehouse->name}",
|
||||
'reason' => "調撥單 {$order->doc_no} 至 {$targetWarehouse->name}",
|
||||
'actual_time' => now(),
|
||||
'user_id' => $userId,
|
||||
]);
|
||||
|
||||
// 2. 處理目的倉 (增加)
|
||||
// 2. 處理目的倉/在途倉 (增加)
|
||||
$targetInventory = Inventory::firstOrCreate(
|
||||
[
|
||||
'warehouse_id' => $order->to_warehouse_id,
|
||||
'warehouse_id' => $targetWarehouseId,
|
||||
'product_id' => $item->product_id,
|
||||
'batch_number' => $item->batch_number,
|
||||
'location' => $item->position, // 同步貨道至庫存位置
|
||||
'location' => $hasTransit ? null : ($item->position ?? null),
|
||||
],
|
||||
[
|
||||
'quantity' => 0,
|
||||
'unit_cost' => $sourceInventory->unit_cost, // 繼承成本
|
||||
'unit_cost' => $sourceInventory->unit_cost,
|
||||
'total_value' => 0,
|
||||
// 繼承其他屬性
|
||||
'expiry_date' => $sourceInventory->expiry_date,
|
||||
'quality_status' => $sourceInventory->quality_status,
|
||||
'origin_country' => $sourceInventory->origin_country,
|
||||
]
|
||||
);
|
||||
|
||||
// 若是新建立的,且成本為0,確保繼承成本
|
||||
if ($targetInventory->wasRecentlyCreated && $targetInventory->unit_cost == 0) {
|
||||
$targetInventory->unit_cost = $sourceInventory->unit_cost;
|
||||
}
|
||||
@@ -213,9 +209,8 @@ class TransferService
|
||||
$targetInventory->total_value = $targetInventory->quantity * $targetInventory->unit_cost;
|
||||
$targetInventory->save();
|
||||
|
||||
// 記錄目的交易
|
||||
$targetInventory->transactions()->create([
|
||||
'type' => '調撥入庫',
|
||||
'type' => $inType,
|
||||
'quantity' => $item->quantity,
|
||||
'unit_cost' => $targetInventory->unit_cost,
|
||||
'balance_before' => $oldTargetQty,
|
||||
@@ -226,28 +221,126 @@ class TransferService
|
||||
]);
|
||||
}
|
||||
|
||||
// 準備品項快照供日誌使用
|
||||
$itemsSnapshot = $order->items->map(function($item) {
|
||||
return [
|
||||
'product_name' => $item->product->name,
|
||||
'old' => [
|
||||
'quantity' => (float)$item->quantity,
|
||||
'notes' => $item->notes,
|
||||
if ($hasTransit) {
|
||||
$order->status = 'dispatched';
|
||||
$order->dispatched_at = now();
|
||||
$order->dispatched_by = $userId;
|
||||
} else {
|
||||
$order->status = 'completed';
|
||||
$order->posted_at = now();
|
||||
$order->posted_by = $userId;
|
||||
}
|
||||
$order->save();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 收貨確認 (Receive) - 在途倉扣除 → 目的倉增加
|
||||
* 僅適用於有在途倉且狀態為 dispatched 的調撥單
|
||||
*/
|
||||
public function receive(InventoryTransferOrder $order, int $userId): void
|
||||
{
|
||||
if ($order->status !== 'dispatched') {
|
||||
throw new \Exception('僅能對已出貨的調撥單進行收貨確認');
|
||||
}
|
||||
|
||||
if (empty($order->transit_warehouse_id)) {
|
||||
throw new \Exception('此調撥單未設定在途倉庫');
|
||||
}
|
||||
|
||||
$order->load('items.product');
|
||||
|
||||
DB::transaction(function () use ($order, $userId) {
|
||||
$transitWarehouse = $order->transitWarehouse;
|
||||
$toWarehouse = $order->toWarehouse;
|
||||
|
||||
foreach ($order->items as $item) {
|
||||
if ($item->quantity <= 0) continue;
|
||||
|
||||
// 1. 在途倉扣除
|
||||
$transitInventory = Inventory::where('warehouse_id', $order->transit_warehouse_id)
|
||||
->where('product_id', $item->product_id)
|
||||
->where('batch_number', $item->batch_number)
|
||||
->first();
|
||||
|
||||
if (!$transitInventory || $transitInventory->quantity < $item->quantity) {
|
||||
$availableQty = $transitInventory->quantity ?? 0;
|
||||
throw ValidationException::withMessages([
|
||||
'items' => ["商品 {$item->product->name} 在途倉庫存不足。現有:{$availableQty},需要:{$item->quantity}。"],
|
||||
]);
|
||||
}
|
||||
|
||||
$oldTransitQty = $transitInventory->quantity;
|
||||
$newTransitQty = $oldTransitQty - $item->quantity;
|
||||
|
||||
$transitInventory->quantity = $newTransitQty;
|
||||
$transitInventory->total_value = $transitInventory->quantity * $transitInventory->unit_cost;
|
||||
$transitInventory->save();
|
||||
|
||||
$transitInventory->transactions()->create([
|
||||
'type' => '在途出庫',
|
||||
'quantity' => -$item->quantity,
|
||||
'unit_cost' => $transitInventory->unit_cost,
|
||||
'balance_before' => $oldTransitQty,
|
||||
'balance_after' => $newTransitQty,
|
||||
'reason' => "調撥單 {$order->doc_no} 配送至 {$toWarehouse->name}",
|
||||
'actual_time' => now(),
|
||||
'user_id' => $userId,
|
||||
]);
|
||||
|
||||
// 2. 目的倉增加
|
||||
$targetInventory = Inventory::firstOrCreate(
|
||||
[
|
||||
'warehouse_id' => $order->to_warehouse_id,
|
||||
'product_id' => $item->product_id,
|
||||
'batch_number' => $item->batch_number,
|
||||
'location' => $item->position,
|
||||
],
|
||||
'new' => [
|
||||
'quantity' => (float)$item->quantity,
|
||||
'notes' => $item->notes,
|
||||
[
|
||||
'quantity' => 0,
|
||||
'unit_cost' => $transitInventory->unit_cost,
|
||||
'total_value' => 0,
|
||||
'expiry_date' => $transitInventory->expiry_date,
|
||||
'quality_status' => $transitInventory->quality_status,
|
||||
'origin_country' => $transitInventory->origin_country,
|
||||
]
|
||||
];
|
||||
})->toArray();
|
||||
);
|
||||
|
||||
if ($targetInventory->wasRecentlyCreated && $targetInventory->unit_cost == 0) {
|
||||
$targetInventory->unit_cost = $transitInventory->unit_cost;
|
||||
}
|
||||
|
||||
$oldTargetQty = $targetInventory->quantity;
|
||||
$newTargetQty = $oldTargetQty + $item->quantity;
|
||||
|
||||
$targetInventory->quantity = $newTargetQty;
|
||||
$targetInventory->total_value = $targetInventory->quantity * $targetInventory->unit_cost;
|
||||
$targetInventory->save();
|
||||
|
||||
$targetInventory->transactions()->create([
|
||||
'type' => '調撥入庫',
|
||||
'quantity' => $item->quantity,
|
||||
'unit_cost' => $targetInventory->unit_cost,
|
||||
'balance_before' => $oldTargetQty,
|
||||
'balance_after' => $newTargetQty,
|
||||
'reason' => "調撥單 {$order->doc_no} 來自 {$transitWarehouse->name}",
|
||||
'actual_time' => now(),
|
||||
'user_id' => $userId,
|
||||
]);
|
||||
}
|
||||
|
||||
$order->status = 'completed';
|
||||
$order->posted_at = now();
|
||||
$order->posted_by = $userId;
|
||||
$order->save(); // 觸發自動日誌
|
||||
$order->received_at = now();
|
||||
$order->received_by = $userId;
|
||||
$order->save();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 作廢 (Void) - 僅限草稿狀態
|
||||
*/
|
||||
public function void(InventoryTransferOrder $order, int $userId): void
|
||||
{
|
||||
if ($order->status !== 'draft') {
|
||||
|
||||
Reference in New Issue
Block a user