mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-09 08:18:20 +00:00
api
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Badge, Button, Card, Divider, Group, Loader, Modal, Stack, Text } from '@mantine/core';
|
||||
import { useState } from 'react';
|
||||
import { Badge, Button, Card, Divider, Group, Loader, Modal, SegmentedControl, Stack, Text } from '@mantine/core';
|
||||
import { CalendarClock, Coins, DoorOpen, FileText } from 'lucide-react';
|
||||
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
@@ -91,10 +92,11 @@ function Row({ label, value }: { label: string; value: string }) {
|
||||
/** Batch 5 fee preview + Batch 6 invoice generation / gate clearance for an inventory item. */
|
||||
export function FeePreviewModal({ opened, onClose, inventoryId }: FeePreviewModalProps) {
|
||||
const { toast } = useToast();
|
||||
const [billingCurrency, setBillingCurrency] = useState<'ETB' | 'USD'>('USD');
|
||||
const enabledId = opened ? inventoryId ?? undefined : undefined;
|
||||
const { data, isLoading } = useQuery(
|
||||
api.warehouses.feePreview.queryOptions({
|
||||
input: { inventoryId: enabledId ?? '' },
|
||||
input: { inventoryId: enabledId ?? '', billingCurrency },
|
||||
enabled: Boolean(enabledId),
|
||||
}),
|
||||
);
|
||||
@@ -108,21 +110,22 @@ export function FeePreviewModal({ opened, onClose, inventoryId }: FeePreviewModa
|
||||
const gateClear = useMutation(api.warehouses.gateClearance.mutationOptions());
|
||||
|
||||
const activeInvoice = (invoices ?? []).find((i) => i.status !== 'CANCELLED');
|
||||
const totalPreviewAmount = (data ?? []).reduce((sum, fee) => sum + Number(fee.amount || 0), 0);
|
||||
|
||||
const handleGenerate = async (confirmZero = false) => {
|
||||
const handleGenerate = async () => {
|
||||
if (!inventoryId) return;
|
||||
if (totalPreviewAmount <= 0) {
|
||||
toast({
|
||||
title: 'No fee to invoice',
|
||||
description: 'The item is still within the configured free days, or no active fee rule matched it.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const inv = await generate.mutateAsync({ inventoryId, confirmZero });
|
||||
const inv = await generate.mutateAsync({ inventoryId, billingCurrency });
|
||||
toast({ title: 'Invoice generated', description: `${inv.invoiceNumber} - ${money(inv.totalAmount, inv.currency)}` });
|
||||
} catch (error) {
|
||||
const msg = extractErrorMessage(error);
|
||||
if (/no payable warehouse fee/i.test(msg)) {
|
||||
if (window.confirm('No payable warehouse fee found. Create a zero-amount invoice anyway?')) {
|
||||
handleGenerate(true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
toast({ variant: 'destructive', title: 'Generate failed', description: msg });
|
||||
toast({ variant: 'destructive', title: 'Generate failed', description: extractErrorMessage(error) });
|
||||
}
|
||||
};
|
||||
|
||||
@@ -172,6 +175,22 @@ export function FeePreviewModal({ opened, onClose, inventoryId }: FeePreviewModa
|
||||
|
||||
<Divider label="Invoice & Release" labelPosition="left" />
|
||||
|
||||
<Group justify="space-between" align="center">
|
||||
<Text size="sm" fw={600}>
|
||||
Billing currency
|
||||
</Text>
|
||||
<SegmentedControl
|
||||
size="xs"
|
||||
value={billingCurrency}
|
||||
onChange={(value) => setBillingCurrency(value as 'ETB' | 'USD')}
|
||||
data={[
|
||||
{ value: 'USD', label: 'USD' },
|
||||
{ value: 'ETB', label: 'Birr' },
|
||||
]}
|
||||
disabled={Boolean(activeInvoice)}
|
||||
/>
|
||||
</Group>
|
||||
|
||||
{activeInvoice ? (
|
||||
<Group justify="space-between">
|
||||
<Group gap="xs">
|
||||
@@ -191,7 +210,7 @@ export function FeePreviewModal({ opened, onClose, inventoryId }: FeePreviewModa
|
||||
color="orange"
|
||||
leftSection={<FileText size={16} />}
|
||||
loading={generate.isPending}
|
||||
onClick={() => handleGenerate(false)}
|
||||
onClick={handleGenerate}
|
||||
>
|
||||
Generate Fee Invoice
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user