2026-04-23 14:38:41 +03:00

96 lines
3.4 KiB
Markdown

# Invoice Pattern Skill
This skill defines the standard for implementing invoice-like forms (Invoice, Estimate, Job Card, Purchase Order, Bill, etc.) in the carage-erp dashboard. All such forms must follow this pattern for layout, discount/tax handling, and summary calculation. The current Invoice form is the canonical reference.
---
## 1. Layout
- **Two-column grid:**
- **Main column (9/12):**
- Subject, invoice number/title
- Status select, Discount Type select
- Conditional Transaction Discount field
- Line item selectors: Parts, Services, Expense Items (with optional line-level discount)
- Notes, Terms & Conditions
- Submit button
- **Sidebar column (3/12):**
- Invoice date, Due date
- Customer, Vehicle selectors
- Tax select (see below)
- Department, Payment Terms, Invoice Sequence, Insurance fields
- Summary card (see below)
## 2. Discount Implementation
- **Discount Type:**
- Field: `discount` (enum: 'no', 'line_item_level', 'transaction_level')
- Select field in main column
- **Transaction-level Discount:**
- Field: `discount_amount` (number)
- Only shown when `discount === "transaction_level"`
- **Line-level Discount:**
- Each line item (parts, services, expenses) has `discount_amount` field
- Only shown when `discount === "line_item_level"`
- **Payload Mapping:**
- Only include `discount_amount` at transaction level if `discount === "transaction_level"`
- Only include per-line `discount_amount` if `discount === "line_item_level"`
## 3. Tax Type Implementation
- **Tax Field:**
- Field: `tax` (relationFieldSchema: `{ value: string, label: string } | null`)
- Uses `RhfAsyncSelectField` in sidebar
- `mapOption`: `{ value: String(item.id), label: `${item.title} (${item.rate}%)` }`
- The selected tax's rate is parsed from the label string in the summary (regex: `/\((\d+(?:\.\d+)?)%\)/`)
## 4. Summary Implementation
- **Summary Card:**
- Always rendered in the sidebar below the Details card
- Uses `InvoiceFormSummary` (form-aware adapter)
- `InvoiceFormSummary` flattens all line items, reads discount/tax fields, and passes them to `useDocumentTotals` hook
- `useDocumentTotals` (pure hook) computes subtotal, discounts, tax, and total
- `DocumentTotalsSummary` (pure display component) renders the summary
---
## Reference: Invoice Form
- See `apps/dashboard/modules/invoices/invoice-form.tsx` for the canonical implementation.
- Schema: `apps/dashboard/modules/invoices/invoice.schema.ts`
- Summary logic: `apps/dashboard/modules/invoices/invoice-form-summary.tsx`, `shared/hooks/use-document-totals.ts`, `shared/components/document-totals-summary.tsx`
---
## Required for All Invoice-like Forms
- Follow the above layout and field conventions
- Use the same discount/tax logic and summary calculation
- Use the same field and payload mapping patterns
- Use the same summary component structure
---
## Example: Tax Field (in sidebar)
```tsx
<RhfAsyncSelectField
name="tax"
label="Tax"
placeholder="Select tax rate"
queryKey={[TAX_ROUTES.INDEX]}
listFn={() => api.taxes.list()}
mapOption={(item: any) => ({
value: String(item.id),
label: item.title ? `${item.title} (${item.rate}%)` : `#${item.id}`,
})}
{...STORE_OBJECT}
/>
```
## Example: Discount Type Select (in main column)
```tsx
<RhfSelectField name="discount" label="Discount Type" options={DISCOUNT_OPTIONS} />
```
## Example: Summary Card
```tsx
<div className="mt-4">
<InvoiceFormSummary />
</div>
```