import { useState, useEffect } from "react"; import AuthenticatedLayout from "@/Layouts/AuthenticatedLayout"; import { Head, router, Link } from "@inertiajs/react"; import { Button } from "@/Components/ui/button"; import { Input } from "@/Components/ui/input"; import { Label } from "@/Components/ui/label"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/Components/ui/table"; import { Badge } from "@/Components/ui/badge"; import { Checkbox } from "@/Components/ui/checkbox"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, } from "@/Components/ui/dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/Components/ui/alert-dialog"; import { Plus, Save, Trash2, ArrowLeft, CheckCircle, Package, ArrowLeftRight, Printer, Search } from "lucide-react"; import { toast } from "sonner"; import axios from "axios"; import { Can } from '@/Components/Permission/Can'; import { usePermission } from '@/hooks/usePermission'; export default function Show({ order }: any) { const { can } = usePermission(); const [items, setItems] = useState(order.items || []); const [remarks, setRemarks] = useState(order.remarks || ""); const [isSaving, setIsSaving] = useState(false); const [deleteId, setDeleteId] = useState(null); const [isPostDialogOpen, setIsPostDialogOpen] = useState(false); // Product Selection const [isProductDialogOpen, setIsProductDialogOpen] = useState(false); const [availableInventory, setAvailableInventory] = useState([]); const [loadingInventory, setLoadingInventory] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [selectedInventory, setSelectedInventory] = useState([]); // product_id-batch useEffect(() => { if (isProductDialogOpen) { loadInventory(); setSelectedInventory([]); setSearchQuery(''); } }, [isProductDialogOpen]); const loadInventory = async () => { setLoadingInventory(true); try { // Fetch inventory from SOURCE warehouse const response = await axios.get(route('api.warehouses.inventories', order.from_warehouse_id)); setAvailableInventory(response.data); } catch (error) { console.error("Failed to load inventory", error); toast.error("無法載入庫存資料"); } finally { setLoadingInventory(false); } }; const toggleSelect = (key: string) => { setSelectedInventory(prev => prev.includes(key) ? prev.filter(k => k !== key) : [...prev, key] ); }; const toggleSelectAll = () => { const filtered = availableInventory.filter(inv => inv.product_name.toLowerCase().includes(searchQuery.toLowerCase()) || inv.product_code.toLowerCase().includes(searchQuery.toLowerCase()) ); const filteredKeys = filtered.map(inv => `${inv.product_id}-${inv.batch_number}`); if (filteredKeys.length > 0 && filteredKeys.every(k => selectedInventory.includes(k))) { setSelectedInventory(prev => prev.filter(k => !filteredKeys.includes(k))); } else { setSelectedInventory(prev => Array.from(new Set([...prev, ...filteredKeys]))); } }; const handleAddSelected = () => { if (selectedInventory.length === 0) return; const newItems = [...items]; let addedCount = 0; availableInventory.forEach(inv => { const key = `${inv.product_id}-${inv.batch_number}`; if (selectedInventory.includes(key)) { // Check if already added const exists = newItems.find((i: any) => i.product_id === inv.product_id && i.batch_number === inv.batch_number ); if (!exists) { newItems.push({ product_id: inv.product_id, product_name: inv.product_name, product_code: inv.product_code, batch_number: inv.batch_number, unit: inv.unit_name, quantity: 1, // Default 1 max_quantity: inv.quantity, // Max available notes: "", }); addedCount++; } } }); setItems(newItems); setIsProductDialogOpen(false); if (addedCount > 0) { toast.success(`已成功加入 ${addedCount} 個項目`); } else { toast.info("選取的商品已在清單中"); } }; const handleUpdateItem = (index: number, field: string, value: any) => { const newItems = [...items]; newItems[index][field] = value; setItems(newItems); }; const handleRemoveItem = (index: number) => { const newItems = items.filter((_: any, i: number) => i !== index); setItems(newItems); }; const handleSave = async () => { setIsSaving(true); try { await router.put(route('inventory.transfer.update', [order.id]), { items: items, remarks: remarks, }, { onSuccess: () => { }, onError: () => toast.error("儲存失敗,請檢查輸入"), }); } finally { setIsSaving(false); } }; const handlePost = () => { router.put(route('inventory.transfer.update', [order.id]), { action: 'post' }, { onSuccess: () => { setIsPostDialogOpen(false); } }); }; const handleDelete = () => { router.delete(route('inventory.transfer.destroy', [order.id]), { onSuccess: () => { setDeleteId(null); } }); }; const canEdit = can('inventory_transfer.edit'); const isReadOnly = order.status !== 'draft' || !canEdit; return (

調撥單: {order.doc_no}

{order.status === 'completed' && 已完成} {order.status === 'draft' && 草稿} {order.status === 'voided' && 已作廢}

來源: {order.from_warehouse_name} 目的: {order.to_warehouse_name} | 建立人: {order.created_by}

{!isReadOnly && (
!open && setDeleteId(null)}> 確定要刪除此調撥單嗎? 此動作無法復原。如果單據已存在重要資料,請謹慎操作。 取消 確認刪除 確定要過帳嗎? 過帳後庫存將立即從「{order.from_warehouse_name}」轉移至「{order.to_warehouse_name}」,且無法再進行修改。 取消 確認過帳
)}
{isReadOnly ? (
{order.remarks || '無備註'}
) : ( setRemarks(e.target.value)} className="h-9 focus:ring-primary-main" placeholder="填寫調撥單備註..." /> )}

調撥明細

請選擇要調撥的商品並輸入數量。所有商品將從「{order.from_warehouse_name}」轉出。

{!isReadOnly && ( 選擇來源庫存 ({order.from_warehouse_name})
setSearchQuery(e.target.value)} />
{loadingInventory ? (

庫存資料載入中...

) : (
0 && (() => { const filtered = availableInventory.filter(inv => inv.product_name.toLowerCase().includes(searchQuery.toLowerCase()) || inv.product_code.toLowerCase().includes(searchQuery.toLowerCase()) ); const filteredKeys = filtered.map(inv => `${inv.product_id}-${inv.batch_number}`); return filteredKeys.length > 0 && filteredKeys.every(k => selectedInventory.includes(k)); })()} onCheckedChange={() => toggleSelectAll()} /> 商品代號 品名 批號 現有庫存 {(() => { const filtered = availableInventory.filter(inv => inv.product_name.toLowerCase().includes(searchQuery.toLowerCase()) || inv.product_code.toLowerCase().includes(searchQuery.toLowerCase()) ); if (filtered.length === 0) { return ( {searchQuery ? `找不到與 "${searchQuery}" 相關的商品` : '尚無庫存資料'} ); } return filtered.map((inv) => { const key = `${inv.product_id}-${inv.batch_number}`; const isSelected = selectedInventory.includes(key); return ( toggleSelect(key)} > e.stopPropagation()}> toggleSelect(key)} /> {inv.product_code} {inv.product_name} {inv.batch_number || '-'} {inv.quantity} {inv.unit_name} ); }); })()}
)}
已選取 {selectedInventory.length} 項商品
{selectedInventory.length > 0 && ( )}
)}
# 商品名稱 / 代號 批號 {order.status === 'completed' ? '過帳時庫存' : '可用庫存'} 調撥數量 單位 備註 {!isReadOnly && } {items.length === 0 ? ( 尚未加入商品 ) : ( items.map((item: any, index: number) => ( {index + 1}
{item.product_name} {item.product_code}
{item.batch_number || '-'} {item.max_quantity} {item.unit || item.unit_name} {isReadOnly ? (
{item.quantity}
) : (
handleUpdateItem(index, 'quantity', e.target.value)} className="h-9 w-32 text-right font-medium focus:ring-primary-main" />
)}
{item.unit || item.unit_name} {isReadOnly ? ( {item.notes} ) : ( handleUpdateItem(index, 'notes', e.target.value)} placeholder="備註..." className="h-9 text-sm" /> )} {!isReadOnly && ( )}
)) )}
); }