npm package: @logicdine/medusa-plugin-invoice
Description: Invoice generator plugin for Medusa v2 with multi-template support (PDF + HTML), POS receipts, and admin management
Author: LogicDine
Latest version: 0.1.0

# @logicdine/medusa-plugin-invoice

Invoice generator plugin for **Medusa v2** with multi-template support, PDF & HTML engines, POS receipt sizes, and full admin management.

## Features

- **Multiple Invoice Templates** - Classic Invoice, Kitchen Order, Packing Slip, POS Receipt, or create your own
- **Two Template Engines** - Native PDF (pdfmake) with section toggles, or HTML (Handlebars) for full customization
- **POS Receipt Support** - 80mm and 58mm thermal receipt paper sizes with dynamic height estimation
- **Template Preview** - Preview templates with sample data (75 items) before saving
- **Style Editor** - Customize font sizes, colors, bold/italic per style category from the admin UI
- **Section Visibility** - Toggle company header, addresses, item prices, totals, notes per template
- **Title Control** - Show header on first page only or repeat on every page
- **Auto Invoice on Order** - Generates invoice automatically when an order is placed
- **Email Notification** - Sends invoice PDF as email attachment on order placement
- **Stale Invoice Tracking** - Marks invoices as stale when orders are updated (edits, returns, exchanges)
- **Admin Widget** - Invoice list with template selector on order detail page
- **Store API** - Customers can download their invoices

## Prerequisites

- Medusa v2 application (v2.13+)
- PostgreSQL database
- Node.js >= 20

## Installation

```bash
npm install @logicdine/medusa-plugin-invoice
# or
pnpm add @logicdine/medusa-plugin-invoice
```

## Configuration

Add the plugin to your `medusa-config.ts`:

```ts
import { defineConfig } from '@medusajs/framework/utils'

export default defineConfig({
  // ...
  plugins: [
    {
      resolve: "@logicdine/medusa-plugin-invoice",
    },
  ],
})
```

Run migrations:

```bash
npx medusa db:migrate
```

This creates the required tables and seeds 5 built-in templates.

## Built-in Templates

| Template | Type | Page Size | Description |
|----------|------|-----------|-------------|
| Classic Invoice | PDF | A4 | Full invoice with all sections |
| Kitchen Order | PDF | A5 | Items + quantities only, no prices |
| Packing Slip | PDF | A4 | Items + shipping address, no prices |
| POS Receipt | PDF | 80mm | Compact thermal receipt layout |
| HTML Invoice (Example) | HTML | A4 | Handlebars HTML template example |

## Admin Pages

### Invoice Settings (`/app/settings/invoice-config`)

Configure company information used across all templates:
- Company name, address, phone, email
- Company logo URL
- Notes/footer text
- Default template selection

### Invoice Templates (`/app/settings/invoice-templates`)

Manage templates:
- View all templates with type, page size, and default indicator
- Create new templates (PDF or HTML type)
- Edit templates with live PDF preview panel
- Toggle section visibility (11 sections)
- Customize styles (font size, color, bold, italic) for PDF templates
- Delete custom templates (built-in templates are protected)

### Order Invoice Widget

On each order detail page (`order.details.side.before`):
- Template selector dropdown
- Generate button
- Invoice list with template name, status, date, and download link

## API Endpoints

### Admin

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/admin/invoice-config` | Get invoice configuration |
| POST | `/admin/invoice-config` | Update invoice configuration |
| GET | `/admin/orders/:id/invoices` | List invoices for an order |
| POST | `/admin/orders/:id/invoices` | Generate invoice (optional `template_id` in body) |
| GET | `/admin/invoices/:id/download` | Download invoice PDF |
| GET | `/admin/invoice-templates` | List all templates |
| POST | `/admin/invoice-templates` | Create a template |
| GET | `/admin/invoice-templates/:id` | Get a template |
| PUT | `/admin/invoice-templates/:id` | Update a template |
| DELETE | `/admin/invoice-templates/:id` | Delete a template (blocks built-ins) |
| POST | `/admin/invoice-templates/:id/preview` | Preview a saved template with sample data |
| POST | `/admin/invoice-templates/preview` | Preview unsaved template config |

### Store

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/store/orders/:id/invoices` | Download invoice PDF (customer) |

## Creating Custom Templates

### PDF Template

Create a template with `type: "pdf"` and configure sections:

```json
{
  "name": "My Custom Invoice",
  "document_title": "TAX INVOICE",
  "type": "pdf",
  "page_size": "A4",
  "sections": {
    "company_header": true,
    "company_details": true,
    "invoice_metadata": true,
    "billing_address": true,
    "shipping_address": true,
    "items_table": true,
    "item_prices": true,
    "totals": true,
    "notes": true,
    "thank_you": true,
    "title_every_page": false
  },
  "styles": {
    "companyName": { "fontSize": 20, "color": "#1a365d", "bold": true },
    "invoiceTitle": { "fontSize": 28, "color": "#2c3e50" }
  }
}
```

### HTML Template

Create a template with `type: "html"` and provide Handlebars HTML:

```html
<div style="font-family: Helvetica, sans-serif;">
  <h1>{{config.company_name}}</h1>
  <h2>{{documentTitle}}</h2>
  <p>Invoice: {{invoiceId}} | Date: {{invoiceDate}}</p>
  <p>Order: #{{order.display_id}}</p>

  <table>
    <tr>
      <th>Item</th><th>Qty</th><th>Price</th><th>Total</th>
    </tr>
    {{#each items}}
    <tr>
      <td>{{this.title}}</td>
      <td>{{this.quantity}}</td>
      <td>{{this.unit_price}} {{../currency}}</td>
      <td>{{multiply this.unit_price this.quantity}} {{../currency}}</td>
    </tr>
    {{/each}}
  </table>

  <p><strong>Total: {{order.total}} {{currency}}</strong></p>
</div>
```

**Available Handlebars variables:**
- `order` - Full order object (display_id, created_at, total, subtotal, tax_total, etc.)
- `config` - Invoice config (company_name, company_address, company_phone, etc.)
- `items` - Order line items array (title, quantity, unit_price, variant)
- `invoiceId` - Invoice display ID
- `invoiceDate` - Formatted invoice date
- `orderDate` - Formatted order date
- `documentTitle` - Template document title
- `billingAddress` - Billing address object
- `shippingAddress` - Shipping address object
- `currency` - Currency code (uppercase)

**Available Handlebars helpers:**
- `{{multiply a b}}` - Multiply two numbers
- `{{uppercase str}}` - Convert to uppercase
- `{{formatDate date}}` - Format a date string
- `{{padStart val len char}}` - Pad a value

## Page Sizes

| Value | Dimensions | Use Case |
|-------|-----------|----------|
| `A4` | 210 x 297mm | Standard invoices |
| `A5` | 148 x 210mm | Kitchen orders, smaller documents |
| `LETTER` | 8.5 x 11in | US letter size |
| `RECEIPT-80MM` | 80mm x auto | Standard thermal receipt (most POS) |
| `RECEIPT-58MM` | 58mm x auto | Narrow thermal receipt |

Receipt sizes use dynamic height estimation based on content — the PDF is always a single page.

## Section Visibility

| Section | Description |
|---------|-------------|
| `company_header` | Logo + company name + document title |
| `company_details` | Address, phone, email |
| `invoice_metadata` | Invoice ID, date, order ID, order date |
| `billing_address` | Bill-to address block |
| `shipping_address` | Ship-to address block |
| `items_table` | Order line items table |
| `item_prices` | Price and total columns in items table |
| `totals` | Subtotal, tax, shipping, total |
| `notes` | Footer notes from config |
| `thank_you` | "Thank you" message |
| `title_every_page` | Repeat header on every page (vs first page only) |

## Events & Subscribers

The plugin listens to these events:

| Event | Action |
|-------|--------|
| `order.placed` | Generate invoice + send email with PDF attachment |
| `order.updated` | Mark existing invoices as stale |
| `order-edit.confirmed` | Mark existing invoices as stale |
| `order.exchange_created` | Mark existing invoices as stale |
| `order.claim_created` | Mark existing invoices as stale |
| `order.return_received` | Mark existing invoices as stale |

## Workflows

Exported workflows you can use in custom code:

```ts
import {
  generateInvoicePdfWorkflow,
  markInvoiceStaleWorkflow,
  updateInvoiceConfigWorkflow,
  createInvoiceTemplateWorkflow,
  updateInvoiceTemplateWorkflow,
  deleteInvoiceTemplateWorkflow,
} from "@logicdine/medusa-plugin-invoice/workflows"
```

### generateInvoicePdfWorkflow

```ts
const { result: pdfBuffer } = await generateInvoicePdfWorkflow(container).run({
  input: {
    order_id: "order_123",
    template_id: "inv_tmpl_kitchen", // optional, uses default if omitted
  },
})
```

## Email Notifications

The plugin sends invoice PDFs as email attachments via Medusa's Notification Module. You need a configured notification provider (e.g. SendGrid, Resend) with an `order-placed` email template.

## License

MIT