Skip to content

Sales Invoices (فواتير المبيعات)

The Sales Invoices API manages the full invoice lifecycle from draft creation through approval, posting (with automatic journal entries and COGS), payment tracking, and cancellation.

Entity Attributes

SalesInvoice (Header)

FieldTypeRequiredDescription
idintegerautoPrimary key
invoice_numberstringautoAuto-generated via SequenceService (e.g., INV-000001)
datedateyesInvoice date
due_datedatenoPayment due date
customer_idFKyesBusiness partner (must be customer)
customer_namestringnoOptional override for customer name
salesperson_idFKnoAssigned salesperson (user)
warehouse_idFKnoDefault warehouse for items
sales_order_idFKnoSource sales order (if created from order)
statusenumautodraft, pending_approval, approved, posted, partially_paid, paid, overdue, cancelled
payment_statusstringautopending, partial, paid
referencestringnoExternal reference number
subjectstringnoInvoice subject line
notes / notes_artextnoNotes in English / Arabic
terms / terms_artextnoTerms & conditions
currency_codestringnoCurrency code (default: KWD)
exchange_ratedecimal(12,6)noExchange rate (default: 1)
payment_terms_daysintegernoPayment terms in days
subtotaldecimal(15,3)autoSum of line totals
discount_amountdecimal(15,3)autoSum of line discounts
tax_amountdecimal(15,3)autoSum of line taxes
totaldecimal(15,3)autosubtotal + tax_amount
amount_paiddecimal(15,3)autoTotal payments received
balance_duedecimal(15,3)autototal - amount_paid
journal_entry_idFKautoRevenue journal entry (set on post)
cogs_journal_entry_idFKautoCOGS journal entry (set on post)
approved_byFKautoUser who approved
approved_atdatetimeautoApproval timestamp
posted_byFKautoUser who posted
posted_atdatetimeautoPosting timestamp
cancelled_byFKautoUser who cancelled
cancelled_atdatetimeautoCancellation timestamp
cancellation_reasontextnoReason for cancellation

SalesInvoiceItem (Line Item)

FieldTypeRequiredDescription
idintegerautoPrimary key
sales_order_item_idFKnoSource order item (if from order)
product_idFKyesProduct reference
product_variant_idFKnoOptional product variant
unit_idFKyesUnit of measure
warehouse_idFKnoSource warehouse for stock deduction
description / description_arstringnoCustom line description
quantitydecimal(15,3)yesInvoiced quantity
unit_pricedecimal(15,3)yesPrice per unit
discount_percentdecimal(8,3)noLine discount percentage
discount_amountdecimal(15,3)autoCalculated discount amount
tax_rate_idFKnoTax rate reference
tax_amountdecimal(15,3)autoCalculated tax amount
line_totaldecimal(15,3)auto(qty x price) - discount
unit_costdecimal(15,3)autoProduct cost at posting time (for COGS)
total_costdecimal(15,3)autoquantity x unit_cost
sort_orderintegernoDisplay order

ER Diagram

Invoice Lifecycle

Posting Flow (Critical)

When an invoice is posted (POST /api/sales/invoices/{id}/post), the following happens atomically:

Step 1: Revenue Journal Entry

AccountDebitCredit
Accounts Receivable (sales.receivable_account_id)total
Revenue (sales.revenue_account_id)subtotal + discount
Sales Discount (sales.discount_account_id)discount
Tax Payable (sales.tax_payable_account_id)tax_amount

Step 2: COGS Journal Entry (for tracked inventory products)

AccountDebitCredit
COGS (sales.cogs_account_id)total_cogs
Inventory (warehouse.account_id)total_cogs

Step 3: Stock Deduction (if sales.stock_deduction_point = 'invoice')

  • For each line with product.track_inventory = true
  • Calculates cost via StockService::getIssueCost() (WAC or FIFO)
  • Decreases stock via StockService::decreaseStock()

Step 4: Update Order

  • Increments invoiced_quantity on linked order items
  • Recalculates order invoice_status

API Endpoints

MethodEndpointDescriptionPermission
GET/api/sales/invoicesList invoices (paginated)sales.invoices.view
POST/api/sales/invoicesCreate a new invoicesales.invoices.create
GET/api/sales/invoices/{id}Get invoice detailssales.invoices.view
PUT/api/sales/invoices/{id}Update a draft invoicesales.invoices.update
DELETE/api/sales/invoices/{id}Delete a draft invoicesales.invoices.delete
POST/api/sales/invoices/{id}/submit-approvalSubmit invoice for approvalsales.invoices.approve
POST/api/sales/invoices/{id}/approveApprove an invoicesales.invoices.approve
POST/api/sales/invoices/{id}/rejectReject invoice (back to draft)sales.invoices.approve
POST/api/sales/invoices/{id}/postPost invoice (JE + COGS + stock)sales.invoices.post
POST/api/sales/invoices/{id}/cancelCancel an invoicesales.invoices.cancel
POST/api/sales/invoices/{id}/duplicateDuplicate an invoicesales.invoices.duplicate
POST/api/sales/orders/{id}/create-invoiceCreate invoice from ordersales.invoices.create

Query Parameters (List)

ParameterTypeDescription
statusstringFilter by invoice status
payment_statusstringFilter by payment status (pending/partial/paid)
customer_idintegerFilter by customer
salesperson_idintegerFilter by salesperson
sales_order_idintegerFilter by source order
date_fromdateInvoices from this date
date_todateInvoices up to this date
due_date_fromdateDue from this date
due_date_todateDue up to this date
overduebooleanOnly overdue invoices
searchstringSearch in invoice_number, reference, subject

Request / Response Examples

Create Invoice

bash
curl -X POST /api/sales/invoices \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "date": "2026-02-24",
    "due_date": "2026-03-26",
    "customer_id": 1,
    "subject": "Monthly Services",
    "payment_terms_days": 30,
    "items": [
      {
        "product_id": 1,
        "unit_id": 1,
        "quantity": 10,
        "unit_price": 25.000,
        "discount_percent": 5,
        "warehouse_id": 1
      }
    ]
  }'
dart
final response = await dio.post('/api/sales/invoices', data: {
  'date': '2026-02-24',
  'due_date': '2026-03-26',
  'customer_id': 1,
  'subject': 'Monthly Services',
  'payment_terms_days': 30,
  'items': [
    {
      'product_id': 1,
      'unit_id': 1,
      'quantity': 10,
      'unit_price': 25.000,
      'discount_percent': 5,
      'warehouse_id': 1,
    },
  ],
});

Post Invoice

bash
curl -X POST /api/sales/invoices/1/post \
  -H "Authorization: Bearer {token}"
dart
final response = await dio.post('/api/sales/invoices/1/post');

Create Invoice from Order

bash
curl -X POST /api/sales/orders/1/create-invoice \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"due_date": "2026-03-26"}'
dart
final response = await dio.post('/api/sales/orders/1/create-invoice', data: {
  'due_date': '2026-03-26',
});

Business Rules

  • Invoices are created in Draft status.
  • Only Draft invoices can be edited or deleted.
  • Approving an invoice requires at least one line item.
  • Only Approved invoices can be posted.
  • Posting creates a Revenue JE (DR Receivable, CR Revenue, DR Discount, CR Tax) and optionally a COGS JE (DR COGS, CR Inventory) for tracked products.
  • Stock is deducted at posting time if sales.stock_deduction_point = 'invoice'.
  • Cancelling a posted invoice reverses both journal entries and restores stock.
  • Cancellation is blocked if the invoice has recorded payments (amount_paid > 0).
  • Creating from an order copies only remaining uninvoiced quantities.
  • Posted invoices track amount_paid and balance_due for payment reconciliation.
  • Duplicating an invoice creates a new Draft with fresh number, resets all payment/JE fields.
  • Required accounting settings: sales.receivable_account_id and sales.revenue_account_id.

Moon ERP API Documentation