dodil/erp-brick-gl
DODIL ERP brick: general ledger
src/brick_gl/application/process.yaml
# The brick's process — module.yaml's workflows, copied (pattern.md §15). BRICK.declare_process serves
# it as BPMN 2.0 at GET /process/bpmn; it is identical to `erp-compose bpmn --from module.yaml`.
# A transition's key (or its derived <entity>_<from>_to_<to>) is frozen once signed: it is the BPMN id.
id: gl
name: Accounting
version: 7
actors:
- id: bookkeeper
name: Bookkeeper
does: proposes manual journals, reconciles the receivable control account, reads what the other modules posted
sees: every account and journal
permissions:
- gl:journal:read
- gl:journal:write
- gl:period:read
- gl:fx:read
- gl:reconcile:run
- gl:report:read
- gl:company:read
- gl:close:task
- id: accountant
name: Accountant / finance manager
does: sets up companies, ledgers, the charts and the account map, approves manual journals, recurring entries, accruals and allocation cycles, reverses mistakes, enters rates, revalues, matches intercompany, runs the closing cockpit, closes periods and years, signs off a close, runs the reports
sees: the companies the owner gives them access to (all by default)
permissions:
- gl:journal:read
- gl:journal:write
- gl:journal:approve
- gl:reversal:approve
- gl:account:write
- gl:company:read
- gl:company:write
- gl:fsv:write
- gl:period:read
- gl:period:close
- gl:period:reopen
- gl:adjustment:post
- gl:fx:read
- gl:fx:write
- gl:reval:run
- gl:reconcile:run
- gl:report:read
- gl:recurring:write
- gl:recurring:approve
- gl:recurring:run
- gl:accrual:write
- gl:accrual:approve
- gl:accrual:run
- gl:allocation:approve
- gl:allocation:run
- gl:budget:approve
- gl:close:manage
- gl:close:task
- gl:close:signoff
- gl:audit:export
- gl:settings:write
- gl:migration:read
- id: controller
name: Controller (management accountant)
does: keeps the allocation cycles and the statistical key figures, simulates and posts allocations, keeps the budget, reads plan against actual by cost and profit centre
sees: the companies the owner gives them access to (all by default)
permissions:
- gl:journal:read
- gl:period:read
- gl:company:read
- gl:report:read
- gl:allocation:write
- gl:allocation:simulate
- gl:allocation:run
- gl:keyfigure:write
- gl:budget:write
- gl:close:task
note: a controller who wrote a cycle cannot approve it (gl:allocation:approve is the accountant's, and the approver must be another person)
- id: auditor
name: Auditor
does: reads the books and the trail behind any number, verifies the hash chain, exports the FEC or SAF-T file
sees: everything, changes nothing
permissions:
- gl:journal:read
- gl:period:read
- gl:fx:read
- gl:report:read
- gl:company:read
- gl:audit:export
- gl:audit:verify
- gl:migration:read
- id: owner
name: The owner (or whoever the owner names)
does: holds the controls on the people who keep the books (time zone, second approver for manual journals, recurring entries, accruals, allocation cycles and reopening, journal numbering, document splitting, who may see which company, the AI step modes) and signs the migration reconciliation
sees: everything
permissions:
- gl:journal:read
- gl:report:read
- gl:company:read
- gl:settings:controls
- gl:migration:read
- gl:migration:sign
note: 'the controls are never held by whoever they control: a bookkeeper or accountant holding gl:settings:controls is a configuration error the access test refuses'
- id: system
name: The system
does: posts what other modules send (fixed-assets included), splits and re-derives balances, derives the carry-forward, posts pending reversals when a period opens, re-splits open items by profit centre at period end, checks the closing cockpit's system tasks, materialises statements, serves consolidation its group data, checks a manual journal before approval, numbers journals, loads and reconciles a migration
sees: everything, through its own service account
permissions:
- gl:journal:read
- gl:period:read
- gl:fx:read
- gl:company:read
- gl:journal:post
- gl:balance:derive
- gl:carry_forward:derive
- gl:resplit:post
- gl:close:check
- gl:group_data:extract
- gl:statement:materialise
- gl:journal:check
- gl:number_series:claim
- gl:migration:load
- gl:migration:reconcile
- gl:cost_centre:read
note: each is an exact grant to one service client, never reached by a wildcard (permissions.machine); gl:journal:post is held by each posting module's client (ar, payments, tax, purchasing, inventory, fixed-assets), gl:group_data:extract by consolidation's client only
workflows:
- entity: journal
states:
- proposed
- posted
- rejected
- reversed
terminal:
- rejected
- reversed
transitions:
- key: post_from_source
name: Post what another module sent
from: none
to: posted
by: system
gate: balanced in document and functional currency; every purpose mapped on the journal date; the company's period open; the ledger (if named) assigned to the company; source-only accounts posted only by their module; field status met; journal_id = derived_id(source_document, event[, ledger_id])
effect: lines written with the source named; split to universal lines (GL-22); hash chain extended; number claimed per company, book and year; balances re-derived in a second transaction; a replay answers the journal already posted
- key: propose_manual
name: Propose a manual journal
from: none
to: proposed
by:
- bookkeeper
- accountant
gate: balanced; dated in an open period and not after today; the proposer has access to the company; no source-only or group account on a line
effect: proposer recorded (email and sub); a ledger may be named (ledger-specific); reverse_next_period may be flagged (GL-50)
- key: propose_cross_company
name: Propose a cross-company journal
from: none
to: proposed
by:
- bookkeeper
- accountant
gate: lines in two or more companies the proposer has access to; ic_receivable and ic_payable mapped for every pair; each company's period open
effect: one ic_document_id; the due-to and due-from lines computed and shown before approval (GL-51)
- key: propose_audit_adjustment
name: Propose a year-end audit adjustment
from: none
to: proposed
by: accountant
gate: dated in the last period of an open financial year; that period closed
effect: flagged adjustment, book adjustment; always waits for a second person (GL-56)
- key: ai_check_journal
name: AI checks the journal (suggest)
from: proposed
to: proposed
by: system
gate: the check is on
effect: findings listed for the approver
- key: accept_journal_ai_findings
name: Accept or dismiss each AI finding
from: proposed
to: proposed
by: accountant
gate: the journal has findings (GL-15)
effect: each decision recorded in gl_ai_finding
- key: post_manual
name: Post without a second approver
from: proposed
to: posted
by: system
gate: manual_journal_approval is none, or the total is at or below the threshold; never an adjustment or a cross-company journal above the threshold
effect: numbered; split; chained; balances re-derived; an auto-reversal posted or held (GL-24)
- key: approve_manual
name: Approve the manual journal
from: proposed
to: posted
by: accountant
approval: true
gate: the approver is not the proposer (email AND sub); every company's period is still open (the last period closed but the year open, for an adjustment)
effect: numbered; approver recorded; one journal per company for a cross-company document, in one transaction; balances re-derived; the carry-forward marked stale for an adjustment
- key: reject_manual
name: Reject the manual journal
from: proposed
to: rejected
by: accountant
requires: reason
- key: reverse_journal
name: Reverse the journal
from: posted
to: reversed
by: accountant
requires: reason
gate: the reversal date (today, or the first day of the next open period) is in an open period; no auto-reversal pending
effect: a mirror journal keyed derived_id(journal_id, 'reversal'), split the same way; neither is deleted; a cross-company journal reverses with its partners
- key: post_pending_reversal
name: Post the auto-reversal
from: posted
to: reversed
by: system
gate: the journal asked for a reversal on the next period's first day (accrual, revaluation, re-split) and that period is open
effect: the mirror journal dated that day, once (GL-24)
note: 'There is no edit: a journal exists or it does not, and a correction is another journal. A journal''s date is an accounting fact dated by its document (an invoice''s issue date, a receipt''s received date, a manual accrual''s month): the one kind of date allowed earlier than today, and only into an open period (or, for an audit adjustment, the closed last period of an open year). A migrated journal (source_module odoo) is read-only history: it is reversed like any other, never re-posted. Recurring entries, accrual releases, allocations and revaluations post through the same use case as post_from_source, with gl itself as the source.'
readiness:
- transition: post_from_source
gate: balanced
reason: debits and credits differ by the amount named
fix: the calling module's journal builder
- transition: post_from_source
gate: purposes_mapped
reason: a purpose (or purpose and qualifier) has no account in force on the journal date in the company's chart
fix: 'Account map: map it (GL-12)'
- transition: post_from_source
gate: period_open
reason: the journal date falls in a closed period of the company, or in no period
fix: post it dated in an open period, or ask the accountant to reopen
- transition: post_from_source
gate: fx_rate
reason: a foreign journal with no rate given and no rate in force on its date
fix: 'Currencies: enter the rate for the date'
- transition: post_from_source
gate: ledger_assigned
reason: the named ledger is not assigned to the company for the journal's year
fix: 'Companies: assign the ledger, or post to all ledgers'
- transition: post_from_source
gate: company_known
reason: the company_code is not a company of these books
fix: 'Companies: add it, or send the default'
- transition: post_from_source
gate: field_status
reason: the account requires (or forbids) a cost centre, profit centre, partner or tax code on the line
fix: the calling module's builder, or the account's field status
- transition: post_from_source
gate: profit_centre_valid
reason: the profit centre is not valid for the company on the journal date
fix: 'Dimensions: extend it, or send another'
- transition: propose_manual
gate: source_only_account
reason: an account on a line is kept by its module (receivable, payable, asset cost, accumulated depreciation)
fix: post it in that module, or use a non-control account
- transition: propose_manual
gate: group_chart_not_postable
reason: a group-chart account cannot carry a line
fix: use the operational account mapped to it
- transition: propose_manual
gate: company_access
reason: you have no access to a company on a line
fix: ask the owner (GL-60)
- transition: propose_cross_company
gate: ic_accounts_mapped
reason: no ic_receivable or ic_payable account for the pair named
fix: 'Account map: map them per partner company'
- transition: propose_cross_company
gate: ic_periods_open
reason: the period is closed in one of the companies, named
fix: date it in a period open in both
- transition: propose_audit_adjustment
gate: year_open
reason: the financial year is closed
fix: post a prior-period correction in the current year
- transition: approve_manual
gate: second_person
reason: you proposed it, and the owner asks for a second person (always, for an audit adjustment)
fix: ask another accountant
- transition: approve_manual
gate: period_open
reason: its period closed while it waited
fix: reject it and propose it in an open period
- transition: reverse_journal
gate: not_reversed
reason: already reversed by the journal named
fix: none needed
- transition: reverse_journal
gate: no_pending_reversal
reason: an auto-reversal of this journal is pending for the next period
fix: open the next period; the reversal posts itself
- transition: post_pending_reversal
gate: next_period_open
reason: the period of the reversal date is not open yet
fix: 'Periods: open it (the reversal posts then)'
- entity: period
states:
- open
- closed
transitions:
- key: open_periods
name: Open the year's periods
from: none
to: open
by: accountant
gate: the company's fiscal year is opened (GL-13)
effect: one period per month of the year, period_id YYYY-MM, per company; pending reversals dated in them posted (GL-24)
- key: reconcile_receivable
name: Reconcile the receivable control account
from: open
to: open
by: bookkeeper
effect: the breaks against payments' settlement recorded as at the period end (GL-08)
- key: reconcile_payable
name: Reconcile the payables control account
from: open
to: open
by: bookkeeper
gate: purchasing is installed
effect: the breaks against purchasing's GET /bills/open recorded as at the period end (GL-08, GL-36)
- key: reconcile_assets
name: Reconcile the asset accounts
from: open
to: open
by: accountant
gate: fixed-assets is installed
effect: the breaks per asset class and ledger against fixed-assets' register (GL-39)
- key: run_recurring_entries
name: Run the recurring entries
from: open
to: open
by: accountant
gate: the period is open
effect: one journal per due approved template; twice posts once (GL-45)
- key: run_accruals
name: Release accruals and deferrals
from: open
to: open
by: accountant
gate: the period is open
effect: one journal per active registration; twice posts once (GL-49)
- key: run_fx_revaluation_balances
name: Revalue foreign balances
from: open
to: open
by: accountant
gate: revaluation is on and a closing rate is in force on the period end for every currency held
effect: per ledger, an unrealised gain or loss journal by the ledger's method, reversing next period (GL-09)
- key: run_fx_revaluation_open_items
name: Revalue foreign open items
from: open
to: open
by: accountant
gate: revaluation is on; a closing rate for every open item's currency
effect: per ledger, one journal of item lines on the control accounts, reversing next period (GL-48)
- key: match_intercompany
name: Match intercompany
from: open
to: open
by: accountant
gate: the company has a partner company with intercompany lines
effect: the pair's match result as at the period end (GL-52)
- key: accept_ic_difference
name: Accept an intercompany difference
from: open
to: open
by: accountant
requires: reason
gate: the break is above tolerance and the accepter is not who posted the difference
effect: recorded beside the break; consolidation reads it
- key: resplit_open_items
name: Re-split open items by profit centre
from: open
to: open
by: system
gate: document splitting is on for the company's year
effect: one adjustment journal on the control accounts, reversing next period (GL-25)
- key: close_period
name: Close the month
from: open
to: closed
by: accountant
gate: readiness met
effect: operational postings dated in it refused for the company; statements materialised (GL-21)
- key: materialise_statements
name: Materialise the statements
from: closed
to: closed
by: system
effect: keyed (report, company, ledger, period); a second run changes nothing
- key: reopen_period
name: Reopen the month
from: closed
to: open
by: accountant
requires: reason
approval: true
gate: the year is not closed; the reopener is not who closed it where the owner asks for it
effect: recorded; the materialised statements and the close sign-off marked stale
readiness:
- transition: close_period
gate: prior_closed
reason: the previous period is still open
fix: close it first
- transition: close_period
gate: no_proposed
reason: manual journals dated in the period are waiting for approval
fix: approve or reject them (GL-11)
- transition: close_period
gate: receivable_reconciled
reason: the receivable control account has breaks as at the period end
fix: 'Reconcile: work the breaks or accept the difference with a reason'
- transition: close_period
gate: payable_reconciled
reason: the payables control account has breaks against purchasing's open bills as at the period end
fix: 'Reconcile: work the breaks or accept the difference with a reason (only when purchasing is installed)'
- transition: close_period
gate: revalued
reason: foreign balances or open items are not revalued at the period end in a ledger, named
fix: 'Currencies: revalue (or revaluation is off)'
- transition: close_period
gate: recurring_posted
reason: approved recurring entries due in the period are not posted
fix: 'Recurring: run them (GL-45)'
- transition: close_period
gate: accruals_posted
reason: active accruals or deferrals have no release in the period
fix: 'Accruals: run them (GL-49)'
- transition: close_period
gate: allocations_posted
reason: approved allocation cycles have no posted run for the period
fix: 'Allocations: post them (GL-46), or retire the cycle'
- transition: close_period
gate: depreciation_posted
reason: fixed-assets has not posted the period's depreciation in a ledger (its readiness named)
fix: run depreciation in fixed-assets (only when it is installed)
- transition: close_period
gate: ic_matched
reason: intercompany breaks above tolerance and not accepted, per partner
fix: 'Intercompany: fix or accept them (ic_match_required refuse; warn lists them)'
- transition: close_period
gate: resplit_done
reason: open items are not re-split by profit centre as at the period end
fix: the system runs it as a close task (splitting on only)
- transition: close_period
gate: pending_reversals_posted
reason: reversals due on the period's first day are not posted
fix: 'a platform fault if the period is open: report it'
- transition: close_period
gate: carry_forward_current
reason: the opening balances of this year are older than a posting into the previous year
fix: the system re-derives them (GL-23)
- transition: close_period
gate: close_signed_off
reason: the close run is not signed off by a second person
fix: 'Close cockpit: finish the tasks and ask another accountant to sign (close_cockpit on)'
- transition: close_period
gate: trial_balance_zero
reason: the trial balance does not net to zero, in a ledger or a segment
fix: 'a platform fault: stop and report it'
- transition: reopen_period
gate: year_open
reason: the financial year is closed
fix: post a correction in the current year
- transition: reopen_period
gate: second_person
reason: you closed it, and the owner asks for a second person
fix: ask another accountant
- transition: accept_ic_difference
gate: second_person
reason: you posted the difference you are accepting
fix: ask another accountant
- entity: fiscal_year
states:
- open
- closed
transitions:
- key: open_year
name: Open the financial year
from: none
to: open
by: accountant
gate: the start month follows the company's previous year end (financial_year_end_month)
effect: twelve periods opened for the company; the carry-forward from the previous year derived (GL-23)
- key: derive_carry_forward
name: Derive the carry-forward
from: open
to: open
by: system
gate: the previous year has postings
effect: opening balances per ledger, account, profit centre, segment and partner company; P&L to retained earnings; re-derivation writes the same rows
- key: close_year
name: Close the year
from: open
to: closed
by: accountant
gate: every period of the year closed; no audit adjustment waiting; retained_earnings mapped
effect: closed to adjustments; the carry-forward to the next year derived for every ledger; the next year opened if it is not; no closing journal
readiness:
- transition: close_year
gate: periods_closed
reason: 'periods of the year are still open: named'
fix: close them (GL-07)
- transition: close_year
gate: no_proposed_adjustment
reason: an audit adjustment is waiting for approval
fix: approve or reject it (GL-56)
- transition: close_year
gate: retained_earnings_mapped
reason: no retained_earnings account in the map on the year's last day
fix: Account map (GL-12)
- entity: recurring_journal
states:
- proposed
- approved
- rejected
- ended
terminal:
- rejected
- ended
transitions:
- key: propose_recurring
name: Propose a recurring entry
from: none
to: proposed
by: accountant
gate: balanced; company, ledger and accounts valid; first and last period within open or future years
effect: version 1, proposer recorded (email and sub)
- key: approve_recurring
name: Approve the recurring entry
from: proposed
to: approved
by: accountant
approval: true
gate: the approver is not the proposer (email AND sub) where manual_journal_approval asks for it
effect: runs from its first period
- key: reject_recurring
name: Reject the recurring entry
from: proposed
to: rejected
by: accountant
requires: reason
- key: post_recurring_period
name: Post the period's entry
from: approved
to: approved
by:
- accountant
- system
gate: due in the period; the period open; not posted for it
effect: a journal keyed derived_id('recurring', recurring_id, version, period_id), through the posting use case
- key: revise_recurring
name: Revise the recurring entry
from: approved
to: ended
by: accountant
requires: reason
effect: this version ends at the change; version + 1 is proposed and needs approval
- key: end_recurring
name: End the recurring entry
from: approved
to: ended
by: accountant
requires: reason
effect: no period after the end posts; posted journals untouched
readiness:
- transition: approve_recurring
gate: second_person
reason: you proposed it, and the owner asks for a second person
fix: ask another accountant
- transition: post_recurring_period
gate: period_open
reason: the period is closed
fix: post it in an open period by revising the template, or reopen
- entity: accrual
states:
- proposed
- active
- rejected
- terminated
- completed
terminal:
- rejected
- terminated
- completed
transitions:
- key: register_accrual
name: Register an accrual or deferral
from: none
to: proposed
by: accountant
gate: amount above zero and within NUMERIC(18,2); start period not after end; accounts valid in the company's chart
effect: the schedule computed per period (straight-line or daily), the last period taking the remainder
- key: approve_accrual
name: Approve the registration
from: proposed
to: active
by: accountant
approval: true
gate: the approver is not the registrant (email AND sub) above manual_journal_threshold
effect: released from its start period
- key: reject_accrual
name: Reject the registration
from: proposed
to: rejected
by: accountant
requires: reason
- key: release_accrual_period
name: Release the period's share
from: active
to: active
by:
- accountant
- system
gate: the period open and in the schedule; not released for it
effect: a journal keyed derived_id('accrual', accrual_id, period_id)
- key: complete_accrual
name: Complete the registration
from: active
to: completed
by: system
gate: every period of the schedule released
effect: released total equals the amount to the penny
- key: terminate_accrual
name: Terminate early
from: active
to: terminated
by: accountant
requires: reason
gate: the termination period open
effect: the remainder released in the termination period; nothing after
readiness:
- transition: approve_accrual
gate: second_person
reason: you registered it, and it is above the threshold
fix: ask another accountant
- transition: release_accrual_period
gate: period_open
reason: the period is closed
fix: release it in the next open period by terminating and re-registering, or reopen
- entity: allocation_cycle
states:
- draft
- approved
- retired
terminal:
- retired
transitions:
- key: define_allocation_cycle
name: Define an allocation cycle
from: none
to: draft
by: controller
gate: every segment has senders, receivers and a rule; receivers valid; percentages total 100 where the rule is percentages
effect: version recorded with its author (email and sub)
- key: approve_allocation_cycle
name: Approve the cycle
from: draft
to: approved
by: accountant
approval: true
gate: the approver is not the author (email AND sub) where allocation_cycle_approval is on
effect: runnable from its valid_from period
- key: retire_allocation_cycle
name: Retire the cycle
from:
- draft
- approved
to: retired
by: accountant
requires: reason
effect: no new run; posted runs untouched; a change is a new cycle version
readiness:
- transition: approve_allocation_cycle
gate: second_person
reason: you wrote the cycle, and the owner asks for a second person
fix: ask another accountant
- entity: allocation_run
states:
- simulated
- posted
- reversed
terminal:
- reversed
transitions:
- key: simulate_allocation
name: Simulate the cycle
from: none
to: simulated
by: controller
gate: the cycle approved; the period open; key figures recorded for every receiver the rules need
effect: senders, receivers and amounts computed and shown with a digest of the sender balances; nothing posted
- key: post_allocation
name: Post the allocation
from: simulated
to: posted
by:
- controller
- accountant
gate: the simulation is current (sender digest unchanged); no posted run for the cycle, period and ledger; the period open
effect: one journal per segment keyed derived_id('allocation', cycle_id, period_id, ledger_id, run_no, segment); key figures used are frozen
- key: reverse_allocation
name: Reverse the allocation run
from: posted
to: reversed
by: accountant
requires: reason
gate: the period open
effect: mirror journals of every segment; the key figures unfrozen; run_no + 1 may post
readiness:
- transition: simulate_allocation
gate: key_figures_present
reason: a receiver has no key figure value for the period, named
fix: 'Allocations: record it (GL-47)'
- transition: post_allocation
gate: simulation_current
reason: postings on the senders since the simulation changed their balance
fix: simulate again
- transition: post_allocation
gate: prior_run_reversed
reason: a run of this cycle is posted for the period and ledger
fix: reverse it first
- transition: reverse_allocation
gate: period_open
reason: the period is closed
fix: reopen it, or leave the run
- entity: statement_version
states:
- draft
- published
- superseded
terminal:
- superseded
transitions:
- key: draft_statement_version
name: Draft a statement version
from: none
to: draft
by: accountant
effect: nodes and account assignments (ranges, group accounts, debit/credit-dependent); kind balance_sheet_pl or cash_flow
- key: publish_statement_version
name: Publish the version
from: draft
to: published
by: accountant
gate: fsv_complete
effect: reports may run on it; it is never edited again
- key: supersede_statement_version
name: Supersede the version
from: published
to: superseded
by: accountant
effect: version_no + 1 drafted from it; reports already run name the old version
readiness:
- transition: publish_statement_version
gate: fsv_complete
reason: accounts with a balance unassigned or assigned twice, named
fix: assign each exactly once
- entity: budget
states:
- draft
- submitted
- approved
- superseded
terminal:
- superseded
transitions:
- key: draft_budget
name: Draft a budget
from: none
to: draft
by: controller
gate: company, ledger and fiscal year exist
effect: lines by account, profit centre, cost centre and period, typed or imported
- key: submit_budget
name: Submit the budget
from: draft
to: submitted
by: controller
gate: every amount within NUMERIC(18,2)
effect: submitter recorded (email and sub)
- key: approve_budget
name: Approve the budget
from: submitted
to: approved
by: accountant
approval: true
gate: the approver is not the submitter (email AND sub)
effect: frozen; plan/actual reads it
- key: return_budget
name: Return the budget
from: submitted
to: draft
by: accountant
requires: reason
- key: revise_budget
name: Revise the budget
from: approved
to: superseded
by: controller
requires: reason
effect: version + 1 drafted from it
readiness:
- transition: approve_budget
gate: second_person
reason: you submitted it
fix: ask another accountant
- entity: close_run
states:
- in_progress
- ready
- signed
transitions:
- key: start_close_run
name: Start the close run
from: none
to: in_progress
by: accountant
gate: a task list exists for the company and the kind (month or year end)
effect: tasks instantiated with owner, due business day and dependencies
- key: complete_close_task
name: Complete a manual task
from: in_progress
to: in_progress
by:
- bookkeeper
- controller
- accountant
gate: the tasks it depends on are done
effect: who, when and the note or evidence key recorded
- key: skip_close_task
name: Mark a task not applicable
from: in_progress
to: in_progress
by: accountant
requires: reason
- key: mark_close_ready
name: All tasks done
from: in_progress
to: ready
by: system
gate: every task done or not applicable; every system task's gate met on read
effect: the trial balance digest taken
- key: sign_off_close
name: Sign off the close
from: ready
to: signed
by: accountant
approval: true
gate: the signer is not the run's starter nor a task's completer (email AND sub); the digest unchanged
effect: who, when and the digest recorded; close_period may proceed
- key: invalidate_close_signoff
name: The sign-off goes stale
from:
- ready
- signed
to: in_progress
by: system
gate: a posting landed in the period after the digest
effect: the run shows what changed and asks for a new sign-off
readiness:
- transition: complete_close_task
gate: dependencies_done
reason: a task it depends on is open, named
fix: finish that first
- transition: sign_off_close
gate: second_person
reason: you started the run or completed a task in it
fix: ask another accountant
- entity: migration_run
states:
- loaded
- reconciled
- signed
- discarded
terminal:
- signed
- discarded
transitions:
- key: load_gl_from_odoo
name: Load the Odoo books
from: none
to: loaded
by: system
gate: the doors decided; business-partner and tax mapped every partner and tax the lines use
effect: companies, accounts, account map, fiscal years, periods (closed up to Odoo's lock date), rates, recurring entries, deferrals and posted journals written read-only, keyed on the Odoo record; a re-run changes nothing
- key: reconcile_gl_migration
name: Reconcile against Odoo
from: loaded
to: reconciled
by: system
effect: every migration.odoo.reconcile check computed on both sides, with the difference
- key: explain_gl_difference
name: Explain each difference
from: reconciled
to: reconciled
by: accountant
requires: reason
gate: the check differs and may be explained
effect: the explanation recorded beside the check
- key: reload_gl_migration
name: Load again after a fix
from:
- loaded
- reconciled
to: loaded
by: system
gate: not signed
effect: the same keys; explanations on unchanged checks kept
- key: sign_gl_reconciliation
name: Sign the reconciliation
from: reconciled
to: signed
by: owner
approval: true
gate: every check equal, or explained where allowed
effect: who, when and checksums recorded; postings from the composed modules accepted from the day after cut-over
- key: discard_gl_migration
name: Discard the run
from:
- loaded
- reconciled
to: discarded
by: owner
requires: reason
effect: the run's rows removed; nothing was posted on them
readiness:
- transition: load_gl_from_odoo
gate: doors_decided
reason: a migration door is undecided
fix: 'Migration: answer it'
- transition: load_gl_from_odoo
gate: partners_mapped
reason: Odoo partners on receivable or payable lines have no bp_id
fix: run the business-partner migration first
- transition: load_gl_from_odoo
gate: taxes_mapped
reason: Odoo taxes on tax lines have no tax_code
fix: run the tax migration first
- transition: sign_gl_reconciliation
gate: zero_unexplained
reason: 'checks neither equal nor explained: named'
fix: explain each, or fix and reload
- transition: sign_gl_reconciliation
gate: must_equal
reason: a check that must be equal differs (the trial balance per company, account and period)
fix: fix the mapping and reload; it cannot be explained away