# rn-pricing-engine-panel URL: /docs/native/docyrus/pricing-engine-panel Invoice / quote builder with line items, product and category catalogs, line and global discounts, per-rate VAT summary, secondary currency and draft / save actions — same data model and totals as the web panel. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-pricing-engine-panel ``` ## Usage ```tsx import { PricingEnginePanel, type IPricingDocumentData } from '@/components/docyrus-native/pricing-engine-panel'; saveQuote(data)} onSaveDraft={data => saveDraft(data)} /> ``` The panel shares `interfaces.ts`, `types.ts`, `constants.ts`, `lib/calculations.ts` and `lib/line-item-helpers.ts` byte-for-byte with the web `@docyrus/ui` panel, so a document gives **identical totals to the cent** on both platforms (banker's rounding, `FINANCIAL_PRECISION = 2`). ### Mobile behaviour - **Lines** render as cards (position, name, category, `qty × unit price`, net or gross headline following `viewMode`, subtotal / discount / VAT chips). Tap a card to edit it in a bottom sheet (numeric keyboards, live subtotal → discount → net → VAT → gross breakdown); `Done` commits, `Cancel` discards. **Add Line** opens the same sheet for a new line. - **Row menu** — the `⋯` button (or a long press) opens Move Up / Move Down / Duplicate Line / Delete Line. - **Products** — with a `productCatalog` the product field opens a searchable list inside the sheet; picking fills name, category, unit price and VAT rate. Without a catalog the field is free text, plus a picker button that calls `onProductSelect` when provided. - **Categories** — with a `categoryCatalog` (flat `parentId` list or nested `children`) the category field opens a drill-down list: tap a row to pick it, tap its chevron to open its children; searching shows matches with their path. Without a catalog: free text + optional `onCategorySelect` button. - **Settings sheet** (header slider button, hidden when `readOnly`) — the same nine switches as the web config popover, a VAT-rates editor (add / remove rates, tap a rate to make it the default for new lines) and the secondary currency. - **Currency** — the header currency chip picks the primary currency; when a secondary currency is set, a currency bar shows the exchange-rate field and the converted grand total. - **Formatting** — amounts use the tenant formatter from `` (installed by ``) when it is mounted, otherwise the device-locale `Intl` currency formatter. - **`size="full"`** fills the available screen: the sections scroll and the actions bar stays pinned at the bottom (safe-area aware). - Copy comes from `useUiTranslation()` with the web `ui.pricing.*` keys and English fallbacks. ## API Reference ### PricingEnginePanelProps | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `Partial` | — | Controlled document state. Every change of the object is applied to the internal state | | `defaultValue` | `Partial` | — | Initial document state (uncontrolled) | | `showVatColumn` | `boolean` | `true` | Show the VAT chip on line cards and the VAT-rate field in the line editor | | `showDiscountColumn` | `boolean` | `true` | Show the line-discount chip and the discount field | | `showGrossColumn` | `boolean` | `true` | Show the gross amount under the net headline (net view) | | `showCategoryColumn` | `boolean` | `true` | Show the line category and the category field | | `discountBeforeVat` | `boolean` | `true` | `true`: discount the net subtotal, then add VAT. `false`: VAT on the subtotal, discount applied to the VAT-inclusive amount | | `enableVat` | `boolean` | `true` | Enable VAT calculation, VAT total and VAT summary | | `enableLineDiscount` | `boolean` | `true` | Enable per-line discount % | | `enableGlobalDiscount` | `boolean` | `true` | Enable the document-level discount % (applied after line discounts) | | `enableAdjustment` | `boolean` | `true` | Enable the signed adjustment amount added to the grand total | | `defaultVatRate` | `number` | `18` | VAT rate for new lines and for products without `vatRate` | | `vatRates` | `number[]` | `[0, 1, 8, 10, 18, 20]` | Selectable VAT rates | | `defaultCurrency` | `string` | `'USD'` | Primary ISO 4217 currency code | | `viewMode` | `'net' \| 'gross'` | `'net'` | Which amount the line cards headline (toggled from the header) | | `onLineItemsChange` | `(items: ILineItem[]) => void` | — | Called when line items change | | `onTotalsChange` | `(totals: ITotals) => void` | — | Called when the calculated totals change | | `onSave` | `(data: IPricingDocumentData) => void` | — | **Save** button. Sets `status` to `'saved'` | | `onSaveDraft` | `(data: IPricingDocumentData) => void` | — | **Save Draft** button. Sets `status` to `'draft'` | | `onProductSelect` | `(lineId: string, callback: (product: IProductCatalogItem) => void) => void` | — | Remote product picker, used when `productCatalog` is empty. Call `callback` with the picked product | | `onCategorySelect` | `(lineId: string, callback: (category: ICategoryCatalogItem) => void) => void` | — | Remote category picker, used when `categoryCatalog` is empty | | `productCatalog` | `IProductCatalogItem[]` | — | Static product list. Enables the searchable product picker | | `categoryCatalog` | `ICategoryCatalogItem[]` | — | Static categories (flat with `parentId`, or nested `children`). Enables the drill-down category picker | | `title` | `string` | — | Header title (a Draft / Saved status badge is shown next to it) | | `readOnly` | `boolean` | `false` | Disable editing: no editor sheet, row menu, settings, add or actions | | `showDescription` | `boolean` | `true` | Show the description editor | | `showTerms` | `boolean` | `true` | Show the terms & conditions editor (tabs when both are shown) | | `showActions` | `boolean` | `true` | Show the Save Draft / Save bar | | `variant` | `'default' \| 'bordered' \| 'compact'` | `'default'` | Visual style. `compact` tightens section padding and type | | `size` | `'sm' \| 'default' \| 'lg' \| 'full'` | `'default'` | `sm` ≤ 480px, `default` ≤ 720px (centered), `lg` full width, `full` fills the screen with a pinned actions bar | | `ref` | `Ref` | — | Ref to the root `View` | | `className` | `string` | — | Additional root classes | | `style` | `StyleProp` | — | Root style | ### IPricingDocumentData | Field | Type | Description | |-------|------|-------------| | `lineItems` | `ILineItem[]` | Line items | | `globalDiscountPercent` | `number` | Document discount % (clamped 0–100) | | `adjustment` | `number` | Signed adjustment amount | | `currency` | `ICurrencyConfig` | Currency configuration | | `config` | `IPricingConfig` | Feature flags / display config | | `description` | `string` | Description text | | `termsAndConditions` | `string` | Terms text | | `status` | `TPricingDocumentStatus` | `'draft'` or `'saved'` | | `totals` | `ITotals` | Calculated totals (output) | ### ILineItem | Field | Type | Description | |-------|------|-------------| | `id` | `string` | Unique id | | `position` | `number` | Zero-based order | | `productId` | `string \| null` | Catalog product id | | `categoryId` | `string \| null` | Catalog category id | | `name` | `string` | Product / service name | | `category` | `string` | Category name | | `quantity` | `number` | Quantity | | `unitPrice` | `number` | Price per unit | | `vatRate` | `number` | VAT rate % | | `discountPercent` | `number` | Line discount % | `ILineItemCalculated` adds the derived `lineSubtotal`, `discountAmount`, `netAfterDiscount`, `vatAmount`, `grossTotal`; `ILineItemRow` is `ILineItem & ILineItemCalculated`. ### ITotals | Field | Type | Description | |-------|------|-------------| | `subtotal` | `number` | Σ quantity × unit price | | `lineDiscountTotal` | `number` | Σ line discounts | | `globalDiscountAmount` | `number` | Global discount amount | | `totalDiscount` | `number` | Line + global discount | | `netTotal` | `number` | Subtotal − total discount | | `vatTotal` | `number` | VAT total (scaled by the global discount) | | `adjustment` | `number` | Effective adjustment (0 when disabled) | | `grandTotal` | `number` | Net + VAT + adjustment | ### ICurrencyConfig | Field | Type | Description | |-------|------|-------------| | `code` | `string` | Primary currency code | | `secondaryCurrencyCode` | `string \| null` | Secondary currency; `null` hides the currency bar | | `exchangeRate` | `number` | Primary → secondary rate (must be > 0) | ### IPricingConfig `showVatColumn`, `showDiscountColumn`, `showGrossColumn`, `showCategoryColumn`, `discountBeforeVat`, `enableVat`, `enableLineDiscount`, `enableGlobalDiscount`, `enableAdjustment` (`boolean`), `defaultVatRate` (`number`), `vatRates` (`number[]`), `viewMode` (`TPricingViewMode`) — the same keys as the panel props above. ### IProductCatalogItem | Field | Type | Description | |-------|------|-------------| | `id` | `string` | Product id | | `name` | `string` | Name | | `category` | `string` | Category name | | `categoryId` | `string` | Optional category id | | `unitPrice` | `number` | Unit price | | `vatRate` | `number` | Optional VAT rate (falls back to `defaultVatRate`) | ### ICategoryCatalogItem | Field | Type | Description | |-------|------|-------------| | `id` | `string` | Category id | | `name` | `string` | Name | | `parentId` | `string \| null` | Parent id (flat lists) | | `children` | `ICategoryCatalogItem[]` | Optional nested children | ### IVatSummaryRow | Field | Type | Description | |-------|------|-------------| | `rate` | `number` | VAT rate % (0 % rows are omitted) | | `taxableAmount` | `number` | Taxable base after all discounts | | `vatAmount` | `number` | VAT for the rate | ## Components | Component | Description | |-----------|-------------| | `PricingEnginePanel` | The full panel | | `PricingEngineProvider` | State provider (same props as web) for building a custom layout | | `usePricingEngine` | Reads the provider: state, `lineItemRows`, `totals`, `vatSummary` and all mutators (`addLineItem`, `updateLineItem`, `reorderLineItems`, `setConfig`, `save`, `saveDraft`, …) | | `pricingEnginePanelVariants` | `tv()` root variants (`variant`, `size`) | `DEFAULT_PRICING_CONFIG`, `DEFAULT_CURRENCY_CONFIG` and `FINANCIAL_PRECISION` are exported too. ## Type Exports | Type | Description | |------|-------------| | `PricingEnginePanelProps` | Props for `PricingEnginePanel` | | `PricingEngineProviderProps` / `IPricingEngineContext` | Provider props / context value | | `TPricingViewMode` | `'net' \| 'gross'` | | `TDiscountMode` | `'before-vat' \| 'after-vat'` | | `TPricingDocumentStatus` | `'draft' \| 'saved'` | | `ILineItem`, `ILineItemCalculated`, `ILineItemRow` | Line item shapes | | `ITotals`, `IVatSummaryRow` | Totals / VAT summary | | `ICurrencyConfig`, `IPricingConfig`, `IPricingDocumentData` | Document configuration + snapshot | | `IProductCatalogItem`, `ICategoryCatalogItem` | Catalog items | | `PricingLineItem`, `PricingTotals` | **Deprecated** aliases of `ILineItem` / `ITotals` |