Skip to content

Journal Entry Complete Walkthrough — Example-Based Documentation

Overview

This document demonstrates the complete lifecycle of a journal entry using real payload examples, showing exactly which tables and columns are affected during Add (Draft), Post, Reversal, and other operations.

Journal entries in the ERP system are the foundation of the General Ledger. They can be manual (created directly by users) or internal (system-generated from transactions like Sales Invoices, Payments, Stock movements).


Example Payload — Manual Journal Entry

json
{
  "refrenceNumber": "JV-001",
  "journalDate": "2026-01-28T16:14:36",
  "type": "Manual",
  "description": "Monthly rent expense recognition",
  "periodId": "FY-2026-Jan",
  "isHeaderDescriptionCopied": true,
  "journalEntryLines": [
    {
      "accountId": 5020,
      "lineDescription": "Monthly rent expense recognition",
      "debitAmount": 5000.00,
      "creditAmount": 0.00,
      "currencyId": 4,
      "currencyRate": 1.0,
      "isVatLine": false,
      "hasVat": false,
      "costCenters": [
        { "costCenterId": 87, "percentage": 100 }
      ]
    },
    {
      "accountId": 1010,
      "lineDescription": "Monthly rent expense recognition",
      "debitAmount": 0.00,
      "creditAmount": 5000.00,
      "currencyId": 4,
      "currencyRate": 1.0,
      "isVatLine": false,
      "hasVat": false,
      "costCenters": []
    }
  ],
  "journalEntryAttachments": [
    { "attachmentId": "abc123", "name": "rent-invoice.pdf" }
  ]
}

Pre-Save Calculations

Line-Level:
  DebitAmountLocal  = DebitAmount  × CurrencyRate = 5000 × 1.0 = 5000
  CreditAmountLocal = CreditAmount × CurrencyRate = 5000 × 1.0 = 5000

Header Totals:
  TotalDebitAmount       = Sum of all line DebitAmounts       = 5000
  TotalCreditAmount      = Sum of all line CreditAmounts      = 5000
  TotalDebitAmountLocal  = Sum of all line DebitAmountLocal   = 5000
  TotalCreditAmountLocal = Sum of all line CreditAmountLocal  = 5000

Balance Check:
  IsBalanced = TotalDebitAmountLocal == TotalCreditAmountLocal → true

Phase 1: Add Journal Entry (Draft)

Handler: AddJournalEntryCommandHandler

Action: Creates a draft journal entry with no GL impact until posted.

Validations Performed

CheckRuleResult
Reference number lengthMax 15 characters✅ "JV-001" = 6 chars
Journal date requiredNot null/empty✅ Provided
Description requiredNot null/empty✅ Provided
Open financial periodPeriod resolved from JournalDate via GetCurrentPeriodQuery must be open✅ Jan 2026 period open
Line debit/credit exclusiveEach line: debit XOR credit (not both non-zero)✅ Mutually exclusive
Currency rate requiredEach line must have currencyRate > 0✅ 1.0 provided
Cost center sumIf account requires cost centers, percentages must sum to 100%✅ Line 1: 100%
Line description (optional)Configurable in GeneralSettings✅ Copied from header

Table 1: JournalEntry (INSERT)

Operation: INSERT new record

Column NameValueSourceNotes
Id[Auto-Generated GUID]Systeme.g., 3fa85f64-5717-4562-b3fc-2c963f66afa6
Code"JE-2026-0001"Sequence ServiceGenerated by UpdateLastSequenceCommand
RefrenceNumber"JV-001"PayloadUser-supplied reference (note: intentional typo in column name)
JournalDate2026-01-28 16:14:36PayloadTransaction date
Type"Manual"PayloadJournalEntryType enum
Description"Monthly rent expense recognition"PayloadJournal description
Status"Draft"SetAsDraft()TransactionStatus enum
FinancialYearPeriodId[Integer DB ID, e.g. 25]GetCurrentPeriodQuery(JournalDate)Resolved from the journal date — command.PeriodId (string) is not used by the handler
TotalDebitAmount5000.00CalculatedSum of line debits (foreign currency)
TotalCreditAmount5000.00CalculatedSum of line credits (foreign currency)
TotalDebitAmountLocal5000.00CalculatedSum of line debits × rate (base currency)
TotalCreditAmountLocal5000.00CalculatedSum of line credits × rate (base currency)
SourceDocumentNULLNot system-generatedSet only for internal journals
SourceDocumentIdNULLNot system-generatedReference to source transaction
SourceDocumentCodeNULLNot system-generatedSource transaction code
IsReversedfalseDefaultNot yet reversed
ReversedJournalIdNULLDefaultNo reversal yet
ReversedFromJournalIdNULLDefaultNot a reversal
IsHeaderDescriptionCopiedtruePayloadDescription propagated to all lines
WorkflowStatusNULLDefaultSet if workflow enabled
CreatedDate[Current Timestamp]AuditAuto-set
CreatedBy[Current User ID]ISecurityServiceAuthenticated user
TenantId[Tenant GUID]ISecurityServiceMulti-tenancy
IsDeletedfalseDefaultSoft delete flag

Table 2: JournalEntryLine (INSERT × 2)

Operation: INSERT one record per line

Line 1 — Rent Expense (Debit)

Column NameValueSourceNotes
Id[Auto-Generated GUID]SystemLine 1 ID
JournalEntryId[Journal GUID above]FKParent journal
AccountId5020PayloadRent expense GL account
AccountCode"5020"Account masterAccount code
AccountName"Rent Expense"Account masterAccount name
AccountNameAr"مصروف الإيجار"Account masterArabic account name
LineDescription"Monthly rent expense recognition"Payload / Header copyCopied from header
DebitAmount5000.00PayloadIn foreign currency
CreditAmount0.00PayloadNo credit on this line
DebitAmountLocal5000.00DebitAmount × CurrencyRateBase currency
CreditAmountLocal0.00CreditAmount × CurrencyRateBase currency
CurrencyId4PayloadSaudi Riyal
CurrencyRate1.0PayloadExchange rate to base currency
IsVatLinefalsePayloadNot a VAT line
HasVatfalsePayloadNo VAT on this line
CreatedDate[Current Timestamp]AuditAuto-set
CreatedBy[Current User ID]ISecurityServiceAuthenticated user
TenantId[Tenant GUID]ISecurityServiceMulti-tenancy

Line 2 — Accounts Payable (Credit)

Column NameValueSourceNotes
Id[Auto-Generated GUID]SystemLine 2 ID
JournalEntryId[Journal GUID above]FKParent journal
AccountId1010PayloadPayable/bank GL account
LineDescription"Monthly rent expense recognition"Payload / Header copyCopied from header
DebitAmount0.00PayloadNo debit on this line
CreditAmount5000.00PayloadIn foreign currency
DebitAmountLocal0.00CalculatedBase currency
CreditAmountLocal5000.00CalculatedBase currency
CurrencyId4PayloadSaudi Riyal
CurrencyRate1.0PayloadExchange rate
IsVatLinefalsePayloadNot a VAT line
HasVatfalsePayloadNo VAT
CreatedDate[Current Timestamp]AuditAuto-set

Table 3: JournalLineCostCenter (INSERT)

Operation: INSERT — one record per cost center allocation per line

Line 1 has one cost center allocation at 100%:

Column NameValueSourceNotes
Id[Auto-Generated GUID]SystemAllocation ID
JournalEntryLineId[Line 1 GUID]FKParent line
CostCenterId87PayloadCost center ID
Percentage100.0Payload100% allocation
Amount5000.00CalculatedLine.DebitAmountLocal × Percentage/100
CreatedDate[Current Timestamp]AuditAuto-set

Line 2 has no cost center (empty array) → no inserts.

Table 4: JournalEntryAttachment (INSERT)

Operation: INSERT — one record per attachment

Column NameValueSourceNotes
Id[Auto-Generated GUID]SystemAttachment link ID
JournalEntryId[Journal GUID]FKParent journal
AttachmentId"abc123"PayloadID in Attachment microservice
Name"rent-invoice.pdf"PayloadDisplay name
CreatedDate[Current Timestamp]AuditAuto-set

Summary of Add Phase (Draft)

Tables Affected:

  • ✅ JournalEntry — 1 INSERT
  • ✅ JournalEntryLine — 2 INSERTs
  • ✅ JournalLineCostCenter — 1 INSERT (Line 1 only)
  • ✅ JournalEntryAttachment — 1 INSERT
  • ❌ General Ledger balances — NO update (draft only)

GL Impact: NONE — no financial effect until posted.


Phase 2: Post Journal Entry

Handler: ChangeStatusJournalEntriesCommandHandler (batch) or ChangeStatusJournalEntryCommandHandler (single)

Action: Marks the entry as Posted. This finalizes the journal entry and makes it part of the official General Ledger.

Prerequisites Check

Before posting, system validates:

CheckRule
Must be Manual typeSourceDocument is NULL
Must be in Draft statusStatus == Draft
Must be balancedTotalDebitAmountLocal == TotalCreditAmountLocal

Table 1: JournalEntry (UPDATE)

Operation: UPDATE existing record

Column NameBefore (Draft)After (Posted)Notes
Status"Draft""Posted"TransactionStatus enum
UpdatedDateNULL[Current Timestamp]Last update timestamp
UpdatedByNULL[Current User ID]Updated by user

All other columns remain unchanged.

GL Impact After Posting

Journal JE-2026-0001 — Rent Expense

  DR  5020  Rent Expense              5,000.00
      CR  1010  Accounts Payable               5,000.00
                                     ---------  ---------
      Total                          5,000.00   5,000.00

Balance Sheet Impact:
  Liabilities:
    Accounts Payable  +5,000.00

Income Statement Impact:
  Expenses:
    Rent Expense      +5,000.00

Phase 3: Reverse Journal Entry

Handler: ReverseJournalEntryCommandHandler (single) or ReverseJournalEntriesCommandHandler (batch)

Action: Creates a new journal entry with all debits and credits swapped. Used to correct or cancel a posted entry.

Prerequisites Check

CheckRule
Must be PostedStatus == Posted
Must be Manual typeType == Manual
Not already reversedIsReversed == false

New Reversed Entry Created

The system calls JournalEntry.CreateReversedEntry() which generates a mirror image:

Table 1: JournalEntry (INSERT — Reversed Entry)

Column NameValueNotes
Id[New GUID]New entry
Code"JE-2026-0002"New sequence code
RefrenceNumber"REV-JE-2026-0001"Prefixed with "REV-"
JournalDate[Current Date]Date of reversal, not original
Type"Reversed"JournalEntryType.Reversed
Description"Reversal of JE-2026-0001: Monthly rent expense recognition"System-generated
Status"Posted"Immediately posted
TotalDebitAmount5000.00Swapped from original credits
TotalCreditAmount5000.00Swapped from original debits
ReversedFromJournalId[Original Journal GUID]Points back to original

Table 2: JournalEntryLine (INSERT × 2 — Swapped)

Original LineDirectionReversed LineDirection
Account 5020DR 5000Account 5020CR 5000
Account 1010CR 5000Account 1010DR 5000
Reversed Journal JE-2026-0002

  DR  1010  Accounts Payable          5,000.00
      CR  5020  Rent Expense                   5,000.00

Table 3: JournalEntry (UPDATE — Original Entry)

Column NameBeforeAfterNotes
IsReversedfalsetrueMarked as reversed
ReversedJournalIdNULL[New Reversed GUID]Points to reversal

Table 4: ReversedJournalEntry (INSERT)

Operation: INSERT — tracking record for the reversal relationship

Column NameValueNotes
JournalEntryId[Original GUID]Original entry
ReversedJournalEntryId[Reversed GUID]New reversal entry

Phase 4: Edit Journal Entry

Handler: EditJournalEntryCommandHandler

Action: Updates an existing Draft journal entry. Resets status to Draft if it was changed elsewhere.

Prerequisites Check

CheckRule
Must be Manual typeType == Manual
Must not be PostedStatus != Posted
Open financial periodNew date must fall in open period

Edit Payload Example

json
{
  "id": "[Journal GUID]",
  "refrenceNumber": "JV-001-R",
  "description": "Monthly rent expense - corrected amount",
  "journalEntryLines": [
    {
      "id": "[Existing Line 1 GUID]",
      "accountId": 5020,
      "debitAmount": 6000.00,
      "creditAmount": 0.00,
      "currencyRate": 1.0,
      "costCenters": [{ "costCenterId": 87, "percentage": 100 }]
    },
    {
      "id": "[Existing Line 2 GUID]",
      "accountId": 1010,
      "debitAmount": 0.00,
      "creditAmount": 6000.00,
      "currencyRate": 1.0
    }
  ]
}

Tables Affected

TableOperationNotes
JournalEntryUPDATERefrenceNumber, Description, TotalDebitAmount, TotalCreditAmount
JournalEntryLine (existing)UPDATEAmounts, account, description
JournalEntryLine (new lines)INSERTIf new lines added
JournalEntryLine (removed lines)Soft DELETEMarked IsDeleted = true
JournalLineCostCenterUPDATE / INSERT / DELETESync cost center allocations

Internal Journals (System-Generated)

Handler: AddInternalJournalCommandHandler

Action: Creates automatically-posted journal entries on behalf of other modules (Sales, Purchase, Inventory, etc.). These entries are not editable by users.

Key Differences from Manual Journals

PropertyManualInternal
TypeManualSales, Purchase, Inventory, PaymentIn, PaymentOut, etc.
Status on creationDraftPosted (immediately)
SourceDocumentNULLSet (e.g., SalesInvoice, StockOut)
SourceDocumentIdNULLGUID of source transaction
Editable by user✅ Yes❌ No
Appears in Change-Status endpoint❌ Filtered out❌ Filtered out

Internal Journal Payload (from Sales Invoice posting)

json
{
  "refrenceNumber": "SI-2026-0001",
  "journalDate": "2026-01-28T16:14:36",
  "type": "Sales",
  "sourceDocument": "SalesInvoice",
  "sourceDocumentId": "[Invoice GUID]",
  "sourceDocumentCode": "SI-2026-0001",
  "description": "",
  "journalEntryLines": [
    { "accountId": 1010, "debitAmount": 1150.00, "creditAmount": 0, "currencyRate": 1.0 },
    { "accountId": 2030, "debitAmount": 0, "creditAmount": 150.00, "currencyRate": 1.0 },
    { "accountId": 4010, "debitAmount": 0, "creditAmount": 1000.00, "currencyRate": 1.0,
      "costCenters": [{ "costCenterId": 87, "percentage": 100 }] }
  ]
}

Returns: JournalCreatedDto { JournalId, JournalCode }

The calling module (e.g., PostSalesInvoiceCommandHandler) stores JournalId and JournalCode back on the source document (e.g., SalesInvoiceHeader.InvoiceJournalId).


Multi-Currency Journal Entry

When a line is recorded in a foreign currency, both local and foreign amounts are stored independently.

Example — USD Invoice at 3.75 SAR/USD

json
{
  "journalEntryLines": [
    {
      "accountId": 1010,
      "debitAmount": 1000.00,
      "creditAmount": 0.00,
      "currencyId": 1,
      "currencyRate": 3.75
    },
    {
      "accountId": 4010,
      "debitAmount": 0.00,
      "creditAmount": 1000.00,
      "currencyId": 1,
      "currencyRate": 3.75
    }
  ]
}

Stored Values Per Line

ColumnLine 1 (AR Debit)Line 2 (Revenue Credit)
DebitAmount1000.00 USD0.00
CreditAmount0.001000.00 USD
CurrencyRate3.753.75
DebitAmountLocal3750.00 SAR0.00
CreditAmountLocal0.003750.00 SAR

Header Totals

ColumnValue
TotalDebitAmount1000.00 USD (foreign)
TotalCreditAmount1000.00 USD (foreign)
TotalDebitAmountLocal3750.00 SAR (base)
TotalCreditAmountLocal3750.00 SAR (base)

Balance check is always performed on local (base currency) amounts.


Cost Center Allocation

Certain GL accounts are configured to require cost center tracking. When a line posts to such an account, one or more cost center allocations must be provided, summing to exactly 100%.

Example — Split Cost Center Allocation

json
{
  "accountId": 5020,
  "debitAmount": 10000.00,
  "costCenters": [
    { "costCenterId": 87, "percentage": 60 },
    { "costCenterId": 92, "percentage": 40 }
  ]
}

JournalLineCostCenter Records

CostCenterIdPercentageAmount (SAR)Notes
8760%6000.0010000 × 60%
9240%4000.0010000 × 40%

Validation: Sum of percentages must equal 100 when the account's CostCenterConfig is set to Mandatory.

Amount recalculation: Called via JournalEntryLine.UpdateCostCentersAmount() which recomputes each allocation's Amount from the line's DebitAmountLocal or CreditAmountLocal.


Batch Operations

Batch Post

Handler: ChangeStatusJournalEntriesCommandHandler

Processes multiple journal entries at once. Returns a list of validation failures — successfully-posted entries are not included.

Returns: List<JournalValidationDto>

json
[
  { "journalId": "[GUID-A]", "message": null },
  { "journalId": "[GUID-B]", "message": "Journal entry is not balanced" },
  { "journalId": "[GUID-C]", "message": "Journal entry type is not Manual" }
]

Entries with a non-null message were not posted. Only entries where message == null were successfully posted.

Batch Reverse

Handler: ReverseJournalEntriesCommandHandler

Same pattern — returns List<JournalValidationDto>. Entries that fail validation are skipped; successful reversals create new reversed journals.

Action Journal (Save / Post / Reverse in One Call)

Handler: ActionJournalEntryCommandHandler

Routes to the correct operation based on the JournalEntryAction enum:

ActionValueRoutes To
Save0ChangeStatus → Draft
Post1ChangeStatus → Posted
Reverse2ReverseJournalEntries

Unpost Journal Entry

Handler: UnpostJournalEntryCommandHandler

Changes a Posted entry back to Draft. Used for corrections before final period close.

Prerequisites

CheckRule
Must be PostedStatus == Posted
Must be Manual typeType == Manual

Table Update

ColumnBeforeAfter
Status"Posted""Draft"
UpdatedDate[Previous][Current Timestamp]

Delete Journal Entry

Handler: DeleteJournalEntryCommandHandler

Soft-deletes the journal entry and all its lines. The sequence number is rolled back if this was the last entry.

Tables Affected

TableOperationNotes
JournalEntrySoft DELETEIsDeleted = true
JournalEntryLineSoft DELETEAll lines marked deleted
JournalEntryAttachmentNo changeAttachment service holds the file

Constraint: Cannot delete a Posted entry. Must unpost first.


Delete Single Line

Handler: DeleteJournalEntryLineCommandHandler

Removes one line from a Draft journal entry and recalculates header totals.

Validation: At least one line must remain after deletion. The journal must remain creatable (cannot delete all lines).

Tables Affected

TableOperationNotes
JournalEntryLineSoft DELETEIsDeleted = true
JournalEntryUPDATETotalDebitAmount/TotalCreditAmount recalculated

Journal Entry Types Reference

TypeValueCreated ByEditableNotes
Manual0User directly✅ YesStandard GL entry
Reversed1ReverseJournalEntry❌ NoReversal of a Manual entry
Sales2PostSalesInvoice❌ NoRevenue + AR + VAT
Purchase3PostPurchaseInvoice❌ NoExpense + AP + VAT
Inventory4AddPostedStockOut / StockIn❌ NoCOGS / inventory
PaymentIn5PostPaymentIn❌ NoCustomer payment
PaymentOut6PostPaymentOut❌ NoVendor payment
Closing7Period Close❌ NoYear-end close
FundTransfer8PostFundTransfer❌ NoBank transfer
FixedAssets9PostFixedAsset❌ NoDepreciation / disposal
SalesReturn10PostSalesReturn❌ NoCredit memo
PurchaseReturn11PostPurchaseReturn❌ NoDebit memo

Source Document Reference

When a journal entry is created internally, the SourceDocument column identifies the originating transaction type:

SourceDocumentCreated DuringLinks To
SalesInvoicePost Sales InvoiceSalesInvoiceHeader
SalesReturnPost Sales ReturnSalesReturnHeader
PurchaseInvoicePost Purchase InvoicePurchaseInvoiceHeader
PurchaseReturnPost Purchase ReturnPurchaseReturnHeader
StockInPost Stock InStockInHeader
StockOutPost Stock OutStockOutHeader
StockTransferPost Stock TransferStockTransferHeader
PaymentInPost Payment InPaymentInHeader
PaymentOutPost Payment OutPaymentOutHeader
FundTransferPost Fund TransferFundTransferHeader
FixedAssetPost Fixed AssetFixedAssetHeader

Complete Transaction Flow Summary (Manual Journal)

1. User submits Add Journal Entry request

2. AddJournalEntryCommandHandler.Handle()

3. Validate: reference length, date, description, period open

4. Validate each line: debit XOR credit, currency rate, cost center sum

5. GetCurrentPeriodQuery(JournalDate) → resolves FinancialYearPeriodId (integer DB ID, e.g. 25)
   Note: command.PeriodId (string) is sent from the frontend but not used by the handler

6. Generate sequence code → "JE-2026-0001"

7. Confirm attachment usage in Attachment microservice

8. Calculate line local amounts:
   - Line 1: DebitAmountLocal = 5000 × 1.0 = 5000
   - Line 2: CreditAmountLocal = 5000 × 1.0 = 5000

9. Calculate header totals:
   - TotalDebitAmountLocal  = 5000
   - TotalCreditAmountLocal = 5000

10. INSERT JournalEntry (Status = Draft)
    INSERT JournalEntryLine × 2
    INSERT JournalLineCostCenter × 1 (Line 1)
    INSERT JournalEntryAttachment × 1

11. SaveChanges → Database transaction committed

12. Return JournalId → Frontend receives draft entry

--- DRAFT PHASE COMPLETE ---

13. User clicks "Post" button

14. ChangeStatusJournalEntriesCommandHandler.Handle()

15. Validate: Manual type, Draft status, Balanced

16. UPDATE JournalEntry SET Status = 'Posted'

17. SaveChanges → Committed

18. Return success → Entry is now part of General Ledger

--- POST PHASE COMPLETE ---

19. (Optional) User clicks "Reverse"

20. ReverseJournalEntryCommandHandler.Handle()

21. Validate: Posted, Manual, not already reversed

22. CreateReversedEntry() → swaps DR/CR on all lines

23. INSERT new JournalEntry (Type = Reversed, Status = Posted)
    INSERT JournalEntryLine × 2 (swapped)
    INSERT ReversedJournalEntry (relationship record)
    UPDATE original JournalEntry SET IsReversed = true, ReversedJournalId = [new GUID]

24. SaveChanges → Committed

25. Return reversed JournalId

--- REVERSAL PHASE COMPLETE ---

Database Transactions

Add Phase

sql
BEGIN TRANSACTION

  INSERT INTO JournalEntry VALUES (...)        -- Status = Draft
  INSERT INTO JournalEntryLine VALUES (...)    -- Line 1
  INSERT INTO JournalEntryLine VALUES (...)    -- Line 2
  INSERT INTO JournalLineCostCenter VALUES (...) -- Cost center
  INSERT INTO JournalEntryAttachment VALUES (...) -- Attachment

COMMIT TRANSACTION

Duration: ~30–60ms

Post Phase

sql
BEGIN TRANSACTION

  UPDATE JournalEntry
    SET Status = 'Posted',
        UpdatedDate = GETUTCDATE(),
        UpdatedBy = @UserId
    WHERE Id = @JournalId

COMMIT TRANSACTION

Duration: ~10–20ms

Reverse Phase

sql
BEGIN TRANSACTION

  INSERT INTO JournalEntry VALUES (...)        -- Reversed entry, Posted
  INSERT INTO JournalEntryLine VALUES (...)    -- Swapped lines
  INSERT INTO ReversedJournalEntry VALUES (...) -- Relationship

  UPDATE JournalEntry
    SET IsReversed = true, ReversedJournalId = @NewGuid
    WHERE Id = @OriginalId

COMMIT TRANSACTION

Duration: ~50–100ms


Error Scenarios

Add / Edit Validation Failures

Error ConditionError MessageResolution
Closed financial period"Journal date must be in an open financial period"Change date or open period
Both debit and credit non-zero"A line cannot have both debit and credit amounts"Set only one per line
Both debit and credit zero"A line must have either a debit or credit amount"Provide at least one
Currency rate missing"Currency rate is required"Provide exchange rate
Cost center sum ≠ 100%"Cost center allocations must total 100%"Adjust percentages
Reference number too long"Reference number must be 15 characters or less"Shorten reference

Post Validation Failures

Error ConditionError MessageResolution
Not Manual type"Journal entry type is not Manual"Cannot manually post system journals
Not Draft"Journal entry is not in Draft status"Already posted or reversed
Not balanced"Journal entry is not balanced"Fix debit/credit totals to match

Reverse Validation Failures

Error ConditionError MessageResolution
Not Posted"Journal entry must be Posted to reverse"Post first
Not Manual"Only Manual journal entries can be reversed"Cannot reverse system journals
Already reversed"Journal entry has already been reversed"Each entry can only be reversed once

Workflow Integration

Journal entries support optional approval workflows via the IWorkflowEnabledCommand interface.

PropertyDescription
EnableWorkflowWhether to trigger the approval workflow
TransactionIdWorkflow transaction reference
ModuleIdModules.Accounting
ServiceIdServices.JournalEntries
ServiceActionIdActionTypes.Save

When workflow is enabled:

  • Entry is saved with WorkflowStatus = Pending
  • Approvers are notified via the Workflow microservice
  • Entry can only be posted after approval

Query / List Behavior

GetAllJournalEntry Filters

FilterTypeBehavior
SearchTermstringMatches Code or RefrenceNumber
FromDate / ToDateDateTime?Filters on JournalDate
TypeList<JournalEntryType>Multi-select filter
StatusList<JournalEntryTransactionStatus>Draft / Posted / Unbalanced
SourceDocumentList<SourceDocument>Filter by originating module
CreatedByList<string>Filter by user name

Note: Entries with Type = PaymentIn or Type = PaymentOut that are in Draft status are excluded from the list. Only their posted state or their reversal entries are shown. This prevents payment journals from appearing before the payment is finalized.

JournalEntryTransactionStatus (special enum for UI)

ValueNameMeaning
1DraftStatus == Draft
2PostedStatus == Posted
3UnbalancedTotalDebitAmountLocal ≠ TotalCreditAmountLocal

Key Interfaces Used

InterfaceUsage
IAppsUnitOfWorkRepository access for all ERP entities
ISecurityServiceCurrent user, tenant context, permission checks
IClockServiceTestable timestamp provider (GetUtcNow())
IGeneralSettingsPublicApiSequence generation, line description settings
IAccountingPublicApiCalled by other modules to create internal journals

Document Version: 1.0
Namespace: AppsPortal.Application.Accounting.GeneralLedger.JournalEntries
Example Date: 2026-01-28
Currency: Saudi Riyal (SAR)

Internal Documentation — Microtec