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

@@ -81,58 +81,74 @@ export default function Index({
setPerPage(filters.per_page || "10");
}, [filters]);
const applyFilters = useCallback(
(overrides: Record<string, string> = {}) => {
const params: Record<string, string> = {
search: searchTerm,
status: statusFilter === "all" ? "" : statusFilter,
warehouse_id: warehouseFilter === "all" ? "" : warehouseFilter,
per_page: perPage,
...overrides,
};
// 清理空值
Object.keys(params).forEach((key) => {
if (!params[key]) delete params[key];
});
const debouncedFilter = useCallback(
debounce((params: any) => {
router.get(route("store-requisitions.index"), params, {
preserveState: true,
replace: true,
preserveScroll: true,
});
},
[searchTerm, statusFilter, warehouseFilter, perPage]
);
const debouncedSearch = useCallback(
debounce((term: string) => {
applyFilters({ search: term });
}, 500),
[applyFilters]
}, 300),
[]
);
const handleSearchChange = (term: string) => {
setSearchTerm(term);
debouncedSearch(term);
debouncedFilter({
...filters,
search: term,
status: statusFilter === "all" ? "" : statusFilter,
warehouse_id: warehouseFilter === "all" ? "" : warehouseFilter,
page: 1,
});
};
const handleClearSearch = () => {
setSearchTerm("");
applyFilters({ search: "" });
debouncedFilter({
...filters,
search: "",
status: statusFilter === "all" ? "" : statusFilter,
warehouse_id: warehouseFilter === "all" ? "" : warehouseFilter,
page: 1,
});
};
const handleStatusChange = (value: string) => {
setStatusFilter(value);
applyFilters({ status: value === "all" ? "" : value });
debouncedFilter({
...filters,
search: searchTerm,
status: value === "all" ? "" : value,
warehouse_id: warehouseFilter === "all" ? "" : warehouseFilter,
page: 1,
});
};
const handleWarehouseChange = (value: string) => {
setWarehouseFilter(value);
applyFilters({ warehouse_id: value === "all" ? "" : value });
debouncedFilter({
...filters,
search: searchTerm,
status: statusFilter === "all" ? "" : statusFilter,
warehouse_id: value === "all" ? "" : value,
page: 1,
});
};
const handlePerPageChange = (value: string) => {
setPerPage(value);
applyFilters({ per_page: value });
router.get(
route("store-requisitions.index"),
{
...filters,
search: searchTerm,
status: statusFilter === "all" ? "" : statusFilter,
warehouse_id: warehouseFilter === "all" ? "" : warehouseFilter,
per_page: value,
page: 1,
},
{ preserveState: true, replace: true, preserveScroll: true }
);
};
const handleDelete = () => {