生產工單BOM以及批號完善
This commit is contained in:
@@ -73,11 +73,19 @@ class ProductionOrderController extends Controller
|
||||
*/
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
$status = $request->input('status', 'draft'); // 預設為草稿
|
||||
|
||||
// 共用驗證規則
|
||||
$baseRules = [
|
||||
'product_id' => 'required|exists:products,id',
|
||||
'output_batch_number' => 'required|string|max:50',
|
||||
'status' => 'nullable|in:draft,completed',
|
||||
];
|
||||
|
||||
// 完成模式需要完整驗證
|
||||
$completedRules = [
|
||||
'warehouse_id' => 'required|exists:warehouses,id',
|
||||
'output_quantity' => 'required|numeric|min:0.01',
|
||||
'output_batch_number' => 'required|string|max:50',
|
||||
'output_box_count' => 'nullable|string|max:10',
|
||||
'production_date' => 'required|date',
|
||||
'expiry_date' => 'nullable|date|after_or_equal:production_date',
|
||||
@@ -86,64 +94,96 @@ class ProductionOrderController extends Controller
|
||||
'items.*.inventory_id' => 'required|exists:inventories,id',
|
||||
'items.*.quantity_used' => 'required|numeric|min:0.0001',
|
||||
'items.*.unit_id' => 'nullable|exists:units,id',
|
||||
], [
|
||||
];
|
||||
|
||||
// 草稿模式的寬鬆規則
|
||||
$draftRules = [
|
||||
'warehouse_id' => 'nullable|exists:warehouses,id',
|
||||
'output_quantity' => 'nullable|numeric|min:0',
|
||||
'output_box_count' => 'nullable|string|max:10',
|
||||
'production_date' => 'nullable|date',
|
||||
'expiry_date' => 'nullable|date',
|
||||
'remark' => 'nullable|string',
|
||||
'items' => 'nullable|array',
|
||||
'items.*.inventory_id' => 'nullable|exists:inventories,id',
|
||||
'items.*.quantity_used' => 'nullable|numeric|min:0',
|
||||
'items.*.unit_id' => 'nullable|exists:units,id',
|
||||
];
|
||||
|
||||
$rules = $status === 'completed'
|
||||
? array_merge($baseRules, $completedRules)
|
||||
: array_merge($baseRules, $draftRules);
|
||||
|
||||
$validated = $request->validate($rules, [
|
||||
'product_id.required' => '請選擇成品商品',
|
||||
'output_batch_number.required' => '請輸入成品批號',
|
||||
'warehouse_id.required' => '請選擇入庫倉庫',
|
||||
'output_quantity.required' => '請輸入生產數量',
|
||||
'output_batch_number.required' => '請輸入成品批號',
|
||||
'production_date.required' => '請選擇生產日期',
|
||||
'items.required' => '請至少新增一項原物料',
|
||||
'items.min' => '請至少新增一項原物料',
|
||||
]);
|
||||
|
||||
DB::transaction(function () use ($validated, $request) {
|
||||
DB::transaction(function () use ($validated, $request, $status) {
|
||||
// 1. 建立生產工單
|
||||
$productionOrder = ProductionOrder::create([
|
||||
'code' => ProductionOrder::generateCode(),
|
||||
'product_id' => $validated['product_id'],
|
||||
'warehouse_id' => $validated['warehouse_id'],
|
||||
'output_quantity' => $validated['output_quantity'],
|
||||
'warehouse_id' => $validated['warehouse_id'] ?? null,
|
||||
'output_quantity' => $validated['output_quantity'] ?? 0,
|
||||
'output_batch_number' => $validated['output_batch_number'],
|
||||
'output_box_count' => $validated['output_box_count'] ?? null,
|
||||
'production_date' => $validated['production_date'],
|
||||
'production_date' => $validated['production_date'] ?? now()->toDateString(),
|
||||
'expiry_date' => $validated['expiry_date'] ?? null,
|
||||
'user_id' => auth()->id(),
|
||||
'status' => 'completed',
|
||||
'status' => $status,
|
||||
'remark' => $validated['remark'] ?? null,
|
||||
]);
|
||||
|
||||
// 2. 建立明細並扣減原物料庫存
|
||||
foreach ($validated['items'] as $item) {
|
||||
// 建立明細
|
||||
ProductionOrderItem::create([
|
||||
'production_order_id' => $productionOrder->id,
|
||||
'inventory_id' => $item['inventory_id'],
|
||||
'quantity_used' => $item['quantity_used'],
|
||||
'unit_id' => $item['unit_id'] ?? null,
|
||||
]);
|
||||
// 2. 建立明細 (草稿與完成模式皆需儲存)
|
||||
if (!empty($validated['items'])) {
|
||||
foreach ($validated['items'] as $item) {
|
||||
if (empty($item['inventory_id'])) continue;
|
||||
|
||||
// 扣減原物料庫存
|
||||
$inventory = Inventory::findOrFail($item['inventory_id']);
|
||||
$inventory->decrement('quantity', $item['quantity_used']);
|
||||
// 建立明細
|
||||
ProductionOrderItem::create([
|
||||
'production_order_id' => $productionOrder->id,
|
||||
'inventory_id' => $item['inventory_id'],
|
||||
'quantity_used' => $item['quantity_used'] ?? 0,
|
||||
'unit_id' => $item['unit_id'] ?? null,
|
||||
]);
|
||||
|
||||
// 若為完成模式,則扣減原物料庫存
|
||||
if ($status === 'completed') {
|
||||
$inventory = Inventory::findOrFail($item['inventory_id']);
|
||||
$inventory->decrement('quantity', $item['quantity_used']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 成品入庫:在目標倉庫建立新的庫存紀錄
|
||||
$product = Product::findOrFail($validated['product_id']);
|
||||
Inventory::create([
|
||||
'warehouse_id' => $validated['warehouse_id'],
|
||||
'product_id' => $validated['product_id'],
|
||||
'quantity' => $validated['output_quantity'],
|
||||
'batch_number' => $validated['output_batch_number'],
|
||||
'box_number' => $validated['output_box_count'],
|
||||
'origin_country' => 'TW', // 生產預設為本地
|
||||
'arrival_date' => $validated['production_date'],
|
||||
'expiry_date' => $validated['expiry_date'] ?? null,
|
||||
'quality_status' => 'normal',
|
||||
]);
|
||||
// 3. 若為完成模式,執行成品入庫
|
||||
if ($status === 'completed') {
|
||||
$product = Product::findOrFail($validated['product_id']);
|
||||
Inventory::create([
|
||||
'warehouse_id' => $validated['warehouse_id'],
|
||||
'product_id' => $validated['product_id'],
|
||||
'quantity' => $validated['output_quantity'],
|
||||
'batch_number' => $validated['output_batch_number'],
|
||||
'box_number' => $validated['output_box_count'],
|
||||
'origin_country' => 'TW', // 生產預設為本地
|
||||
'arrival_date' => $validated['production_date'],
|
||||
'expiry_date' => $validated['expiry_date'] ?? null,
|
||||
'quality_status' => 'normal',
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
$message = $status === 'completed'
|
||||
? '生產單已建立,原物料已扣減,成品已入庫'
|
||||
: '生產單草稿已儲存';
|
||||
|
||||
return redirect()->route('production-orders.index')
|
||||
->with('success', '生產單已建立,原物料已扣減,成品已入庫');
|
||||
->with('success', $message);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +210,7 @@ class ProductionOrderController extends Controller
|
||||
*/
|
||||
public function getWarehouseInventories(Warehouse $warehouse)
|
||||
{
|
||||
$inventories = Inventory::with(['product.baseUnit'])
|
||||
$inventories = Inventory::with(['product.baseUnit', 'product.largeUnit'])
|
||||
->where('warehouse_id', $warehouse->id)
|
||||
->where('quantity', '>', 0)
|
||||
->where('quality_status', 'normal')
|
||||
@@ -188,9 +228,158 @@ class ProductionOrderController extends Controller
|
||||
'arrival_date' => $inv->arrival_date?->format('Y-m-d'),
|
||||
'expiry_date' => $inv->expiry_date?->format('Y-m-d'),
|
||||
'unit_name' => $inv->product->baseUnit?->name,
|
||||
'base_unit_id' => $inv->product->base_unit_id,
|
||||
'base_unit_name' => $inv->product->baseUnit?->name,
|
||||
'large_unit_id' => $inv->product->large_unit_id,
|
||||
'large_unit_name' => $inv->product->largeUnit?->name,
|
||||
'conversion_rate' => $inv->product->conversion_rate,
|
||||
];
|
||||
});
|
||||
|
||||
return response()->json($inventories);
|
||||
}
|
||||
|
||||
/**
|
||||
* 編輯生產單(僅限草稿狀態)
|
||||
*/
|
||||
public function edit(ProductionOrder $productionOrder): Response
|
||||
{
|
||||
// 只有草稿可以編輯
|
||||
if ($productionOrder->status !== 'draft') {
|
||||
return redirect()->route('production-orders.show', $productionOrder->id)
|
||||
->with('error', '只有草稿狀態的生產單可以編輯');
|
||||
}
|
||||
|
||||
$productionOrder->load(['product', 'warehouse', 'items.inventory.product', 'items.unit']);
|
||||
|
||||
return Inertia::render('Production/Edit', [
|
||||
'productionOrder' => $productionOrder,
|
||||
'products' => Product::with(['baseUnit'])->get(),
|
||||
'warehouses' => Warehouse::all(),
|
||||
'units' => Unit::all(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新生產單
|
||||
*/
|
||||
public function update(Request $request, ProductionOrder $productionOrder)
|
||||
{
|
||||
// 只有草稿可以編輯
|
||||
if ($productionOrder->status !== 'draft') {
|
||||
return redirect()->route('production-orders.show', $productionOrder->id)
|
||||
->with('error', '只有草稿狀態的生產單可以編輯');
|
||||
}
|
||||
|
||||
$status = $request->input('status', 'draft');
|
||||
|
||||
// 共用驗證規則
|
||||
$baseRules = [
|
||||
'product_id' => 'required|exists:products,id',
|
||||
'output_batch_number' => 'required|string|max:50',
|
||||
'status' => 'nullable|in:draft,completed',
|
||||
];
|
||||
|
||||
// 完成模式需要完整驗證
|
||||
$completedRules = [
|
||||
'warehouse_id' => 'required|exists:warehouses,id',
|
||||
'output_quantity' => 'required|numeric|min:0.01',
|
||||
'output_box_count' => 'nullable|string|max:10',
|
||||
'production_date' => 'required|date',
|
||||
'expiry_date' => 'nullable|date|after_or_equal:production_date',
|
||||
'remark' => 'nullable|string',
|
||||
'items' => 'required|array|min:1',
|
||||
'items.*.inventory_id' => 'required|exists:inventories,id',
|
||||
'items.*.quantity_used' => 'required|numeric|min:0.0001',
|
||||
'items.*.unit_id' => 'nullable|exists:units,id',
|
||||
];
|
||||
|
||||
// 草稿模式的寬鬆規則
|
||||
$draftRules = [
|
||||
'warehouse_id' => 'nullable|exists:warehouses,id',
|
||||
'output_quantity' => 'nullable|numeric|min:0',
|
||||
'output_box_count' => 'nullable|string|max:10',
|
||||
'production_date' => 'nullable|date',
|
||||
'expiry_date' => 'nullable|date',
|
||||
'remark' => 'nullable|string',
|
||||
'items' => 'nullable|array',
|
||||
'items.*.inventory_id' => 'nullable|exists:inventories,id',
|
||||
'items.*.quantity_used' => 'nullable|numeric|min:0',
|
||||
'items.*.unit_id' => 'nullable|exists:units,id',
|
||||
];
|
||||
|
||||
$rules = $status === 'completed'
|
||||
? array_merge($baseRules, $completedRules)
|
||||
: array_merge($baseRules, $draftRules);
|
||||
|
||||
$validated = $request->validate($rules, [
|
||||
'product_id.required' => '請選擇成品商品',
|
||||
'output_batch_number.required' => '請輸入成品批號',
|
||||
'warehouse_id.required' => '請選擇入庫倉庫',
|
||||
'output_quantity.required' => '請輸入生產數量',
|
||||
'production_date.required' => '請選擇生產日期',
|
||||
'items.required' => '請至少新增一項原物料',
|
||||
'items.min' => '請至少新增一項原物料',
|
||||
]);
|
||||
|
||||
DB::transaction(function () use ($validated, $status, $productionOrder) {
|
||||
// 更新生產工單基本資料
|
||||
$productionOrder->update([
|
||||
'product_id' => $validated['product_id'],
|
||||
'warehouse_id' => $validated['warehouse_id'] ?? null,
|
||||
'output_quantity' => $validated['output_quantity'] ?? 0,
|
||||
'output_batch_number' => $validated['output_batch_number'],
|
||||
'output_box_count' => $validated['output_box_count'] ?? null,
|
||||
'production_date' => $validated['production_date'] ?? now()->toDateString(),
|
||||
'expiry_date' => $validated['expiry_date'] ?? null,
|
||||
'status' => $status,
|
||||
'remark' => $validated['remark'] ?? null,
|
||||
]);
|
||||
|
||||
// 刪除舊的明細
|
||||
$productionOrder->items()->delete();
|
||||
|
||||
// 重新建立明細 (草稿與完成模式皆需儲存)
|
||||
if (!empty($validated['items'])) {
|
||||
foreach ($validated['items'] as $item) {
|
||||
if (empty($item['inventory_id'])) continue;
|
||||
|
||||
ProductionOrderItem::create([
|
||||
'production_order_id' => $productionOrder->id,
|
||||
'inventory_id' => $item['inventory_id'],
|
||||
'quantity_used' => $item['quantity_used'] ?? 0,
|
||||
'unit_id' => $item['unit_id'] ?? null,
|
||||
]);
|
||||
|
||||
// 若為完成模式,則扣減原物料庫存
|
||||
if ($status === 'completed') {
|
||||
$inventory = Inventory::findOrFail($item['inventory_id']);
|
||||
$inventory->decrement('quantity', $item['quantity_used']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 若為完成模式,執行成品入庫
|
||||
if ($status === 'completed') {
|
||||
Inventory::create([
|
||||
'warehouse_id' => $validated['warehouse_id'],
|
||||
'product_id' => $validated['product_id'],
|
||||
'quantity' => $validated['output_quantity'],
|
||||
'batch_number' => $validated['output_batch_number'],
|
||||
'box_number' => $validated['output_box_count'],
|
||||
'origin_country' => 'TW',
|
||||
'arrival_date' => $validated['production_date'],
|
||||
'expiry_date' => $validated['expiry_date'] ?? null,
|
||||
'quality_status' => 'normal',
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
$message = $status === 'completed'
|
||||
? '生產單已完成,原物料已扣減,成品已入庫'
|
||||
: '生產單草稿已更新';
|
||||
|
||||
return redirect()->route('production-orders.index')
|
||||
->with('success', $message);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user