/** * 新增庫存頁面(手動入庫) */ import { useState } from "react"; import { Plus, Trash2, Calendar, ArrowLeft, Save, Boxes } from "lucide-react"; import { Button } from "@/Components/ui/button"; import { Input } from "@/Components/ui/input"; import { Label } from "@/Components/ui/label"; import { Textarea } from "@/Components/ui/textarea"; import { SearchableSelect } from "@/Components/ui/searchable-select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/Components/ui/table"; import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout"; import { Head, Link, router } from "@inertiajs/react"; import { Warehouse, InboundItem, InboundReason } from "@/types/warehouse"; import { getCurrentDateTime } from "@/utils/format"; import { toast } from "sonner"; import { getInventoryBreadcrumbs } from "@/utils/breadcrumb"; interface Product { id: string; name: string; baseUnit: string; largeUnit?: string; conversionRate?: number; } interface Props { warehouse: Warehouse; products: Product[]; } const INBOUND_REASONS: InboundReason[] = [ "期初建檔", "盤點調整", "實際入庫未走採購流程", "生產加工成品入庫", "其他", ]; export default function AddInventoryPage({ warehouse, products }: Props) { const [inboundDate, setInboundDate] = useState(getCurrentDateTime()); const [reason, setReason] = useState("期初建檔"); const [notes, setNotes] = useState(""); const [items, setItems] = useState([]); const [errors, setErrors] = useState>({}); // 新增明細行 const handleAddItem = () => { const defaultProduct = products.length > 0 ? products[0] : { id: "", name: "", baseUnit: "個" }; const newItem: InboundItem = { tempId: `temp-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, productId: defaultProduct.id, productName: defaultProduct.name, quantity: 0, unit: defaultProduct.baseUnit, // 僅用於顯示當前選擇單位的名稱 baseUnit: defaultProduct.baseUnit, largeUnit: defaultProduct.largeUnit, conversionRate: defaultProduct.conversionRate, selectedUnit: 'base', }; setItems([...items, newItem]); }; // 刪除明細行 const handleRemoveItem = (tempId: string) => { setItems(items.filter((item) => item.tempId !== tempId)); }; // 更新明細行 const handleUpdateItem = (tempId: string, updates: Partial) => { setItems( items.map((item) => item.tempId === tempId ? { ...item, ...updates } : item ) ); }; // 處理商品變更 const handleProductChange = (tempId: string, productId: string) => { const product = products.find((p) => p.id === productId); if (product) { handleUpdateItem(tempId, { productId, productName: product.name, unit: product.baseUnit, baseUnit: product.baseUnit, largeUnit: product.largeUnit, conversionRate: product.conversionRate, selectedUnit: 'base', }); } }; // 驗證表單 const validateForm = (): boolean => { const newErrors: Record = {}; if (!reason) { newErrors.reason = "請選擇入庫原因"; } if (reason === "其他" && !notes.trim()) { newErrors.notes = "原因為「其他」時,備註為必填"; } if (items.length === 0) { newErrors.items = "請至少新增一筆庫存明細"; } items.forEach((item, index) => { if (!item.productId) { newErrors[`item-${index}-product`] = "請選擇商品"; } if (item.quantity <= 0) { newErrors[`item-${index}-quantity`] = "數量必須大於 0"; } }); setErrors(newErrors); return Object.keys(newErrors).length === 0; }; // 處理儲存 const handleSave = () => { if (!validateForm()) { toast.error("請檢查表單內容"); return; } router.post(`/warehouses/${warehouse.id}/inventory`, { inboundDate, reason, notes, items: items.map(item => { // 如果選擇大單位,則換算為基本單位數量 const finalQuantity = item.selectedUnit === 'large' && item.conversionRate ? item.quantity * item.conversionRate : item.quantity; return { productId: item.productId, quantity: finalQuantity }; }) }, { onSuccess: () => { toast.success("庫存記錄已儲存"); router.get(`/warehouses/${warehouse.id}/inventory`); }, onError: (err) => { toast.error("儲存失敗,請檢查輸入內容"); console.error(err); } }); }; return (
{/* 頁面標題與導航 - 已於先前任務優化 */}

新增庫存(手動入庫)

{warehouse.name} 新增庫存記錄

{/* 表單內容 */}
{/* 基本資訊區塊 */}

基本資訊

{/* 倉庫 */}
{/* 入庫日期 */}
setInboundDate(e.target.value)} className="border-gray-300 pr-10" />
{/* 入庫原因 */}
setReason(value as InboundReason)} options={INBOUND_REASONS.map((r) => ({ label: r, value: r }))} placeholder="選擇入庫原因" className="border-gray-300" /> {errors.reason && (

{errors.reason}

)}
{/* 備註 */}