inspiren-sem-tool/resources/js/pages/client-invoices/create.tsx

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>
);
}