[FEAT] 新增生產工單實際產量欄位與 UI 規範
- 新增 database/migrations/tenant 實際產量與耗損原因 - ProductionOrder API 狀態推進與實際產量計算 - 完工入庫新增實際產出數量原生數字輸入框 (step=1) - Create.tsx 補上前端資料驗證與狀態保護 - 建立並更新 UI 數字輸入框設計規範
This commit is contained in:
@@ -134,15 +134,15 @@ class ProductionOrderController extends Controller
|
||||
public function store(Request $request)
|
||||
{
|
||||
$status = $request->input('status', 'draft');
|
||||
|
||||
|
||||
$rules = [
|
||||
'product_id' => 'required',
|
||||
'status' => 'nullable|in:draft,completed',
|
||||
'warehouse_id' => $status === 'completed' ? 'required' : 'nullable',
|
||||
'output_quantity' => $status === 'completed' ? 'required|numeric|min:0.01' : 'nullable|numeric',
|
||||
'status' => 'nullable|in:draft,pending,completed',
|
||||
'warehouse_id' => 'required',
|
||||
'output_quantity' => 'required|numeric|min:0.01',
|
||||
'items' => 'nullable|array',
|
||||
'items.*.inventory_id' => $status === 'completed' ? 'required' : 'nullable',
|
||||
'items.*.quantity_used' => $status === 'completed' ? 'required|numeric|min:0.0001' : 'nullable|numeric',
|
||||
'items.*.inventory_id' => 'required',
|
||||
'items.*.quantity_used' => 'required|numeric|min:0.0001',
|
||||
];
|
||||
|
||||
$validated = $request->validate($rules);
|
||||
@@ -159,7 +159,7 @@ class ProductionOrderController extends Controller
|
||||
'production_date' => $request->production_date,
|
||||
'expiry_date' => $request->expiry_date,
|
||||
'user_id' => auth()->id(),
|
||||
'status' => ProductionOrder::STATUS_DRAFT, // 一律存為草稿
|
||||
'status' => $status ?: ProductionOrder::STATUS_DRAFT,
|
||||
'remark' => $request->remark,
|
||||
]);
|
||||
|
||||
@@ -414,6 +414,19 @@ class ProductionOrderController extends Controller
|
||||
return response()->json(['error' => '不合法的狀態轉移或權限不足'], 403);
|
||||
}
|
||||
|
||||
// 送審前的資料完整性驗證
|
||||
if ($productionOrder->status === ProductionOrder::STATUS_DRAFT && $newStatus === ProductionOrder::STATUS_PENDING) {
|
||||
if (!$productionOrder->output_quantity || $productionOrder->output_quantity <= 0) {
|
||||
return back()->with('error', '送審工單前,必須先編輯填寫「生產數量」');
|
||||
}
|
||||
if (!$productionOrder->warehouse_id) {
|
||||
return back()->with('error', '送審工單前,必須先編輯選擇「預計入庫倉庫」');
|
||||
}
|
||||
if ($productionOrder->items()->count() === 0) {
|
||||
return back()->with('error', '送審工單前,請至少新增一項原物料明細');
|
||||
}
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($newStatus, $productionOrder, $request) {
|
||||
// 使用鎖定重新獲取單據,防止併發狀態修改
|
||||
$productionOrder = ProductionOrder::where('id', $productionOrder->id)->lockForUpdate()->first();
|
||||
@@ -444,6 +457,8 @@ class ProductionOrderController extends Controller
|
||||
$warehouseId = $request->input('warehouse_id'); // 由前端 Modal 傳來
|
||||
$batchNumber = $request->input('output_batch_number'); // 由前端 Modal 傳來
|
||||
$expiryDate = $request->input('expiry_date'); // 由前端 Modal 傳來
|
||||
$actualOutputQuantity = $request->input('actual_output_quantity'); // 實際產出數量
|
||||
$lossReason = $request->input('loss_reason'); // 耗損原因
|
||||
|
||||
if (!$warehouseId) {
|
||||
throw new \Exception('必須選擇入庫倉庫');
|
||||
@@ -451,8 +466,14 @@ class ProductionOrderController extends Controller
|
||||
if (!$batchNumber) {
|
||||
throw new \Exception('必須提供成品批號');
|
||||
}
|
||||
if (!$actualOutputQuantity || $actualOutputQuantity <= 0) {
|
||||
throw new \Exception('實際產出數量必須大於 0');
|
||||
}
|
||||
if ($actualOutputQuantity > $productionOrder->output_quantity) {
|
||||
throw new \Exception('實際產出數量不可大於預計產量');
|
||||
}
|
||||
|
||||
// --- 新增:計算原物料投入總成本 ---
|
||||
// --- 計算原物料投入總成本 ---
|
||||
$totalCost = 0;
|
||||
$items = $productionOrder->items()->with('inventory')->get();
|
||||
foreach ($items as $item) {
|
||||
@@ -461,23 +482,25 @@ class ProductionOrderController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
// 計算單位成本 (若產出數量為 0 則設為 0 避免除以零錯誤)
|
||||
$unitCost = $productionOrder->output_quantity > 0
|
||||
? $totalCost / $productionOrder->output_quantity
|
||||
// 單位成本以「實際產出數量」為分母,反映真實生產效率
|
||||
$unitCost = $actualOutputQuantity > 0
|
||||
? $totalCost / $actualOutputQuantity
|
||||
: 0;
|
||||
// --------------------------------
|
||||
|
||||
// 更新單據資訊:批號、效期與自動記錄生產日期
|
||||
// 更新單據資訊:批號、效期、實際產量與耗損原因
|
||||
$productionOrder->output_batch_number = $batchNumber;
|
||||
$productionOrder->expiry_date = $expiryDate;
|
||||
$productionOrder->production_date = now()->toDateString();
|
||||
$productionOrder->warehouse_id = $warehouseId;
|
||||
$productionOrder->actual_output_quantity = $actualOutputQuantity;
|
||||
$productionOrder->loss_reason = $lossReason;
|
||||
|
||||
// 成品入庫數量改用「實際產出數量」
|
||||
$this->inventoryService->createInventoryRecord([
|
||||
'warehouse_id' => $warehouseId,
|
||||
'product_id' => $productionOrder->product_id,
|
||||
'quantity' => $productionOrder->output_quantity,
|
||||
'unit_cost' => $unitCost, // 傳入計算後的單位成本
|
||||
'quantity' => $actualOutputQuantity,
|
||||
'unit_cost' => $unitCost,
|
||||
'batch_number' => $batchNumber,
|
||||
'box_number' => $productionOrder->output_box_count,
|
||||
'arrival_date' => now()->toDateString(),
|
||||
|
||||
Reference in New Issue
Block a user