feat: 完成進貨單自動拋轉應付帳款流程與AP介面優化
All checks were successful
ERP-Deploy-Demo / deploy-demo (push) Successful in 1m8s

1. 新增 AccountPayable (應付帳款) 模組,包含 Migration、Model、Service 與 Controller
2. 修改 GoodsReceipt (進貨單) 流程,在確認進貨時自動產生對應的應付帳款單 (AP-YYYYMMDD-XX)
3. 實作應付帳款詳細頁面 (Show.tsx),包含發票登記與標記付款功能
4. 修正應付帳款 Show 頁面的排版,將發票資訊套用標準的綠色背景區塊,並調整按鈕位置
5. 更新相關的 Service Provider 與 Routes
This commit is contained in:
2026-02-24 16:46:55 +08:00
parent aaa93a921e
commit 455f945296
33 changed files with 1708 additions and 186 deletions

View File

@@ -33,6 +33,7 @@ export default function GoodsReceiptActions({
receipt,
}: { receipt: GoodsReceipt }) {
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
const { delete: destroy, processing } = useForm({});
const handleConfirmDelete = () => {
@@ -46,6 +47,8 @@ export default function GoodsReceiptActions({
});
};
return (
<div className="flex justify-center gap-2">
<Link href={route('goods-receipts.show', receipt.id)}>
@@ -59,43 +62,46 @@ export default function GoodsReceiptActions({
</Button>
</Link>
{/* Delete typically restricted for Goods Receipts, checking permission */}
<Can permission="goods_receipts.delete">
<Button
variant="outline"
size="sm"
className="button-outlined-error"
title="刪除"
onClick={() => setShowDeleteDialog(true)}
disabled={processing}
>
<Trash2 className="h-4 w-4" />
</Button>
{/* 只允許刪除草稿或已退回的進貨單 */}
{(receipt.status === 'draft' || receipt.status === 'rejected') && (
<Can permission="goods_receipts.delete">
<Button
variant="outline"
size="sm"
className="button-outlined-error"
title="刪除"
onClick={() => setShowDeleteDialog(true)}
disabled={processing}
>
<Trash2 className="h-4 w-4" />
</Button>
</Can>
)}
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
{receipt.code}
<br />
<span className="text-red-500 font-bold mt-2 block">
</span>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="button-outlined-primary"></AlertDialogCancel>
<AlertDialogAction
onClick={handleConfirmDelete}
className="button-filled-error"
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog open={showDeleteDialog} onOpenChange={setShowDeleteDialog}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle></AlertDialogTitle>
<AlertDialogDescription>
{receipt.code}
<br />
<span className="text-red-500 font-bold mt-2 block">
</span>
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel className="button-outlined-primary"></AlertDialogCancel>
<AlertDialogAction
onClick={handleConfirmDelete}
className="button-filled-error"
>
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</Can>
</div>
);
}

View File

@@ -3,9 +3,12 @@ import { StatusBadge, StatusVariant } from "@/Components/shared/StatusBadge";
export type GoodsReceiptStatus = 'processing' | 'completed' | 'cancelled';
export const GOODS_RECEIPT_STATUS_CONFIG: Record<string, { label: string; variant: StatusVariant }> = {
draft: { label: "草稿", variant: "neutral" },
pending_audit: { label: "待審核", variant: "warning" },
processing: { label: "處理中", variant: "info" },
completed: { label: "已完成", variant: "success" },
cancelled: { label: "已取消", variant: "destructive" },
rejected: { label: "已退回", variant: "destructive" },
};
interface GoodsReceiptStatusBadgeProps {