@devx-retailos/invoice
v0.0.2
Published
GST-compliant PDF invoice generation for retailOS POS backends.
Keywords
Readme
@devx-retailos/invoice
GST-compliant PDF invoice generation for Medusa v2 POS backends: assembles invoice data from a POS order, computes CGST/SGST/IGST breakdowns, and renders an A4 PDF (or HTML) via a pluggable renderer.
Part of retailOS, a Medusa v2 SDK for offline-store POS systems. Each @devx-retailos/* package is an independently installable Medusa plugin; a brand backend composes the ones it needs in medusa-config.ts.
Installation
npm install @devx-retailos/invoicePeer dependencies: @medusajs/framework, @medusajs/medusa, and @medusajs/admin-shared ^2.15.0. puppeteer (^22.0.0) is an optional peer — the default PDF renderer requires it; skip it if you register a custom renderer or only serve HTML.
Requires @devx-retailos/order in the same backend (invoices are assembled from its orders). @devx-retailos/store-details is optional but recommended — it supplies the seller block (address, GSTIN, state code); without it those fields render empty.
Setup
// medusa-config.ts
module.exports = defineConfig({
plugins: [
{ resolve: "@devx-retailos/order", options: {} },
{ resolve: "@devx-retailos/store-details", options: {} },
{ resolve: "@devx-retailos/invoice", options: {} },
],
})The module registers under the key retailos_invoice (exported as INVOICE_MODULE).
Usage
import { INVOICE_MODULE } from "@devx-retailos/invoice"
const invoices = container.resolve(INVOICE_MODULE)
// Render a PDF for an order
const pdf: Buffer = await invoices.renderInvoice("order_01...", {
organization_id: "org_...",
store_id: "store_...",
customer_state_code: "27", // optional — drives intra/interstate GST split
employee_name: "Cashier name", // optional — printed on the invoice
})
// Or get the structured data without rendering
const data = await invoices.assembleInvoiceData("order_01...", { organization_id: "org_..." })GST notes
- Per-line GST rate and HSN code are read from the order line item's
metadata.gst_rateandmetadata.hsn_code. computeGstBreakdown(taxableAmount, gstRate, isIntrastate)splits tax into CGST + SGST (intrastate) or IGST (interstate). The sale is treated as intrastate when the customer state code is unknown or equals the store's state code.formatInvoiceNumber(orderId, createdAt, override?)prefersorder.metadata.invoice_number, falling back toINV-{YYYYMMDD}-{last 8 of order id}.amountToWords(1250.5)→"One Thousand Two Hundred Fifty Rupees and Fifty Paise Only"(Indian numbering: Lakh/Crore).
These helpers, plus renderInvoiceHtml(data), are exported for direct use.
Extension points
The default PuppeteerInvoiceRenderer launches headless Chromium and prints the HTML template to A4. To use a different PDF engine (or a hosted rendering service), implement InvoiceRenderer and register it at bootstrap:
import type { InvoiceRenderer, InvoiceData } from "@devx-retailos/invoice"
const myRenderer: InvoiceRenderer = {
async render(data: InvoiceData): Promise<Buffer> {
// produce a PDF buffer however you like
return buffer
},
}
invoices.setRenderer(myRenderer)Permissions
Exported as INVOICE_PERMISSIONS from @devx-retailos/invoice/permissions:
| Key | Description |
| --- | --- |
| order.invoice | Generate and download GST-compliant PDF invoices for orders |
API routes
| Method | Path | Purpose | Permission |
| --- | --- | --- | --- |
| GET | /admin/retailos/orders/:id/invoice | Download the invoice. Query: organization_id (required), store_id, customer_state_code, employee_name, format=pdf\|html (default pdf). | order.invoice |
Returns 404 if the order doesn't exist and 503 if no PDF renderer is available (puppeteer missing and no custom renderer registered).
Errors
All extend RetailOSError from @devx-retailos/core (also importable from @devx-retailos/invoice/errors); switch on err.code:
RETAILOS_INVOICE_NOT_FOUND— no order found for the given id.RETAILOS_INVOICE_RENDERER_NOT_AVAILABLE— puppeteer not installed and no custom renderer registered.RETAILOS_INVOICE_RENDER_FAILED— the renderer threw; original error oncause.
Related packages
@devx-retailos/core— shared types,RetailOSError,Logger, permission registry.@devx-retailos/order— the POS orders invoices are generated from.@devx-retailos/store-details— store address/GST metadata printed on the invoice.@devx-retailos/rbac— roles and permission checks enforced by the invoice route.
License
MIT
