Order Tax & Invoicing

The Orders module provides Indian GST-compliant tax calculation and sequential invoice generation with support for regular invoices, proforma invoices, and credit notes.

GST Calculation

The TaxService handles Indian GST rules:

  • Intra-state (buyer and seller in same state): Tax splits into CGST + SGST (half rate each)
  • Inter-state (different states): Full rate charged as IGST

Usage

use Modules\Orders\App\Services\TaxService;

$taxService = app(TaxService::class);

// Intra-state: Rs 1000 @ 18% GST
$result = $taxService->calculateTax(1000, null, true, 18);
// Returns:
// [
//   'rate' => 18.0,
//   'type' => 'gst',
//   'cgst_rate' => 9.0,
//   'sgst_rate' => 9.0,
//   'cgst' => 90.0,
//   'sgst' => 90.0,
//   'igst' => 0,
//   'total_tax' => 180.0,
// ]

// Inter-state: Rs 1000 @ 18% GST
$result = $taxService->calculateTax(1000, null, false, 18);
// Returns:
// [
//   'rate' => 18.0,
//   'type' => 'gst',
//   'cgst_rate' => 0, 'sgst_rate' => 0,
//   'cgst' => 0, 'sgst' => 0,
//   'igst_rate' => 18.0,
//   'igst' => 180.0,
//   'total_tax' => 180.0,
// ]

Method Signature

calculateTax(
    float $amount,          // Taxable amount
    ?string $hsnCode,       // HSN code for rate lookup (optional)
    bool $sameState = true, // true = CGST+SGST, false = IGST
    ?float $rateOverride    // Override rate (skips HSN lookup)
): array

Tax Rate Lookup

Tax rates are resolved in order:

  1. If $rateOverride is provided, use it directly
  2. If $hsnCode is provided, look up rate from TaxConfiguration table
  3. If no match found, use the default GST configuration
  4. If no default exists, fall back to 18%

Supported GST Rates

Indian GST has standard rates: 0%, 5%, 12%, 18%, 28%. Configure via the TaxConfiguration model.

Tax Configuration Model

Field Type Description
id int Primary key
name string Configuration name (e.g., "Standard GST 18%")
rate decimal(5,2) Tax rate percentage
type string gst, vat, sales_tax
is_default boolean Default configuration for this type
hsn_codes jsonb Array of HSN codes this rate applies to

Example Configuration

TaxConfiguration::create([
    'name' => 'GST 5% - Essential Items',
    'rate' => 5.00,
    'type' => 'gst',
    'is_default' => false,
    'hsn_codes' => ['0401', '0402', '1001', '1006'],
]);

TaxConfiguration::create([
    'name' => 'GST 18% - Standard',
    'rate' => 18.00,
    'type' => 'gst',
    'is_default' => true,
    'hsn_codes' => [],
]);

TaxConfiguration::create([
    'name' => 'GST 28% - Luxury Items',
    'rate' => 28.00,
    'type' => 'gst',
    'is_default' => false,
    'hsn_codes' => ['8703', '8711'],
]);

Invoice Generation

Invoice Model

Field Type Description
id UUID Primary key
order_id UUID Parent order
invoice_number string Sequential number (e.g., INV-202603-0001)
type string invoice, credit_note, proforma
status string draft, issued, paid, cancelled
subtotal decimal Pre-tax amount
tax_details jsonb CGST/SGST/IGST breakdown
total_tax decimal Total tax amount
discount decimal Discount amount
total decimal Grand total
billing_name string Customer name
billing_gstin string Customer GSTIN
billing_address jsonb Billing address
shipping_address jsonb Shipping address
line_items jsonb Invoice line items
pdf_path string Generated PDF file path
issued_at datetime Issue date
due_at datetime Payment due date

Creating an Invoice

POST /api/v1/orders/{order}/invoices

{
  "type": "invoice"
}

The InvoiceService automatically:

  1. Loads order items, customer, and addresses
  2. Maps order items to invoice line items
  3. Calculates tax using TaxService
  4. Generates a sequential invoice number
  5. Sets due date to 30 days from issue

Response:

{
  "data": {
    "id": "uuid",
    "invoice_number": "INV-202603-0001",
    "type": "invoice",
    "status": "issued",
    "subtotal": 5000.00,
    "tax_details": {
      "rate": 18.0,
      "type": "gst",
      "cgst_rate": 9.0,
      "sgst_rate": 9.0,
      "cgst": 450.0,
      "sgst": 450.0,
      "igst": 0,
      "total_tax": 900.0
    },
    "total_tax": 900.00,
    "discount": 0.00,
    "total": 5900.00,
    "billing_name": "John Doe",
    "line_items": [
      {
        "name": "Widget A",
        "sku": "WA-001",
        "quantity": 2,
        "unit_price": 2500.00,
        "tax": 450.00,
        "discount": 0,
        "total": 5000.00
      }
    ],
    "issued_at": "2026-03-08T10:00:00Z",
    "due_at": "2026-04-07T10:00:00Z"
  }
}

Invoice Number Format

Type Prefix Format Example
Invoice INV INV-YYYYMM-NNNN INV-202603-0001
Credit Note CN CN-YYYYMM-NNNN CN-202603-0001
Proforma PI PI-YYYYMM-NNNN PI-202603-0001

Numbers are sequential within each prefix and month. The sequence resets each month.

Credit Notes

Issue a credit note for partial or full refunds:

use Modules\Orders\App\Services\InvoiceService;

$invoiceService = app(InvoiceService::class);

$creditNote = $invoiceService->generateCreditNote(
    $order,
    1500.00,        // Amount
    'Partial refund - defective item'
);

The credit note is created with:

  • Type credit_note
  • CN- prefixed number
  • A single line item describing the reason
  • Status issued

Listing Invoices

GET /api/v1/orders/{order}/invoices

Returns all invoices and credit notes for an order.

GET /api/v1/invoices/{invoice}

Returns a single invoice with full details.

Integration with Returns

When a return is processed via ReturnService::processRefundForReturn(), you can generate a credit note alongside the refund:

$invoiceService->generateCreditNote(
    $return->order,
    $return->refund_amount,
    "Return #{$return->return_number}: {$return->reason_category}"
);