Skip to content

Purchase Invoice Complete Walkthrough - Example-Based Documentation

Overview

This document demonstrates the complete lifecycle of a purchase invoice using a real payload example, showing exactly which tables and columns are affected during both Add (Draft) and Post phases.

Example Payload

json

{

"id": "73b9e8d2-fe21-4ef7-2e68-08ddfb5b40cf",

"invoiceDate": "2026-01-28T23:06:04",

"description": null,

"warehouseId": 53,

"warehouseName": "Default Warehouse",

"vendorId": 44,

"vendorName": "مؤسسة هدية الجودة للدعاية والاعلان",

"currencyRate": 1,

"paymentTermId": 9,

"reference": "",

"currencyId": 4,

"currencyName": "Saudi Riyal",

"currencyNameAr": "ريال سعودي",

"isExternalPurchase": false,

"invoiceDetails": [

    {

        "id": "2b55acb1-180e-48d6-9107-08ddfb5b40dd",

        "itemId": 169,

        "itemCode": "4137",

        "itemName": "نوت وسط",

        "itemVariantId": 245,

        "categoryId": 50,

        "categoryType": "Storable",

        "uomId": "56835e4d-4593-4481-9839-10a6321a42d3",

        "uomCode": "G8roW",

        "quantity": 600,

        "cost": 10,

        "netCost": 10,

        "subCost": 6000,

        "discountPercentage": 0,

        "discountAmount": 0,

        "vatPercentage": 15,

        "vatAmount": 900,

        "totalAfterDiscount": 6000,

        "grandTotal": 6900,

        "isServiceItem": false,

        "trackingType": "NoTracking",

        "hasExpiryDate": false,

        "stockInEntryMode": "Manual",

        "taxId": 18,

        "vatId": 18,

        "vatName": "الضريبة القياسية"

    }

  ],

  "sourceId": "73b9e8d2-fe21-4ef7-2e68-08ddfb5b40cf",

  "sourceType": 2,

  "sourceCode": "PO-2025-00004"

  }

  ```

  

---

  

## Phase 1: Add Purchase Invoice (Draft)

**Handler:** AddPurchaseInvoiceCommandHandler  

**Action:** Creates draft purchase invoice — no inventory or GL impact in this phase.

  

### Calculations (Before Saving)

**Line Item Calculations:**

- Quantity × Cost = Net Amount  

  600 × 10 = 6,000 SAR

  

- VAT Amount (if not included):

  6,000 × 15% = 900 SAR

  

- Grand Total:

  6,000 + 900 = 6,900 SAR

  

**Header Totals:**

- TotalNetAmount = 6,000 SAR  

- TotalDiscount = 0 SAR  

- TotalAfterDiscount = 6,000 SAR  

- GrandTotal = 6,900 SAR  

- LocalGrandTotal = 6,900 SAR (currencyRate = 1)

  

### Table 1: PurchaseInvoiceHeader (INSERT)

**Operation:** INSERT new record

  

| Column Name        | Value                          | Source                          | Notes                                      |

|--------------------|--------------------------------|---------------------------------|--------------------------------------------|

| Id                 | [Auto-Generated GUID]         | System                          | e.g., `73b9e8d2-fe21-4ef7-2e68-08ddfb5b40cf` |

| Code               | "PI-2026-0001"                | Sequence Service                | Generated by UpdateLastSequenceCommand    |

| InvoiceDate        | 2026-01-28 23:06:04           | Payload                         | Transaction date                          |

| InvoiceDueDate     | calculated from payment term   | PaymentTerm                     | Last installment date                     |

| VendorId           | 44                             | Payload                         | Vendor identifier                         |

| VendorName         | "مؤسسة هدية الجودة للدعاية والاعلان" | Payload                         | Display name                             |

| WarehouseId        | 53                             | Payload                         | Receiving warehouse                       |

| Status             | "Draft"                       | SetAsDraft()                   | Draft status                             |

| CurrencyId         | 4                              | Payload                         | Saudi Riyal                              |

| CurrencyRate       | 1.0                            | Payload                         | Exchange rate to base currency           |

| TotalNetAmount     | 6000.00                       | CalculateHeaderTotals()        | Sum of line net amounts                   |

| GrandTotal         | 6900.00                       | CalculateHeaderTotals()        | Total + VAT                               |

| IsExternalPurchase | false                          | Payload                         |                                            |

| CreatedDate        | [Current Timestamp]           | Audit                           |                                            |

| CreatedBy          | [Current User ID]             | Security                        |                                            |

| TenantId           | [Tenant GUID]                 | Multi-tenancy                  |                                            |

  

### Table 2: PurchaseInvoiceDetail (INSERT)

**Operation:** INSERT new record (one per line item)

  

| Column Name        | Value                          | Source                          | Notes                                      |

|--------------------|--------------------------------|---------------------------------|--------------------------------------------|

| Id                 | [Auto-Generated GUID]         | System                          | e.g., `2b55acb1-180e-48d6-9107-08ddfb5b40dd` |

| PurchaseInvoiceHeaderId | [Header GUID]               | FK                              | Parent invoice                           |

| ItemId             | 169                            | Payload                         | Inventory item                           |

| ItemCode           | "4137"                        | Payload                         |                                            |

| ItemName           | "نوت وسط"                     | Payload                         |                                            |

| ItemVariantId      | 245                            | Payload                         |                                            |

| UomId              | "56835e4d-4593-4481-9839-10a6321a42d3" | Payload                         |                                            |

| Quantity            | 600.0                          | Payload                         |                                            |

| Cost               | 10.00                         | Payload                         | Unit cost                                 |

| SubCost            | 6000.00                       | Computed                        |                                            |

| DiscountPercentage  | 0                              | Payload                         |                                            |

| VatPercentage       | 15.0                          | Payload                         |                                            |

| VatAmount          | 900.00                        | Computed                        |                                            |

| TotalAfterDiscount | 6000.00                        | Computed                        |                                            |

| GrandTotal         | 6900.00                       | Computed                        |                                            |

| TrackingType       | "NoTracking"                  | Payload                         |                                            |

| HasExpiryDate      | false                          | Payload                         |                                            |

| StockInEntryMode   | "Manual"                      | Payload                         |                                            |

| CreatedDate        | [Current Timestamp]           | Audit                           |                                            |

| CreatedBy          | [Current User ID]             | Security                        |                                            |

  

### Table 3: PurchaseInvoiceTracking (INSERT)

**Operation:** INSERT when detail.InvoiceTracking is provided (NoTracking record created if present)

  

| Column Name        | Value                          | Source                          | Notes                                      |

|--------------------|--------------------------------|---------------------------------|--------------------------------------------|

| Id                 | [Auto-Generated GUID]         | System                          |                                            |

| PurchaseInvoiceDetailId | [Detail GUID]               | FK                              |                                            |

| TrackingNo         | NULL                           | NoTracking                     |                                            |

| Quantity            | 600.0                          | Detail.Quantity                 |                                            |

| ExpireDate         | NULL                           | No expiry                       |                                            |

| TrackingType       | "NoTracking"                  | Detail.TrackingType             |                                            |

| CreatedDate        | [Current Timestamp]           | Audit                           |                                            |

  

### Table 4: PurchaseInvoiceBalance (INSERT)

**Operation:** INSERT new record(s) per payment term installment

  

| Column Name        | Value                          | Source                          | Notes                                      |

|--------------------|--------------------------------|---------------------------------|--------------------------------------------|

| Id                 | [Auto-Generated GUID]         | System                          |                                            |

| PurchaseInvoiceHeaderId | [Header GUID]               | FK                              |                                            |

| DueDate             | [From PaymentTermId 9]        | Payment Term                    |                                            |

| DueAmount          | 6,900.00                       | Payment Term % of GrandTotal   |                                            |

| DueBalance         | 6,900.00                       | Initially = DueAmount          |                                            |

| CreatedDate        | [Current Timestamp]           | Audit                           |                                            |

  

### Summary of Add Phase (Draft)

**Tables Affected:**

- ✅ PurchaseInvoiceHeader — 1 INSERT  

- ✅ PurchaseInvoiceDetail — 1 INSERT  

- ✅ PurchaseInvoiceTracking — 1 INSERT (if tracking object present)  

- ✅ PurchaseInvoiceBalance — 1 INSERT (installment record)  

  

**Inventory Impact:** NONE — No stock movement in draft phase  

**GL Impact:** NONE — No journal entries in draft phase  

  

**Validations Performed:**

- ✅ Invoice date in open financial period  

- ✅ Vendor exists and has payable account configured (validation step)  

- ✅ Price/cost and tax metadata read from Tax table  

- ✅ Payment term applied

  

## Phase 2: Post Purchase Invoice

**Handler:** PostPurchaseInvoiceCommandHandler  

**Action:** Finalizes invoice, creates stock-in and accounting journal entries.

  

### Prerequisites Check (before posting)

1. ✅ Invoice Status = Draft  

2. ✅ Period allows posted journals  

3. ✅ Vendor has payable account configured  

4. ✅ Item 169 has inventory asset account configured (for storable items)  

5. ✅ Tax 18 has GL account configured  

6. ✅ Warehouse 53 exists and accepts stock-in

  

### Table 1: PurchaseInvoiceHeader (UPDATE)

**Operation:** UPDATE existing record

  

| Column Name        | Before (Draft)               | After (Posted)                 | Change Description                        |

|--------------------|------------------------------|-------------------------------|------------------------------------------|

| Status             | "Draft"                      | "Posted"                      | Status changed to Posted                  |

| StockInId          | NULL                         | [StockIn GUID]               | Reference to stock-in transaction       |

| StockInCode        | NULL                         | "SI-2026-0001"               | Stock-in sequential code (example)      |

| InvoiceJournalId   | NULL                         | [Journal GUID]                | Purchase journal entry ID                |

| InvoiceJournalCode | NULL                         | "JE-2026-0200"               | Journal entry code                      |

| PostedDate         | NULL                         | 2026-01-28 23:10:00           | Timestamp of posting                    |

| UpdatedDate/By     | [Original]                  | [Current]                     | Audit fields                             |

  

### Table 2: StockInHeader (INSERT - Inventory Module)

**Operation:** INSERT new record (created by Inventory service)

  

| Column Name        | Value                          | Source                          | Notes                                      |

|--------------------|--------------------------------|---------------------------------|--------------------------------------------|

| Id                 | [Auto-Generated GUID]         | System                          |                                            |

| Code               | "SI-2026-0001"                | Sequence Service                |                                            |

| TransactionDate    | 2026-01-28 23:06:04          | Invoice.InvoiceDate            |                                            |

| WarehouseId        | 53                             | Invoice.WarehouseId            |                                            |

| SourceDocumentType | "PurchaseInvoice"             | Fixed                          |                                            |

| SourceDocumentId   | [Invoice Header GUID]         | Invoice.Id                    |                                            |

| TotalCost          | 6000.00                      | Sum of detail costs            |                                            |

| JournalId          | [Journal GUID]                | Inventory journal created by Inventory service |                                            |

| CreatedDate        | 2026-01-28 23:10:00           | Audit                           |                                            |

  

### Table 3: StockInDetail (INSERT - Inventory Module)

**Operation:** INSERT new record (per inventory line)

  

| Column Name        | Value                          | Source                          | Notes                                      |

|--------------------|--------------------------------|---------------------------------|--------------------------------------------|

| Id                 | [Auto-Generated GUID]         | System                          |                                            |

| StockInHeaderId    | [Header GUID]                 | FK                              |                                            |

| ItemId             | 169                            | Detail.ItemId                  |                                            |

| ItemCode           | "4137"                        | Detail.ItemCode                |                                            |

| ItemVariantId      | 245                            | Detail.ItemVariantId            |                                            |

| UomId              | "56835e4d-4593-4481-9839-10a6321a42d3" | Detail.UomId                  |                                            |

| Quantity            | 600.0                          | Detail.Quantity                 |                                            |

| UnitCost           | 10.00                         | Detail.Cost                    |                                            |

| LineCost           | 6000.00                       | Quantity × UnitCost            |                                            |

| TrackingType       | "NoTracking"                  | Detail.TrackingType             |                                            |

| SourceDetailId     | [Invoice Detail GUID]         | Link to invoice line           |                                            |

| CreatedDate        | 2026-01-28 23:10:00           | Audit                           |                                            |

  

### Table 4: StockInTracking (INSERT)

**Operation:** INSERT new record (created by Inventory service)

  

| Column Name        | Value                          | Source                          | Notes                                      |

|--------------------|--------------------------------|---------------------------------|--------------------------------------------|

| Id                 | [Auto-Generated GUID]         | System                          |                                            |

| StockInDetailId    | [StockIn Detail GUID]         | FK                              |                                            |

| TrackingNo         | NULL                           | NoTracking                     |                                            |

| Quantity            | 600.0                          | StockInDetail.Quantity          |                                            |

| UnitCost           | 10.00                         | Unit cost                      |                                            |

| CreatedDate        | 2026-01-28 23:10:00           | Audit                           |                                            |

  

### Table 5: ItemStockDetail (INSERT / UPDATE - Inventory Module)

**Operation:** UPDATE or INSERT depending on existing stock record

  

**Before Post (example):**

- AvailableQuantity = 0 (no prior on-hand)  

  

**After Post:**

- AvailableQuantity = 600.0 — increased by 600  

- TotalCost = 6000.00  

- UnitCost = 10.00 (WAC or last cost, depending on policy)

  

**Update Logic (example):**

- If existing ItemStockDetail exists for ItemVariantId 245 & WarehouseId 53, update AvailableQuantity += 600 and adjust TotalCost / UnitCost per costing method; otherwise insert a new ItemStockDetail record.

  

### Table 6: JournalEntry (INSERT - Purchase Journal)

**Operation:** INSERT new record (created by Accounting service)

  

| Column Name        | Value                          | Source                          | Notes                                      |

|--------------------|--------------------------------|---------------------------------|--------------------------------------------|

| Id                 | [Auto-Generated GUID]         | System                          |                                            |

| Code               | "JE-2026-0200"               | Sequence Service                |                                            |

| JournalDate        | 2026-01-28 23:06:04          | Invoice.InvoiceDate            |                                            |

| SourceDocument     | "PurchaseInvoice"             | Fixed                          |                                            |

| SourceDocumentId   | [Invoice Header GUID]         | Invoice.Id                     |                                            |

| TotalDebit         | 6900.00                       | Sum debits                     |                                            |

| TotalCredit        | 6900.00                       | Balanced totals                 |                                            |

| FinancialYearPeriodId | [Period ID]                 | Invoice.FinancialYearPeriodId  |                                            |

| CreatedDate        | 2026-01-28 23:10:00           | Audit                           |                                            |

  

### Table 7: JournalEntryLine (INSERT Multiple - Purchase Journal)

**Operation:** INSERT 3 records (example)

  

**Line 1: Inventory Asset (Debit)**

- AccountId: 1030 (Inventory) — Debit 6000.00

  

**Line 2: VAT Input (Debit)**

- AccountId: 2040 (VAT Input/Recoverable) — Debit 900.00

  

**Line 3: Accounts Payable (Credit)**

- AccountId: 2010 (Vendor Payable) — Credit 6900.00

  

**Accounting Summary (JE-2026-0200)**

- DR Inventory 6,000.00  

- DR VAT Input 900.00  

- CR Vendor Payable 6,900.00

  

### Table 8: VendorFinancial (UPDATE)

**Operation:** UPDATE existing vendor financials

  

**Before Post:**

- OutstandingPayable = X

  

**After Post:**

- OutstandingPayable += 6,900.00

  

**Update SQL (example):**

```sql

UPDATE VendorFinancial

SET OutstandingPayable = OutstandingPayable + 6900.00,

    TotalPurchases = TotalPurchases + 6000.00

WHERE VendorId = 44

Summary of Post Phase

Tables Affected:

  • ✅ PurchaseInvoiceHeader — 1 UPDATE (status, journal/stock references)

  • ✅ StockInHeader — 1 INSERT (inventory transaction created by Inventory service)

  • ✅ StockInDetail — 1 INSERT (line detail)

  • ✅ StockInTracking — 1 INSERT (NoTracking record to store quantity)

  • ✅ ItemStockDetail — 1 INSERT/UPDATE (increased AvailableQuantity)

  • ✅ JournalEntry — 1 INSERT (Purchase journal in Accounting service)

  • ✅ JournalEntryLine — 3 INSERTS (Inventory, VAT, Payable)

  • ✅ VendorFinancial — 1 UPDATE (outstanding payable increased)

Inventory Impact:

  • Item: 4137 (نوت وسط)

  • Warehouse: Default Warehouse (53)

  • Before: Available Qty = (example) 0

  • After: Available Qty = 600

GL Impact (Combined Journals):

  • DR Inventory (Asset) +6,000.00

  • DR VAT Input (Asset/Recoverable) +900.00

  • CR Vendor Payable (Liability) +6,900.00

Complete Transaction Flow Summary

Step-by-Step Execution

  1. User submits Add Purchase Invoice request

  2. AddPurchaseInvoiceCommandHandler.Handle()

  3. Validate invoice date → Get FinancialYearPeriodId

  4. Validate vendor accounting setup, tax metadata, UOM & item existence

  5. Generate sequence code → "PI-2026-0001"

  6. Calculate line totals and header totals

  7. Create payment term balances (DueDate(s), DueAmount(s))

  8. INSERT PurchaseInvoiceHeader (Status = Draft)

INSERT PurchaseInvoiceDetail (line(s))

INSERT PurchaseInvoiceTracking (if present)

INSERT PurchaseInvoiceBalance (installment(s))

  1. SaveChanges → Database transaction committed (Draft returned)

  2. User triggers Post request

  3. PostPurchaseInvoiceCommandHandler.Handle()

  4. Validate preconditions (status, period, vendor accounts, tax GL)

  5. Build purchase journal payload and call Accounting service → returns JournalId/Code

- Accounting creates JournalEntry and JournalEntryLines

  1. Call Inventory service AddPostedStockIn() → returns StockInId/Code and Inventory Journal references

- Inventory creates StockInHeader, StockInDetail, StockInTracking, updates ItemStockDetail

  1. Update PurchaseInvoiceHeader with StockIn and Journal references, set Status = Posted, set PostedDate

  2. Update VendorFinancial (Outstanding payable)

  3. SaveChanges → All references persisted

  4. Publish webhook/event (if enabled)

  5. Return InvoiceId → Frontend receives posted invoice

Database Transaction Isolation

Add Phase (Draft)

  • Single DB transaction: INSERT header, details, balances → COMMIT

Post Phase

  • Main transaction in Purchase.Invoice service:

- Call Accounting.AddJournal (external)

- Call Inventory.AddPostedStockIn (external)

- Update PurchaseInvoiceHeader, VendorFinancial

- COMMIT

  • External services (Accounting, Inventory) run their own transactions and return identifiers which are persisted by the Purchase service.

Error Scenarios

Add Phase Validation Failures

  • Invalid Period → "Invoice date must be in an open financial period"

  • Missing Vendor → "Vendor not found"

  • Price/Cost missing → "Item cost is required for storable items"

  • Missing Tax metadata → "Tax configuration for taxId 18 is missing"

Post Phase Validation Failures

  • Invoice Not Draft → "Invoice must be in draft status to post"

  • Missing Vendor Payable Account → "Vendor payable account not configured"

  • Missing Item Inventory Account → "Inventory GL account not configured for item 169"

  • Missing Tax GL Account → "Tax account required for tax 18"

  • Period Closed → "Cannot post journals in closed period"


End of Walkthrough

This example demonstrates a typical purchase invoice with:

  • ✅ Single storable item (NoTracking)

  • ✅ Direct purchase (possibly from PO)

  • ✅ No discount on line

  • ✅ VAT not included (calculated separately)

  • ✅ Single payment installment (payment term id 9)

  • ✅ Inventory stock-in on post

  • ✅ Purchase journal created (Inventory + VAT Debit, Vendor Payable Credit)

Document Version: 1.0

Example Date: 2026-01-28

Currency: Saudi Riyal (SAR)

Total Amount: 6,900.00 SAR (including 15% VAT)

Internal Documentation — Microtec