diff --git a/app/Modules/Inventory/Controllers/AdjustDocController.php b/app/Modules/Inventory/Controllers/AdjustDocController.php index be7f96c..be8ddf4 100644 --- a/app/Modules/Inventory/Controllers/AdjustDocController.php +++ b/app/Modules/Inventory/Controllers/AdjustDocController.php @@ -129,7 +129,7 @@ class AdjustDocController extends Controller public function show(InventoryAdjustDoc $doc) { - $doc->load(['items.product.baseUnit', 'createdBy', 'postedBy', 'warehouse']); + $doc->load(['items.product.baseUnit', 'createdBy', 'postedBy', 'warehouse', 'countDoc']); $docData = [ 'id' => (string) $doc->id, @@ -141,6 +141,8 @@ class AdjustDocController extends Controller 'remarks' => $doc->remarks, 'created_at' => $doc->created_at->format('Y-m-d H:i'), 'created_by' => $doc->createdBy?->name, + 'count_doc_id' => $doc->count_doc_id ? (string)$doc->count_doc_id : null, + 'count_doc_no' => $doc->countDoc?->doc_no, 'items' => $doc->items->map(function ($item) { return [ 'id' => (string) $item->id, @@ -171,7 +173,7 @@ class AdjustDocController extends Controller if ($request->input('action') === 'post') { $this->adjustService->post($doc, auth()->id()); return redirect()->route('inventory.adjust.index') - ->with('success', '調整單已過帳生效'); + ->with('success', '盤調單已過帳生效'); } // 僅儲存資料 @@ -203,6 +205,6 @@ class AdjustDocController extends Controller $doc->delete(); return redirect()->route('inventory.adjust.index') - ->with('success', '調整單已刪除'); + ->with('success', '盤調單已刪除'); } } diff --git a/app/Modules/Inventory/Services/AdjustService.php b/app/Modules/Inventory/Services/AdjustService.php index 7272eb8..d4d5cd6 100644 --- a/app/Modules/Inventory/Services/AdjustService.php +++ b/app/Modules/Inventory/Services/AdjustService.php @@ -54,7 +54,7 @@ class AdjustService } /** - * 更新調整單內容 (Items) + * 更新盤調單內容 (Items) * 此處採用 "全量更新" 方式處理 items (先刪後加),簡單可靠 */ public function updateItems(InventoryAdjustDoc $doc, array $itemsData): void @@ -123,7 +123,7 @@ class AdjustService 'unit_cost' => $inventory->unit_cost, 'balance_before' => $oldQty, 'balance_after' => $newQty, - 'reason' => "調整單 {$doc->doc_no}: " . ($doc->reason ?? '手動調整'), + 'reason' => "盤調單 {$doc->doc_no}: " . ($doc->reason ?? '手動調整'), 'actual_time' => now(), 'user_id' => $userId, ]); @@ -134,6 +134,13 @@ class AdjustService 'posted_at' => now(), 'posted_by' => $userId, ]); + + // 4. 若關聯盤點單,連動更新盤點單狀態 + if ($doc->count_doc_id) { + InventoryCountDoc::where('id', $doc->count_doc_id)->update([ + 'status' => 'adjusted' + ]); + } }); } diff --git a/resources/js/Pages/Inventory/Adjust/Index.tsx b/resources/js/Pages/Inventory/Adjust/Index.tsx index 381e8e8..2cc05af 100644 --- a/resources/js/Pages/Inventory/Adjust/Index.tsx +++ b/resources/js/Pages/Inventory/Adjust/Index.tsx @@ -1,5 +1,5 @@ import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'; -import { Head, useForm, router } from '@inertiajs/react'; +import { Head, useForm, router, Link } from '@inertiajs/react'; import { Table, TableBody, @@ -20,8 +20,18 @@ import { DialogTitle, } from "@/Components/ui/dialog"; import { Label } from "@/Components/ui/label"; -import { Plus, Search, X, Eye, Pencil, ClipboardCheck } from "lucide-react"; +import { Plus, Search, X, Eye, Pencil, ClipboardCheck, Trash2 } from "lucide-react"; import { useState, useCallback, useEffect } from 'react'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/Components/ui/alert-dialog"; import Pagination from '@/Components/shared/Pagination'; import { SearchableSelect } from '@/Components/ui/searchable-select'; import { Can } from '@/Components/Permission/Can'; @@ -63,6 +73,7 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat const [searchQuery, setSearchQuery] = useState(filters.search || ''); const [warehouseId, setWarehouseId] = useState(filters.warehouse_id || ''); const [perPage, setPerPage] = useState(filters.per_page || '15'); + const [deleteId, setDeleteId] = useState(null); // For Count Doc Selection const [pendingCounts, setPendingCounts] = useState([]); @@ -110,6 +121,20 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat debouncedFilter({ search: searchQuery, warehouse_id: warehouseId, per_page: val }); }; + const confirmDelete = (id: string, e: React.MouseEvent) => { + e.stopPropagation(); + setDeleteId(id); + }; + + const handleDelete = () => { + if (deleteId) { + router.delete(route('inventory.adjust.destroy', [deleteId]), { + onSuccess: () => setDeleteId(null), + onError: () => setDeleteId(null), + }); + } + }; + const { data, setData, post, processing, reset } = useForm({ count_doc_id: null as string | null, warehouse_id: '', @@ -161,9 +186,11 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat

- 庫存盤調單 + 庫存盤調管理

-

針對盤點差異進行庫存調整與過帳 (盤盈、盤虧、報廢等)

+

+ 針對盤點差異進行庫存調整與過帳 (盤盈、盤虧、報廢等)。 +

@@ -229,7 +256,7 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat 建立者 建立時間 過帳時間 - 操作 + 操作 @@ -246,30 +273,46 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat className="hover:bg-gray-50/50 transition-colors cursor-pointer group" onClick={() => router.visit(route('inventory.adjust.show', [doc.id]))} > - + {(docs.current_page - 1) * docs.per_page + index + 1} - + {doc.doc_no} - {doc.warehouse_name} - {doc.reason} + {doc.warehouse_name} + {doc.reason} {getStatusBadge(doc.status)} - {doc.created_by} - {doc.created_at} - {doc.posted_at} - - + + {doc.status === 'draft' && ( + )} - + )) @@ -300,6 +343,21 @@ export default function Index({ docs, warehouses, filters }: { docs: DocsPaginat + + !open && setDeleteId(null)}> + + + 確定要刪除此盤調單嗎? + + 此動作無法復原。刪除後請重新建立盤調單。 + + + + 取消 + 確認刪除 + + + {/* Create Dialog */} diff --git a/resources/js/Pages/Inventory/Adjust/Show.tsx b/resources/js/Pages/Inventory/Adjust/Show.tsx index 2c05fe7..a7489d1 100644 --- a/resources/js/Pages/Inventory/Adjust/Show.tsx +++ b/resources/js/Pages/Inventory/Adjust/Show.tsx @@ -23,8 +23,7 @@ import { AlertDialogTrigger, } from "@/Components/ui/alert-dialog"; import { Label } from "@/Components/ui/label"; -import { Textarea } from "@/Components/ui/textarea"; -import { Save, CheckCircle, Trash2, ArrowLeft, Plus, X, Search, FileText, ClipboardCheck } from "lucide-react"; +import { Save, CheckCircle, Trash2, ArrowLeft, Plus, X, Search, ClipboardCheck } from "lucide-react"; import { useState, useEffect } from 'react'; import { Dialog, @@ -122,23 +121,15 @@ export default function Show({ doc }: { auth: any, doc: AdjDoc }) { }; const handlePost = () => { - // Validate if (data.items.length === 0) { alert('請至少加入一個調整項目'); return; } - const hasZero = data.items.some(i => Number(i.adjust_qty) === 0); - if (hasZero && !confirm('部分項目的調整數量為 0,確定要繼續嗎?')) { - return; - } - - if (confirm('確定要過帳嗎?過帳後將無法修改,並直接影響庫存。')) { - router.visit(route('inventory.adjust.update', [doc.id]), { - method: 'put', - data: { ...data, action: 'post' } as any, - }); - } + router.visit(route('inventory.adjust.update', [doc.id]), { + method: 'put', + data: { ...data, action: 'post' } as any, + }); }; const handleDelete = () => { @@ -282,7 +273,7 @@ export default function Show({ doc }: { auth: any, doc: AdjDoc }) {

調整項目

- {isDraft && ( + {isDraft && !doc.count_doc_id && ( addItem(product, batch)} @@ -353,7 +344,7 @@ export default function Show({ doc }: { auth: any, doc: AdjDoc }) { {item.notes || '-'} )} - {isDraft && ( + {isDraft && !doc.count_doc_id && ( - {doc.status !== 'completed' && ( + {!['completed', 'adjusted'].includes(doc.status) && (
@@ -119,7 +123,7 @@ export default function Show({ doc }: any) { 列印 - {isCompleted && ( + {doc.status === 'completed' && (