Skip to content

m-invoice: Core Invoice Module

Reusable invoice DocTypes for any Framework M application.

Depends on: m-core


This module provides:

  • Purchase Invoice (uses Supplier from m-core)
  • Sales Invoice (uses Customer from m-core)
  • Invoice Item (uses Item from m-core)
  • Tax handling (generic, not country-specific)
  • Payment linkage

class PurchaseInvoice(BaseDocType):
"""Invoice from Supplier for goods/services received."""
invoice_number: str = Field(max_length=50)
supplier: str # FK to Supplier
posting_date: date
due_date: date | None = None
# Items
items: list["InvoiceItem"] = Field(default_factory=list)
# Totals
total_qty: Decimal = Decimal("0")
total_amount: Decimal = Decimal("0")
tax_amount: Decimal = Decimal("0")
grand_total: Decimal = Decimal("0")
# Payment
paid_amount: Decimal = Decimal("0")
outstanding_amount: Decimal = Decimal("0")
# Status
status: str = "Draft" # Draft, Submitted, Paid, Cancelled
class Meta:
table_name = "purchase_invoice"
class SalesInvoice(BaseDocType):
"""Invoice to Customer for goods/services sold."""
invoice_number: str = Field(max_length=50)
customer: str # FK to Customer (generic)
posting_date: date
due_date: date | None = None
# Items
items: list["InvoiceItem"] = Field(default_factory=list)
# Totals
total_qty: Decimal = Decimal("0")
total_amount: Decimal = Decimal("0")
tax_amount: Decimal = Decimal("0")
grand_total: Decimal = Decimal("0")
# Payment
paid_amount: Decimal = Decimal("0")
outstanding_amount: Decimal = Decimal("0")
# Status
status: str = "Draft"
class Meta:
table_name = "sales_invoice"
class InvoiceItem(BaseDocType):
"""Line item in an invoice."""
parent: str # FK to PurchaseInvoice or SalesInvoice
parent_type: str # "PurchaseInvoice" or "SalesInvoice"
item: str # FK to Item
item_name: str
qty: Decimal
rate: Decimal
amount: Decimal # qty * rate
# Tax (generic)
tax_rate: Decimal = Decimal("0")
tax_amount: Decimal = Decimal("0")
# Warehouse/Location
warehouse: str | None = None
class Meta:
table_name = "invoice_item"
child_of = ["PurchaseInvoice", "SalesInvoice"]

m-invoice provides generic tax fields. Country-specific tax logic is in separate modules:

Region Module What It Adds
India m-india CGST, SGST, IGST, HSN, E-Invoice
UAE m-uae (future) VAT
EU m-eu (future) VAT

Invoices post to Book Keeper ledger on submit:

async def on_submit(invoice: PurchaseInvoice):
# Debit: Stock/Expense account
# Credit: Supplier payable account
await book_keeper.transfer(
debit_account=invoice.expense_account,
credit_account=invoice.supplier_account,
amount=invoice.grand_total,
ledger="gl",
reference_type="PurchaseInvoice",
reference_id=invoice.id,
)

Event When
invoice.created Invoice saved as draft
invoice.submitted Invoice submitted (GL posted)
invoice.paid Full payment received
invoice.cancelled Invoice cancelled

  • Create PurchaseInvoice DocType
  • Create SalesInvoice DocType
  • Create InvoiceItem child DocType
  • Book Keeper GL posting on submit
  • Payment linkage
  • Event publishing

Task Duration
DocTypes 0.5 day
Book Keeper integration 0.5 day
Tests 0.5 day
Total 1.5 days