125 lines
4.2 KiB
TypeScript
125 lines
4.2 KiB
TypeScript
import InvoiceForm, {
|
|
BillingItemTypeOption,
|
|
InvoiceFormValues,
|
|
createPayment,
|
|
} from '@/forms/account/InvoiceForm';
|
|
import AppLayout from '@/layouts/app-layout';
|
|
import { ClientInvoice } from '@/types';
|
|
import { Link, useForm } from '@inertiajs/react';
|
|
import { Button, Container, Group, Title } from '@mantine/core';
|
|
import { IconArrowLeft } from '@tabler/icons-react';
|
|
import React from 'react';
|
|
|
|
type InvoiceOptionSource = Pick<ClientInvoice, 'id' | 'invoice_no'> & {
|
|
linked_invoice_id?: number | null;
|
|
};
|
|
|
|
interface Props {
|
|
clientId: number;
|
|
customerId: string;
|
|
availableInvoices: InvoiceOptionSource[];
|
|
billingItemTypes: BillingItemTypeOption[];
|
|
}
|
|
|
|
const parseAmount = (value?: string) => {
|
|
const parsed = Number.parseFloat(value ?? '');
|
|
|
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
};
|
|
|
|
const finalNetAmount = (netAmount: number, withholdingTax: number) =>
|
|
netAmount / (1 + withholdingTax / 100);
|
|
|
|
export default function Page({
|
|
clientId,
|
|
customerId,
|
|
availableInvoices,
|
|
billingItemTypes,
|
|
}: Props) {
|
|
const form = useForm<InvoiceFormValues>({
|
|
client_id: String(clientId),
|
|
customer_id: customerId,
|
|
invoice_no: '',
|
|
linked_invoice_id: '',
|
|
is_paid: false,
|
|
total_sem_amount: '0.00',
|
|
total_net_amount: '0.00',
|
|
payments: [createPayment(billingItemTypes)],
|
|
});
|
|
|
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
|
event.preventDefault();
|
|
|
|
form.transform((data) => ({
|
|
...data,
|
|
linked_invoice_id: data.linked_invoice_id || null,
|
|
is_paid: !!data.is_paid,
|
|
total_sem_amount: parseFloat(data.total_sem_amount) || 0,
|
|
total_net_amount: parseFloat(data.total_net_amount) || 0,
|
|
payments: data.payments.map((payment) => ({
|
|
...payment,
|
|
payment_no: payment.payment_no || null,
|
|
payment_total_amount:
|
|
parseFloat(payment.payment_total_amount) || 0,
|
|
payment_nett_amount:
|
|
parseFloat(payment.payment_nett_amount) || 0,
|
|
items: payment.items.map((item) => ({
|
|
...item,
|
|
start_date: item.start_date || null,
|
|
end_date: item.end_date || null,
|
|
billing_item_types_id: parseInt(
|
|
item.billing_item_types_id,
|
|
10,
|
|
),
|
|
payment_item_amount: parseAmount(
|
|
item.payment_item_amount,
|
|
),
|
|
tax_percentage: parseAmount(item.tax_percentage),
|
|
net_amount: parseAmount(item.net_amount),
|
|
withholding_tax: parseAmount(item.withholding_tax),
|
|
final_net_amount: finalNetAmount(
|
|
parseAmount(item.net_amount),
|
|
parseAmount(item.withholding_tax),
|
|
),
|
|
spending: parseFloat(item.spending) || 0,
|
|
is_creditcard: !!item.is_creditcard,
|
|
})),
|
|
})),
|
|
}));
|
|
|
|
form.post(route('client-invoices.store'));
|
|
};
|
|
|
|
const invoiceOptions = availableInvoices.map((invoice) => ({
|
|
value: String(invoice.id),
|
|
label: invoice.invoice_no,
|
|
}));
|
|
|
|
return (
|
|
<AppLayout>
|
|
<Container size="lg" px="xs">
|
|
<Group position="apart" mb="md">
|
|
<Title order={2}>Add invoice</Title>
|
|
<Button
|
|
component={Link}
|
|
href={route('google-ads.accounts.show', {
|
|
id: customerId,
|
|
})}
|
|
leftIcon={<IconArrowLeft size={16} />}
|
|
variant="outline"
|
|
>
|
|
Back to account
|
|
</Button>
|
|
</Group>
|
|
|
|
<InvoiceForm
|
|
form={form}
|
|
onSubmit={handleSubmit}
|
|
invoiceOptions={invoiceOptions}
|
|
billingItemTypes={billingItemTypes}
|
|
/>
|
|
</Container>
|
|
</AppLayout>
|
|
);
|
|
}
|