Journal-Format Reports — Sales / Cash Receipts / Purchase / Cash Disbursements¶
Date: 2026-07-01
Problem / motivation¶
Peachtree (Sage 50) exposes four "journal" reports that show transactions in debit/credit journal
format rather than as document registers: Sales Journal, Cash Receipts Journal,
Purchase Journal, and Cash Disbursements Journal (plus the client's Sales Journal_only /
INFORME DE CAJA custom variants). We already post every source document to the general ledger with a
source_type discriminator, so these reports are the same posted ledger_entries, filtered by the
kind of source document and rendered as debit/credit lines. This closes the highest-leverage rows on the
Peachtree Report Parity tracker (Phase 1).
In scope¶
- One shared journal primitive (
JournalReportRepository+JournalLineResponse) that lists posted ledger lines for a set ofSourceTypes in a date range, in journal order (date, entry number, account). - Four read-only queries, each a thin wrapper binding the primitive to a source type:
sales_journal_report→SourceType.INVOICEScash_receipts_journal_report→SourceType.RECEIPTSpurchase_journal_report→SourceType.SUPPLIER_INVOICEScash_disbursements_journal_report→SourceType.PAYMENTS- Only posted entries (
is_posted = TRUE). CSV export via the standardgenerate_csvflag. Path.REPORTSpermission.
Out of scope¶
- General Journal (all source types) and manual-entry journal — already covered by
JOURNAL_MOVEMENTS. - Payroll Journal — deferred to the payroll reporting suite.
- Including
EXPENSESin cash disbursements — expenses on credit are not disbursements; the Expense Register already covers them. The repo takes alist[SourceType], so this is a one-line extension later.
Data model changes¶
None. Reads existing ledger_entries (entry_date, entry_number, source_type, memo, is_posted)
and ledger_entry_lines (debit_amount, credit_amount, account_id, memo) joined to accounts.
No migration, no new Path/Resource.
What's being implemented¶
- Response —
app/graphql/reports/strawberry/journals/journal_report_response.py(JournalLineResponse, one row per ledger line). - Repository —
app/graphql/reports/repositories/journals/journal_report_repository.py. - Service —
app/graphql/reports/services/journals/journal_report_service.py(get_journal(source_types, date_filter, generate_csv)). - Queries —
app/graphql/reports/queries/journals/journal_report_queries.py(JournalReportQueries). - Tests —
tests/graphql/reports/test_journal_reports.py.
GraphQL surface¶
salesJournalReport(dateFilter: DateRangeFilter!, generateCsv: Boolean! = false): GenericReportPage!
cashReceiptsJournalReport(dateFilter: DateRangeFilter!, generateCsv: Boolean! = false): GenericReportPage!
purchaseJournalReport(dateFilter: DateRangeFilter!, generateCsv: Boolean! = false): GenericReportPage!
cashDisbursementsJournalReport(dateFilter: DateRangeFilter!, generateCsv: Boolean! = false): GenericReportPage!
type JournalLine {
entryDate: Date!
entryNumber: String!
sourceType: SourceType
memo: String
accountCode: Int!
accountName: String
lineMemo: String
debitAmount: Decimal!
creditAmount: Decimal!
}
Frontend contract¶
- Four journal reports under Reports; each takes a
DateRangeFilterand returnsrecords: [JournalLine]. - Render grouped by
entryNumber(a transaction), each group's lines showingaccountCode/accountNamewithdebitAmount/creditAmountin two columns; column totals per group and overall must balance. generateCsv: truereturnscsvUrland an emptyrecords, matching every other report.
Future additions¶
- Group-subtotal rollups server-side (currently a flat balanced line list; the client groups).
- Payroll Journal once payroll postings carry a dedicated source type.