QA Testing Guide โ€” Missio General Ledger

A dependency-ordered walkthrough for the QA tester. Read the phase intros to understand why each step comes where it does. Follow the numbered test steps inside each phase to verify the entire integration.

117 test cases
6 logical phases
~2 hours end-to-end
Works against real CSD data

๐ŸŽฏ How to use this guide

Testing the GL engine is like testing a factory: you can't test the quality check at the end without first putting raw materials through the machine. That's why we test in phases that mirror the real accounting cycle โ€” input โ†’ controls โ†’ close โ†’ reporting. Skipping ahead usually means retesting later.

Why dependency order matters: Many test cases rely on the state created by earlier ones. You can't test "apply a payment to an invoice" (AR-026) before you've tested "issue the invoice" first. You can't test "month-end reconciliation" before you've posted transactions for the month. This guide orders the 117 cases the way a real monthly close would run โ€” so if a test fails early, everything downstream can still be reasoned about instead of falling like dominoes.

Your mental model: the accounting cycle

PHASE 0 Setup Seed + Login PHASE 1 Foundation JE + Period PHASE 2 Input AR/AP/Payroll/FA PHASE 3 Controls Budget + Approvals PHASE 4 Close Month + Year PHASE 5 Reports TB / BS / P&L The accounting cycle โ€” follow the arrows Each phase produces data the next phase depends on

00 Phase 0 โ€” Setup & Prerequisites Do this ONCE before any testing begins

Before touching a single test case, the tester needs a working environment. Don't skip this โ€” half the "bugs" QA teams file in GL systems are actually missing prereqs.

โš ๏ธ Before you start โ€” run these 4 things

1. Verify you can reach the app

Navigate to https://dev.missio.io and log in as Admin for City Schools of Decatur (company ID 105).

2. Confirm Frappe is reachable

Visit /account/chart-of-accounts. You should see the Decatur School District chart โ€” accounts like 1115 - Operating Account - DSD, 5213 - Salary - DSD, etc. If the list is empty or you see a red error banner, Frappe is not connected โ€” stop and escalate.

3. Seed the AR demo data (one-time)

The AR test cases need parents + students seeded. Run on the server:

php artisan db:seed --class=ArCsdDemoSeeder

After this: visit /account/ar-customers. You should see Karoline Durbin, Melissa Bass, Thomas Decker, Raymond & Sue Leonard, and others.

4. Confirm payroll mappings exist

Visit /account/finance/other/payroll-gl-mapping. You should see at least 21 active mappings (pto_expense, pto_liability, salary_expense, tax_expense, benefit_expense, etc.). If the table is empty, seeds didn't run โ€” escalate.

Open these tabs and keep them open

  1. Tab 1 โ€” Test Checklist
    /account/docs/gl-test-checklist.html
    Your scorecard. Mark each case Pass / Fail / Pending as you go. Progress bar top-right shows overall completion.
  2. Tab 2 โ€” Journal Entries
    /account/journal-entries
    The master JE list. Every test case that posts a JE should show up here. Keep this tab open as your "did it actually post?" reference.
  3. Tab 3 โ€” Trial Balance
    /account/financial-reports/trial-balance
    Live running totals from Frappe. Refresh it after posting a JE โ€” the affected account balances should update immediately.
  4. Tab 4 โ€” Frappe UI (optional)
    https://decatur-demo.frappe.cloud
    If the accountant wants to see the "other side" of the integration, open Frappe's Journal Entry list to confirm every Missio JE has a matching ACC-JV-YYYY-XXXXX document. Ask the product team for credentials.

01 Phase 1 โ€” Foundation Manual JE, period close, validation โ€” ~25 min

Start with the core general ledger operations, before touching any business module. If these fail, nothing else will work.

Why here: Manual JE is the most basic operation โ€” every other module eventually produces a JE. Period close is a guard that every other module checks. If these two are broken, every downstream test will cascade-fail. Test them first so you know the foundation is solid.

1.1 Initialize the fiscal year

  1. Create the fiscal year periods (if not already done)
    PER-017/account/period-close
    Click Initialize Fiscal Year โ†’ pick 2026 โ†’ confirm. System should create 12 monthly periods (Jul 2025 โ€“ Jun 2026) all in "open" state.
    12 rows appear in the period calendar, all status = "open"

1.2 Manual Journal Entry โ€” the core flow

  1. Post a balanced manual JE
    PRE-030PRE-031/account/journal-entries/create
    Click + New Journal Entry. Posting date = today. Reference = "TEST-001". Add 2 lines: DR Salary Expense $100 / CR Cash $100. Save as draft, then Post.
    JE saves, posts successfully, Frappe voucher ID appears in the show page (e.g. ACC-JV-2026-XXXXX)
  2. Try to post an out-of-balance JE
    PRE-024GLCL-035
    Create a new JE with DR $100 / CR $50. Try to save.
    Rejected with error "Out of balance: debits $100 โ‰  credits $50"
  3. Validate invalid date
    JEV-041
    Try to post with posting date = "abc" or a date from year 2019.
    Rejected with "posting_date must be a valid date"
  4. Reverse a posted JE
    PRE-025
    Open the JE you posted in step 1 โ†’ click Reverse Entry โ†’ confirm.
    A new JE is created with the debits and credits flipped. Both JEs are linked.
  5. Upload attachments + copy from prior
    PRE-027PRE-029
    On the JE show page, upload a PDF file. Then click Copy as New Draft.
    PDF appears in attachments section. A new draft JE appears with identical lines.

1.3 Period close guard

  1. Close a prior month
    PER-019GLCL-037
    In /account/period-close, pick October 2025 โ†’ click Close โ†’ supply reason.
    Row status flips to "closed"
  2. Try to post a JE into the closed period
    PER-020JEV-040
    Create a JE with posting date = 2025-10-15.
    Rejected with "Period is closed (closed on YYYY-MM-DD by user X)"
  3. Reopen the period with reason
    PER-022
    In period-close, click Reopen on October โ†’ supply reason "audit correction".
    Period state = "reopened", now accepts posts again
Checkpoint: If any of these 8 steps failed, stop here and escalate. Everything in phases 2โ€“5 assumes manual JE + period close work.

02 Phase 2 โ€” Input modules AR, AP, Payroll, FA, Encumbrances, Bank โ€” ~40 min

Now test the modules that feed data into the GL. Each of these represents a real CSD business event: a parent pays tuition, a bus gets bought, an employee gets paid.

Why this order inside phase 2: We sequence from simple single-event modules (FA acquisition, manual JE) toward stateful chain modules (AP 3-way matching, AR recurring billing). Start with what's self-contained, then move to what depends on prior state. When an AR payment test fails, it's easier to debug if you've already verified AR invoicing works.

2.1 Fixed Assets โ€” the simplest module

  1. Acquire a school bus ($350,000, 10-year life)
    FA-031/account/fixed-assets/create
    Enter asset EF-001 "School Bus #14", cost $350,000, useful life 120 months, salvage $0. Asset account "1711 - Capital Assets - DSD", cash account "1115 - Operating Account - DSD". Save.
    JE auto-posts: DR Capital Assets $350K / CR Cash $350K. Asset status = "active". Monthly depreciation shows $2,916.67.
  2. Post monthly depreciation
    FA-033
    On the asset show page, click Post Depreciation for the current month.
    JE posts: DR Depreciation Expense $2,916.67 / CR Accumulated Depreciation $2,916.67. NBV decreases by same amount.
  3. Manually adjust asset value
    FA-032
    Click Adjust Value โ†’ new value $340,000 โ†’ offset "4999 - Gain on Revaluation" โ†’ reason "condition reassessment".
    Balanced JE posts reflecting the $10K writedown. Asset carrying value updates.
  4. Dispose the asset at a gain
    FA-034
    Click Dispose โ†’ sale proceeds $345,000 โ†’ save.
    JE clears Capital Assets + Accumulated Depreciation, debits Cash, credits a Gain on Disposal account for the difference.

2.2 Encumbrances โ€” governmental commitments

  1. Create a pre-encumbrance
    SCM-051/account/encumbrances/create
    Description: "Dell laptop requisition โ€” 25 units", amount $25,000, account "5201 - Administrative Expenses - DSD", type = pre_encumbrance.
    Record created, status = pre_encumbrance. No JE yet (pre-encumbrances are not posted to GL).
  2. Promote to encumbrance
    SCM-052
    Click Promote to Encumbrance (PO issued).
    Status flips to encumbrance. JE posts reserving the budget line.
  3. Convert to expenditure
    SCM-053
    Click Convert to Expenditure (invoice received and paid).
    JE posts: DR Expense / CR AP. Encumbrance released, available budget restored.

2.3 Accounts Receivable โ€” parents and tuition

  1. Verify AR customers are seeded
    /account/ar-customers
    You should see Karoline Durbin, Melissa Bass, Thomas Decker, Raymond & Sue Leonard. Click into Karoline.
    Customer detail page shows student "Melissa" + a recurring charge of $225/month for After-School Care.
  2. Issue a new invoice
    AR-026/account/ar-invoices/create
    Customer = Karoline Durbin, amount $225, description "April 2026 after-school".
    JE posts: DR AR Receivable $225 / CR After-School Revenue $225. Invoice status = "sent".
  3. Apply full payment
    AR-026
    On the invoice show page click Apply Payment โ†’ amount $225 โ†’ method = check โ†’ reference "#1234".
    JE posts: DR Cash $225 / CR AR Receivable $225. Invoice status = "paid", outstanding = $0.
  4. Apply partial payment to a different customer
    AR-027
    Issue a $1,500 invoice to Thomas Decker (tuition). Apply payment $1,250.
    Invoice status = "partially_paid", outstanding = $250. Two JEs visible in history.
  5. Write off a doubtful account
    AR-028
    Open Natalie & Ben Beisner's invoice. Click Write Off โ†’ reason "uncollectible, moved out of district" โ†’ bad debt account "6999 - Bad Debt Expense - DSD".
    JE posts: DR Bad Debt Expense / CR AR Receivable. Invoice status = "written_off".
  6. Non-invoice cash receipt
    AR-029
    At /account/ar-invoices/cash-receipt/create, record a $150 miscellaneous receipt with no invoice linkage.
    JE posts: DR Cash / CR Miscellaneous Revenue. Receipt stored in ar_cash_receipts.
  7. Check aging report
    /account/ar-aging
    Open the AR Aging page after the above steps.
    Each customer shows correct outstanding balance bucketed by age (current / 30 / 60 / 90+).

2.4 Accounts Payable โ€” purchase orders and matching

  1. Create a Purchase Order
    AP-003/account/ap-purchase-orders/create
    Vendor "ABC School Supply", item "School supplies for Glennwood", qty 1, amount $1,050, GL expense account "5211 - School Supplies - DSD".
    PO saved with status "open". No JE yet โ€” POs don't post to GL until goods are received.
  2. Receive the goods (3-way matching)
    AP-003
    Click Receive Goods. Enter received qty = 1.
    JE posts: DR School Supplies Expense $1,050 / CR RNI (Received Not Invoiced) $1,050.
  3. Match vendor invoice
    AP-003
    Click Match Invoice. Enter invoice # 69781, amount $1,050.
    JE posts: DR RNI $1,050 / CR Accounts Payable $1,050. RNI account clears for this PO.
  4. Verify RNI reconciliation
    AP-001AP-002/account/subsidiary-recon
    Check that RNI subledger matches the GL RNI account balance.
    RNI balance = $0 after step 3 (cleared).
  5. 1099 tracking
    AP-004/account/tracking1099
    Verify the page shows Melvin Smith Custodial Services + Tom's Transmission with 1099 flag.
    Both vendors listed with YTD payment totals; CSV export available.

2.5 Payroll โ€” the biggest integration

  1. Verify payroll mappings
    PAY-047/account/finance/other/payroll-gl-mapping
    Confirm 21+ active mappings exist covering salary_expense, tax_expense, benefit_expense, payable variants, cash, and pto_expense/pto_liability.
    Summary cards: 21+ Active, 0 Unmapped Codes In Use, 0 Failed Mirrors.
  2. Run payroll (outside this guide's scope)
    The QA tester does NOT need to run a fresh payroll from scratch โ€” that's the payroll team's job. Instead, use an existing run: Run #6 from the test data already shows mirrored successfully.
    Run #6 appears in the "Recent Successful Mirrors" panel at the bottom of the payroll-gl-mapping page with JE #7, voucher ACC-JV-2026-00193.
  3. Drill through to the mirrored JE
    PAY-044PAY-048PAY-049
    Click the JE number on the Recent Successful Mirrors row. The JE detail page should show 7 lines totaling $7,324.01, source banner "Payroll Run #6".
    All 7 lines visible, balanced (DR = CR = $7,324.01), drill-link back to payroll run.
  4. Check cost-center split / garnishment breakdown
    PAY-044PAY-046PAY-050BEN-059
    Lines should include Salary Expense (5213), Payroll Payable (2120 โ€” withholdings), Tax Expense (5225), Admin Expenses (5201 โ€” benefits), Benefits Obligation (2520 โ€” employer benefits + PTO + retirement).
    All account types represented in the single consolidated JE.

2.6 Benefits & PTO Accrual

  1. Post the monthly accrual
    BEN-058BEN-060/account/finance/other/benefits-pto-gl
    Pick the current month in the dropdown โ†’ click Preview โ†’ review the 4 preview lines โ†’ click Post Accrual JE.
    JE posts: DR Compensated Absences Expense (PTO), DR Benefit Expense, CR Compensated Absences Payable, CR Benefit Obligation. Idempotent โ€” re-posting the same month returns the existing JE.

2.7 Bank Import & Reconciliation

  1. Import a BAI file
    CR-018CR-022/account/bai-imports/create
    Upload a .bai file (sample in tests/fixtures/bai if available, otherwise download a fresh one from your bank).
    Parser shows each transaction with a suggested GL account. Approve โ†’ each line becomes a JE.
  2. Auto-match against the books
    CR-019CR-024/account/bank-recon
    Create a new reconciliation โ†’ click Auto-Match โ†’ it matches bank items against existing JEs by amount within 3 days.
    Most items auto-match. Remaining unmatched items can be manually paired or investigated.
  3. Complete the reconciliation
    CR-025
    Click Complete Reconciliation. Should only succeed when difference = $0 and all items matched.
    Reconciliation status = complete, shows bank balance matches GL balance.

2.8 P-Cards and Travel Expenses

  1. Enter a P-Card transaction
    PC-010PC-011/account/p-cards
    Oakhurst Elementary P-Card โ†’ charge Zoo Atlanta $1,780 โ†’ assign GL account "Field Trips".
    JE posts on submission: DR Field Trips / CR P-Card Clearing.
  2. Travel expense reimbursement
    TE-008TE-009/account/travel-expenses
    Tori Williams expense report for conference travel โ€” flight $798, lodging $2,659, meals $150.
    On approval, JE posts for the total with line-level GL mapping.

03 Phase 3 โ€” Controls Budget, approvals, dimensions โ€” ~20 min

Now that data is flowing, test the guards that prevent bad data from flowing in.

Why here: Controls only matter when there's real data to apply them against. Testing budget control on an empty system is pointless โ€” you need transactions in the budget lines before "exceeded" makes sense. That's why controls come AFTER input.

3.1 Budget Control

  1. Create a small budget allocation for testing
    BUD-012/account/budgets
    Create Budget "FY2026 Test", fiscal year 2026. Add allocation: account 5211, fund 100, category "Supplies", amount $500.
    Budget row appears. Allocation shows $500 allocated, $0 expended, $500 available.
  2. Post an in-budget JE
    BUD-013
    Post a JE with DR 5211 $300 / CR Cash $300.
    JE posts successfully. Budget Control dashboard shows 5211 at 60% utilization.
  3. Post an over-budget JE
    BUD-013BUD-014
    Post another JE: DR 5211 $300 / CR Cash $300 (now we'd be at $600 vs $500 budget).
    In soft mode: posts successfully with WARN message. In hard mode (set GL_BUDGET_ENFORCEMENT_MODE=hard in .env): blocked with BudgetExceededException.
  4. Category rollup
    BUD-015/account/finance/other/budget-control
    Open Budget Control โ†’ scroll to Per-Category Rollup section.
    "Supplies" category shows aggregated allocated / expended / available across all accounts in that category.
  5. Fiscal-year carryforward
    BUD-016
    Click Run Carryforward โ†’ from FY 2026 โ†’ to FY 2027. Confirm.
    New draft budget created in FY 2027 with remaining balances carried forward.

3.2 Approval workflow

  1. Submit a small JE (bypass approval)
    PRE-026
    Post a JE for $1,000 (below $5,000 threshold).
    Posts directly without going through approval.
  2. Submit a large JE (requires approval)
    PRE-026
    Post a JE for $10,000 (above threshold).
    JE is routed to the Approvals Inbox. Message: "Entry submitted for approval ($10,000 โ‰ฅ threshold $5,000)".
  3. Approve (or reject) from the inbox
    /account/je-approvals
    Go to Approvals Inbox โ†’ open the pending JE โ†’ click Approve.
    JE auto-posts after approval. Approval history is visible on the JE show page.

3.3 Dimensions and validation

  1. Create a master data category
    SYSGOV-003/account/master-data-categories
    Click + New Category โ†’ name "Test Location" โ†’ add values "Oakhurst", "Glennwood" โ†’ save.
    Category appears with 2 values. Can toggle values active/inactive, delete.
  2. COA change request approval
    SYSGOV-007/account/coa-change-requests
    Submit a request for a new COA segment value โ†’ approve through workflow.
    Request flows through pending โ†’ approved โ†’ applied states.

3.4 Dual-basis tagging

  1. Tag a JE as GAAP-only
    SYSGOV-008
    Create a new JE (e.g., depreciation adjustment) โ†’ in the Reporting Basis dropdown select "GAAP Only".
    JE saves with basis = gaap. On the JE list, the basis badge shows blue "GAAP Only".
  2. Filter the JE list by basis
    On /account/journal-entries, use the Basis filter dropdown โ†’ select GAAP (full accrual).
    Only "both" + "gaap" entries are shown. The GAAP-tagged depreciation JE appears.

3.5 Recurring template approval workflow + Finance Approver role (added 2026-04-15)

Why this section exists: the CFO meeting on 2026-04-14 introduced new requirements: every JE must be reviewed before posting, recurring templates need to be approved once (so monthly instances aren't re-approved 12 times/year), and access to the Approvals Inbox must be restricted to specific staff. This section walks through the full approval lifecycle for both flavors. Allow ~15 minutes.

Setup โ€” set the threshold to $0 first

  1. Lower the approval threshold (one-time .env edit)
    In the server's .env file, add: GL_JE_APPROVAL_THRESHOLD=0 and GL_JE_ALLOW_SELF_APPROVAL=false. Then run php artisan config:clear. This makes every non-template JE require approval and prevents submitters from approving their own work.
    After refresh, the Approvals Inbox header shows "Approval threshold: $0.00".

Test A โ€” Admin can access the Approvals Inbox

  1. Open the inbox as Admin
    /account/je-approvals
    Logged in as an admin user, navigate to Finance โ†’ Other โ†’ Approvals Inbox.
    Page loads with the 3 KPI cards (Pending / Approved Today / Rejected Today). No 403/404.

Test B โ€” Recurring template approval (draft โ†’ pending โ†’ approved)

  1. Create a draft recurring template
    /account/recurring-jes/create
    Click + New Recurring Template. Fill in: name "TEST โ€” Monthly Internet", reference prefix INTERNET, monthly frequency, day-of-month 1, lines DR 5201 $200 / CR 1115 $200. Save.
    Template appears in the list with a gray DRAFT approval badge and a yellow Submit button.
  2. Submit for approval
    Click the yellow Submit button on the draft template row.
    Badge flips from DRAFT to amber PENDING. A green Approve button appears (visible because admin has approval rights).
  3. Approve the template
    Click the green Approve button โ†’ confirm the dialog.
    Banner: "Template approved. JEs spawned from this template will skip the per-JE approval gate unless modified." Badge flips to green APPROVED.

Test C โ€” Spawned JE auto-posts (unmodified path)

  1. Spawn a JE from the approved template
    /account/journal-entries
    Click + New Journal Entry. At the top of the form, in the indigo "Start from a recurring template" dropdown, pick the TEST template. The form auto-fills (memo, reference, both lines).
    Memo, reference, and 2 balanced lines populate without page reload. Hidden field spawned_from_template_id is set.
  2. Save and post WITHOUT changing anything
    Don't edit any field. Click Save โ†’ land on JE show page โ†’ click Post.
    Banner: "Posted to GL: ACC-JV-2026-XXXXX". JE goes directly to Frappe โ€” does NOT go through the approvals inbox. This is the pre-approval skip working.

Test D โ€” Modified spawned JE routes to approval

  1. Spawn the same template, modify a line
    + New Journal Entry โ†’ pick the same TEST template โ†’ form auto-fills. Change the debit on line 1 from $200 to $250 AND the credit on line 2 from $200 to $250 (keep balanced).
    Totals row shows $250/$250 โœ“ Balanced. Hidden modification flag will be set on save.
  2. Save and observe the button label change
    Click Save โ†’ land on JE show page.
    Top-right action button now reads "Submit For Approval" instead of "Post" โ€” proof that the system detected the modification and routed the JE to the approval gate.
  3. Submit for approval
    Click Submit For Approval โ†’ confirm.
    Banner: "Entry submitted for approval (amount $250.00 โ‰ฅ threshold $0.00)". JE status flips to PENDING_APPROVAL.
  4. Verify in the inbox + try self-approve (should fail)
    /account/je-approvals
    Navigate to the Approvals Inbox. Find the new pending JE. Click Approve.
    Red banner: "You cannot approve your own submission." The Approve and Reject buttons are visually disabled for that row. This is the segregation-of-duties guard in action.

Test E โ€” Non-approver employee cannot access the Approvals Inbox

  1. Create a test employee
    /account/employees/create
    Add an employee with email test.employee@local, password password123, role = Employee (NOT Admin). Save.
    New employee appears in the list.
  2. Log in as the test employee
    Logout from admin, log in as test.employee@local.
    Logged in successfully, lands on dashboard.
  3. Verify the Approvals Inbox menu item is hidden
    Open Finance โ†’ Other in the sidebar.
    The "Approvals Inbox" menu item is NOT visible. Other GL menu items may also be hidden depending on module type filters.
  4. Try direct URL access
    /account/je-approvals
    In the address bar, type the inbox URL directly and press Enter.
    404 Not Found (or 403 Forbidden depending on Missio's exception handler). Either way, the test employee cannot reach the inbox.

Test F โ€” Granting Finance Approver role unblocks the user

  1. Logout, log back in as admin
    Switch back to your admin account.
    Sidebar shows the full GL menu including Approvals Inbox.
  2. Open the test employee's profile and grant Finance Approver
    /account/employees/{id}?tab=permissions
    Navigate to the test employee's profile โ†’ click the Permissions tab. At the very top, find the new "Finance Approver" section with a green Grant Approver Role button. Click it โ†’ confirm.
    Alert: "Finance Approver role granted. The user can now approve journal entries and recurring JE templates." Badge flips from gray NOT GRANTED to green GRANTED. Button text changes to "Revoke Approver Role" (red).
  3. Logout, log in as the test employee again
    Switch back to test employee credentials.
    Lands on dashboard.
  4. Verify the Approvals Inbox is now accessible
    Open Finance โ†’ Other in the sidebar.
    "Approvals Inbox" menu item is now visible. Click it โ†’ page loads (no 404).
  5. Cross-user approval (segregation of duties)
    In the inbox, find the JE that admin submitted in Test D. Click Approve.
    Banner: "Approved and posted: ACC-JV-2026-YYYYY". Pending count drops to 0. The row moves to Recent History showing the test employee as approver. The original submitter (admin) and the approver (test employee) are different users โ€” segregation of duties enforced.
Test F closes the loop. The complete chain is now proven: admin grants role โ†’ employee gains access โ†’ employee approves admin's submission โ†’ JE auto-posts to Frappe with full audit trail. The CFO's request from the meeting is fully implemented and verified.

04 Phase 4 โ€” Period & Year-End Close Monthly + annual close procedures โ€” ~15 min

Close out the month to lock the books, then test the year-end close.

Why here: Close procedures only make sense after data has been posted (phase 2) and guards have been validated (phase 3). Closing the month is a consequence of data flow, not a precondition. If you close too early, there's nothing to reconcile.

4.1 Sub-ledger reconciliation

  1. Subsidiary reconciliation
    GLCL-034/account/subsidiary-recon
    Open the Subsidiary Reconciliation dashboard.
    4 summary cards (AR, AP, FA, Payroll). Each shows GL balance matches subledger balance. If anything's off, red variance indicator.
  2. Interfund reconciliation
    PER-012/account/interfund-recon
    Open the Inter-fund Reconciliation page.
    Per-fund transfer summary. All intra-fund transfers balance across funds.

4.2 Month-end close

  1. Post recurring JE templates
    SCM-051/account/recurring-jes
    Run any due recurring JE templates (Run Now button). These might include monthly accruals, prepaid amortization, etc.
    Each template produces a JE. Auto-reverse templates also produce a reversal JE on the first of the next month.
  2. Close the current month
    PER-019/account/period-close
    Click Close on the current month โ†’ supply reason "monthly close".
    Status flips to closed. Any further JE attempt with that month's posting date is blocked.

4.3 Year-end close

  1. Year-end closing entries (via CLI)
    GLCL-038
    SSH into the server and run:
    php artisan tinker --execute='(new App\Services\Gl\YearEndCloseService(105))->postClosingEntry(2025, "6212 - Fund Balance - DSD", null)'
    One big closing JE with dozens of lines โ€” zeroing out revenue/expense accounts and posting the net to Fund Balance. Reference = YEARCLOSE-2025.
  2. Verify new FY opening balances
    Open /account/financial-reports/trial-balance for the new FY.
    All revenue/expense accounts show $0 opening. Fund Balance carries forward the prior year net.

05 Phase 5 โ€” Financial Reporting TB, BS, P&L, SEFA, saved queries โ€” ~15 min

The pay-off. After closing, generate the statements and verify they reflect all the activity from phases 1โ€“4.

Why last: Reports pull from whatever's in the ledger. Running a Trial Balance on an empty system tells you nothing. Running it after phase 4 tells you whether every transaction you fed in was recorded correctly. Reports are the final quality check.

5.1 Core statements

  1. Trial Balance
    PER-015/account/financial-reports/trial-balance
    Open the Trial Balance page.
    Every account shows its balance. Total debits = total credits at the bottom.
  2. Trial Balance with subtotals only
    PER-014
    Append ?subtotals_only=1 to the URL and refresh.
    Report collapses to parent-account rollups (Assets, Liabilities, Revenue, Expenses). Individual accounts hidden.
  3. Balance Sheet
    PER-015/account/financial-reports/balance-sheet
    Open Balance Sheet.
    Assets + Liabilities + Net Position sections. Total Assets = Total Liabilities + Net Position.
  4. Profit & Loss (Income Statement)
    PER-015/account/financial-reports/profit-loss
    Open P&L.
    Revenue section, Expenditure section, Excess (Deficiency) line. Bottom line = the Fund Balance delta.

5.2 Multi-year + drill-down

  1. Multi-year Trial Balance
    TXN-051
    Open TB for the prior year (FY2024) then the current year. Verify you can see both.
    Both years accessible via year selector.
  2. GL inquiry with drill-down
    PER-016PRE-021/account/general-ledger
    Open the General Ledger inquiry page. Filter to account "5213 - Salary - DSD". Click a transaction.
    Drill-down to the Missio JE, which shows source banner (e.g. "Payroll Run #6") with a link back to the source record.

5.3 Grant reporting

  1. SEFA Report (federal awards)
    GR-042/account/grant-sefa-report
    Open the SEFA cross-grant page.
    Table of federal grants with CFDA number, award amount, expenditures, unbilled balance. Totals row at bottom.
  2. Grant closeout
    GR-043/account/grant-closeout
    Open the Grant Closeout page.
    List of grants in Closeout or Closed status with closeout progress percentage.

5.4 Saved queries (user report builder)

  1. Create a saved query
    PER-013/account/finance/other/saved-queries/create
    Name: "Q4 Salary Over $10K". Type: Journal Entries. Filters: date range Oct-Dec, min_amount 10000, memo_contains "salary". Visibility: Role โ†’ allowed_roles "finance-manager". Save.
    Query saves. Appears in the list with "ROLE" visibility badge.
  2. Run the query
    Click Run on the saved row.
    Result table shows matching JEs. Run counter increments. last_run_at stamped.

5.5 Bonus: Excel upload

  1. Download template
    PRE-028/account/journal-entries
    Click โ†‘ Upload Excel โ†’ Download sample template.
    je-upload-template.xlsx downloads with 2 sample balanced JEs.
  2. Upload a modified template
    Edit the template, change the accounts to match your CoA, save, upload via the same modal.
    Preview page shows each JE as a balanced green card. Click Commit โ†’ both become draft JEs in the list.
  3. Upload a broken file (error test)
    Delete one line from one JE so it's unbalanced. Upload.
    Preview shows that JE as a red error card with "Out of balance" message. Commit button is replaced with error state โ€” nothing written to DB.

๐Ÿ“‹ QA Cheat Sheet

Quick references for the most common questions that come up during testing.

Where do I see all journal entries posted today?

/account/journal-entries โ€” sorted by date descending by default.

A JE didn't post to Frappe โ€” where do I check why?

Open the JE detail page and look at the Frappe Voucher field. If empty, the local Missio JE was created but Frappe posting failed. The error is usually in the Laravel log (/storage/logs/laravel.log). On the specific subsystem, check that system's "failed" panel:

Payroll: /account/finance/other/payroll-gl-mapping (Failed Mirrors panel)

How do I know a test case's expected outcome?

Open /account/docs/gl-test-checklist.html and find the case ID. Each case has a green PASSED banner with (1) what's built, (2) step-by-step test instructions, (3) expected result.

How do I reset state between tests?

You generally DON'T need to. GL testing is additive โ€” each JE adds to the ledger without touching prior ones. If you truly need to reset, ask for a database refresh from the dev team.

A budget block is preventing a test JE โ€” how do I proceed?

Two options: (1) increase the budget allocation at /account/budgets, or (2) temporarily set GL_BUDGET_ENFORCEMENT_MODE=off in the server's .env file and run php artisan config:clear. Remember to restore it before finishing testing.

I'm seeing "Period is closed" and can't post

Go to /account/period-close and click Reopen on the relevant month. This is expected behavior โ€” the guard is working correctly. In production, reopening requires a documented reason.

How do I tell if the Frappe side is actually in sync?

Compare the Trial Balance total on /account/financial-reports/trial-balance with the totals in Frappe's own Trial Balance. They should match to the penny. If they don't, there's a skew โ€” escalate with the specific JE number.

What if I discover a real bug during testing?

Log it against the specific test case ID (e.g. "PAY-044: bus monitor allocation posts to wrong account"). Include: (1) the exact URL you were on, (2) the input you provided, (3) the actual vs expected result, (4) a screenshot if possible. Set the case to Fail in the checklist.

๐ŸŽ“ When you're done

If all 117 test cases come back green, the GL integration is certified working against the current codebase. Export your results from the checklist page (Export Results as JSON button at the bottom) and archive them with the date.

If cases come back yellow (pending) or red (failing), go back to the specific phase, re-test just that case with the detailed instructions on gl-test-checklist.html, and escalate to the dev team with the case ID + the actual vs expected outcomes.

Remember: This guide is organized by dependency order, not by test case number. A case like BUD-013 (budget exceeded) appears in Phase 3 even though its ID is lower than PAY-044 (payroll), because BUD-013 depends on having transactions to test against. Following this order saves you from retesting half the suite every time you catch a prerequisite bug.