Architecture and Structure Comfac ERPN Loc
Architecture & Structure Reference — Comfac Philippine Localization
Comfac Philippine Localization (CPL) is a Frappe/ERPNext custom app providing BIR (Bureau of Internal Revenue) statutory compliance for Philippine businesses. Built by Ambibuzz Technologies LLP as part of the BetterGov.ph Civic Tech Initiative.
Core principle: Zero modifications to ERPNext core. All customizations use Frappe's fixture, hooks, and JavaScript injection mechanisms — keeping the app upgrade-safe.
Directory Structure
phlocalization/ ├── bureau_of_internal_revenue/ # Frappe app root (app_name) │ ├── __init__.py │ ├── hooks.py # Frappe hook definitions │ ├── modules.txt # Declares module: "Bureau of Internal Revenue" │ ├── patches.txt # Migration patches (pre/post model sync) │ │ │ ├── bureau_of_internal_revenue/ # Module package (matches module name) │ │ ├── __init__.py │ │ └── report/ │ │ ├── balance_sheet_bir/ # Script Report: Balance Sheet BIR │ │ │ ├── balance_sheet_bir.json # Report DocType definition │ │ │ ├── balance_sheet_bir.py # Server-side report logic │ │ │ ├── balance_sheet_bir.js # Client-side filters & formatting │ │ │ ├── balance_sheet_bir.html # HTML template (includes ERPNext base) │ │ │ └── test_balance_sheet_bir.py │ │ └── balance_sheet_schedule_bir/ # Script Report: Schedule-grouped variant │ │ ├── balance_sheet_schedule_bir.json │ │ ├── balance_sheet_schedule_bir.py │ │ ├── balance_sheet_schedule_bir.js │ │ ├── balance_sheet_schedule_bir.html │ │ └── test_balance_sheet_schedule_bir.py │ │ │ ├── config/ # App configuration (empty) │ ├── fixtures/ │ │ └── custom_field.json # Custom "Schedule" field on Account DocType │ ├── public/ │ │ └── js/ │ │ └── account.js # Client script injected into Account form │ └── templates/pages/ # Web templates (empty) │ ├── docs/ │ ├── Project Requirements Document.md │ └── Roadmap.md ├── pyproject.toml # Python packaging & Frappe dependency config └── README.md
Component Details
1. hooks.py — Frappe Integration Point
The central configuration file that registers the app with Frappe.
| Hook | Value | Purpose |
|---|---|---|
app_name |
"bureau_of_internal_revenue" |
Internal identifier; must match the top-level directory name |
app_title |
"Bureau of Internal Revenue" |
Display name in Frappe desk |
doctype_js |
{"Account": "public/js/account.js"} |
Injects custom JS into the Account form |
fixtures |
Custom Field where module = "Bureau of Internal Revenue" | Auto-imports the Schedule field on bench migrate
|
Commented-out hooks available for future use:
before_install/after_install— setup scriptsscheduler_events— cron-like scheduled tasksdoc_events— hooks on document save/cancel/trashoverride_whitelisted_methods— replace API endpointspermission_query_conditions/has_permission— custom permissions
2. modules.txt
Single line: Bureau of Internal Revenue. Registers this as a Frappe module, which is required for reports, doctypes, and fixtures to be associated with the app.
3. patches.txt
Contains [pre_model_sync] and [post_model_sync] sections (currently empty). This is where database migration scripts are registered when schema changes are needed.
4. fixtures/custom_field.json — Schedule Field
Adds a "Schedule" dropdown field to the standard Account DocType:
- Position: After
parent_account - Field type: Select
- Options: SCHED 1 through SCHED 23
- Visibility: Controlled by
account.js— only shown whenis_groupis checked
This maps Chart of Accounts groups to BIR schedule categories, enabling schedule-based financial reporting without modifying the Account DocType source.
5. public/js/account.js — Client-Side Logic
Injected into the Account form via doctype_js hook:
- Shows the Schedule field only when
is_groupis true - Clears the schedule value when
is_groupis unchecked - Triggers on form
refreshandis_groupfield change
6. Balance Sheet BIR Report
Type: Frappe Script Report (ref doctype: GL Entry)
Server (.py):
- Reuses ERPNext's
erpnext.accounts.report.financial_statementsfunctions (get_period_list,get_data,get_columns) - Renames standard labels to BIR terminology (e.g., "Total Asset (Debit)" → "Total Current Assets (Debit)")
- Adds level-based filtering (show hierarchy levels 1–4 or All)
- Calculates provisional Profit/Loss row
- Checks for unclosed prior fiscal years
- Generates chart data (bar or line)
Client (.js):
- Extends
erpnext.financial_statementsreport class - Adds filters:
selected_view,accumulated_values,include_default_book_entries,level - Custom formatter hides numeric values on indent-0 (top-level) rows
Template (.html):
- Includes ERPNext's
accounts/report/financial_statements.html
7. Balance Sheet Schedule BIR Report
Type: Frappe Script Report (ref doctype: GL Entry)
Key difference from Balance Sheet BIR: Groups accounts by their assigned BIR Schedule (SCHED 1–23) and inserts subtotal rows per schedule.
Server (.py):
- Queries all Accounts that have a non-empty
schedulefield - Iterates through standard balance sheet data, grouping rows under their schedule
- Inserts "Total" subtotal rows at the end of each schedule group
- Adds a "Schedule" column at position 0
- Removes
cost_centerandprojectfilters (not relevant for BIR schedules) - No chart generation
Client (.js):
- Filters out
cost_centerandprojectfrom the standard filter set - Same indent-0 numeric hiding as the Balance Sheet BIR
Key Elements for Frappe Cloud / Frappe Instance Deployment
These are the files and conventions that make this app installable on any Frappe/ERPNext instance (including Frappe Cloud):
Required Files
| File | Role |
|---|---|
pyproject.toml |
Declares the Python package, build system (flit_core), and frappe~=15.95.0 dependency. The [tool.bench.frappe-dependencies] section tells Bench which Frappe version is required.
|
bureau_of_internal_revenue/__init__.py |
Makes the top-level directory a Python package (required for bench get-app / pip install)
|
bureau_of_internal_revenue/hooks.py |
The single most critical file. Frappe reads this to discover app_name, app_title, fixtures, JS includes, and all integration hooks. Without this, Frappe doesn't recognize the app.
|
bureau_of_internal_revenue/modules.txt |
Registers the module name. Without this, reports and doctypes won't be associated with the app. |
bureau_of_internal_revenue/patches.txt |
Required by Frappe's migration system even if empty. Lists migration scripts to run during bench migrate.
|
Deployment Mechanism
# Standard Bench installation flow: bench get-app https://github.com/xunema/phlocalization bench --site <site-name> install-app bureau_of_internal_revenue bench --site <site-name> migrate # applies fixtures + patches
What happens on install/migrate:
- Frappe reads
hooks.pyto register the app modules.txtcreates the module record in the databasepatches.txtruns any pending migrationsfixturesarray inhooks.pytriggers import ofcustom_field.json, creating the Schedule field on Accountdoctype_jsregistersaccount.jsfor injection into Account forms- Report JSON files (
balance_sheet_bir.json, etc.) are loaded as Script Report records
Frappe Cloud Specifics
For Frappe Cloud deployment, the repo needs:
- A valid
pyproject.tomlwith[tool.bench.frappe-dependencies]— present - The app directory name must match
app_nameinhooks.py— matches (bureau_of_internal_revenue) - The repo must be a valid Python package (has
__init__.py) — present - Dependencies on
frappe(and optionallyerpnext) declared — declared (frappe~=15.95.0)
Note: The pyproject.toml still has placeholder values (name = "your_app_name", generic author/description) that should be updated to match the actual app identity for proper Frappe Cloud listing.
Fixture System
The fixture in hooks.py:
fixtures = [
{
"doctype": "Custom Field",
"filters": [["Custom Field", "module", "=", "Bureau of Internal Revenue"]]
}
]
This means:
bench export-fixturesexports all Custom Fields belonging to this module →fixtures/custom_field.jsonbench migrateon a fresh site imports them back, creating the Schedule field automatically- This is the upgrade-safe pattern for extending standard DocTypes without touching core
Data Flow Summary
Account Form (Desk UI) ↓ hooks.py doctype_js ↓ account.js toggles Schedule field visibility ↓ Account DocType + Schedule custom field (from fixtures) ↓ GL Entry data (standard ERPNext) ↓ balance_sheet_bir.py → Standard BIR Balance Sheet (level-filtered) balance_sheet_schedule_bir.py → Schedule-grouped Balance Sheet (SCHED 1-23 subtotals)
Technology Stack
| Layer | Technology |
|---|---|
| Framework | Frappe v15 |
| ERP | ERPNext v15 |
| Language | Python >= 3.10 |
| Build | flit_core |
| Formatter | Black (99 chars), isort (Black-compatible) |
| License | MIT |