feat(invoices): polymorphic billing entity with snapshot clientName
Wires the billingEntityType/billingEntityId columns (added in PR 1) through
the invoice validator and service. Clients can now be billed as either a
client or a company; clientName becomes a snapshot derived from the entity
at create time.
- createInvoiceSchema: replace clientName with billingEntity {type,id}
- listInvoicesSchema: add billingEntityType/billingEntityId filters
- createInvoice: resolveBillingEntity helper (tenant-scoped; tx-aware)
falls back to entity primary email/address when not supplied
- listInvoices: honor new billing-entity filters
- updateInvoice: unchanged — billing entity is fixed after create
- invoice wizard step 1: temporary billing-entity id input (Task 10.2
replaces this with a proper picker)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,10 @@ import { baseListQuerySchema } from '@/lib/api/route-helpers';
|
||||
|
||||
export const createInvoiceSchema = z
|
||||
.object({
|
||||
clientName: z.string().min(1).max(200),
|
||||
billingEntity: z.object({
|
||||
type: z.enum(['client', 'company']),
|
||||
id: z.string().min(1),
|
||||
}),
|
||||
billingEmail: z.string().email().optional(),
|
||||
billingAddress: z.string().max(500).optional(),
|
||||
dueDate: z.string().min(1),
|
||||
@@ -35,9 +38,7 @@ export const updateInvoiceSchema = z.object({
|
||||
billingEmail: z.string().email().optional(),
|
||||
billingAddress: z.string().max(500).optional(),
|
||||
dueDate: z.string().min(1).optional(),
|
||||
paymentTerms: z
|
||||
.enum(['immediate', 'net10', 'net15', 'net30', 'net45', 'net60'])
|
||||
.optional(),
|
||||
paymentTerms: z.enum(['immediate', 'net10', 'net15', 'net30', 'net45', 'net60']).optional(),
|
||||
currency: z.string().length(3).optional(),
|
||||
notes: z.string().max(2000).optional(),
|
||||
lineItems: z
|
||||
@@ -63,6 +64,8 @@ export const listInvoicesSchema = baseListQuerySchema.extend({
|
||||
clientName: z.string().optional(),
|
||||
dateFrom: z.string().optional(),
|
||||
dateTo: z.string().optional(),
|
||||
billingEntityType: z.enum(['client', 'company']).optional(),
|
||||
billingEntityId: z.string().optional(),
|
||||
});
|
||||
|
||||
export type CreateInvoiceInput = z.infer<typeof createInvoiceSchema>;
|
||||
|
||||
Reference in New Issue
Block a user