fix(Inventory): 修復庫存列表批號欄位與新增庫存頁面儲位欄位遺失問題,並還原批號輸入佈局
This commit is contained in:
@@ -98,6 +98,7 @@ class InventoryController extends Controller
|
|||||||
'safetyStock' => null, // 批號層級不再有安全庫存
|
'safetyStock' => null, // 批號層級不再有安全庫存
|
||||||
'status' => '正常',
|
'status' => '正常',
|
||||||
'batchNumber' => $inv->batch_number ?? 'BATCH-' . $inv->id,
|
'batchNumber' => $inv->batch_number ?? 'BATCH-' . $inv->id,
|
||||||
|
'location' => $inv->location,
|
||||||
'expiryDate' => $inv->expiry_date ? $inv->expiry_date->format('Y-m-d') : null,
|
'expiryDate' => $inv->expiry_date ? $inv->expiry_date->format('Y-m-d') : null,
|
||||||
'lastInboundDate' => $inv->lastIncomingTransaction ? ($inv->lastIncomingTransaction->actual_time ? $inv->lastIncomingTransaction->actual_time->format('Y-m-d') : $inv->lastIncomingTransaction->created_at->format('Y-m-d')) : null,
|
'lastInboundDate' => $inv->lastIncomingTransaction ? ($inv->lastIncomingTransaction->actual_time ? $inv->lastIncomingTransaction->actual_time->format('Y-m-d') : $inv->lastIncomingTransaction->created_at->format('Y-m-d')) : null,
|
||||||
'lastOutboundDate' => $inv->lastOutgoingTransaction ? ($inv->lastOutgoingTransaction->actual_time ? $inv->lastOutgoingTransaction->actual_time->format('Y-m-d') : $inv->lastOutgoingTransaction->created_at->format('Y-m-d')) : null,
|
'lastOutboundDate' => $inv->lastOutgoingTransaction ? ($inv->lastOutgoingTransaction->actual_time ? $inv->lastOutgoingTransaction->actual_time->format('Y-m-d') : $inv->lastOutgoingTransaction->created_at->format('Y-m-d')) : null,
|
||||||
@@ -171,6 +172,7 @@ class InventoryController extends Controller
|
|||||||
'items.*.inventoryId' => 'required_if:items.*.batchMode,existing|nullable|exists:inventories,id',
|
'items.*.inventoryId' => 'required_if:items.*.batchMode,existing|nullable|exists:inventories,id',
|
||||||
'items.*.originCountry' => 'required_if:items.*.batchMode,new|nullable|string|max:2',
|
'items.*.originCountry' => 'required_if:items.*.batchMode,new|nullable|string|max:2',
|
||||||
'items.*.expiryDate' => 'nullable|date',
|
'items.*.expiryDate' => 'nullable|date',
|
||||||
|
'items.*.location' => 'nullable|string|max:50',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return DB::transaction(function () use ($validated, $warehouse) {
|
return DB::transaction(function () use ($validated, $warehouse) {
|
||||||
@@ -233,6 +235,7 @@ class InventoryController extends Controller
|
|||||||
'quantity' => 0,
|
'quantity' => 0,
|
||||||
'unit_cost' => $item['unit_cost'] ?? 0, // 新增
|
'unit_cost' => $item['unit_cost'] ?? 0, // 新增
|
||||||
'total_value' => 0, // 稍後計算
|
'total_value' => 0, // 稍後計算
|
||||||
|
'location' => $item['location'] ?? null,
|
||||||
'arrival_date' => $validated['inboundDate'],
|
'arrival_date' => $validated['inboundDate'],
|
||||||
'expiry_date' => $item['expiryDate'] ?? null,
|
'expiry_date' => $item['expiryDate'] ?? null,
|
||||||
'origin_country' => $originCountry,
|
'origin_country' => $originCountry,
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ class InventoryDataSheet implements FromArray, WithHeadings, WithTitle, ShouldAu
|
|||||||
'商品代號',
|
'商品代號',
|
||||||
'商品名稱',
|
'商品名稱',
|
||||||
'數量',
|
'數量',
|
||||||
|
'單位',
|
||||||
'入庫單價',
|
'入庫單價',
|
||||||
|
'儲位/貨道',
|
||||||
'批號',
|
'批號',
|
||||||
'產地',
|
'產地',
|
||||||
'效期',
|
'效期',
|
||||||
@@ -56,14 +58,22 @@ class InventoryInstructionSheet implements FromArray, WithHeadings, WithTitle, S
|
|||||||
['商品代號', '擇一輸入', '若條碼未填寫,系統會依據代號匹配商品'],
|
['商品代號', '擇一輸入', '若條碼未填寫,系統會依據代號匹配商品'],
|
||||||
['商品名稱', '選填', '僅供對照參考,匯入時系統會自動忽略此欄位內容'],
|
['商品名稱', '選填', '僅供對照參考,匯入時系統會自動忽略此欄位內容'],
|
||||||
['數量', '必填', '入庫的商品數量,須為大於 0 的數字'],
|
['數量', '必填', '入庫的商品數量,須為大於 0 的數字'],
|
||||||
|
['單位', '必填', '單位 (如:個、件)'],
|
||||||
['入庫單價', '選填', '未填寫時將預設使用商品的「採購成本價」'],
|
['入庫單價', '選填', '未填寫時將預設使用商品的「採購成本價」'],
|
||||||
|
['儲位/貨道', '選填', '一般倉庫請填寫「儲位(位址)」,販賣機倉庫請填寫「貨道編號」(如: A1)'],
|
||||||
['批號', '選填', '如需批次控管請填寫,若留空系統會自動標記為 "NO-BATCH"'],
|
['批號', '選填', '如需批次控管請填寫,若留空系統會自動標記為 "NO-BATCH"'],
|
||||||
['產地', '選填', '商品的生產地資訊 (如:TW)'],
|
['產地', '選填', '商品的生產地資訊 (如:TW)'],
|
||||||
['效期', '選填', '格式請務必使用 YYYY-MM-DD (例如: 2026-12-31)'],
|
['效期', '選填', '格式請務必使用 YYYY-MM-DD (例如: 2026-12-31)'],
|
||||||
['', '', ''],
|
['', '', ''],
|
||||||
['匹配規則說明', '', '1. 系統會優先比對「商品條碼」。'],
|
['倉庫類型參考', '', '系統支援以下倉庫性質:'],
|
||||||
['', '', '2. 若條碼欄位為空,則嘗試比對「商品代號」。'],
|
['標準倉', '', '一般總倉、儲備倉'],
|
||||||
['', '', '3. 若上述兩者皆無法匹配到既有商品,該列資料將匯入失敗。'],
|
['生產倉', '', '加工廠、中央廚房、原材料存放處'],
|
||||||
|
['門市倉', '', '前台通路、店舖銷售現場'],
|
||||||
|
['販賣機', '', 'IoT 自動販賣機設備,建議搭配「貨道」填寫'],
|
||||||
|
['', '', ''],
|
||||||
|
['匹配與匯入規則', '', '1. 系統會優先比對「商品條碼」,其次為「商品代號」。'],
|
||||||
|
['', '', '2. 庫存將匯入至您在匯入前於系統介面所選擇的目標倉庫。'],
|
||||||
|
['', '', '3. 若需區分不同貨道或批次,請分行填寫。'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,18 +55,20 @@ class InventoryImport implements ToModel, WithHeadingRow, WithValidation, WithMa
|
|||||||
|
|
||||||
$quantity = (float) $row['數量'];
|
$quantity = (float) $row['數量'];
|
||||||
$unitCost = isset($row['入庫單價']) ? (float) $row['入庫單價'] : ($product->cost_price ?? 0);
|
$unitCost = isset($row['入庫單價']) ? (float) $row['入庫單價'] : ($product->cost_price ?? 0);
|
||||||
|
$location = $row['儲位/貨道'] ?? null;
|
||||||
|
|
||||||
// 批號邏輯:若 Excel 留空則使用 NO-BATCH
|
// 批號邏輯:若 Excel 留空則使用 NO-BATCH
|
||||||
$batchNumber = !empty($row['批號']) ? $row['批號'] : 'NO-BATCH';
|
$batchNumber = !empty($row['批號']) ? $row['批號'] : 'NO-BATCH';
|
||||||
$originCountry = $row['產地'] ?? 'TW';
|
$originCountry = $row['產地'] ?? 'TW';
|
||||||
$expiryDate = !empty($row['效期']) ? $row['效期'] : null;
|
$expiryDate = !empty($row['效期']) ? $row['效期'] : null;
|
||||||
|
|
||||||
return DB::transaction(function () use ($product, $quantity, $unitCost, $batchNumber, $originCountry, $expiryDate) {
|
return DB::transaction(function () use ($product, $quantity, $unitCost, $location, $batchNumber, $originCountry, $expiryDate) {
|
||||||
// 使用與 InventoryController 相同的 firstOrNew 邏輯
|
// 使用與 InventoryController 相同的 firstOrNew 邏輯
|
||||||
$inventory = $this->warehouse->inventories()->withTrashed()->firstOrNew(
|
$inventory = $this->warehouse->inventories()->withTrashed()->firstOrNew(
|
||||||
[
|
[
|
||||||
'product_id' => $product->id,
|
'product_id' => $product->id,
|
||||||
'batch_number' => $batchNumber
|
'batch_number' => $batchNumber,
|
||||||
|
'location' => $location, // 加入儲位/貨道作為區分關鍵字
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'quantity' => 0,
|
'quantity' => 0,
|
||||||
@@ -114,7 +116,10 @@ class InventoryImport implements ToModel, WithHeadingRow, WithValidation, WithMa
|
|||||||
'商品條碼' => ['nullable', 'string'],
|
'商品條碼' => ['nullable', 'string'],
|
||||||
'商品代號' => ['nullable', 'string'],
|
'商品代號' => ['nullable', 'string'],
|
||||||
'數量' => ['required', 'numeric', 'min:0.01'],
|
'數量' => ['required', 'numeric', 'min:0.01'],
|
||||||
|
'單位' => ['required', 'string'],
|
||||||
'入庫單價' => ['nullable', 'numeric', 'min:0'],
|
'入庫單價' => ['nullable', 'numeric', 'min:0'],
|
||||||
|
'儲位/貨道' => ['nullable', 'string', 'max:50'],
|
||||||
|
'批號' => ['nullable', 'string'],
|
||||||
'效期' => ['nullable', 'date'],
|
'效期' => ['nullable', 'date'],
|
||||||
'產地' => ['nullable', 'string', 'max:2'],
|
'產地' => ['nullable', 'string', 'max:2'],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ export default function InventoryImportDialog({ open, onOpenChange, warehouseId
|
|||||||
<div className="text-sm text-gray-600 space-y-2 pb-2 pl-6">
|
<div className="text-sm text-gray-600 space-y-2 pb-2 pl-6">
|
||||||
<ul className="list-disc space-y-1">
|
<ul className="list-disc space-y-1">
|
||||||
<li><span className="font-medium text-gray-700">商品匹配</span>:優先使用「商品條碼」匹配,其次為「商品代號」。</li>
|
<li><span className="font-medium text-gray-700">商品匹配</span>:優先使用「商品條碼」匹配,其次為「商品代號」。</li>
|
||||||
<li><span className="font-medium text-gray-700">無批號模式</span>:若 Excel 中的「批號」欄位<span className="underline">保持空白</span>,系統將自動累加至該商品的「通用紀錄」。</li>
|
<li><span className="font-medium text-gray-700">無批號模式</span>:若 Excel 中的「批號」欄位保持空白,系統將自動累加至該商品的「通用紀錄」。(對販賣機而言,此紀錄通常對應至預設的一般庫存)。</li>
|
||||||
<li><span className="font-medium text-gray-700">效期設定</span>:若商品無效期概念可留空,或輸入格式如:2026/12/31。</li>
|
<li><span className="font-medium text-gray-700">效期設定</span>:若商品無效期概念可留空,或輸入格式如:2026/12/31。</li>
|
||||||
<li><span className="font-medium text-gray-700">入庫單價</span>:未填寫時將預設使用商品的「採購成本價」。</li>
|
<li><span className="font-medium text-gray-700">入庫單價</span>:未填寫時將預設使用商品的「採購成本價」。</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -42,7 +42,11 @@ export default function InventoryTable({
|
|||||||
onView,
|
onView,
|
||||||
onDelete,
|
onDelete,
|
||||||
onViewProduct,
|
onViewProduct,
|
||||||
}: InventoryTableProps) {
|
warehouse,
|
||||||
|
}: InventoryTableProps & { warehouse: any }) {
|
||||||
|
// 判斷是否為販賣機倉庫
|
||||||
|
const isVending = warehouse?.type === "vending";
|
||||||
|
|
||||||
// 每個商品的展開/折疊狀態
|
// 每個商品的展開/折疊狀態
|
||||||
const [expandedProducts, setExpandedProducts] = useState<Set<string>>(new Set());
|
const [expandedProducts, setExpandedProducts] = useState<Set<string>>(new Set());
|
||||||
|
|
||||||
@@ -197,7 +201,8 @@ export default function InventoryTable({
|
|||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableHead className="w-[5%]">#</TableHead>
|
<TableHead className="w-[5%]">#</TableHead>
|
||||||
<TableHead className="w-[12%]">批號</TableHead>
|
<TableHead className="w-[15%]">批號</TableHead>
|
||||||
|
<TableHead className="w-[10%]">{isVending ? "貨道" : "儲位"}</TableHead>
|
||||||
<TableHead className="w-[10%]">庫存數量</TableHead>
|
<TableHead className="w-[10%]">庫存數量</TableHead>
|
||||||
<Can permission="inventory.view_cost">
|
<Can permission="inventory.view_cost">
|
||||||
<TableHead className="w-[10%]">單位成本</TableHead>
|
<TableHead className="w-[10%]">單位成本</TableHead>
|
||||||
@@ -215,6 +220,7 @@ export default function InventoryTable({
|
|||||||
<TableRow key={batch.id}>
|
<TableRow key={batch.id}>
|
||||||
<TableCell className="text-grey-2">{index + 1}</TableCell>
|
<TableCell className="text-grey-2">{index + 1}</TableCell>
|
||||||
<TableCell>{batch.batchNumber || "-"}</TableCell>
|
<TableCell>{batch.batchNumber || "-"}</TableCell>
|
||||||
|
<TableCell>{batch.location || "-"}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<span>{batch.quantity} {batch.unit}</span>
|
<span>{batch.quantity} {batch.unit}</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
@@ -510,6 +510,9 @@ export default function AddInventoryPage({ warehouse, products }: Props) {
|
|||||||
數量 <span className="text-red-500">*</span>
|
數量 <span className="text-red-500">*</span>
|
||||||
</TableHead>
|
</TableHead>
|
||||||
<TableHead className="w-[90px]">單位</TableHead>
|
<TableHead className="w-[90px]">單位</TableHead>
|
||||||
|
<TableHead className="w-[120px]">
|
||||||
|
{warehouse.type === 'vending' ? '貨道' : '儲位'}
|
||||||
|
</TableHead>
|
||||||
<TableHead className="w-[50px]"></TableHead>
|
<TableHead className="w-[50px]"></TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
@@ -714,6 +717,16 @@ export default function AddInventoryPage({ warehouse, products }: Props) {
|
|||||||
)}
|
)}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
||||||
|
{/* 儲位/貨道 */}
|
||||||
|
<TableCell>
|
||||||
|
<Input
|
||||||
|
value={item.location || ""}
|
||||||
|
onChange={(e) => handleUpdateItem(item.tempId, { location: e.target.value })}
|
||||||
|
className="border-gray-300"
|
||||||
|
placeholder={warehouse.type === 'vending' ? "貨道 (如: A1)" : "儲位 (選填)"}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
{/* 刪除按鈕 */}
|
{/* 刪除按鈕 */}
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ export default function WarehouseInventoryPage({
|
|||||||
onView={handleView}
|
onView={handleView}
|
||||||
onDelete={confirmDelete}
|
onDelete={confirmDelete}
|
||||||
onViewProduct={handleViewProduct}
|
onViewProduct={handleViewProduct}
|
||||||
|
warehouse={warehouse}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<AlertDialog open={!!deleteId} onOpenChange={(open) => !open && setDeleteId(null)}>
|
<AlertDialog open={!!deleteId} onOpenChange={(open) => !open && setDeleteId(null)}>
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export interface WarehouseInventory {
|
|||||||
safetyStock: number | null;
|
safetyStock: number | null;
|
||||||
status?: '正常' | '低於'; // 後端可能回傳的狀態
|
status?: '正常' | '低於'; // 後端可能回傳的狀態
|
||||||
batchNumber: string; // 批號 (Mock for now)
|
batchNumber: string; // 批號 (Mock for now)
|
||||||
|
location?: string; // 儲位/貨道
|
||||||
expiryDate: string;
|
expiryDate: string;
|
||||||
lastInboundDate: string | null;
|
lastInboundDate: string | null;
|
||||||
lastOutboundDate: string | null;
|
lastOutboundDate: string | null;
|
||||||
@@ -177,6 +178,7 @@ export interface InboundItem {
|
|||||||
batchMode?: 'existing' | 'new' | 'none'; // 批號模式
|
batchMode?: 'existing' | 'new' | 'none'; // 批號模式
|
||||||
inventoryId?: string; // 選擇現有批號時的 ID
|
inventoryId?: string; // 選擇現有批號時的 ID
|
||||||
batchNumber?: string;
|
batchNumber?: string;
|
||||||
|
location?: string; // 儲位/貨道
|
||||||
originCountry?: string; // 新增產地
|
originCountry?: string; // 新增產地
|
||||||
expiryDate?: string;
|
expiryDate?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user