- 優化採購單更新與刪除的活動紀錄邏輯 (PurchaseOrderController) - 整合更新異動為單一紀錄,包含品項差異 - 刪除時記錄當下品項快照 - 統一採購單刪除確認介面,使用 AlertDialog 取代原生 confirm (PurchaseOrderActions) - Refactor: 將 ActivityDetailDialog 移至 Components/ActivityLog 並優化樣式與大數據顯示 - 調整 UI 文字:將「總金額」統一為「小計」 - 其他模型與 Controller 的活動紀錄支援更新
193 lines
6.9 KiB
TypeScript
193 lines
6.9 KiB
TypeScript
/**
|
|
* 採購單表單管理 Hook
|
|
*/
|
|
|
|
import { useState, useEffect } from "react";
|
|
import type { PurchaseOrder, PurchaseOrderItem, Supplier, PurchaseOrderStatus } from "@/types/purchase-order";
|
|
import { calculateSubtotal } from "@/utils/purchase-order";
|
|
|
|
interface UsePurchaseOrderFormProps {
|
|
order?: PurchaseOrder;
|
|
suppliers: Supplier[];
|
|
}
|
|
|
|
export function usePurchaseOrderForm({ order, suppliers }: UsePurchaseOrderFormProps) {
|
|
const [supplierId, setSupplierId] = useState(order?.supplierId || "");
|
|
const [expectedDate, setExpectedDate] = useState(order?.expectedDate || "");
|
|
const [items, setItems] = useState<PurchaseOrderItem[]>(order?.items || []);
|
|
const [notes, setNotes] = useState(order?.remark || "");
|
|
const [status, setStatus] = useState<PurchaseOrderStatus>(order?.status || "draft");
|
|
const [warehouseId, setWarehouseId] = useState<string | number>(order?.warehouse_id || "");
|
|
const [invoiceNumber, setInvoiceNumber] = useState(order?.invoiceNumber || "");
|
|
const [invoiceDate, setInvoiceDate] = useState(order?.invoiceDate || "");
|
|
const [invoiceAmount, setInvoiceAmount] = useState(order?.invoiceAmount ? String(order.invoiceAmount) : "");
|
|
const [taxAmount, setTaxAmount] = useState<string | number>(
|
|
order?.taxAmount !== undefined && order.taxAmount !== null ? order.taxAmount :
|
|
(order?.tax_amount !== undefined && order.tax_amount !== null ? order.tax_amount : "")
|
|
);
|
|
const [isTaxManual, setIsTaxManual] = useState(!!(order?.taxAmount !== undefined || order?.tax_amount !== undefined));
|
|
|
|
// 同步外部傳入的 order 更新 (例如重新執行 edit 路由)
|
|
useEffect(() => {
|
|
if (order) {
|
|
setSupplierId(order.supplierId);
|
|
setExpectedDate(order.expectedDate);
|
|
setItems(order.items || []);
|
|
setNotes(order.remark || "");
|
|
setStatus(order.status);
|
|
setWarehouseId(order.warehouse_id || "");
|
|
setInvoiceNumber(order.invoiceNumber || "");
|
|
setInvoiceDate(order.invoiceDate || "");
|
|
setInvoiceAmount(order.invoiceAmount ? String(order.invoiceAmount) : "");
|
|
|
|
const val = order.taxAmount !== undefined && order.taxAmount !== null ? order.taxAmount :
|
|
(order.tax_amount !== undefined && order.tax_amount !== null ? order.tax_amount : "");
|
|
setTaxAmount(val);
|
|
if (val !== "") {
|
|
setIsTaxManual(true);
|
|
}
|
|
}
|
|
}, [order]);
|
|
|
|
const resetForm = () => {
|
|
setSupplierId("");
|
|
setExpectedDate("");
|
|
setItems([]);
|
|
setNotes("");
|
|
setStatus("draft");
|
|
setWarehouseId("");
|
|
setInvoiceNumber("");
|
|
setInvoiceDate("");
|
|
setInvoiceAmount("");
|
|
setTaxAmount("");
|
|
setIsTaxManual(false);
|
|
};
|
|
|
|
const selectedSupplier = suppliers.find((s) => String(s.id) === String(supplierId));
|
|
const isOrderSent = order && order.status !== "draft";
|
|
|
|
// 新增商品項目
|
|
const addItem = () => {
|
|
if (!selectedSupplier) return;
|
|
|
|
setItems([
|
|
...items,
|
|
{
|
|
productId: "",
|
|
productName: "",
|
|
quantity: 1,
|
|
unitPrice: 0,
|
|
subtotal: 0,
|
|
selectedUnit: "base",
|
|
},
|
|
]);
|
|
};
|
|
|
|
// 移除商品項目
|
|
const removeItem = (index: number) => {
|
|
setItems(items.filter((_, i) => i !== index));
|
|
};
|
|
|
|
// 更新商品項目
|
|
const updateItem = (index: number, field: keyof PurchaseOrderItem, value: any) => {
|
|
const newItems = [...items];
|
|
const item = { ...newItems[index] };
|
|
|
|
if (field === "productId" && selectedSupplier) {
|
|
// value is productId string
|
|
const product = selectedSupplier.commonProducts.find((p) => p.productId === value);
|
|
if (product) {
|
|
// @ts-ignore
|
|
item.productId = value;
|
|
item.productName = product.productName;
|
|
item.base_unit_id = product.base_unit_id;
|
|
item.base_unit_name = product.base_unit_name;
|
|
item.large_unit_id = product.large_unit_id;
|
|
item.large_unit_name = product.large_unit_name;
|
|
item.purchase_unit_id = product.purchase_unit_id;
|
|
item.conversion_rate = product.conversion_rate;
|
|
item.unitPrice = product.lastPrice;
|
|
item.previousPrice = product.lastPrice;
|
|
|
|
// 決定預設單位
|
|
const isPurchaseUnitLarge = product.purchase_unit_id && product.large_unit_id && product.purchase_unit_id === product.large_unit_id;
|
|
|
|
if (isPurchaseUnitLarge) {
|
|
item.selectedUnit = 'large';
|
|
item.unitId = product.large_unit_id;
|
|
} else {
|
|
item.selectedUnit = 'base';
|
|
item.unitId = product.base_unit_id;
|
|
}
|
|
|
|
// 初始小計 = 數量 * 單價
|
|
item.subtotal = calculateSubtotal(Number(item.quantity), Number(item.unitPrice));
|
|
}
|
|
} else if (field === "selectedUnit") {
|
|
// @ts-ignore
|
|
item.selectedUnit = value;
|
|
if (value === 'large') {
|
|
item.unitId = item.large_unit_id;
|
|
} else {
|
|
item.unitId = item.base_unit_id;
|
|
}
|
|
// Switch unit doesn't change Total Amount (Subtotal), but implies Unit Price changes?
|
|
// Actually if I switch unit, the Quantity is usually for that unit.
|
|
// If I have 1 Box ($100), and switch to Pc. Quantity is still 1.
|
|
// Total is $100. So Unit Price (per Pc) becomes $100.
|
|
// This seems safely consistent with "Total Amount" anchor.
|
|
} else {
|
|
// @ts-ignore
|
|
item[field] = value;
|
|
}
|
|
|
|
// 重新計算 (Always derive UnitPrice from Subtotal and Quantity)
|
|
// 除了剛剛已經算過 subtotal 的 productId case
|
|
if (field !== "productId") {
|
|
if (item.quantity > 0) {
|
|
item.unitPrice = Number(item.subtotal) / Number(item.quantity);
|
|
} else {
|
|
item.unitPrice = 0;
|
|
}
|
|
}
|
|
|
|
newItems[index] = item;
|
|
setItems(newItems);
|
|
};
|
|
|
|
return {
|
|
// State
|
|
supplierId,
|
|
expectedDate,
|
|
items,
|
|
notes,
|
|
status,
|
|
selectedSupplier,
|
|
isOrderSent,
|
|
warehouseId,
|
|
invoiceNumber,
|
|
invoiceDate,
|
|
invoiceAmount,
|
|
taxAmount,
|
|
isTaxManual,
|
|
|
|
// Setters
|
|
setSupplierId,
|
|
setExpectedDate,
|
|
setNotes,
|
|
setStatus,
|
|
setWarehouseId,
|
|
setInvoiceNumber,
|
|
setInvoiceDate,
|
|
setInvoiceAmount,
|
|
setTaxAmount,
|
|
setIsTaxManual,
|
|
|
|
// Methods
|
|
addItem,
|
|
removeItem,
|
|
updateItem,
|
|
resetForm,
|
|
};
|
|
}
|