import InvoicePdfButton from '@/components/invoice-pdf-button'; import InvoiceForm, { BillingItemTypeOption, InvoiceFormValues, InvoicePaymentFormValues, createPayment, createPaymentItem, } from '@/forms/account/InvoiceForm'; import AppLayout from '@/layouts/app-layout'; import { ClientInvoice } from '@/types'; import { Link, router, useForm } from '@inertiajs/react'; import { Badge, Button, Container, Group, Title } from '@mantine/core'; import { IconArrowLeft } from '@tabler/icons-react'; import React from 'react'; type InvoiceOptionSource = Pick & { linked_invoice_id?: number | null; }; type InvoicePaymentItemSource = { billing_item_types_id: number; start_date: string | null; end_date: string | null; payment_item_amount: string | number; tax_percentage: string | number; net_amount: string | number; withholding_tax: string | number; final_net_amount: string | number; spending: string | number; is_creditcard: boolean; }; type InvoicePaymentSource = { payment_no: string | null; payment_total_amount: string | number; payment_nett_amount: string | number; items: InvoicePaymentItemSource[]; }; interface Props { invoice: ClientInvoice & { pending_sql_acc_code?: string | null; pending_client_name?: string | null; client?: { id: number; name: string; customer_id: string; }; payments?: InvoicePaymentSource[]; total_sem_amount?: number | string | null; total_net_amount?: number | string | null; }; existingClient?: { id: number; name: string; customer_id: string } | null; unlinkedClients: { value: string; label: string }[]; availableInvoices: InvoiceOptionSource[]; billingItemTypes: BillingItemTypeOption[]; } const valueString = (value: string | number | null | undefined) => value === null || value === undefined ? '' : String(value); const parseAmount = (value?: string | number | null) => { const parsed = Number.parseFloat(String(value ?? '')); return Number.isFinite(parsed) ? parsed : 0; }; const finalNetAmount = (netAmount: number, withholdingTax: number) => netAmount / (1 + withholdingTax / 100); const paymentItemsFromInvoice = ( invoice: Props['invoice'], billingItemTypes: BillingItemTypeOption[], ): InvoicePaymentFormValues[] => { if (invoice.payments?.length) { return invoice.payments.map((payment) => ({ payment_no: payment.payment_no ?? '', payment_total_amount: valueString(payment.payment_total_amount), payment_nett_amount: valueString(payment.payment_nett_amount), items: payment.items.map((item) => ({ billing_item_types_id: String(item.billing_item_types_id), start_date: item.start_date ?? '', end_date: item.end_date ?? '', payment_item_amount: valueString(item.payment_item_amount), tax_percentage: valueString(item.tax_percentage), net_amount: valueString(item.net_amount), withholding_tax: valueString(item.withholding_tax), final_net_amount: valueString(item.final_net_amount), spending: valueString(item.spending), is_creditcard: !!item.is_creditcard, })), })); } const mediaSearch = billingItemTypes.find( (itemType) => itemType.name === 'Google Ads Search (Media Fee)', ); const managementSearch = billingItemTypes.find( (itemType) => itemType.name === 'Management Fee (Google Search Ads)', ); const fallbackPayment = createPayment(billingItemTypes, { payment_no: invoice.payment_no ?? '', payment_total_amount: valueString( (Number(invoice.media_fee ?? 0) || 0) + (Number(invoice.management_fee ?? 0) || 0), ), payment_nett_amount: valueString( invoice.total_net_amount ?? invoice.nett_amount ?? 0, ), }); return [ { ...fallbackPayment, items: [ createPaymentItem( String(mediaSearch?.id ?? billingItemTypes[0]?.id ?? ''), { start_date: invoice.start_date ?? '', end_date: invoice.end_date ?? '', payment_item_amount: valueString( invoice.media_fee ?? 0, ), tax_percentage: valueString(invoice.tax_percent ?? 0), net_amount: valueString( invoice.media_fee_amount ?? invoice.nett_amount ?? 0, ), withholding_tax: '0', final_net_amount: valueString( invoice.nett_amount ?? invoice.media_fee_amount ?? 0, ), spending: valueString(invoice.total_spending ?? 0), is_creditcard: !!invoice.is_credit_card, }, ), createPaymentItem( String( managementSearch?.id ?? billingItemTypes[0]?.id ?? '', ), { payment_item_amount: valueString( invoice.management_fee ?? 0, ), tax_percentage: valueString(invoice.tax_percent ?? 0), net_amount: valueString( invoice.management_fee_amount ?? invoice.management_fee ?? 0, ), withholding_tax: '0', final_net_amount: valueString( invoice.management_fee_amount ?? invoice.management_fee ?? 0, ), spending: '0', }, ), ], }, ]; }; export default function Page({ invoice, existingClient, unlinkedClients, availableInvoices, billingItemTypes, }: Props) { const resolvedClient = invoice.client ?? existingClient ?? null; const form = useForm({ invoice_no: invoice.invoice_no ?? '', linked_invoice_id: invoice.linked_invoice_id ? String(invoice.linked_invoice_id) : '', is_paid: !!invoice.is_paid, client_id: resolvedClient ? String(resolvedClient.id) : '', customer_id: resolvedClient?.customer_id ?? '', total_sem_amount: valueString(invoice.total_sem_amount ?? '0.00'), total_net_amount: valueString( invoice.total_net_amount ?? invoice.nett_amount ?? '0.00', ), payments: paymentItemsFromInvoice(invoice, billingItemTypes), }); const isApproved = !!invoice.approved_at; const handleSubmit = (event: React.FormEvent) => { 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.put(route('client-invoices.update', { invoice: invoice.id })); }; const handleApprove = () => { router.patch(route('client-invoices.approve', { invoice: invoice.id })); }; const invoiceOptions = availableInvoices.map((item) => ({ value: String(item.id), label: item.invoice_no, })); const requiresClient = !invoice.client_id; return ( Edit invoice {isApproved ? 'Approved' : 'Pending approval'} {/* {!isApproved && ( )} */} ); }