feat(production): 優化生產單 BOM 原物料選取邏輯,支援商品 -> 倉庫 -> 批號連動與 API 分佈查詢

This commit is contained in:
2026-02-04 13:08:05 +08:00
parent a0c450d229
commit 4ba85ce446
17 changed files with 285 additions and 227 deletions

View File

@@ -2,12 +2,11 @@
* 編輯配方頁面
*/
import { useState, useEffect } from "react";
import { useEffect } from "react";
import { BookOpen, Plus, Trash2, ArrowLeft, Save } from 'lucide-react';
import { Button } from "@/Components/ui/button";
import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout";
import { Head, router, useForm, Link } from "@inertiajs/react";
import { toast } from "sonner";
import { Head, useForm, Link } from "@inertiajs/react";
import { getBreadcrumbs } from "@/utils/breadcrumb";
import { SearchableSelect } from "@/Components/ui/searchable-select";
import { Input } from "@/Components/ui/input";
@@ -59,6 +58,7 @@ interface RecipeItemForm {
// UI Helpers
ui_product_name?: string;
ui_product_code?: string;
ui_unit_name?: string;
}
interface Props {
@@ -80,7 +80,8 @@ export default function RecipeEdit({ recipe, products, units }: Props) {
unit_id: String(item.unit_id),
remark: item.remark || "",
ui_product_name: item.product?.name,
ui_product_code: item.product?.code
ui_product_code: item.product?.code,
ui_unit_name: item.unit?.name
})) as RecipeItemForm[],
});
@@ -118,6 +119,8 @@ export default function RecipeEdit({ recipe, products, units }: Props) {
// Default to base unit if not set
if (product.base_unit_id) {
newItems[index].unit_id = String(product.base_unit_id);
const unit = units.find(u => u.id === product.base_unit_id);
newItems[index].ui_unit_name = unit?.name;
}
}
}
@@ -127,14 +130,7 @@ export default function RecipeEdit({ recipe, products, units }: Props) {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
put(route('recipes.update', recipe.id), {
onSuccess: () => {
toast.success("配方已更新");
},
onError: (errors) => {
toast.error("儲存失敗,請檢查欄位");
}
});
put(route('recipes.update', recipe.id));
};
return (
@@ -299,17 +295,10 @@ export default function RecipeEdit({ recipe, products, units }: Props) {
placeholder="數量"
/>
</TableCell>
<TableCell className="align-top">
<SearchableSelect
value={item.unit_id}
onValueChange={(v) => updateItem(index, 'unit_id', v)}
options={units.map(u => ({
label: u.name,
value: String(u.id)
}))}
placeholder="單位"
className="w-full"
/>
<TableCell className="align-middle">
<div className="text-sm text-gray-700 bg-gray-50 px-3 py-2 rounded-md border border-gray-100 min-h-[38px] flex items-center">
{item.ui_unit_name || (units.find(u => String(u.id) === item.unit_id)?.name) || '-'}
</div>
</TableCell>
<TableCell className="align-top">
<Input