feat(warehouse): Batch 5 — config-driven allocation + storage/demurrage fee rules

- Allocation rules engine: cargo/container/trade criteria -> deterministic yard/warehouse/zone by code; wired into auto-unload (fallback to default)
- Storage/demurrage fee rules: configurable freeDays + ratePerDay; most-specific match; fee preview per inventory item
- Inventory demurrage timestamps: inspectionStartedAt, inspectionCompletedAt, readyForPickupAt, releaseDate, gateClearedAt
- Migration 1790000000000 (allocation_rules + fee_rules tables + inventory date columns)
- Frontend: Allocation & Fees config page, automatic Fee Preview modal, plumbing/hooks
- No invoice/payment (Batch 6)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hagernesh
2026-06-17 23:53:04 +00:00
parent 270e39edd6
commit 01aec12ee9
23 changed files with 1429 additions and 12 deletions

View File

@@ -2,12 +2,19 @@ import { api as apiClient } from '../auth/http';
import { URL_CONSTANTS } from '@/constants/URLS';
import type {
AllocationCriteria,
AllocationPreviewResult,
AllocationRule,
ArrivalQueueItem,
AutoLoadResult,
AutoUnloadResult,
FeePreview,
FeeRule,
InspectionAttachment,
InspectionReport,
InspectionReportPayload,
SaveAllocationRulePayload,
SaveFeeRulePayload,
BookingScheduleView,
InventoryFilter,
InventoryInquiryFilter,
@@ -145,4 +152,25 @@ export const warehouseService = {
{ headers: { 'Content-Type': 'multipart/form-data' } },
);
},
// ── Batch 5: Allocation + Fee rules / previews ─────────────────────────────
listAllocationRules: () =>
apiClient.get<AllocationRule[]>(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION),
createAllocationRule: (payload: SaveAllocationRulePayload) =>
apiClient.post<AllocationRule>(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION, payload),
updateAllocationRule: (id: string, payload: Partial<SaveAllocationRulePayload>) =>
apiClient.patch<AllocationRule>(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION_BY_ID(id), payload),
deleteAllocationRule: (id: string) =>
apiClient.delete(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION_BY_ID(id)),
previewAllocation: (criteria: AllocationCriteria) =>
apiClient.post<AllocationPreviewResult | null>(URL_CONSTANTS.WAREHOUSE_RULES.ALLOCATION_PREVIEW, criteria),
listFeeRules: () => apiClient.get<FeeRule[]>(URL_CONSTANTS.WAREHOUSE_RULES.FEES),
createFeeRule: (payload: SaveFeeRulePayload) =>
apiClient.post<FeeRule>(URL_CONSTANTS.WAREHOUSE_RULES.FEES, payload),
updateFeeRule: (id: string, payload: Partial<SaveFeeRulePayload>) =>
apiClient.patch<FeeRule>(URL_CONSTANTS.WAREHOUSE_RULES.FEES_BY_ID(id), payload),
deleteFeeRule: (id: string) => apiClient.delete(URL_CONSTANTS.WAREHOUSE_RULES.FEES_BY_ID(id)),
feePreview: (inventoryId: string) =>
apiClient.get<FeePreview[]>(URL_CONSTANTS.WAREHOUSE_RULES.FEE_PREVIEW(inventoryId)),
};