153 lines
5.9 KiB
PHP
153 lines
5.9 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
namespace App\Modules\Inventory\Services;
|
|||
|
|
|
|||
|
|
use App\Modules\Inventory\Models\Inventory;
|
|||
|
|
use App\Modules\Inventory\Models\InventoryTransferOrder;
|
|||
|
|
use App\Modules\Inventory\Models\InventoryTransferItem;
|
|||
|
|
use App\Modules\Inventory\Models\Warehouse;
|
|||
|
|
use Illuminate\Support\Facades\DB;
|
|||
|
|
use Illuminate\Validation\ValidationException;
|
|||
|
|
|
|||
|
|
class TransferService
|
|||
|
|
{
|
|||
|
|
/**
|
|||
|
|
* 建立調撥單草稿
|
|||
|
|
*/
|
|||
|
|
public function createOrder(int $fromWarehouseId, int $toWarehouseId, ?string $remarks, int $userId): InventoryTransferOrder
|
|||
|
|
{
|
|||
|
|
return InventoryTransferOrder::create([
|
|||
|
|
'from_warehouse_id' => $fromWarehouseId,
|
|||
|
|
'to_warehouse_id' => $toWarehouseId,
|
|||
|
|
'status' => 'draft',
|
|||
|
|
'remarks' => $remarks,
|
|||
|
|
'created_by' => $userId,
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新調撥單明細
|
|||
|
|
*/
|
|||
|
|
public function updateItems(InventoryTransferOrder $order, array $itemsData): void
|
|||
|
|
{
|
|||
|
|
DB::transaction(function () use ($order, $itemsData) {
|
|||
|
|
$order->items()->delete();
|
|||
|
|
|
|||
|
|
foreach ($itemsData as $data) {
|
|||
|
|
$order->items()->create([
|
|||
|
|
'product_id' => $data['product_id'],
|
|||
|
|
'batch_number' => $data['batch_number'] ?? null,
|
|||
|
|
'quantity' => $data['quantity'],
|
|||
|
|
'notes' => $data['notes'] ?? null,
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 過帳 (Post) - 執行調撥 (直接扣除來源,增加目的)
|
|||
|
|
*/
|
|||
|
|
public function post(InventoryTransferOrder $order, int $userId): void
|
|||
|
|
{
|
|||
|
|
DB::transaction(function () use ($order, $userId) {
|
|||
|
|
$fromWarehouse = $order->fromWarehouse;
|
|||
|
|
$toWarehouse = $order->toWarehouse;
|
|||
|
|
|
|||
|
|
foreach ($order->items as $item) {
|
|||
|
|
if ($item->quantity <= 0) continue;
|
|||
|
|
|
|||
|
|
// 1. 處理來源倉 (扣除)
|
|||
|
|
$sourceInventory = Inventory::where('warehouse_id', $order->from_warehouse_id)
|
|||
|
|
->where('product_id', $item->product_id)
|
|||
|
|
->where('batch_number', $item->batch_number)
|
|||
|
|
->first();
|
|||
|
|
|
|||
|
|
if (!$sourceInventory || $sourceInventory->quantity < $item->quantity) {
|
|||
|
|
throw ValidationException::withMessages([
|
|||
|
|
'items' => ["商品 {$item->product->name} (批號: {$item->batch_number}) 在來源倉庫存不足"],
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$oldSourceQty = $sourceInventory->quantity;
|
|||
|
|
$newSourceQty = $oldSourceQty - $item->quantity;
|
|||
|
|
|
|||
|
|
$sourceInventory->quantity = $newSourceQty;
|
|||
|
|
// 更新總值 (假設成本不變)
|
|||
|
|
$sourceInventory->total_value = $sourceInventory->quantity * $sourceInventory->unit_cost;
|
|||
|
|
$sourceInventory->save();
|
|||
|
|
|
|||
|
|
// 記錄來源交易
|
|||
|
|
$sourceInventory->transactions()->create([
|
|||
|
|
'type' => '調撥出庫',
|
|||
|
|
'quantity' => -$item->quantity,
|
|||
|
|
'unit_cost' => $sourceInventory->unit_cost,
|
|||
|
|
'balance_before' => $oldSourceQty,
|
|||
|
|
'balance_after' => $newSourceQty,
|
|||
|
|
'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,
|
|||
|
|
],
|
|||
|
|
[
|
|||
|
|
'quantity' => 0,
|
|||
|
|
'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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$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} 來自 {$fromWarehouse->name}",
|
|||
|
|
'actual_time' => now(),
|
|||
|
|
'user_id' => $userId,
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
$order->update([
|
|||
|
|
'status' => 'completed',
|
|||
|
|
'posted_at' => now(),
|
|||
|
|
'posted_by' => $userId,
|
|||
|
|
]);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public function void(InventoryTransferOrder $order, int $userId): void
|
|||
|
|
{
|
|||
|
|
if ($order->status !== 'draft') {
|
|||
|
|
throw new \Exception('只能作廢草稿狀態的單據');
|
|||
|
|
}
|
|||
|
|
$order->update([
|
|||
|
|
'status' => 'voided',
|
|||
|
|
'updated_by' => $userId
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
}
|