feat: accounting module — schema, migration, Zod schemas, AR balance service
Phase 1 foundation: - Migration 0047: all accounting tables (invoice, payment_application, account_balance, account_code, journal_entry, billing_run), chart of accounts seed, nextBillingDate on enrollment, accounting module config - Drizzle schema file with all table definitions and type exports - Zod validation schemas (invoice, payment, billing, reports) - AccountBalanceService: materialized AR balance per account Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
83
packages/shared/src/schemas/accounting.schema.ts
Normal file
83
packages/shared/src/schemas/accounting.schema.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { z } from 'zod'
|
||||
|
||||
// Enums
|
||||
export const AccountType = z.enum(['asset', 'liability', 'revenue', 'contra_revenue', 'cogs', 'expense'])
|
||||
export type AccountType = z.infer<typeof AccountType>
|
||||
|
||||
export const InvoiceStatus = z.enum(['draft', 'sent', 'paid', 'partial', 'overdue', 'void', 'written_off'])
|
||||
export type InvoiceStatus = z.infer<typeof InvoiceStatus>
|
||||
|
||||
export const JournalLineType = z.enum(['debit', 'credit'])
|
||||
export type JournalLineType = z.infer<typeof JournalLineType>
|
||||
|
||||
export const BillingRunStatus = z.enum(['pending', 'running', 'completed', 'failed'])
|
||||
export type BillingRunStatus = z.infer<typeof BillingRunStatus>
|
||||
|
||||
// Invoice
|
||||
export const InvoiceLineItemInput = z.object({
|
||||
description: z.string().min(1).max(255),
|
||||
qty: z.coerce.number().int().min(1).default(1),
|
||||
unitPrice: z.coerce.number().min(0),
|
||||
discountAmount: z.coerce.number().min(0).default(0),
|
||||
taxRate: z.coerce.number().min(0).default(0),
|
||||
accountCodeId: z.string().uuid().optional(),
|
||||
})
|
||||
export type InvoiceLineItemInput = z.infer<typeof InvoiceLineItemInput>
|
||||
|
||||
export const InvoiceCreateSchema = z.object({
|
||||
accountId: z.string().uuid(),
|
||||
locationId: z.string().uuid().optional(),
|
||||
issueDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
||||
dueDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
||||
notes: z.string().optional(),
|
||||
lineItems: z.array(InvoiceLineItemInput).min(1),
|
||||
})
|
||||
export type InvoiceCreateInput = z.infer<typeof InvoiceCreateSchema>
|
||||
|
||||
export const PaymentApplicationSchema = z.object({
|
||||
transactionId: z.string().uuid().optional(),
|
||||
amount: z.coerce.number().min(0.01),
|
||||
notes: z.string().optional(),
|
||||
})
|
||||
export type PaymentApplicationInput = z.infer<typeof PaymentApplicationSchema>
|
||||
|
||||
export const InvoiceVoidSchema = z.object({
|
||||
reason: z.string().min(1),
|
||||
})
|
||||
export type InvoiceVoidInput = z.infer<typeof InvoiceVoidSchema>
|
||||
|
||||
export const InvoiceWriteOffSchema = z.object({
|
||||
reason: z.string().min(1),
|
||||
})
|
||||
export type InvoiceWriteOffInput = z.infer<typeof InvoiceWriteOffSchema>
|
||||
|
||||
// Journal Entry
|
||||
export const JournalEntryVoidSchema = z.object({
|
||||
reason: z.string().min(1),
|
||||
})
|
||||
export type JournalEntryVoidInput = z.infer<typeof JournalEntryVoidSchema>
|
||||
|
||||
// Billing
|
||||
export const BillingRunSchema = z.object({
|
||||
runDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
||||
})
|
||||
export type BillingRunInput = z.infer<typeof BillingRunSchema>
|
||||
|
||||
export const BillEnrollmentSchema = z.object({
|
||||
periodStart: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
||||
periodEnd: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
||||
})
|
||||
export type BillEnrollmentInput = z.infer<typeof BillEnrollmentSchema>
|
||||
|
||||
// Report filters
|
||||
export const DateRangeFilterSchema = z.object({
|
||||
dateFrom: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
||||
dateTo: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
|
||||
})
|
||||
export type DateRangeFilter = z.infer<typeof DateRangeFilterSchema>
|
||||
|
||||
export const StatementFilterSchema = z.object({
|
||||
from: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
||||
to: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
|
||||
})
|
||||
export type StatementFilter = z.infer<typeof StatementFilterSchema>
|
||||
@@ -200,3 +200,31 @@ export type {
|
||||
|
||||
export { LogLevel, AppConfigUpdateSchema } from './config.schema.js'
|
||||
export type { AppConfigUpdateInput } from './config.schema.js'
|
||||
|
||||
export {
|
||||
AccountType,
|
||||
InvoiceStatus,
|
||||
JournalLineType,
|
||||
BillingRunStatus,
|
||||
InvoiceLineItemInput,
|
||||
InvoiceCreateSchema,
|
||||
PaymentApplicationSchema,
|
||||
InvoiceVoidSchema,
|
||||
InvoiceWriteOffSchema,
|
||||
JournalEntryVoidSchema,
|
||||
BillingRunSchema,
|
||||
BillEnrollmentSchema,
|
||||
DateRangeFilterSchema,
|
||||
StatementFilterSchema,
|
||||
} from './accounting.schema.js'
|
||||
export type {
|
||||
InvoiceCreateInput,
|
||||
PaymentApplicationInput,
|
||||
InvoiceVoidInput,
|
||||
InvoiceWriteOffInput,
|
||||
JournalEntryVoidInput,
|
||||
BillingRunInput,
|
||||
BillEnrollmentInput,
|
||||
DateRangeFilter,
|
||||
StatementFilter,
|
||||
} from './accounting.schema.js'
|
||||
|
||||
Reference in New Issue
Block a user