Phase 4 — Per-Branch Journals
The structural fix that ends the date/sequence collision class for good. Implemented as the baywaters_branch_journals Odoo module at common/baywaters_branch_journals/.
Design decisions
What gets replicated, what doesn't
| Journal | Replicated per branch? | Why |
|---|---|---|
Customer Invoices (INV) | ✅ yes | Each branch has its own invoice numbering; users want per-branch sales reports |
Vendor Bills (BILL) | ✅ yes | Same — per-branch purchasing |
Miscellaneous (MISC) | ✅ yes | This is the journal that originally caused the validation errors |
Cash (CSH) | ✅ yes | Each branch counts its own till; needs independent sequence |
Bank (BNK1, BNK2) | ❌ no — stays at parent | One physical AUB account per fuel-station chain; all branches deposit/draw from the same account |
Inventory Valuation (STJ) | already replicated per company (legacy) | Unchanged |
11 branches × 4 templates = 44 new journals created on install.
Branch code prefixes
Source of truth: BRANCH_CODE_MAP in hooks.py.
| Company | Prefix |
|---|---|
| Shawarma Shacks (Petron) | SS |
| Jamaican Patties (Petron) | JP |
| Miguelitos Ice Cream (Petron) | MI |
| Auntie Annes (Petron) | AA |
| Bibingkinitan (Caltex) | BB |
| Jamaican Patties (Caltex) | JC |
| Henlin (Caltex) | HN |
| Potato Corner (Caltex) | PC |
| Sam's Everything on Stick (Caltex) | SC |
| Buko Juan (Petron) | BJ |
| Sams Everything on Sticks (Petron) | SE |
:::caution Why 2-char prefixes
Odoo 17 caps account.journal.code at varchar(5) and silently truncates longer values at write time. With a 3-char suffix (INV/BIL/MSC/CSH), the prefix has to fit in 2 chars. The hook now raises ValueError if computed code exceeds 5 chars to prevent silent corruption.
:::
Default debit/credit accounts
The Baywaters CoA is shared across all 12 companies (only one set of 142 accounts, owned by company_id=1). New branch journals reference the same accounts as the parent's journals:
| Journal | Default account |
|---|---|
<XX>INV | 430400 Sales/Revenues |
<XX>BIL | 620000 Admin Expense |
<XX>MSC | (none — Odoo allows blank) |
<XX>CSH | 100101 Cash |
Per-branch differentiation in reporting goes through analytic accounts, not separate GL accounts. See Multi-Branch Setup.
Naming pattern after install
Once a branch posts its first entry, Odoo auto-seeds the move-name pattern from the journal code field:
PCINV/2026/05/0001
PCINV/2026/05/0002
SSMSC/2026/05/0001
...
Sequences are independent per branch + per journal — no more cross-branch collisions.
Why a post_init_hook instead of XML data
The new journals depend on:
- Existing child companies — created by Odoo admins, not this module
- Existing CoA accounts — looked up by code at runtime
XML data records would require either:
- Hardcoded company/account database IDs (fragile across environments), or
- Complex
evalexpressions doing the same lookups
The Python hook is shorter, more readable, and idempotent (safe to re-run).
Install
cd /Users/jsalinga/odoo/odoo17
python odoo-bin -c odoo.conf -d rts_baywaters \
-i baywaters_branch_journals --stop-after-init
Watch the log for Created lines (44 expected) or skip lines if re-running.
Verification queries
-- Count: should be 44 (11 branches × 4 templates)
SELECT COUNT(*)
FROM account_journal
WHERE code ~ '^[A-Z]{2}(INV|BIL|MSC|CSH)$';
-- Per-branch breakdown
SELECT c.name AS branch, j.code, j.type, a.code AS default_account
FROM account_journal j
JOIN res_company c ON c.id = j.company_id
LEFT JOIN account_account a ON a.id = j.default_account_id
WHERE c.parent_id IS NOT NULL
AND j.code ~ '^[A-Z]{2}(INV|BIL|MSC|CSH)$'
ORDER BY c.name, j.code;
Smoke test after install
For each branch, the accounting team should:
- Switch active company to the branch
- Accounting → Customer Invoices → New — verify
<XX>INVis the default journal - Save as draft and check the entry will be numbered
<XX>INV/YYYY/MM/0001on post - Repeat for one bill, one MISC entry, one cash receipt
Cutover
Installing the module is additive — old journals stay untouched, new ones appear alongside. The user-facing switch (production install + branch user defaults + accounting handoff) is documented separately as Phase 5 — Cutover.
Adding a new branch later
- Create the new child company in Odoo
- Edit
hooks.py, add the new branch toBRANCH_CODE_MAP - Update the module:
Existing branches' journals are detected and skipped — only the new branch's 4 journals get created.python odoo-bin -c odoo.conf -d rts_baywaters \-u baywaters_branch_journals --stop-after-init
Rollback
Uninstall preserves data by default. To remove the journals, archive them
manually (active = false) — don't delete if any posted entries reference them.
-- Find branch journals to archive (if reverting)
SELECT id, code, company_id, active
FROM account_journal
WHERE code ~ '^[A-Z]{2}(INV|BIL|MSC|CSH)$';