{"id":"ce7f470b-5f6e-4661-93ff-1e64fa6cc58a","entityType":"agent","slug":"clawhub-mailnike-erpclaw","name":"Erpclaw Publish 4.15.0 20260816 103527","canonicalUrl":"https://www.xpersona.co/agent/clawhub-mailnike-erpclaw","canonicalPath":"/agent/clawhub-mailnike-erpclaw","generatedAt":"2026-10-09T20:33:40.263Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-10-09T04:52:01.050Z","emptyReason":null},"description":"AI-native ERP system. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and financial reporting (including P&L / trial balance / spend grouped by department, project, cost center, location, or fund). 496 actions across 14 domains, 45 optional expansion modules (user-approved install from GitHub). Double-entry GL, immutable audit trail, US GAAP compliant. Licensed under GNU GPL v3 (the marketplace \"MIT-0\" badge is a ClawHub platform default; the LICENSE.txt in the bundle is GPL v3). Skill: Erpclaw Publish 4.15.0 20260816 103527 Owner: mailnike Summary: AI-native ERP system. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and financial reporting (including P&L / trial balance / spend grouped by department, project, cost center, location, or fund). 496 actions across 14 domains, 45 optional expansion modu","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 4.8K downloads reported by the source. Last updated 10/9/2026.","installCommand":"clawhub skill install s17ee5y8kxe1zfx1t7te9wcgzx83j215:erpclaw","sourceUrl":"https://clawhub.ai/mailnike/erpclaw","homepage":"https://clawhub.ai/mailnike/skills/erpclaw","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/mailnike/erpclaw","kind":"source"},{"label":"Homepage","url":"https://clawhub.ai/mailnike/skills/erpclaw","kind":"homepage"}],"safetyScore":84,"overallRank":62,"popularityScore":61,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"AI-native ERP system. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidatio"},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-10-09T04:52:01.050Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T04:52:01.050Z","emptyReason":null},"stars":null,"forks":null,"downloads":4783,"packageName":null,"latestVersion":"4.15.0","tractionLabel":"4.8K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-10-09T04:52:01.049Z","emptyReason":null},"lastUpdatedAt":"2026-10-09T04:52:01.050Z","lastCrawledAt":"2026-10-09T04:52:01.049Z","lastIndexedAt":null,"nextCrawlAt":"2026-10-10T04:52:01.049Z","lastVerifiedAt":null,"highlights":[{"version":"4.15.0","createdAt":"2026-08-16T16:00:47.188Z","changelog":"Books-integrity fixes (invoice cancel releases allocations; party balances no longer double-counted; write-offs with real accounting; idempotent intercompany eliminations with surplus correction; billing-period honesty), database seam with PostgreSQL-verified module installs, retired one-sided actions now steer to the correct flows, updated agent guidance. Nine end-to-end business scenarios pass as the release gate.","fileCount":368,"zipByteSize":2872884},{"version":"4.14.0","createdAt":"2026-07-27T19:42:13.328Z","changelog":"Complete rating engine (all 7 rate plan types), crash-safe billing runs with resume, always-on AR/AP ledger reconciliation invariant, hardened money validation","fileCount":138,"zipByteSize":875755},{"version":"4.13.0","createdAt":"2026-07-24T05:12:18.129Z","changelog":"Foundation hardening: landed costs reprice inventory valuation with a new stock-GL reconciliation invariant, open-order receiving/issue guards, invoice-time price resolution, material-request to PO, payment deductions, retro-pay in salary slips, HR government-ID protection.","fileCount":136,"zipByteSize":841659},{"version":"4.12.2","createdAt":"2026-07-06T06:43:56.940Z","changelog":"Documentation hygiene release: internal design-document references in migration docstrings and the table provenance manifest replaced with stable opaque provenance keys and neutral descriptions. No functional change to any migration, action, or schema.","fileCount":134,"zipByteSize":816515},{"version":"4.12.1","createdAt":"2026-07-06T05:12:41.994Z","changelog":"M33 feature-completion: upgrades now report the correct version afterward (version-row heal); drug-interaction checking returns an honest not-configured answer instead of a false all-clear until reference data is loaded; construction progress bills produce full G703 continuation sheets that foot to the cent; education program requirements, CRM lead sources, and deeper Stripe/Shopify accounting sync (transfers, credit notes, fee details, payout transactions with honest reconciliation). Signed registry v69.","fileCount":134,"zipByteSize":817341},{"version":"4.12.0","createdAt":"2026-07-05T04:01:24.109Z","changelog":"M31 foundation-hygiene: 33 previously-unreachable actions wired into the router (upgrade path, custom fields, credit control); upgrades now apply pending schema migrations in the same confirmed step (ADR-0028); 3 dead tables removed (776 total); table provenance manifest + audit gates; shared integration lib with AES-256-GCM credential crypto. Signed registry v67.","fileCount":134,"zipByteSize":814902},{"version":"4.11.0","createdAt":"2026-07-02T02:37:31.231Z","changelog":"v4.11.0: GPL v3 license lands on ClawHub; Waves 0-2 since v4.1.6 - registry-driven validation, custom fields, migration runner, bank-statement import + matching, multi-dimensional GL, asset lifecycle, built-in CRM (contacts, pipelines, tasks, saved views, CSV import/export), inventory + warehouse depth (binding stock reservations, putaway rules, pick lists, item substitutes, subcontracting); runtime portability (ERPCLAW_HOME); signed registry v62","fileCount":129,"zipByteSize":791858},{"version":"4.8.0","createdAt":"2026-06-11T15:20:29.605Z","changelog":"v4.8.0: GPL v3 license; Wave 0+1 financials (registry-driven type CHECKs, custom-field runtime, migration runner, advance-payment GL routing); bank-statement import + reconciliation; multi-dimensional GL reporting; asset lifecycle (CWIP, impairment, revaluation); email/drip backbone; signed registry v58","fileCount":114,"zipByteSize":733614}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install s17ee5y8kxe1zfx1t7te9wcgzx83j215:erpclaw","setupComplexity":"low","setupSteps":["Setup complexity is classified as HIGH. You must provision dedicated cloud infrastructure or an isolated VM. Do not run this directly on your local workstation.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-09T20:33:40.260Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-mailnike-erpclaw/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-10-09T04:52:01.050Z","emptyReason":null},"readme":"Skill: Erpclaw Publish 4.15.0 20260816 103527\n\nOwner: mailnike\n\nSummary: AI-native ERP system. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and financial reporting (including P&L / trial balance / spend grouped by department, project, cost center, location, or fund). 496 actions across 14 domains, 45 optional expansion modules (user-approved install from GitHub). Double-entry GL, immutable audit trail, US GAAP compliant. Licensed under GNU GPL v3 (the marketplace \"MIT-0\" badge is a ClawHub platform default; the LICENSE.txt in the bundle is GPL v3).\n\nTags: latest:4.15.0\n\nVersion history:\n\nv4.15.0 | 2026-08-16T16:00:47.188Z | user\n\nBooks-integrity fixes (invoice cancel releases allocations; party balances no longer double-counted; write-offs with real accounting; idempotent intercompany eliminations with surplus correction; billing-period honesty), database seam with PostgreSQL-verified module installs, retired one-sided actions now steer to the correct flows, updated agent guidance. Nine end-to-end business scenarios pass as the release gate.\n\nv4.14.0 | 2026-07-27T19:42:13.328Z | user\n\nComplete rating engine (all 7 rate plan types), crash-safe billing runs with resume, always-on AR/AP ledger reconciliation invariant, hardened money validation\n\nv4.13.0 | 2026-07-24T05:12:18.129Z | user\n\nFoundation hardening: landed costs reprice inventory valuation with a new stock-GL reconciliation invariant, open-order receiving/issue guards, invoice-time price resolution, material-request to PO, payment deductions, retro-pay in salary slips, HR government-ID protection.\n\nv4.12.2 | 2026-07-06T06:43:56.940Z | user\n\nDocumentation hygiene release: internal design-document references in migration docstrings and the table provenance manifest replaced with stable opaque provenance keys and neutral descriptions. No functional change to any migration, action, or schema.\n\nv4.12.1 | 2026-07-06T05:12:41.994Z | user\n\nM33 feature-completion: upgrades now report the correct version afterward (version-row heal); drug-interaction checking returns an honest not-configured answer instead of a false all-clear until reference data is loaded; construction progress bills produce full G703 continuation sheets that foot to the cent; education program requirements, CRM lead sources, and deeper Stripe/Shopify accounting sync (transfers, credit notes, fee details, payout transactions with honest reconciliation). Signed registry v69.\n\nv4.12.0 | 2026-07-05T04:01:24.109Z | user\n\nM31 foundation-hygiene: 33 previously-unreachable actions wired into the router (upgrade path, custom fields, credit control); upgrades now apply pending schema migrations in the same confirmed step (ADR-0028); 3 dead tables removed (776 total); table provenance manifest + audit gates; shared integration lib with AES-256-GCM credential crypto. Signed registry v67.\n\nv4.11.0 | 2026-07-02T02:37:31.231Z | user\n\nv4.11.0: GPL v3 license lands on ClawHub; Waves 0-2 since v4.1.6 - registry-driven validation, custom fields, migration runner, bank-statement import + matching, multi-dimensional GL, asset lifecycle, built-in CRM (contacts, pipelines, tasks, saved views, CSV import/export), inventory + warehouse depth (binding stock reservations, putaway rules, pick lists, item substitutes, subcontracting); runtime portability (ERPCLAW_HOME); signed registry v62\n\nv4.8.0 | 2026-06-11T15:20:29.605Z | user\n\nv4.8.0: GPL v3 license; Wave 0+1 financials (registry-driven type CHECKs, custom-field runtime, migration runner, advance-payment GL routing); bank-statement import + reconciliation; multi-dimensional GL reporting; asset lifecycle (CWIP, impairment, revaluation); email/drip backbone; signed registry v58\n\nv4.5.0 | 2026-06-07T19:40:57.739Z | user\n\nv4.5.0: FINDING-008 resolve-item cascade (loose/plural item lookup); FINDING-009/010 stock-receipt GL + 3-way-match valuation (ADR-0014); FINDING-004 reply-jargon guidance; registry v51 re-signed.\n\nv4.4.0 | 2026-06-05T21:34:13.607Z | user\n\nv4.4.0 (catch-up from v4.1.6): AR/AP payments now clear invoices/bills; dedicated customer+supplier email/phone (migration 016); gateway voucher-type canonicalization. Bundles the GPL v3 re-license (4.2.0) and dunning/credit-limit (4.3.x).\n\nv4.3.1 | 2026-05-11T05:09:01.496Z | user\n\nv4.3.1 hotfix: re-add --description argparse flag (was removed as dupe in v4.3.0; add-dunning-level needs it).\n\nv4.3.0 | 2026-05-11T05:05:38.164Z | user\n\nv4.3.0: customer credit limit + dunning escalation (ROADMAP S1). 5 new actions in selling: check-credit-limit, place-customer-on-hold, add-dunning-level, run-dunning-cycle, list-dunning-runs. Invoice-submit credit policy hook enforces credit_limit + blocks on_hold/suspended customers. 17 new tests pass. Foundation 478 → 483 actions.\n\nv4.2.3 | 2026-05-11T01:17:38.722Z | user\n\nv4.2.3 internal hygiene: shared skip filters (prevents future manifest/walk drift), registry cache auto-refresh on foundation upgrade (removes false-positive integrity warnings), snapshot trim (removed tests/.github/.gitkeep — 187 → 90 files, matches what ClawHub actually ships). No behavioural changes for end users.\n\nv4.2.2 | 2026-05-10T16:08:54.285Z | user\n\nv4.2.2 fix-up: aligns module_manager.py install-time integrity walk with regen_module_manifests.py manifest skip set. Fixes 'extra files' integrity failure that affected v4.2.1 vertical installs. Tested end-to-end on OpenClaw 2026.5.7.\n\nv4.2.1 | 2026-05-10T16:04:01.846Z | user\n\nv4.2.1 fix-up release: aligns manifest with ClawHub-shipped file set (fixes install-time integrity failures), pushes vertical parser fixes to all avansaber/* public repos. Foundation 478 actions, 46 modules. Trust root d471:335b:0e4d:75ce. Tested on OpenClaw 2026.5.7.\n\nv4.2.0 | 2026-05-10T15:26:03.616Z | user\n\nv4.2.0: GPL v3 re-license + 4 vertical parser bug fixes (healthclaw, constructclaw, legalclaw, retailclaw). Foundation 478 actions, 46 modules. Trust root d471:335b:0e4d:75ce. Tested on OpenClaw 2026.5.7.\n\nv4.1.6 | 2026-05-05T00:30:52.281Z | auto\n\nerpclaw 4.1.6\n\n- Introduced digital signature functionality (new signing module and logging file).\n- Updated internal libraries and module registry to support signing capabilities.\n- Modified database query and module management scripts for integration with new features.\n- Documentation updates in SKILL.md and CHANGELOG.md.\n\nv4.1.5 | 2026-05-04T22:44:12.052Z | auto\n\n- Updated runtime gating policy wording for clarity: high-impact (\"materially change financial state\") actions require `--user-confirmed` flag; read-only actions are unaffected.\n- Version bump from 4.1.3 to 4.1.5.\n- Documentation refinements in SKILL.md and CHANGELOG.md.\n- Minor script and module registry updates for consistency.\n\nv4.1.3 | 2026-05-04T19:53:12.911Z | auto\n\n- Expanded from 467 to 475 actions, including new admin and credential management operations.\n- Added encrypted credential management: `set-credential`, `get-credential`, `list-credentials`, `delete-credential`, `migrate-credentials`.\n- Introduced cross-machine restore with `import-master-key-from-backup`.\n- Updated documentation to reflect new features and increased action count.\n- General internal script and module registry updates to support these enhancements.\n\nv4.1.2 | 2026-05-04T15:49:25.893Z | auto\n\nVersion 4.1.2\n\n- Introduces a runtime gate requiring a `--user-confirmed` flag on any action that materially changes financial state; the system rejects unflagged calls with a structured JSON error.\n- Read-only actions (verbs `list`, `get`, reports) no longer require user confirmation.\n- Documentation updated to describe the new runtime safety requirement.\n\nv4.1.1 | 2026-05-04T15:06:15.365Z | auto\n\n- Improved security documentation in SKILL.md (removed implementation details about file locations and encryption specifics).\n- Minor metadata and documentation clean-up for clarity and simplicity.\n- No core functional or API changes; all 467 ERP actions remain supported.\n- Internal scripts and module registry updated for maintainability.\n\nv4.1.0 | 2026-05-04T13:54:17.018Z | auto\n\n**4.1.0 introduces column-level encryption and OS keychain credential management.**\n\n- Sensitive database fields are now encrypted at the column level using AES-256-GCM.\n- Credentials are no longer stored in code or environment variables; new `set-credential` action manages them via OS keychain.\n- Security section and credential handling details updated in documentation.\n- Legacy bootstrap file removed; new credential and encryption modules added.\n- Various internal scripts updated to support encryption and keychain integration.\n\nv4.0.2 | 2026-05-04T06:29:43.279Z | auto\n\n**Scheduling behavior updated; no cron jobs registered by default.**\n\n- Removed default background job scheduling; jobs like recurring processing or invoice checks are now user-scheduled via the OpenClaw cron CLI if desired.\n- Updated documentation to reflect on-demand execution and new optional cron registration instructions.\n- Retained self-healing shared library feature and library sync details.\n- Minor metadata and documentation updates for clarity.\n\nv4.0.1 | 2026-05-04T05:12:34.448Z | auto\n\nerpclaw 4.0.1\n\n- Added extensive documentation on credential handling, data protection, module installation safety, and background automation.\n- Switched all scheduled jobs (cron) to announce runs in the OpenClaw activity feed by default.\n- Introduced automatic self-healing for the shared library on version mismatch; added instructions and environment variable to disable for sensitive deployments.\n- Added CHANGELOG.md and bootstrap logic (_bootstrap.py) to the setup library.\n- Minor updates to existing scripts and documentation for clarity and consistency.\n\nv4.0.0 | 2026-05-04T03:37:48.149Z | user\n\nMAJOR architectural split: erpclaw-os-engine moved to optional addon. Foundation now scan-clean by design (no dynamic-execution patterns). 28 user-facing actions renamed with os- prefix and live in addon. 12-step GL validation kept in foundation via erpclaw_lib.gl_invariants. Install via clawhub install erpclaw; for module generation, install erpclaw-os-engine separately via module_manager.py from GitHub.\n\nv3.5.1 | 2026-05-04T00:19:32.328Z | user\n\nMulti-currency Phase 1 (7 currencies USD/EUR/GBP/CAD/INR/SGD/AED, invoice currency must equal payment currency, no FX). Project framework refactor: STATUS / ROADMAP / ARCHITECTURE / server/README at repo root. GST IRN float-fix. Action-count reconciliation in registry.\n\nv3.5.0 | 2026-04-27T03:22:08.298Z | user\n\nv3.5.0: bin/erpclaw shim + module_manager publish path + Deep Integrations README (Stripe + Shopify) + 22 marketing site posts live\n\nv3.4.3 | 2026-04-02T17:01:25.791Z | user\n\nNo changes detected in this release.\n\n- Version number updated to 3.4.3, but no file or visible content changes from version 3.4.1.\n- All features, descriptions, and actions remain unchanged.\n\nv3.4.2 | 2026-03-23T05:18:46.438Z | user\n\n- Improved security: optional expansion modules are now only installed from GitHub with user approval.\n- Integration API keys (e.g., Stripe, Shopify) are passed via flags and stored only in the local DB; never kept in code or environment variables.\n- Updated auto-detection: always ask user to confirm detected business/industry before proceeding.\n- Slightly condensed skill description and tags for better clarity and privacy.\n- No changes to actions, scheduling, or base functionality.\n\nv3.4.1 | 2026-03-21T01:52:54.845Z | user\n\nFix: set PYTHONPATH for module init_db.py — vertical modules now install correctly\n\nv3.4.0 | 2026-03-21T01:29:13.854Z | user\n\nRemote registry: auto-fetch modules from GitHub. SKILL.md completeness fix. Stripe integration module. 778 tables, 3024 actions.\n\nv3.3.0 | 2026-03-20T21:57:08.081Z | user\n\nerpclaw 3.3.0 is a feature update from 3.2.0.\n\n- Version bump to 3.3.0 with updated metadata; no file changes detected.\n- No breaking changes; functionality and configuration remain consistent with previous release.\n\nv3.2.1 | 2026-03-20T04:41:09.895Z | user\n\n- Introduced three new modules: feature matrix (feature_matrix.py), in-module generator (in_module_generator.py), and research engine (research_engine.py).\n- These additions enhance the modularity and research capabilities of the system.\n- No other changes to existing features or documentation.\n\nv3.2.0 | 2026-03-19T17:10:21.524Z | user\n\nNo code changes in this release; documentation updates only.\n\n- Updated SKILL.md to correct and expand setup, security, and action documentation.\n- Mentioned the increase to 413 actions and improved module/industry onboarding instructions.\n- Expanded and clarified auto-detection for industry/country and user onboarding flow.\n- Provided guidance for regional module (tax/compliance) actions and web dashboard options.\n- Streamlined security section and improved bulleted explanations for clarity.\n\nv3.1.10 | 2026-03-18T02:41:15.264Z | user\n\nNo changes were detected in this version.\n\n- Version bumped from 3.1.10 to 3.2.0 with no file changes.\n- No new features, fixes, or updates in this release.\n\nv3.1.9 | 2026-03-17T18:09:37.227Z | user\n\nVersion 3.1.9\n\n- Added new core modules: dgm_engine, gap_detector, heartbeat_analysis, improvement_log, semantic_engine, and variant_manager.\n- Expanded the scripts/erpclaw-os/ directory with advanced analysis and monitoring capabilities.\n- Lays groundwork for future features in diagnostics, semantic processing, and continuous improvement.\n\nv3.1.8 | 2026-03-16T17:17:06.741Z | user\n\nNo changes detected in this version.\n\n- Version bump from 3.1.8 to 3.2.0 only; no updates to code, features, or documentation.\n- All functionality and cron settings remain unchanged.\n\nv3.1.7 | 2026-03-16T00:54:57.463Z | user\n\n**Major new version with advanced OS features, guardrails, and automation**\n\n- Added 88 new files including accounting, billing, buying, inventory, HR, advanced accounting, and OS automation scripts\n- Introduced a self-extending ERP OS with constitutional guardrails, adversarial audit, and schema migration support\n- Improved security: clarified network action user confirmations, local-first architecture, and environment config options\n- Auto-detects business industry, confirms with user, and suggests/install modules as needed\n- Added scheduled (cron) actions for recurring billing, journal processing, inventory checks, and overdue invoice alerts\n- Expanded and reorganized documentation for setup, quick start, and all available actions\n\nv3.1.6 | 2026-03-13T16:15:12.632Z | user\n\nMajor change: Core scripts and assets have been removed from this skill version and installation now depends on downloading from GitHub.\n\n- All scripts and data files (including db_query.py, assets, and advanced accounting modules) have been removed from the package.\n- Initial setup and execution now require cloning the ERPClaw repository from GitHub and copying its contents manually.\n- README, LICENSE, and all module code are no longer bundled; installation and updates are managed via Git.\n- Metadata and instructions updated to reflect the new external installation process.\n- The skill’s operation depends on the presence of the downloaded codebase; guidance provided for checking and installing from source.\n\nv3.1.5 | 2026-03-13T13:08:22.810Z | user\n\nNo file changes detected in this release.\n\n- Version bumped to 3.1.5, but no functional changes or updates were made.\n- All features and capabilities remain the same as previous version (3.1.2).\n\nv3.1.4 | 2026-03-13T05:15:28.160Z | user\n\n- Added explicit user confirmation before installing any modules or enabling network features.\n- Enhanced auto-detection logic to require user confirmation of detected business type and proposed modules before setup actions.\n- Updated security model: All network features (including module installs) are only available after user confirmation.\n- Revised onboarding and module installation instructions for clarity and compliance.\n- No changes to application functionality or codebase; documentation and process refinements only.\n\nv3.1.3 | 2026-03-13T05:07:12.145Z | user\n\n- Added README.md for improved documentation and onboarding.\n- Removed legacy metadata and old manufacturing database query script.\n- Updated SKILL.md: revised maintainer details, expanded auto-detection and industry onboarding instructions, and clarified module-installation guidance.\n- Total actions count updated to 371 across 14 domains.\n\nv3.1.2 | 2026-03-09T14:46:33.042Z | auto\n\n- Added initial support for manufacturing domain with new script (`scripts/erpclaw-manufacturing/db_query.py`)\n- Included module metadata file (`_meta.json`)\n- Updated selling, setup, and module manager database query scripts\n- Minor documentation and version updates in SKILL.md\n\nv3.1.1 | 2026-03-09T01:10:04.673Z | auto\n\n- Version bump from 3.1.0 to 3.1.1 in SKILL.md.\n- No functional or feature changes; documentation update only.\n\nv3.1.0 | 2026-03-09T01:08:18.102Z | auto\n\nVersion 3.1.0\n\n- Added or updated core database query scripts across modules (GL, inventory, setup).\n- Enhanced schema initialization and onboarding procedures.\n- Improved module management with updates to manager and registry scripts.\n- Refined document naming and cross-skill utilities for greater consistency.\n- Documentation updated to reflect new features and usage details.\n\nv3.0.0 | 2026-03-08T22:51:59.776Z | user\n\nMajor update: Added HR and Payroll domains with new actions and expanded functionality.\n\n- Introduced HR and Payroll modules, including new scripts for each (erpclaw-hr/db_query.py, erpclaw-payroll/db_query.py)\n- Expanded supported domains from 12+ to 14, and actions from 325+ to over 365\n- Added features for HR (employees, leave, attendance, expenses) and US payroll (salary, taxes, W-2s, garnishments)\n- Updated tags, skill triggers, and documentation to include HR and payroll topics\n- Enhanced security model with explicit PII protection for employee and payroll data\n\nv2.2.0 | 2026-03-08T19:04:00.509Z | user\n\nv2.2.0: Module installer system, 18 onboarding profiles, transparent security model, SQL hardening\n\nv2.1.1 | 2026-03-08T18:03:00.823Z | user\n\n**Expanded to support advanced accounting and modular extensions.**\n\n- Added advanced accounting modules: ASC 606/842 revenue recognition, intercompany, consolidation, and lease accounting.\n- Introduced modular expansion framework and module manager for installing new features from GitHub.\n- Updated skill activation triggers to cover modules/onboarding and more domains (CRM, manufacturing, retail, healthcare).\n- Over 56 new actions added, extending to 325+ actions across 12+ domains.\n- LICENSE and onboarding scripts now included for compliance and easier first-time setup.\n\nv2.1.0 | 2026-03-07T18:21:15.532Z | user\n\nBug fixes: UI.yaml plurals, seed-demo-data routing, shared lib sync, payroll account mapping\n\nv2.0.4 | 2026-03-07T05:47:53.775Z | user\n\nFix SKILL.md: correct action names, flag names, post-install init_db to match actual argparse\n\nArchive index:\n\nArchive v4.15.0: 368 files, 2872884 bytes\n\nFiles: assets/demo.svg (5900b), assets/logo.svg (849b), bin/erpclaw (7829b), CHANGELOG.md (74054b), LICENSE.txt (35659b), mcp/__init__.py (757b), mcp/confirm.py (4162b), mcp/paths.py (1982b), mcp/server.py (9787b), mcp/skill_reader.py (8053b), mcp/tool_router.py (5163b), README.md (8624b), scripts/db_query.py (48078b), scripts/erpclaw-accounting-adv/consolidation.py (29200b), scripts/erpclaw-accounting-adv/db_query.py (6083b), scripts/erpclaw-accounting-adv/intercompany.py (16324b), scripts/erpclaw-accounting-adv/leases.py (22113b), scripts/erpclaw-accounting-adv/reports.py (3800b), scripts/erpclaw-accounting-adv/revenue.py (29744b), scripts/erpclaw-billing/db_query.py (151439b), scripts/erpclaw-buying/db_query.py (209830b), scripts/erpclaw-gl/assets/charts/us_gaap.json (20634b), scripts/erpclaw-gl/db_query.py (89820b), scripts/erpclaw-hr/db_query.py (143737b), scripts/erpclaw-inventory/db_query.py (181573b), scripts/erpclaw-journals/db_query.py (62014b), scripts/erpclaw-meta/db_query.py (78631b), scripts/erpclaw-os/__init__.py (0b), scripts/erpclaw-os/constitution.py (9342b), scripts/erpclaw-os/db_query.py (10099b), scripts/erpclaw-os/dependency_resolver.py (5880b), scripts/erpclaw-os/gl_invariant_checker.py (405b), scripts/erpclaw-os/references/pattern_catalog.md (30665b), scripts/erpclaw-os/schema_diff.py (15168b), scripts/erpclaw-os/schema_migrator.py (14650b), scripts/erpclaw-os/validate_module.py (58089b), scripts/erpclaw-payments/db_query.py (106626b), scripts/erpclaw-payroll/db_query.py (189866b), scripts/erpclaw-reports/db_query.py (72839b), scripts/erpclaw-selling/db_query.py (238424b), scripts/erpclaw-setup/assets/currencies.json (1551b), scripts/erpclaw-setup/assets/default_payment_terms.json (1113b), scripts/erpclaw-setup/assets/default_uom.json (870b), scripts/erpclaw-setup/assets/us_gaap.json (20634b), scripts/erpclaw-setup/db_query.py (125843b), scripts/erpclaw-setup/init_schema.py (225292b), scripts/erpclaw-setup/lib/erpclaw_lib/__init__.py (134b), scripts/erpclaw-setup/lib/erpclaw_lib/action_validators.py (1749b), scripts/erpclaw-setup/lib/erpclaw_lib/args.py (2408b), scripts/erpclaw-setup/lib/erpclaw_lib/audit.py (8340b), scripts/erpclaw-setup/lib/erpclaw_lib/bank_import.py (4824b), scripts/erpclaw-setup/lib/erpclaw_lib/billing_run.py (11683b), scripts/erpclaw-setup/lib/erpclaw_lib/credentials.py (2649b), scripts/erpclaw-setup/lib/erpclaw_lib/cross_skill.py (14298b), scripts/erpclaw-setup/lib/erpclaw_lib/crypto.py (19374b), scripts/erpclaw-setup/lib/erpclaw_lib/csv_export.py (4989b), scripts/erpclaw-setup/lib/erpclaw_lib/csv_import.py (11691b), scripts/erpclaw-setup/lib/erpclaw_lib/custom_fields.py (12581b), scripts/erpclaw-setup/lib/erpclaw_lib/cwip_posting.py (9224b), scripts/erpclaw-setup/lib/erpclaw_lib/datetime_utils.py (353b), scripts/erpclaw-setup/lib/erpclaw_lib/db.py (19011b), scripts/erpclaw-setup/lib/erpclaw_lib/decimal_utils.py (3406b), scripts/erpclaw-setup/lib/erpclaw_lib/dependencies.py (10657b), scripts/erpclaw-setup/lib/erpclaw_lib/encrypted_columns.py (1787b), scripts/erpclaw-setup/lib/erpclaw_lib/foundation_registry.py (6071b), scripts/erpclaw-setup/lib/erpclaw_lib/fx_posting.py (7228b), scripts/erpclaw-setup/lib/erpclaw_lib/gl_invariants.py (24877b), scripts/erpclaw-setup/lib/erpclaw_lib/gl_posting.py (30424b), scripts/erpclaw-setup/lib/erpclaw_lib/govid_shape.py (4086b), scripts/erpclaw-setup/lib/erpclaw_lib/integration_actions.py (6953b), scripts/erpclaw-setup/lib/erpclaw_lib/integration_secrets.py (2939b), scripts/erpclaw-setup/lib/erpclaw_lib/master_key.py (3090b), scripts/erpclaw-setup/lib/erpclaw_lib/naming.py (11674b), scripts/erpclaw-setup/lib/erpclaw_lib/pagination.py (2187b), scripts/erpclaw-setup/lib/erpclaw_lib/party_ledger.py (7597b), scripts/erpclaw-setup/lib/erpclaw_lib/passwords.py (1201b), scripts/erpclaw-setup/lib/erpclaw_lib/paths.py (2856b), scripts/erpclaw-setup/lib/erpclaw_lib/payment_clearing.py (28964b), scripts/erpclaw-setup/lib/erpclaw_lib/query_helpers.py (8629b), scripts/erpclaw-setup/lib/erpclaw_lib/query.py (18305b)\n\nFile v4.15.0:SKILL.md\n\n---\nname: erpclaw\nversion: 4.15.0\ndescription: >\n  AI-native ERP system. Full accounting, invoicing, inventory, purchasing,\n  tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation),\n  and financial reporting (including P&L / trial balance / spend grouped by department, project, cost center, location, or fund). 496 actions across 14 domains, 45 optional expansion modules (user-approved install from GitHub).\n  Double-entry GL, immutable audit trail, US GAAP compliant. Licensed under GNU GPL v3 (the marketplace \"MIT-0\" badge is a ClawHub platform default; the LICENSE.txt in the bundle is GPL v3).\nauthor: AvanSaber\nhomepage: https://github.com/avansaber/erpclaw\nsource: https://github.com/avansaber/erpclaw\nuser-invocable: true\ntags: [erp, accounting, invoicing, inventory, purchasing, tax, billing, payments, gl, reports, sales, buying, setup, hr, payroll, employees, leave, attendance, salary, revenue-recognition, lease-accounting, intercompany, consolidation]\nmetadata: {\"openclaw\":{\"type\":\"executable\",\"install\":{\"post\":\"python3 scripts/erpclaw-setup/db_query.py --action initialize-database\"},\"requires\":{\"bins\":[\"python3\",\"git\"],\"env\":[],\"optionalEnv\":[\"ERPCLAW_DB_PATH\"]},\"os\":[\"darwin\",\"linux\"]},\"hermes\":{\"category\":\"productivity\",\"config\":[{\"key\":\"erpclaw.home\",\"description\":\"ERPClaw install root; lib, install-state, and the default SQLite DB resolve under it. Unset/blank defaults to ~/.openclaw/erpclaw (byte-identical to OpenClaw).\",\"default\":\"${ERPCLAW_HOME}\",\"prompt\":\"ERPClaw home directory (blank = ~/.openclaw/erpclaw)\"}]},\"mcp\":{\"transport\":\"stdio\",\"server\":\"source/erpclaw/mcp/server.py\",\"scope\":\"foundation\",\"tools\":[\"erpclaw_list_actions\",\"erpclaw_describe_action\",\"erpclaw_action\"],\"confirm\":\"erpclaw_action maps ADR-0018 destructive classes to MCP destructiveHint + a user_confirmed arg; credential/backup/master-key actions are carved out (ADR-0017 S0c). Transport-only over db_query.py — no new write path (ADR-0024).\",\"read\":\"erpclaw_read deferred to v2\"}}\n---\n\n# erpclaw\n\n**Full-Stack ERP Controller** for ERPClaw. Company setup, chart of accounts, journal entries, payments, tax, financial reports, customers, sales, suppliers, purchasing, inventory, billing, HR, US payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and 45 optional industry modules. Local-first SQLite, double-entry GL, immutable audit trail.\n\n**Security:** Local-first. Parameterized queries. RBAC (PBKDF2). Immutable GL. Sensitive fields encrypted at the column level. Network access limited to `fetch-exchange-rates` (public API) and user-approved `install-module` from `github.com/avansaber/*`.\n\n**Runtimes:** Runs on OpenClaw (primary). Experimental support for the Hermes Agent runtime via a GitHub tap. Install root is set by the `ERPCLAW_HOME` environment variable; unset/blank defaults to `~/.openclaw/erpclaw` (zero behavior change for OpenClaw).\n\n## System of record (the ERP is authoritative)\n\nThe ERPClaw database is the single source of truth for every business entity — companies, customers, suppliers, items, invoices, bills, payments, and the general ledger. Before answering what exists or acting on an entity, look it up in the ERP and ground your reply in that result:\n\n- \"Which companies/customers/items do we have?\" → query it (`list-companies`, `list-customers`, `list-items`). Never answer from memory, earlier conversations, workspace files, or any other context.\n- When a user names a product loosely or in plural (\"20 Folding Chairs\"), call `resolve-item --name \"<their words>\"` first; use the single match, or ask the user to choose when `multiple_matches` is true, before invoicing/ordering.\n- Adding/invoicing when exactly one company exists → use that company; do not ask which business. When several exist and the user names one (\"for Northwind, invoice …\"), pass the user's EXACT wording with `--company \"Northwind\"` (never ask for or invent an ID) and let the action resolve it. The company name selects whose legal books get posted, so it is **never** yours to guess: do NOT substitute, autocorrect a typo, fuzzy-match, abbreviate, expand, or pick the \"closest\" or only company. Exact match only: \"Northwynd\" is not \"Northwind Traders\", and \"Northwind\" is not \"Northwind Traders\". Read `list-companies` to ground, NOT to choose a near-match.\n- If `--company \"<name>\"` returns a not-found error (it lists `available_companies`), STOP: tell the user that company does not exist, show those available names, and ask which they mean. Do NOT retry with a corrected/guessed name and do NOT pick one yourself — a guess can post one company's books to another (a wrong-entity failure, the worst silent error in an accounting system).\n- Never keep or reconcile against freeform file-based books (JSON/markdown business folders, scratch notes). They are not the ledger and may be stale. The ERP database is the only authoritative record. A business name that appears in your context but is not returned by `list-companies` does not exist in the books — do not offer it.\n- **Before claiming an entity exists, is a duplicate, or has a balance/count — you MUST have called a `list-*`/`get-*` for it in THIS turn and seen it returned.** This is mandatory and has no exception. Asked to add or create something (a new customer, a received shipment of stock, a new supplier)? Do not refuse it as an existing duplicate from memory: call the relevant lookup (e.g. `list-customers`) this turn first; if it returns no match, CREATE it — the default for an add/create request is to act, not to refuse. A name, number, or \"already set up\" feeling from earlier in the chat, your workspace context, or training is NOT evidence it is in the books; if you have not run the lookup this turn, you do not know it exists. Conversely, when the ERP DOES return a document another session created, treat it as authoritative and act on it — do not refuse because a session was reset or your notes say the data is stale. The ERP query is the only truth; your memory is not.\n\n## Speaking to the user\n\nThe action names listed in the catalog further down (`setup-company`, `add-customer`, `submit-payment`, etc.) are internal routing identifiers. Never use them in replies the user sees.\n\nWhen you tell the user what you are about to do or what you just did, describe the business outcome in plain English:\n\n| Internal name | Say to user |\n|---|---|\n| `setup-company` | \"set up the company\" |\n| `add-customer` | \"add the customer\" |\n| `add-item` | \"add the product\" |\n| `submit-sales-invoice` | \"send the invoice\" |\n| `submit-payment` | \"record the payment\" |\n| `restore-database` | \"restore from backup\" |\n| `install-module` | \"install the X module\" |\n\nFor an action not in the table, derive a friendly form by removing the verb prefix and using the entity in plain English (`record-1099-payment` → \"record the 1099 payment\").\n\nThe user is a small business owner, founder, or store operator. They know \"customer\", \"invoice\", \"payment\". They have not seen the action catalog and never should.\n\nWhen asking for confirmation, say what you'll do, not which action you'll call.\n\n- **Wrong:** \"I'll run `add-customer`, confirm?\"\n- **Right:** \"I'll add Bob from BigCo as a customer. Confirm?\"\n\nFor action chains and multi-step routines (month-end, year-end, payroll), describe the whole sequence in plain English without naming the underlying actions.\n\n- **Wrong:** \"I'll `add-customer` ABC, then `create-sales-invoice`, then `submit-sales-invoice`.\" / \"Month-end: `revalue-foreign-balances`, `close-fiscal-year`, `trial-balance`.\"\n- **Right:** \"I'll add ABC as a customer and send them an invoice for 5 widgets at $50 (total $250).\" / \"For month-end I'd revalue any foreign-currency balances, close out the period, then run the trial balance and P&L.\"\n\nWhen narrating a completed action, do not include the action name.\n\n- **Wrong:** \"I called `add-customer` and got ID 12345.\"\n- **Right:** \"I added Bob as a customer (ID 12345 if you need to look him up).\"\n\nIf the user explicitly asks \"which command did you run?\" or \"what's the technical name?\", politely decline.\n\n- **Wrong:** \"`add-customer` with name=Bob, company=BigCo.\"\n- **Right:** \"That's an internal routing detail; I'd rather keep the conversation in business terms. I added Bob from BigCo as a customer, if that's what you wanted to confirm.\"\n\nIf the user uses an internal name themselves (\"what happens if I run setup-company twice?\"), gently translate in your reply (\"setting up a company twice would be rejected, since names are unique\") without echoing the name or correcting the user.\n\n### Accounting and ledger internals\nThe same rule applies to the bookkeeping behind an action: erpclaw returns the technical record (double-entry GL legs, ledger fields, status flags, internal IDs), but you confirm the business outcome, translate every internal label, and keep the mechanics out of the reply. Never describe the double-entry posting, the debit/credit legs, the account names, \"no stock movement\", or \"stock ledger entry\". Say what changed in business terms: \"I recorded the bill, it's in your books and shows as owed to Gotham Steel.\"\n- Translate internal labels, don't echo them: draft means \"saved but not sent yet\"; submitted or gl posted means \"recorded in your books\"; outstanding means \"still owed\"; valuation rate means \"cost\"; posting date means \"date\"; naming series and the gl or sle entry counts should be omitted entirely.\n- Show an internal ID only as a trailing reference, never as the headline.\n  - **Wrong:** \"Posted. status: submitted. Posting date: 2026-06-07. gl entries created: 2 (debit Inventory, credit Accounts Payable). 3f2a-...\" **Right:** \"Done, I recorded that bill in your books; you still owe Gotham Steel $600 (reference 3f2a if you need to look it up).\"\n\n### Skill Activation Triggers\n\nActivate when user mentions: ERP, accounting, invoice, sales order, purchase order, customer, supplier, inventory, payment, GL, trial balance, P&L, balance sheet, P&L / spend / revenue by department or project or cost center or location or fund, tax, billing, modules, install module, onboard, CRM, manufacturing, healthcare, education, retail, employee, HR, payroll, salary, leave, attendance, expense claim, W-2, garnishment, integration. **\"By department / project / cost center / location / fund\" reporting is a first-class capability (accounting dimensions), never hand-rolled:** tag entries at booking time with `--dimension-key/--dimension-value`, then report with `profit-and-loss --group-by <dimension>` (P&L by that dimension) or `multi-dim-trial-balance --group-by <dimension>` (whole trial balance) — NOT a `cost_center` column or raw SQL — see the Journal Entries and Financial Reports rows.\n\n### Auto-Detection\n\nWhen a user describes their business: detect type (e.g., \"dental practice\" → dental), **ask the user to confirm** before proceeding, then set the company up with that industry. (Internal routing only: invoke `setup-company` with `--industry <type>`. Never name the action to the user.) Industry values: retail, restaurant, healthcare, dental, veterinary, construction, manufacturing, legal, agriculture, hospitality, property, school, university, nonprofit, automotive, therapy, home-health, consulting, distribution, saas. When a user asks about a service or integration not currently installed, search the module registry and **suggest** installation (never auto-install without user approval).\n\n### Setup\n```\npython3 {baseDir}/scripts/erpclaw-setup/db_query.py --action initialize-database\npython3 {baseDir}/scripts/db_query.py --action seed-defaults --company-id <id>\npython3 {baseDir}/scripts/db_query.py --action setup-chart-of-accounts --company-id <id> --template us_gaap\n```\n\n## Runtime gate\n\nHigh-impact actions require the `--user-confirmed` flag on every invocation; the foundation router rejects unflagged calls with a structured JSON error. Read-only actions (`list`, `get`, reports) run without the flag.\n\n**The flag confirms consent the user already gave — it is not a request to pause.** When the user has clearly asked for an action (\"send the invoice\", \"record the payment\", \"post that entry\"), pass `--user-confirmed` in that same call and act. Do NOT draft the steps and then ask \"want me to submit?\" — that re-asks for a yes you already have, and nothing is recorded. This is the default for every routine, reversible action: `submit-*`, `add-*`, `create-*`, `approve-*`.\n\nRe-confirm a second time ONLY for the small destructive set, where a mistake is hard or impossible to undo: closing the fiscal year (`close-fiscal-year`), restoring from backup (`restore-database`), installing a module (`install-module`), reconciling foundation files (`rollback-foundation`), and generating a bank-payment file (`generate-nacha-file`). For these, state plainly what will happen and get an explicit yes before passing the flag.\n\n## Action Catalog\n\n### Setup & Admin (66)\n| Action | Description |\n|--------|-------------|\n| `initialize-database` / `setup-company` / `update-company` / `get-company` / `list-companies` | DB init & company CRUD |\n| `migrate` | Run pending schema migrations (`migrations/NNN_*.py`) in order, recording each in the `erpclaw_schema_migration` ledger. Idempotent + dialect-aware; `--dry-run` lists pending without applying. Run on install and on upgrades. |\n| `add-currency` / `list-currencies` / `add-exchange-rate` / `get-exchange-rate` / `list-exchange-rates` / `fetch-exchange-rates` | Currency & FX |\n| `add-payment-terms` / `list-payment-terms` / `add-uom` / `list-uoms` / `add-uom-conversion` | Terms & UoMs |\n| `seed-defaults` / `seed-demo-data` / `check-installation` / `install-guide` / `setup-web-dashboard` / `tutorial` / `onboarding-step` / `status` | Seeding & utilities |\n| `add-account-type` / `list-account-types` / `deactivate-account-type` / `add-voucher-type` / `list-voucher-types` / `deactivate-voucher-type` / `validate-registry-completeness` | Type/status registry admin (M0): register/inspect/soft-disable the account_type / voucher_type values that replaced hardcoded CHECK constraints |\n| `add-custom-field` / `list-custom-fields` / `remove-custom-field` / `set-custom-field-value` / `get-custom-field-values` | Custom fields (M1): define user-defined fields on any core table and store/read their values. Write-side actions in selling/buying/inventory accept `--custom-fields '{\"name\":\"value\"}'` and return them on get |\n| `set-advance-account` | Advances (S2): configure a company's B1-style advance sub-account (`--type customer`→liability, `--type supplier`→asset); submit-payment then routes the unallocated advance leg there |\n| `add-user` / `update-user` / `get-user` / `list-users` / `set-password` | User management |\n| `add-role` / `list-roles` / `assign-role` / `revoke-role` / `seed-permissions` | RBAC & security |\n| `link-telegram-user` / `unlink-telegram-user` / `check-telegram-permission` | Telegram integration |\n| `backup-database` / `list-backups` / `verify-backup` / `restore-database` / `cleanup-backups` | DB backup/restore. `cleanup-backups` permanently deletes old backup files per the retention policy (keeps 7 daily / 4 weekly / 12 monthly) and requires `--user-confirmed` like `restore-database` |\n| `set-credential` / `get-credential` / `list-credentials` / `delete-credential` / `migrate-credentials` | Encrypted credential management |\n| `import-master-key-from-backup` | Cross-machine restore: install master key from a backup taken on another machine |\n| `get-audit-log` / `get-schema-version` / `update-regional-settings` / `onboard` | System admin |\n\n### General Ledger (30)\n| Action | Description |\n|--------|-------------|\n| `setup-chart-of-accounts` / `add-account` / `update-account` / `get-account` / `list-accounts` | Account CRUD. `update-account --account-type` retypes an account (validated against `account_type_registry`, same group guard as `add-account`); when the account already carries posted GL entries it also needs `--reclassify-posted`, because every report that filters on `account_type` re-reads that history under the new type. `root_type` is not updatable. Both surfaces also refuse an `account_type` that cannot sit on the account's `root_type` (a `bank`/`cash`/`receivable` type on a P&L root, a `revenue` type on a balance-sheet root, and so on) — the accepted roots per type are `ACCOUNT_TYPE_ROOT_TYPES` in `erpclaw-gl/db_query.py`, derived from the shipped charts; a type nobody registered a root for is unconstrained |\n| `freeze-account` / `unfreeze-account` / `get-account-balance` / `check-gl-integrity` | Account management |\n| `post-gl-entries` / `reverse-gl-entries` / `list-gl-entries` | GL posting |\n| `add-fiscal-year` / `list-fiscal-years` / `validate-period-close` / `close-fiscal-year` / `reopen-fiscal-year` | Fiscal year |\n| `add-cost-center` / `list-cost-centers` / `add-budget` / `list-budgets` | Cost centers & budgets |\n| `add-dimension` / `list-dimensions` / `update-dimension` / `deactivate-dimension` | Accounting dimensions (M6): register/inspect/update/retire the dimension keys (department, project, location, fund, etc.) that drive `gl_entry.dimensions_json` (enforced as GL validation step 13). **To make a posting reportable \"by <dimension>\", tag it at entry time with `--dimension-key/--dimension-value` on the journal/invoice/payment actions** — then report with `multi-dim-trial-balance` / `dimension-balance-report` (see Financial Reports). `deactivate-dimension` is blocked while recent live GL still references the key |\n| `seed-naming-series` / `next-series` / `revalue-foreign-balances` | Naming & FX revaluation |\n| `import-chart-of-accounts` / `import-opening-balances` | CSV import |\n\n### Journal Entries (16)\n| Action | Description |\n|--------|-------------|\n| `add-journal-entry` / `update-journal-entry` / `get-journal-entry` / `list-journal-entries` | JE CRUD. **When the user attributes an entry to a department / project / location / fund / cost center (e.g. \"office supplies for Engineering\", \"travel on the Apollo project\"), tag it with `--dimension-key department --dimension-value Engineering` — do NOT add a \"cost center\" line/column or hand-roll the attribution; the dimension tag is what makes it reportable later with `multi-dim-trial-balance`.** `add-journal-entry --cwip-asset-id <A>` tags the JE; submit records its CWIP debit leg as a cost accumulation against the asset (S3) |\n| `submit-journal-entry` / `cancel-journal-entry` / `amend-journal-entry` / `delete-journal-entry` / `duplicate-journal-entry` | JE lifecycle |\n| `create-intercompany-je` | Intercompany JE |\n| `add-recurring-template` / `update-recurring-template` / `list-recurring-templates` / `get-recurring-template` / `process-recurring` / `delete-recurring-template` | Recurring JEs |\n\n### Payments (16)\n| Action | Description |\n|--------|-------------|\n| `add-payment` / `update-payment` / `get-payment` / `list-payments` / `submit-payment` / `cancel-payment` / `delete-payment` | Payment CRUD & lifecycle. **Plain-words route — \"customer paid less; treat the missing amount as a write-off / discount / fee, in one payment\": that is ONE `add-payment` with `--deductions`, then `submit-payment` — never a payment followed by a separate write-off step.** `add-payment --deductions` (JSON array of `{account_id, amount, type}`; type = tds/commission/early_payment_discount/write_off/other) records short-pays: submit posts the deduction GL legs and the deducted total clears the allocated invoices ($980 wire + $20 discount fully clears a $1,000 invoice); cancel reverses them. `write_off` is the residual a customer never pays, taken at payment time ($950 on a $1,000 invoice) — for a no-cash write-off use `write-off-invoice` |\n| `create-payment-ledger-entry` / `get-outstanding` / `get-unallocated-payments` / `allocate-payment` / `reconcile-payments` / `bank-reconciliation` / `write-off-invoice` | Reconciliation. `write-off-invoice` writes off bad debt on ONE open invoice with no cash involved: `--voucher-id` + `--write-off-amount` + `--write-off-account-id` (bad-debt expense) + `--reason` posts a balanced GL pair under the invoice's own voucher and drops its outstanding (writing off the whole residual marks it paid); cancelling the invoice reverses it. Dated by the DECISION: posting date defaults to today, so an invoice in a closed year is still write-off-able; `--posting-date` backdates deliberately. Confirmation-gated. One invoice at a time — there is no batch write-off |\n| `list-open-advances` / `apply-advance-to-invoice` | Advances (S2): SAP Business One vocabulary aliases for `get-unallocated-payments` / `allocate-payment` (same semantics) |\n\n### Tax (17)\n| Action | Description |\n|--------|-------------|\n| `add-tax-template` / `update-tax-template` / `get-tax-template` / `list-tax-templates` / `delete-tax-template` | Tax template CRUD |\n| `resolve-tax-template` / `calculate-tax` / `add-tax-category` / `list-tax-categories` / `add-tax-rule` / `list-tax-rules` | Tax rules |\n| `add-item-tax-template` / `add-tax-withholding-category` / `get-withholding-details` | Withholding |\n| `record-withholding-entry` / `record-1099-payment` / `generate-1099-data` | 1099 reporting |\n\n### Financial Reports (21)\n| Action | Description |\n|--------|-------------|\n| `trial-balance` / `profit-and-loss` / `balance-sheet` / `cash-flow` / `general-ledger` / `party-ledger` / `multi-dim-trial-balance` / `dimension-balance-report` | Core statements. **For a \"P&L by DEPARTMENT / project / cost center / location / fund / any dimension\" ask, call `profit-and-loss --group-by <dimension>` — it returns revenue / expenses / net per dimension value (with an explicit `(untagged)` bucket), so you NEVER hand-roll the split or write SQL over `cost_center`.** Example: \"show me this month's P&L broken down by department\" → `profit-and-loss --group-by department --from-date <start> --to-date <end>`. For grouping the WHOLE trial balance (all account types, not just income/expense) use `multi-dim-trial-balance --group-by \"project,department\"`; `dimension-balance-report --dimension K` gives one dimension's balances. `--group-by` takes ONE dimension on `profit-and-loss`; group by an UNREGISTERED dimension errors (run `list-dimensions`). Without `--group-by`, `profit-and-loss` returns ONE company-wide statement. All four headline statements + `general-ledger` also accept repeated `--dimension-key/--dimension-value` filters to scope (and `profit-and-loss` filters THEN groups) |\n| `ar-aging` / `ap-aging` / `budget-vs-actual` (alias: `budget-variance`) | Aging & budget |\n| `tax-summary` / `payment-summary` / `gl-summary` / `comparative-pl` / `check-overdue` | Summaries |\n| `add-elimination-rule` / `list-elimination-rules` / `run-elimination` / `list-elimination-entries` | **RETIRED — do not call.** They posted group eliminations into the operating companies' own books, which unbalanced each entity. Intercompany elimination is the consolidation layer's job: `add-consolidation-group` → `add-group-entity` (one per entity) → `add-ic-transaction` → `approve-ic-transaction` → `post-ic-transaction` → `generate-elimination-entries` → `consolidation-trial-balance-report` / `ic-elimination-report`. approve/post are required: only POSTED IC transactions are eliminated. Calling one returns that steer |\n\n### Selling (57)\n| Action | Description |\n|--------|-------------|\n| `add-customer` / `update-customer` / `get-customer` / `list-customers` / `import-customers` | Customer CRUD (add/update accept `--email` / `--phone` — dedicated structured columns; `--default-price-list-id` sets the customer's default selling price list, consulted first when a line has no explicit rate) |\n| `add-quotation` / `update-quotation` / `get-quotation` / `list-quotations` / `submit-quotation` / `convert-quotation-to-so` | Quotations |\n| `add-sales-order` / `update-sales-order` / `get-sales-order` / `list-sales-orders` / `submit-sales-order` / `cancel-sales-order` / `amend-sales-order` / `close-sales-order` | Sales orders |\n| `add-blanket-order` / `get-blanket-order` / `list-blanket-orders` / `submit-blanket-order` / `create-so-from-blanket` | Blanket orders |\n| `create-delivery-note` / `get-delivery-note` / `list-delivery-notes` / `submit-delivery-note` / `cancel-delivery-note` / `add-packing-slip` / `get-packing-slip` / `list-packing-slips` | Delivery & packing |\n| `create-sales-invoice` / `update-sales-invoice` / `get-sales-invoice` / `list-sales-invoices` / `submit-sales-invoice` / `cancel-sales-invoice` | Invoicing |\n| `create-credit-note` / `list-credit-notes` / `update-invoice-outstanding` | Credit notes; `update-invoice-outstanding` (AP twin: `update-purchase-outstanding`) applies a payment amount to an open invoice and posts the matching payment-ledger adjustment row in the same transaction (summary ≡ detail) |\n| `add-sales-partner` / `list-sales-partners` | Sales partners |\n| `add-recurring-template` / `update-recurring-template` / `list-recurring-templates` / `generate-recurring-invoices` | Recurring invoices |\n| `add-intercompany-account-map` / `list-intercompany-account-maps` / `create-intercompany-invoice` / `list-intercompany-invoices` / `cancel-intercompany-invoice` | Intercompany |\n| `check-credit-limit` / `place-customer-on-hold` | Credit control: compute available credit (limit minus outstanding AR); place customer on hold / suspend / restore active |\n| `add-dunning-level` / `run-dunning-cycle` / `list-dunning-runs` | Dunning: configure escalation levels (at N days overdue → email / call / hold / suspend); run a cycle that matches overdue invoices to their highest applicable level and applies the configured action — `email` levels enqueue a dunning email via the erpclaw-alerts send-email action and record the outbox id on `dunning_run.generated_email_id` (missing customer email or template skips-with-note, never failing the cycle); view run history |\n\n### Buying (51)\n| Action | Description |\n|--------|-------------|\n| `add-supplier` / `update-supplier` / `get-supplier` / `list-suppliers` / `import-suppliers` | Supplier CRUD (add/update accept `--email` / `--phone` — dedicated structured columns) |\n| `add-material-request` / `submit-material-request` / `list-material-requests` / `get-material-request` / `create-po-from-material-request` | Material requests. `create-po-from-material-request --material-request-id MR --supplier-id S` copies the remaining unordered lines onto a draft PO (rate = item's last purchase rate, else standard rate; per-line overrides via `--items` JSON, qty 0 skips a line), bumps each line's ordered_qty, and rolls the request status to partially_ordered/ordered — call again to order the remainder |\n| `add-rfq` / `submit-rfq` / `list-rfqs` / `add-supplier-quotation` / `list-supplier-quotations` / `compare-supplier-quotations` | RFQs & quotes |\n| `add-purchase-order` / `update-purchase-order` / `get-purchase-order` / `list-purchase-orders` / `submit-purchase-order` / `cancel-purchase-order` / `close-purchase-order` | Purchase orders |\n| `add-blanket-po` / `get-blanket-po` / `list-blanket-pos` / `submit-blanket-po` / `create-po-from-blanket` / `create-po-from-so` / `create-drop-ship-order` | Blanket POs & drop ship |\n| `create-purchase-receipt` / `get-purchase-receipt` / `list-purchase-receipts` / `submit-purchase-receipt` / `cancel-purchase-receipt` | Receipts |\n| `create-purchase-invoice` / `update-purchase-invoice` / `get-purchase-invoice` / `list-purchase-invoices` / `submit-purchase-invoice` / `cancel-purchase-invoice` | Purchase invoices. `create-purchase-invoice --cwip-asset-id <A>` (standalone cost bill) routes the expense GL to the asset's CWIP account + records a cost accumulation on submit (S3) |\n| `create-debit-note` / `add-landed-cost-voucher` / `list-landed-cost-vouchers` / `get-landed-cost-voucher` / `cancel-landed-cost-voucher` / `update-receipt-tolerance` / `update-three-way-match-policy` | Adjustments. Landed cost (freight/duty/insurance on received stock): `add-landed-cost-voucher` posts the GL AND reprices SLE valuation + FIFO layers in one step; `cancel-landed-cost-voucher` reverses both halves (cancel = reverse, never edit) |\n| `add-recurring-bill-template` / `list-recurring-bill-templates` / `generate-recurring-bills` | Recurring AP bills (rent, utilities, subscriptions billed TO you): template drafts a purchase invoice each cycle |\n\n**Receiving purchased stock — flow:** to bring purchased goods into inventory, receive them against their source document so valuation carries automatically. Canonical flow: `submit-purchase-order` (confirms the order + rate) → `create-purchase-receipt --purchase-order-id <PO>` then `submit-purchase-receipt` (this values the stock at the PO rate and posts inventory GL) → `create-purchase-invoice` + `submit-purchase-invoice` for the bill (leave stock update off — the receipt already moved it) → pay. NEVER use a standalone `add-stock-entry` `material_receipt` for purchased goods — even with the cost stated, it does not mark the purchase order received, so the later bill/receipt flow will receive the goods AGAIN and double-count stock. Before any bare \"receive stock\" request, run `list-purchase-orders` for an open order covering the item; if one exists, receive via `create-purchase-receipt --purchase-order-id`. (A rate-less standalone receipt cannot be valued and will be refused regardless.)\n\n### Inventory (65)\n| Action | Description |\n|--------|-------------|\n| `add-item` / `update-item` / `get-item` / `list-items` / `resolve-item` / `import-items` / `add-item-group` / `list-item-groups` | Item master (`resolve-item`: resolve a loose/plural user phrase like \"20 Brake Pad Sets\" to the stored item) |\n| `add-item-attribute` / `create-item-variant` / `generate-item-variants` / `list-item-variants` | Item variants |\n| `add-item-supplier` / `list-item-suppliers` / `set-item-purchase-uom` | Item suppliers |\n| `add-warehouse` / `update-warehouse` / `list-warehouses` | Warehouses |\n| `add-stock-entry` / `add-repack-stock-entry` / `add-material-consumption` / `get-stock-entry` / `list-stock-entries` / `submit-stock-entry` / `cancel-stock-entry` | Stock entries. `--entry-type` accepts receive / issue / transfer / manufacture / repack / subcontract / consume. A `material_receipt` requires a stated rate or the item's standard cost (non-purchase adjustments ONLY — check `list-purchase-orders` first: receiving PO-backed goods here leaves the PO un-received and the Buying flow will double-count the stock later; purchased goods go through the procure-to-pay flow). `repack` consumes input lines and produces output lines in one warehouse with input value == output value (cost-balanced within $0.01); `add-repack-stock-entry --warehouse W --from-item-id I1 --from-qty Q1 --to-item-id I2 --to-qty Q2 [--standard-rate R]` is the one-in/one-out shortcut. `subcontract` (`send_to_subcontractor`) transfers stock out to a `--supplier-warehouse-id` (a transit/production warehouse). `consume` (`material_consumption`) issues raw material against an active `--work-order-id`; `add-material-consumption --warehouse W --work-order-id WO --item-id I --qty Q` is the shortcut. Product-layer guard: a `material_receipt` for an item on an open purchase-order line, or a `material_issue` for an item on an open sales-order line, is refused at add and submit with a pointer to the order-backed flow (`create-purchase-receipt` / `create-delivery-note`) |\n| `create-stock-ledger-entries` / `reverse-stock-ledger-entries` | **RETIRED — do not call.** They wrote stock-ledger rows with no balancing GL leg, leaving stock untied to the books. Stock postings go through `add-stock-entry` → `submit-stock-entry` (both legs, one transaction); reversal is `cancel-stock-entry`; corrections are `add-stock-reconciliation` / `revalue-stock`. Calling one returns that steer |\n| `get-stock-balance` / `stock-balance` / `stock-balance-report` / `stock-ledger-report` / `get-projected-qty` | Stock reports. `get-projected-qty`'s `reserved_qty` reads persisted active reservations (M5); falls back to open sales-order lines when none exist |\n| `add-putaway-rule` / `list-putaway-rules` / `update-putaway-rule` / `delete-putaway-rule` / `apply-putaway-on-receipt` | Putaway (M5, warehouse-level). Route received stock to a target warehouse by item or item-group match (`--match-item I` beats `--match-item-group G`, then `--priority` ASC). `delete-putaway-rule` soft-disables. `apply-putaway-on-receipt --stock-entry SE` computes the deterministic routing for a `material_receipt` |\n| `create-pick-list` / `add-pick-list-item` / `submit-pick-list` / `mark-picked` / `complete-pick-list` / `cancel-pick-list` | Pick lists (M5). `create-pick-list --from-sales-order SO` drafts a pick from open SO lines; `submit-pick-list` reserves the qty (hard); `mark-picked --pick-list P --item I --picked-qty Q` records actuals (full pick → `picked`); `complete-pick-list` consumes the reservations and generates a delivery note; `cancel-pick-list` releases them |\n| `add-reservation` / `release-reservation` / `list-reservations` | Hard stock reservations (M5, ADR-0026). `add-reservation --voucher-type T --item I --warehouse W --qty Q` holds stock and is refused if it would exceed available (`actual − active reserved`); a `material_issue` that would breach active reservations is blocked. `release-reservation --id I` frees it |\n| `add-item-alternative` / `list-item-alternatives` / `get-best-alternative-for-item` / `remove-item-alternative` | Item-global substitutes (S7, directional). `add-item-alternative --item I --alternative A [--priority P --conversion-factor C --notes \"...\"]` (lower priority = preferred; self-ref rejected; pair (a,b) unique but (b,a) is a distinct valid row). `get-best-alternative-for-item --item I [--required-qty Q --warehouse W]` returns the highest-priority active alternative with enough stock at W (ties by available qty); no match is a clean empty result. `remove-item-alternative --id I` soft-disables. Manufacturing BOM substitutes inherit from these when a BOM line has none of its own |\n| `add-batch` / `list-batches` / `add-serial-number` / `list-serial-numbers` / `add-price-list` / `add-item-price` / `get-item-price` / `add-pricing-rule` | Batch & serial; NAMED price lists (customer/currency tiers). Rate resolution at document entry (quotation / sales order / standalone invoice) for a line with no explicit rate follows the order: explicit `--items` rate (respected even at 0) > `item_price` (the customer's `--default-price-list-id` if set, else any enabled selling list, honoring min-qty + valid-from/to window) > `item.standard_rate` > 0. An item's default catalog price is still `add-item`/`update-item --standard-rate`; use `add-item-price` for customer/tier/date-specific selling prices |\n| `add-stock-reconciliation` / `submit-stock-reconciliation` / `revalue-stock` / `list-stock-revaluations` / `get-stock-revaluation` / `cancel-stock-revaluation` / `check-reorder` | Reconciliation, revaluation & reorder |\n\n### Billing & Metering (24)\n| Action | Description |\n|--------|-------------|\n| `add-meter` / `update-meter` / `get-meter` / `list-meters` / `add-meter-reading` / `list-meter-readings` | Meters |\n| `add-usage-event` / `add-usage-events-batch` | Usage tracking |\n| `add-rate-plan` / `update-rate-plan` / `get-rate-plan` / `list-rate-plans` / `rate-consumption` | Rate plans. All 7 plan types rate: flat / tiered / volume_discount / time_of_use / demand / prepaid_credit / hybrid. TOU tiers (`time_of_use_period` + `time_of_use_hours` ranges) must cover 24h with no gaps/overlaps (validated at add/update); demand plans need one `demand_type='demand'` tier (optional `energy` tier prices consumption); hybrid plans take `--tier-strategy '{\"components\": [{\"type\": ..., \"tiers\": [...]}]}'` (non-hybrid components only, no prepaid_credit). `rate-consumption` extras: `--usage-by-period '{\"peak\": \"120\", ...}'` (TOU), `--peak-demand` (demand), `--customer-id` (prepaid_credit — balance preview only, no deduction) |\n| `create-billing-period` / `run-billing` / `generate-invoices` / `sync-billing-period-status` / `link-billing-period-invoice` / `unlink-billing-period-invoice` / `get-billing-period` / `list-billing-periods` | Billing cycles. **Plain-words routes — \"bill July and make it a real invoice\": `create-billing-period` → `run-billing` (rates it) → `generate-invoices` (drafts it) → submit that draft in selling; the period counts as `invoiced` only once its invoice is GL-posted. \"Accounting already invoiced them by hand\": that invoice must exist in the books exactly ONCE — if it is already in the system do NOT create it again; if it exists only on paper, record it once in selling (create → submit) first. Either way `link-billing-period-invoice` then attaches that one submitted invoice to the period; never run `generate-invoices` for a period already covered — the link is exactly what stops July being billed twice.** `run-billing` reports per-meter rating failures in an `errors` list (a bad plan reference or unpartitionable TOU usage is a data error, never a silent skip) and deducts prepaid_credit charges from `prepaid_credit_balance` (insufficient balance = explicit `over_limit` outcome, no deduction). `generate-invoices` creates a DRAFT invoice and records the `invoice_id` link (the double-generation guard) while the period stays `rated`; it reads `invoiced` only once that invoice is submitted (GL-posted), materialized by `sync-billing-period-status` (also run at the start of generate-/run-billing) — a cancelled invoice auto-reverts the period to `rated`, and only `rated`/`invoiced` periods are ever written (other statuses reported, never touched); it also **flags** (never blocks/skips) a covering live invoice in a per-period `warnings` array. An invoice can be raised without closing its period: `link-billing-period-invoice --billing-period-id P --invoice-id I` attaches a submitted/GL-posted invoice (customer must match, period `rated`, one link only — a second is rejected) and marks the period `invoiced`; `unlink-billing-period-invoice --billing-period-id P [--reason R]` resets it to `rated`/NULL (`--reason` required while the invoice is live, and to remedy an `invoiced`-with-NULL-link period, which it reverts to `rated`); a `rated` period holding a live DRAFT link is deliberately not unlinkable — submit then cancel the draft in selling, then `sync-billing-period-status`. Failures leave a period `rated` for retry with the reason in `results` |\n| `add-billing-adjustment` / `add-prepaid-credit` / `get-prepaid-balance` | Adjustments & prepaid |\n| `list-billing-runs` / `get-billing-run` / `resume-billing-run` | Crash-safe batch registry (billing_run). `run-billing`, `generate-recurring-invoices` and `process-recurring` record one target per meter/template, each in its own transaction; `resume-billing-run --run-id R` re-runs a crashed/partial run with zero duplicate documents (completed runs refuse). `list-billing-runs [--status S --run-type T --from-date D --to-date D]`; `get-billing-run --run-id R` returns header + per-target statuses |\n\n### Advanced Accounting (48)\n| Action | Description |\n|--------|-------------|\n| `add-revenue-contract` / `update-revenue-contract` / `get-revenue-contract` / `list-revenue-contracts` | Revenue contracts |\n| `add-performance-obligation` / `list-performance-obligations` / `satisfy-performance-obligation` | ASC 606 |\n| `add-variable-consideration` / `list-variable-considerations` / `modify-contract` | Variable consideration |\n| `calculate-revenue-schedule` / `generate-revenue-entries` / `revenue-waterfall-report` / `revenue-recognition-summary` | Revenue recognition |\n| `recognize-schedule-entry` / `update-performance-obligation` / `update-schedule-amounts` | Revenue schedule management |\n| `add-lease` / `update-lease` / `get-lease` / `list-leases` / `classify-lease` | ASC 842 leases |\n| `calculate-rou-asset` / `calculate-lease-liability` / `generate-amortization-schedule` / `record-lease-payment` | Lease calculations |\n| `lease-maturity-report` / `lease-disclosure-report` / `lease-summary` | Lease reports |\n| `add-ic-transaction` / `update-ic-transaction` / `get-ic-transaction` / `list-ic-transactions` | Intercompany |\n| `approve-ic-transaction` / `post-ic-transaction` / `add-transfer-price-rule` / `list-transfer-price-rules` | IC approvals |\n| `ic-reconciliation-report` / `ic-elimination-report` | IC reports |\n| `add-consolidation-group` / `list-consolidation-groups` / `add-group-entity` / `add-currency-translation` | Consolidation setup |\n| `run-consolidation` / `generate-elimination-entries` / `consolidation-trial-balance-report` / `consolidation-summary` / `list-elimination-surplus` / `remove-elimination-surplus` | Consolidation. `generate-elimination-entries` is safe to re-run: it eliminates only posted IC transactions this group+period has not eliminated yet, and `outcome` reports `created` / `already_eliminated` / `nothing_to_eliminate`. The trial balance also names any UNLINKED ic_elimination rows (residue an earlier duplicate-generation defect may have left, inflating `total_eliminations`); `list-elimination-surplus` shows them and `remove-elimination-surplus` deletes exactly those rows — report-only until `--confirm`, every deletion audited with the full removed row |\n| `standards-compliance-dashboard` | ASC 606/842 compliance |\n\n### HR & Payroll (68)\n| Action | Description |\n|--------|-------------|\n| `add-employee` / `update-employee` / `get-employee` / `list-employees` / `record-lifecycle-event` | Employee CRUD; `--ssn` stored encrypted, returned as last-4 only |\n| `add-employee-bank-account` / `list-employee-bank-accounts` / `add-employee-document` / `get-employee-document` / `list-employee-documents` / `check-expiring-documents` | Employee details |\n| `add-department` / `list-departments` / `add-designation` / `list-designations` | Org structure |\n| `add-leave-type` / `list-leave-types` / `add-leave-allocation` / `get-leave-balance` | Leave config |\n| `add-leave-application` / `approve-leave` / `reject-leave` / `list-leave-applications` | Leave requests |\n| `mark-attendance` / `bulk-mark-attendance` / `list-attendance` / `add-holiday-list` | Attendance |\n| `add-shift-type` / `update-shift-type` / `list-shift-types` / `assign-shift` / `list-shift-assignments` | Shift management |\n| `add-regularization-rule` / `apply-attendance-regularization` | Attendance regularization |\n| `add-expense-claim` / `submit-expense-claim` / `approve-expense-claim` / `reject-expense-claim` / `update-expense-claim-status` / `list-expense-claims` | Expenses |\n| `add-salary-component` / `list-salary-components` / `add-salary-structure` / `get-salary-structure` / `list-salary-structures` | Salary config |\n| `add-salary-assignment` / `list-salary-assignments` / `add-income-tax-slab` / `add-state-tax-slab` / `update-employee-state-config` | Payroll config |\n| `update-fica-config` / `update-futa-suta-config` / `add-overtime-policy` / `calculate-overtime` / `calculate-retro-pay` | Tax & overtime; retro calc is idempotent (skips periods already pending/applied) |\n| `create-payroll-run` / `generate-salary-slips` / `get-salary-slip` / `list-salary-slips` / `submit-payroll-run` / `cancel-payroll-run` | Payroll processing; slips auto-include pending retro pay as an earning line (applied on submit, reverted on cancel) |\n| `generate-w2-data` / `generate-nacha-file` / `add-garnishment` / `update-garnishment` / `get-garnishment` / `list-garnishments` | W-2, NACHA, garnishments |\n| `get-amendment-history` | Amendment tracking |\n\n### Module Management & Schema (22)\n| Action | Description |\n|--------|-------------|\n| `install-module` / `remove-module` / `update-modules` / `list-modules` / `available-modules` / `search-modules` / `module-status` | Module catalog (install/remove require user approval) |\n| `rebuild-action-cache` / `list-all-actions` / `list-profiles` / `onboard` | Actions & profiles |\n| `validate-module` / `list-articles` / `build-table-registry` | Constitution + module discovery (read-only) |\n| `schema-plan` / `schema-apply` / `schema-rollback` / `schema-drift` | Schema migration (apply/rollback require user approval) |\n| `regenerate-skill-md` | Regenerate SKILL.md |\n| `update-foundation` / `rollback-foundation` / `verify-trust-root` | Reconcile installed foundation files with the published manifest; reconcile actions require user approval |\n\n> **Foundation reconciliation.** Reconciliation verifies an ed25519 signature on the registry against an embedded public key before trusting any file hash. Two user-invoked actions keep an installed foundation aligned with the published manifest in `module_registry.json`. `update-foundation --user-confirmed` compares each installed file's SHA256 against the signed manifest, and for any drift, replaces the file from the published source after re-verifying the declared hash; a pre-flight verifies all replacements before any rename, so a hash failure leaves the install unchanged. Each replaced file is preserved as `.bak` for one cycle, and `rollback-foundation --user-confirmed` reverts that cycle. A confirmed apply-path `update-foundation` also applies any pending schema migrations after the file reconcile (idempotent; loud, ledgered failure) — note that `rollback-foundation` restores files only, never schema (migrations are forward-only). `verify-trust-root` prints the embedded key fingerprint for out-of-band verification. A periodic convenience check, suppressed by the marker file `~/.openclaw/erpclaw/.skip_reconcile` or the per-invocation flag `--no-reconcile-check`, may surface a reminder when version drift is present; the user runs `update-foundation` to apply. The router never modifies installed code without an explicit gated invocation. Signature verification is mandatory on the reconciliation path; the only exception is a documented operator recovery path that records to the audit log.\n\n> **Module authoring + variant analysis (developer tooling):** module generation, in-module feature injection, sandboxed test execution, deploy pipeline, variant analysis, gap detection, heartbeat analysis, semantic checks, and the OS-engine status command live in the optional `erpclaw-os-engine` addon (~30 actions, all `os-` prefixed). The addon is GitHub-only and not installed by default. Install via `module_manager.py --action install-module --module-name erpclaw-os-engine`. Foundation does not run module-generation or auto-deploy code paths.\n\n**Confirmation follows the two-class protocol in `## Runtime gate`:** for the destructive set (`close-fiscal-year`, `restore-database`, `install-module`, `rollback-foundation`, `generate-nacha-file`, plus `initialize-database --force` and `remove-module`/`schema-rollback`) get a genuine second yes before acting; for routine reversible work (`submit-*` / `cancel-*` / `approve-*` / `reject-*`, `setup-company`, `onboard`, `run-consolidation`) pass `--user-confirmed` on a clear request without re-asking. Speak in business terms; the action names are routing-only and never spoken to the user.\n\n## Optional scheduling (background email workers)\n\nERPClaw never installs cron jobs automatically. Two background workers are meant to run on a schedule once a business turns on email automation. Register them with the OpenClaw cron facility — the same `openclaw cron add` path used for any recurring ERPClaw job (confirm with the user first):\n\n```bash\n# Send queued emails — every 1 minute (erpclaw-alerts: process-email-queue)\nopenclaw cron add --name erpclaw-email-queue --cron \"* * * * *\" \\\n  --message \"Using erpclaw, run the process-email-queue action.\"\n\n# Advance drip-campaign sends — every 5 minutes (erpclaw-growth: process-drip-sends)\nopenclaw cron add --name erpclaw-drip-sends --cron \"*/5 * * * *\" \\\n  --message \"Using erpclaw, run the process-drip-sends action.\"\n```\n\n`process-email-queue` (every 1 minute) drains the email outbox with exponential backoff; `process-drip-sends` (every 5 minutes) advances due drip enrollments. Both are idempotent, so re-running inside an interval will not double-send. Remove a schedule with `openclaw cron remove --name <name>`. SKILL.md `cron:` blocks are decorative and never auto-register — explicit `openclaw cron add` is the only active scheduling path (see CHANGELOG v4.1.0).\n\n## Technical Details (Tier 3)\nRouter: `scripts/db_query.py` -> 14 core domains. Optional modules installed from GitHub (`avansaber/*`) to `~/.openclaw/erpclaw/modules/` (user-approved only). Single SQLite DB (WAL). 188 core tables (688 with modules). Money=TEXT(Decimal), IDs=TEXT(UUID4), GL immutable. Python 3.10+. All network activity limited to: (1) `fetch-exchange-rates`, the public exchange rate API; (2) `install-module`, git clone from `github.com/avansaber/*` only, requires user approval.\n\nFile v4.15.0:README.md\n\n<p align=\"center\">\n  <img src=\"assets/logo.svg\" alt=\"ERPClaw\" width=\"120\" height=\"120\">\n</p>\n\n<h1 align=\"center\">ERPClaw</h1>\n\n<p align=\"center\">\n  <em>The AI-native ERP. Chat with your books.</em>\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/license-GPL--3.0-green\" alt=\"License: GPL-3.0\">\n  <!-- version badge auto-synced by release/scripts/sync_facts.py (badge URL pattern) -->\n  <img src=\"https://img.shields.io/badge/version-v4.15.0-0d9488\" alt=\"Version v4.15.0\">\n  <img src=\"https://img.shields.io/badge/OpenClaw-skill-14b8a6\" alt=\"OpenClaw Skill\">\n  <img src=\"https://img.shields.io/badge/database-SQLite%20%7C%20PostgreSQL-0d9488\" alt=\"SQLite or PostgreSQL\">\n  <a href=\"https://www.erpclaw.ai\"><img src=\"https://img.shields.io/badge/website-erpclaw.ai-14b8a6\" alt=\"Website erpclaw.ai\"></a>\n  <img src=\"https://img.shields.io/badge/Stripe%20Marketplace-Live-635bff\" alt=\"Stripe Marketplace · Live\">\n</p>\n\n<p align=\"center\">\n  <img src=\"assets/demo.svg\" alt=\"ERPClaw demo: add a customer, invoice them, record their payment, and see a P&amp;L by department, all in plain English\" width=\"760\">\n</p>\n\n**ERPClaw runs your whole business from plain English.** Tell it \"invoice Wayne\nIndustries for 10 widgets at $100\" and it writes the invoice, posts the\nbalanced journal entries, and replies with the invoice number. Record a payment,\nclose the month, pull a P&L by department, run payroll, the assistant you\nalready use does the work and the books stay audit-grade underneath. No forms to\nclick through, no screens to learn, no per-seat bill. It is open source, it is\nfree forever, and it runs on your own machine so your data never leaves it.\n\nThe difference from every other \"AI\" accounting tool is structural: ERPClaw was\nbuilt AI-native from the first commit, with the assistant as the primary\ninterface and the accounting rules as auditable code. That is not a chat sidebar\nbolted onto a forms app, and it is not something a legacy product can retrofit.\n\n<sub>The rest of this README is the technical reference for installing and\nrunning the foundation. If you just want the product overview, see\n<a href=\"https://www.erpclaw.ai\">erpclaw.ai</a>.</sub>\n\n## Quick start\n\nERPClaw is one product. You install it once, then describe your business and it\nsets itself up.\n\n```\nclawhub install erpclaw\n```\n\nThis installs the foundation and initializes the database. From there you just\ntalk to your AI assistant:\n\n```\n\"I'm opening a retail store called Sunrise Goods in Portland, Oregon. Set me up.\"\n→ creates your company, a US GAAP chart of accounts, fiscal year, and tax rates\n```\n\nIndustry coverage comes the same way, with no second install command:\n\n```\n\"I'm a school\"           → ERPClaw pulls the education vertical and creates its tables\n\"I need manufacturing\"   → pulls Manufacturing, Projects, Assets, Quality, Support\n\"Set me up for a clinic\" → pulls clinical practice management\n```\n\nVertical code lives on GitHub and is fetched on demand by sparse checkout, so\nonly the module you asked for is downloaded. You never run another install\ncommand and never have to know what a module is.\n\n## What it covers\n\nOne install, one shared database, every major business function:\n\n- **Accounting**: double-entry general ledger, US GAAP chart of accounts,\n  immutable journal entries, multi-company, multi-currency\n- **Sales & buying**: customers, suppliers, orders, delivery notes, invoices,\n  credit notes, goods received notes\n- **Inventory**: items, warehouses, stock moves, serial and batch tracking,\n  reorder levels, valuation\n- **Payments & billing**: payment entries, bank reconciliation, usage-based\n  billing, recurring invoices, subscriptions\n- **Tax & payroll**: tax templates and returns, salary structures, FICA,\n  federal and state withholding, W-2s, garnishments\n- **Advanced accounting**: ASC 606 revenue recognition, ASC 842 leases,\n  intercompany transactions, consolidation\n- **Reporting**: trial balance, P&L, balance sheet, cash flow, AR/AP aging\n- **Industries**: retail, healthcare, education, property, construction,\n  agriculture, automotive, food, hospitality, legal, nonprofit, plus regional\n  tax packs for Canada, the UK, India, and the EU\n- **Deep integrations**: Stripe and Shopify sync straight into your general\n  ledger, free and self-hosted, with ASC 606 revenue recognition built in\n\n## Runs your way\n\n- **SQLite by default, PostgreSQL fully supported.** The same code runs on\n  either through a query-builder abstraction. SQLite needs zero setup; point it\n  at PostgreSQL when you outgrow a single file.\n- **Self-hosted.** The database lives at `~/.openclaw/erpclaw/data.sqlite` on\n  your own machine. Your books never leave your infrastructure.\n- **$0 forever.** GPL v3, open source, no freemium tier, no per-seat pricing.\n\n## Runtimes\n\nERPClaw runs on OpenClaw (primary). Experimental support for the Hermes Agent runtime.\n\n- **OpenClaw** (primary): `clawhub install erpclaw`. Everything above.\n- **Hermes Agent** (experimental, best-effort). Install from the tap:\n\n  ```bash\n  hermes skills tap add avansaber/hermes-skills\n  hermes skills install avansaber/hermes-skills/skills/erpclaw --force\n  export ERPCLAW_HOME=~/.hermes/erpclaw-home    # blank = ~/.openclaw/erpclaw\n  python3 ~/.hermes/skills/erpclaw/scripts/erpclaw-setup/db_query.py --action initialize-database\n  ```\n\n  `--force` acknowledges the Hermes security audit's \"caution\" rating: ERPClaw's\n  accounting engine runs local commands by design (every action is local Python\n  through a command router; nothing leaves your machine). Prefer no `--force`?\n  The manual install works identically:\n\n  ```bash\n  git clone https://github.com/avansaber/erpclaw ~/.hermes/skills/erp/erpclaw\n  export ERPCLAW_HOME=~/.hermes/erpclaw-home\n  python3 ~/.hermes/skills/erp/erpclaw/scripts/erpclaw-setup/db_query.py --action initialize-database\n  ```\n\n  Then talk to it (keep `ERPCLAW_HOME` exported):\n  `hermes chat -s erpclaw --yolo -q \"Set up my company\"`. Recommended:\n  `hermes curator pin erpclaw` so the skill stays exactly as installed.\n  Credential values never pass through the agent on any runtime: lookups return\n  existence plus a redacted preview only, and high-impact actions (restores,\n  credential changes, master-key operations) always require an explicit\n  confirmation flag.\n\n### Built to be trusted with money\n\n- All financial amounts stored as TEXT (Python `Decimal`), never float\n- IDs are UUID4 (TEXT)\n- GL entries are immutable; a cancellation posts reversing entries, never an edit\n- Every cross-table write happens in a single transaction\n- Every posting passes the full GL invariant validation before it commits\n\n## Reference\n\nThe foundation routes every operation through one entry point,\n`scripts/db_query.py --action {action} --args`, across 14 user-facing accounting\ndomains (`setup`, `gl`, `selling`, `buying`, `inventory`, `billing`, `tax`,\n`payments`, `journals`, `reports`, `hr`, `payroll`, `accounting-adv`) plus\n`meta` and `os` infrastructure. The module registry\n(`scripts/module_registry.json`) tracks every additional module across the\n`github.com/avansaber/*` repos and installs them on demand by sparse checkout.\n\nModule authoring and DGM evolution (code generation, sandboxed test runs, the\ndeploy pipeline) live in the optional\n[`erpclaw-os-engine`](https://github.com/avansaber/erpclaw-addons/tree/main/erpclaw-os-engine)\naddon, which is not installed by default. The foundation runs no\nmodule-generation or auto-deploy code paths.\n\n<sub>Current build:\n<!-- SYNC:facts:start -->\nERPClaw v4.15.0 | 46 modules (46 active + 0 preview) | 3,235 actions\n<!-- SYNC:facts:end -->\n</sub>\n\n## Web dashboard\n\nThe primary interface is the AI assistant, but two optional dashboards exist:\n\n- **[ERPClaw Web](https://github.com/avansaber/erpclaw-web)**: purpose-built\n  dashboard with live data tables, action execution, AI chat, and real-time\n  updates.\n- **[WebClaw](https://github.com/avansaber/webclaw)**: universal OpenClaw\n  dashboard that reads ERPClaw's SKILL.md and generates forms, tables, and\n  charts with zero per-skill setup (`clawhub install webclaw`).\n\n## Links\n\n- **Website:** [erpclaw.ai](https://www.erpclaw.ai)\n- **Stripe Marketplace:** [marketplace.stripe.com/apps/erpclaw-accounting](https://marketplace.stripe.com/apps/erpclaw-accounting)\n- **Docs:** [erpclaw.ai/docs](https://www.erpclaw.ai/docs)\n- **All modules:** [github.com/avansaber](https://github.com/avansaber)\n- **OpenClaw:** [openclaw.org](https://openclaw.org)\n\n## License\n\nGNU General Public License v3. Copyright © 2026 AvanSaber Inc.\n\nSee [LICENSE.txt](LICENSE.txt) for details.\n\nFile v4.15.0:_meta.json\n\n{\n  \"ownerId\": \"kn71eyhsjyvmm561yrd4k2dbzh80a07r\",\n  \"slug\": \"erpclaw\",\n  \"version\": \"4.15.0\",\n  \"publishedAt\": 1786896047188\n}\n\nFile v4.15.0:scripts/erpclaw-os/references/pattern_catalog.md\n\n# ERPClaw OS Pattern Catalog\n\nReusable patterns extracted from the existing 44-module codebase. Each pattern documents a common business entity type with schema, action, and test templates. Used by `generate_module.py` to map natural-language business descriptions to code templates.\n\n**Source of truth:** `pattern_library.py` defines the machine-readable version. This document is the human-readable reference.\n\n---\n\n## 1. CRUD Entity\n\n**Description:** The foundational pattern. A persistent business entity with create, read, update, and list operations. No financial posting, no lifecycle transitions beyond active/inactive.\n\n**Source modules:** legalclaw (`legalclaw_client_ext`), healthclaw-vet (`healthclaw_animal_patient`), automotiveclaw (`automotiveclaw_customer_ext`), nonprofitclaw (`nonprofitclaw_donor_ext`)\n\n**When to use:**\n- Any entity that needs to be stored and queried (clients, pets, vehicles, properties, employees, locations)\n- Master data tables that other records reference\n- Extension tables that augment core ERP entities (customer, item, employee)\n\n**Schema pattern:**\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_entity (\n    id          TEXT PRIMARY KEY,            -- UUID4\n    company_id  TEXT NOT NULL REFERENCES company(id),\n    name        TEXT NOT NULL,\n    status      TEXT DEFAULT 'active'\n                CHECK(status IN ('active','inactive','archived')),\n    notes       TEXT,\n    created_at  TEXT NOT NULL DEFAULT (datetime('now')),\n    updated_at  TEXT NOT NULL DEFAULT (datetime('now'))\n);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_entity_company\n    ON {prefix}_entity(company_id);\n```\n\nFor **extension tables** that link to a core entity:\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_customer_ext (\n    id              TEXT PRIMARY KEY,\n    naming_series   TEXT DEFAULT '{PREFIX}-',\n    customer_id     TEXT NOT NULL REFERENCES customer(id),\n    -- domain-specific fields here --\n    is_active       INTEGER DEFAULT 1,\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    created_at      TEXT DEFAULT (datetime('now')),\n    updated_at      TEXT DEFAULT (datetime('now'))\n);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_customer_ext_customer\n    ON {prefix}_customer_ext(customer_id);\n```\n\n**Action pattern:**\n```python\ndef handle_add_entity(args, conn):\n    entity_id = str(uuid.uuid4())\n    conn.execute(\n        \"INSERT INTO {prefix}_entity (id, company_id, name) VALUES (?, ?, ?)\",\n        (entity_id, args.company_id, args.name)\n    )\n    conn.commit()\n    ok({\"message\": \"Entity added\", \"id\": entity_id})\n\ndef handle_update_entity(args, conn):\n    conn.execute(\n        \"UPDATE {prefix}_entity SET name = ?, updated_at = datetime('now') WHERE id = ? AND company_id = ?\",\n        (args.name, args.id, args.company_id)\n    )\n    conn.commit()\n    ok({\"message\": \"Entity updated\"})\n\ndef handle_get_entity(args, conn):\n    row = conn.execute(\n        \"SELECT * FROM {prefix}_entity WHERE id = ? AND company_id = ?\",\n        (args.id, args.company_id)\n    ).fetchone()\n    if not row:\n        return err(\"Entity not found\")\n    ok(dict(row))\n\ndef handle_list_entities(args, conn):\n    rows = conn.execute(\n        \"SELECT * FROM {prefix}_entity WHERE company_id = ? ORDER BY created_at DESC\",\n        (args.company_id,)\n    ).fetchall()\n    ok({\"entities\": [dict(r) for r in rows], \"count\": len(rows)})\n```\n\n**Test pattern:**\n```python\ndef test_add_entity(db_conn, company_id):\n    result = run_action(\"add-entity\", name=\"Test\", company_id=company_id)\n    assert result[\"id\"]\n    row = db_conn.execute(\"SELECT * FROM {prefix}_entity WHERE id = ?\", (result[\"id\"],)).fetchone()\n    assert row[\"name\"] == \"Test\"\n\ndef test_list_entities_empty(db_conn, company_id):\n    result = run_action(\"list-entities\", company_id=company_id)\n    assert result[\"count\"] == 0\n\ndef test_update_entity(db_conn, company_id):\n    add_result = run_action(\"add-entity\", name=\"Old\", company_id=company_id)\n    run_action(\"update-entity\", id=add_result[\"id\"], name=\"New\", company_id=company_id)\n    row = db_conn.execute(\"SELECT name FROM {prefix}_entity WHERE id = ?\", (add_result[\"id\"],)).fetchone()\n    assert row[\"name\"] == \"New\"\n```\n\n---\n\n## 2. Appointment/Booking\n\n**Description:** A scheduled event with date/time, status lifecycle (scheduled -> confirmed -> in_progress -> completed | cancelled), and association to both a service provider and a client/patient/pet.\n\n**Source modules:** healthclaw (`healthclaw_appointment`), healthclaw-vet (via healthclaw appointments), hospitalityclaw (reservations)\n\n**When to use:**\n- Any business that schedules time-bound events (medical visits, grooming sessions, inspections, consultations)\n- When you need to track appointment status transitions and prevent double-booking\n\n**Schema pattern:**\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_appointment (\n    id              TEXT PRIMARY KEY,\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    customer_id     TEXT NOT NULL REFERENCES customer(id),\n    -- optional link to domain entity (pet, patient, vehicle, property) --\n    entity_id       TEXT REFERENCES {prefix}_entity(id),\n    provider_id     TEXT,                   -- staff/practitioner ID\n    date            TEXT NOT NULL,           -- ISO-8601 date\n    start_time      TEXT,                    -- HH:MM\n    end_time        TEXT,                    -- HH:MM\n    appointment_type TEXT,\n    status          TEXT NOT NULL DEFAULT 'scheduled'\n                    CHECK(status IN ('scheduled','confirmed','in_progress',\n                                     'completed','cancelled','no_show')),\n    notes           TEXT,\n    created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n    updated_at      TEXT NOT NULL DEFAULT (datetime('now'))\n);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_appointment_company\n    ON {prefix}_appointment(company_id);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_appointment_date\n    ON {prefix}_appointment(date);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_appointment_status\n    ON {prefix}_appointment(status);\n```\n\n**Action pattern:**\n```python\ndef handle_add_appointment(args, conn):\n    appt_id = str(uuid.uuid4())\n    conn.execute(\n        \"\"\"INSERT INTO {prefix}_appointment\n           (id, company_id, customer_id, date, start_time, status)\n           VALUES (?, ?, ?, ?, ?, 'scheduled')\"\"\",\n        (appt_id, args.company_id, args.customer_id, args.date, args.start_time)\n    )\n    conn.commit()\n    ok({\"message\": \"Appointment scheduled\", \"id\": appt_id})\n\ndef handle_update_appointment(args, conn):\n    # Only allow updates when status is 'scheduled' or 'confirmed'\n    row = conn.execute(\n        \"SELECT status FROM {prefix}_appointment WHERE id = ? AND company_id = ?\",\n        (args.id, args.company_id)\n    ).fetchone()\n    if not row:\n        return err(\"Appointment not found\")\n    if row[\"status\"] not in (\"scheduled\", \"confirmed\"):\n        return err(f\"Cannot update appointment in '{row['status']}' status\")\n    # ... perform update ...\n```\n\n**Test pattern:**\n```python\ndef test_add_appointment(db_conn, company_id, customer_id):\n    result = run_action(\"add-appointment\", company_id=company_id,\n                        customer_id=customer_id, date=\"2026-04-01\")\n    assert result[\"id\"]\n    row = db_conn.execute(\"SELECT status FROM {prefix}_appointment WHERE id = ?\",\n                          (result[\"id\"],)).fetchone()\n    assert row[\"status\"] == \"scheduled\"\n\ndef test_cannot_update_completed_appointment(db_conn, company_id):\n    # Setup: create and complete an appointment\n    # Assert: update attempt returns error\n```\n\n---\n\n## 3. Service Record\n\n**Description:** A record of work performed, linked to an entity (patient, pet, vehicle) and optionally to an appointment. Captures service type, description, and amount charged.\n\n**Source modules:** healthclaw-vet (via treatment records), automotiveclaw (service records), hospitalityclaw (service tickets)\n\n**When to use:**\n- When work is performed and needs to be documented (treatments, repairs, inspections, cleanings)\n- Often linked to both an appointment and an entity being serviced\n\n**Schema pattern:**\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_service_record (\n    id              TEXT PRIMARY KEY,\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    entity_id       TEXT NOT NULL REFERENCES {prefix}_entity(id),\n    appointment_id  TEXT REFERENCES {prefix}_appointment(id),\n    service_type    TEXT NOT NULL,\n    description     TEXT,\n    amount          TEXT DEFAULT '0',       -- TEXT for Decimal precision\n    performed_by    TEXT,                    -- staff/practitioner\n    performed_date  TEXT NOT NULL DEFAULT (date('now')),\n    notes           TEXT,\n    created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n    updated_at      TEXT NOT NULL DEFAULT (datetime('now'))\n);\n```\n\n**Action pattern:**\n```python\ndef handle_add_service_record(args, conn):\n    record_id = str(uuid.uuid4())\n    conn.execute(\n        \"\"\"INSERT INTO {prefix}_service_record\n           (id, company_id, entity_id, service_type, amount, description)\n           VALUES (?, ?, ?, ?, ?, ?)\"\"\",\n        (record_id, args.company_id, args.entity_id,\n         args.service_type, args.amount or \"0\", args.description)\n    )\n    conn.commit()\n    ok({\"message\": \"Service record added\", \"id\": record_id})\n```\n\n**Test pattern:**\n```python\ndef test_add_service_record(db_conn, company_id, entity_id):\n    result = run_action(\"add-service-record\", company_id=company_id,\n                        entity_id=entity_id, service_type=\"bath\",\n                        amount=\"45.00\")\n    assert result[\"id\"]\n    row = db_conn.execute(\"SELECT amount FROM {prefix}_service_record WHERE id = ?\",\n                          (result[\"id\"],)).fetchone()\n    assert Decimal(row[\"amount\"]) == Decimal(\"45.00\")\n```\n\n---\n\n## 4. Invoice Delegation (cross_skill Billing)\n\n**Description:** The correct way for non-core modules to create financial documents. Uses `cross_skill.create_invoice()` or `cross_skill.call_skill_action()` to delegate to the core billing engine. Never directly writes to `gl_entry`, `sales_invoice`, or `payment_entry`.\n\n**Source modules:** healthclaw, hospitalityclaw, automotiveclaw (verticals that bill via cross-skill delegation)\n\n**When to use:**\n- Any time a vertical module needs to generate an invoice, process a payment, or trigger GL postings\n- Constitution Article 6 mandates this: no direct GL writes allowed\n\n**Schema pattern:**\nNo additional tables needed -- the module creates line items in its own tables and delegates the invoice creation to core ERP.\n\n**Action pattern:**\n```python\nfrom erpclaw_lib.cross_skill import create_invoice\n\ndef handle_create_invoice(args, conn):\n    # 1. Gather billable items from module's own tables\n    records = conn.execute(\n        \"SELECT * FROM {prefix}_service_record WHERE appointment_id = ? AND status = 'completed'\",\n        (args.appointment_id,)\n    ).fetchall()\n\n    if not records:\n        return err(\"No billable records found\")\n\n    # 2. Build invoice lines\n    items = []\n    for r in records:\n        items.append({\n            \"description\": r[\"service_type\"],\n            \"qty\": 1,\n            \"rate\": r[\"amount\"],\n        })\n\n    # 3. Delegate to core billing via cross_skill\n    invoice_result = create_invoice(\n        conn=conn,\n        company_id=args.company_id,\n        customer_id=args.customer_id,\n        items=items,\n        posting_date=date.today().isoformat(),\n    )\n\n    ok({\"message\": \"Invoice created\", \"invoice_id\": invoice_result[\"id\"]})\n```\n\n**Test pattern:**\n```python\ndef test_create_invoice_delegates(db_conn, company_id, customer_id, mocker):\n    mock_create = mocker.patch(\"erpclaw_lib.cross_skill.create_invoice\",\n                                return_value={\"id\": \"inv-001\"})\n    result = run_action(\"create-invoice\", company_id=company_id,\n                        customer_id=customer_id, appointment_id=\"apt-1\")\n    mock_create.assert_called_once()\n    assert result[\"invoice_id\"] == \"inv-001\"\n```\n\n---\n\n## 5. Compliance/Licensing\n\n**Description:** Tracks certifications, licenses, inspections, and compliance records with expiry dates and renewal workflows. Commonly paired with alerts for upcoming expirations.\n\n**Source modules:** legalclaw (`legalclaw_bar_admission`, `legalclaw_cle_record`), constructclaw (permits, certifications), healthclaw (provider licenses)\n\n**When to use:**\n- Any business that requires professional licenses (legal, medical, construction)\n- Regulatory compliance tracking (inspections, audits, certifications)\n- When expiry dates trigger renewal workflows\n\n**Schema pattern:**\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_license (\n    id              TEXT PRIMARY KEY,\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    holder_id       TEXT NOT NULL,           -- employee, practitioner, or entity\n    license_type    TEXT NOT NULL,\n    license_number  TEXT,\n    jurisdiction    TEXT,\n    issue_date      TEXT NOT NULL,\n    expiry_date     TEXT,\n    status          TEXT DEFAULT 'active'\n                    CHECK(status IN ('active','expired','suspended','revoked','pending')),\n    notes           TEXT,\n    created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n    updated_at      TEXT NOT NULL DEFAULT (datetime('now'))\n);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_license_expiry\n    ON {prefix}_license(expiry_date);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_license_status\n    ON {prefix}_license(status);\n```\n\n**Action pattern:**\n```python\ndef handle_add_license(args, conn):\n    license_id = str(uuid.uuid4())\n    conn.execute(\n        \"\"\"INSERT INTO {prefix}_license\n           (id, company_id, holder_id, license_type, license_number,\n            issue_date, expiry_date)\n           VALUES (?, ?, ?, ?, ?, ?, ?)\"\"\",\n        (license_id, args.company_id, args.holder_id, args.license_type,\n         args.license_number, args.issue_date, args.expiry_date)\n    )\n    conn.commit()\n    ok({\"message\": \"License added\", \"id\": license_id})\n\ndef handle_check_expiring(args, conn):\n    \"\"\"List licenses expiring within N days.\"\"\"\n    rows = conn.execute(\n        \"\"\"SELECT * FROM {prefix}_license\n           WHERE company_id = ? AND status = 'active'\n             AND expiry_date <= date('now', '+' || ? || ' days')\n           ORDER BY expiry_date\"\"\",\n        (args.company_id, args.days or 30)\n    ).fetchall()\n    ok({\"expiring\": [dict(r) for r in rows], \"count\": len(rows)})\n```\n\n**Test pattern:**\n```python\ndef test_add_license(db_conn, company_id):\n    result = run_action(\"add-license\", company_id=company_id,\n                        holder_id=\"emp-1\", license_type=\"bar_admission\",\n                        issue_date=\"2025-01-01\", expiry_date=\"2027-01-01\")\n    assert result[\"id\"]\n\ndef test_check_expiring_finds_upcoming(db_conn, company_id):\n    # Add a license expiring in 10 days\n    run_action(\"add-license\", ..., expiry_date=ten_days_from_now)\n    result = run_action(\"check-expiring\", company_id=company_id, days=30)\n    assert result[\"count\"] >= 1\n```\n\n---\n\n## 6. Prepaid Package (Credit Balance)\n\n**Description:** A bundle of prepaid sessions or credits that a customer purchases upfront and uses over time. Tracks total sessions, used sessions, and remaining balance. Packages expire or become exhausted.\n\n**Source modules:** hospitalityclaw (prepaid stay bundles), erpclaw-billing (prepaid credits)\n\n**When to use:**\n- Prepaid service bundles (10-pack of sessions, monthly retainer hours)\n- Gift cards or service credits\n- Membership tiers with included services\n\n**Schema pattern:**\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_package (\n    id              TEXT PRIMARY KEY,\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    customer_id     TEXT NOT NULL REFERENCES customer(id),\n    package_type    TEXT NOT NULL,\n    total_sessions  INTEGER NOT NULL,\n    used_sessions   INTEGER NOT NULL DEFAULT 0,\n    amount          TEXT DEFAULT '0',        -- purchase price\n    purchase_date   TEXT DEFAULT (date('now')),\n    expiry_date     TEXT,\n    status          TEXT NOT NULL DEFAULT 'active'\n                    CHECK(status IN ('active','exhausted','expired','cancelled')),\n    created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n    updated_at      TEXT NOT NULL DEFAULT (datetime('now'))\n);\n\nCREATE TABLE IF NOT EXISTS {prefix}_package_usage (\n    id              TEXT PRIMARY KEY,\n    package_id      TEXT NOT NULL REFERENCES {prefix}_package(id),\n    used_date       TEXT NOT NULL DEFAULT (date('now')),\n    notes           TEXT,\n    created_at      TEXT NOT NULL DEFAULT (datetime('now'))\n);\n```\n\n**Action pattern:**\n```python\ndef handle_use_session(args, conn):\n    pkg = conn.execute(\n        \"SELECT * FROM {prefix}_package WHERE id = ? AND company_id = ?\",\n        (args.package_id, args.company_id)\n    ).fetchone()\n    if not pkg:\n        return err(\"Package not found\")\n    if pkg[\"status\"] != \"active\":\n        return err(f\"Package is {pkg['status']}\")\n    if pkg[\"used_sessions\"] >= pkg[\"total_sessions\"]:\n        return err(\"Package exhausted\")\n\n    new_used = pkg[\"used_sessions\"] + 1\n    new_status = \"exhausted\" if new_used >= pkg[\"total_sessions\"] else \"active\"\n\n    conn.execute(\"BEGIN\")\n    conn.execute(\n        \"UPDATE {prefix}_package SET used_sessions = ?, status = ?, updated_at = datetime('now') WHERE id = ?\",\n        (new_used, new_status, args.package_id)\n    )\n    usage_id = str(uuid.uuid4())\n    conn.execute(\n        \"INSERT INTO {prefix}_package_usage (id, package_id) VALUES (?, ?)\",\n        (usage_id, args.package_id)\n    )\n    conn.execute(\"COMMIT\")\n    ok({\"remaining\": pkg[\"total_sessions\"] - new_used, \"status\": new_status})\n```\n\n**Test pattern:**\n```python\ndef test_use_session_decrements(db_conn, package_id, company_id):\n    result = run_action(\"use-session\", package_id=package_id, company_id=company_id)\n    assert result[\"remaining\"] == 9  # if total was 10\n\ndef test_use_session_exhausts_package(db_conn, company_id):\n    # Create package with 1 session, use it\n    pkg = run_action(\"add-package\", total_sessions=1, ...)\n    result = run_action(\"use-session\", package_id=pkg[\"id\"], ...)\n    assert result[\"status\"] == \"exhausted\"\n\ndef test_use_session_on_exhausted_fails(db_conn, exhausted_package_id, company_id):\n    result = run_action(\"use-session\", package_id=exhausted_package_id, ...)\n    assert \"error\" in result\n```\n\n---\n\n## 7. Recurring Billing (Monthly Charges)\n\n**Description:** Generates invoices on a recurring schedule (monthly, quarterly, annually). Handles proration, late fees, and delinquency tracking. Uses invoice delegation (Pattern 4) for actual GL posting.\n\n**Source modules:** propertyclaw (`propertyclaw_lease`, rent charges), erpclaw-billing (recurring template)\n\n**When to use:**\n- Subscription or lease-based businesses (storage, property, SaaS, memberships)\n- Any scenario with periodic billing cycles\n- When late fees and delinquency tracking are needed\n\n**Schema pattern:**\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_billing_cycle (\n    id              TEXT PRIMARY KEY,\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    entity_id       TEXT NOT NULL,           -- rental, lease, subscription\n    billing_date    TEXT NOT NULL,            -- next billing date\n    amount          TEXT NOT NULL DEFAULT '0',\n    frequency       TEXT NOT NULL DEFAULT 'monthly'\n                    CHECK(frequency IN ('weekly','monthly','quarterly','annually')),\n    status          TEXT NOT NULL DEFAULT 'pending'\n                    CHECK(status IN ('pending','invoiced','paid','overdue','delinquent')),\n    late_fee_amount TEXT DEFAULT '0',\n    days_overdue    INTEGER DEFAULT 0,\n    created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n    updated_at      TEXT NOT NULL DEFAULT (datetime('now'))\n);\n```\n\n**Action pattern:**\n```python\nfrom erpclaw_lib.cross_skill import create_invoice\n\ndef handle_generate_invoices(args, conn):\n    \"\"\"Generate invoices for all billing cycles due on or before today.\"\"\"\n    today = date.today().isoformat()\n    due_cycles = conn.execute(\n        \"\"\"SELECT * FROM {prefix}_billing_cycle\n           WHERE company_id = ? AND status = 'pending' AND billing_date <= ?\"\"\",\n        (args.company_id, today)\n    ).fetchall()\n\n    created = 0\n    for cycle in due_cycles:\n        invoice = create_invoice(conn=conn, company_id=args.company_id,\n                                  customer_id=cycle[\"customer_id\"],\n                                  items=[{\"description\": \"Monthly charge\", \"qty\": 1,\n                                          \"rate\": cycle[\"amount\"]}],\n                                  posting_date=today)\n        conn.execute(\n            \"UPDATE {prefix}_billing_cycle SET status = 'invoiced', updated_at = datetime('now') WHERE id = ?\",\n            (cycle[\"id\"],)\n        )\n        created += 1\n    conn.commit()\n    ok({\"invoices_created\": created})\n\ndef handle_apply_late_fees(args, conn):\n    \"\"\"Apply late fees to overdue billing cycles.\"\"\"\n    # ... find overdue cycles, calculate late fees, update records ...\n```\n\n**Test pattern:**\n```python\ndef test_generate_invoices_creates_for_due(db_conn, company_id, mocker):\n    # Setup: create a billing cycle due today\n    mock_invoice = mocker.patch(\"erpclaw_lib.cross_skill.create_invoice\",\n                                 return_value={\"id\": \"inv-1\"})\n    result = run_action(\"generate-invoices\", company_id=company_id)\n    assert result[\"invoices_created\"] == 1\n    mock_invoice.assert_called_once()\n\ndef test_generate_invoices_skips_future(db_conn, company_id):\n    # Setup: create a billing cycle due next month\n    result = run_action(\"generate-invoices\", company_id=company_id)\n    assert result[\"invoices_created\"] == 0\n```\n\n---\n\n## 8. Inventory Tracking\n\n**Description:** Items with stock levels in warehouses. Uses the core ERP item and warehouse tables via READ access, and delegates stock movements via `cross_skill` or the core selling/buying workflows.\n\n**Source modules:** retailclaw (`retailclaw_price_list` extends core item), automotiveclaw (`automotiveclaw_vehicle` is inventory)\n\n**When to use:**\n- When the module has physical goods to track (retail products, vehicle inventory, parts)\n- For domain-specific item extensions (retail pricing, vehicle VINs)\n- Always reads from core `item` and `warehouse_stock` tables; never writes directly\n\n**Schema pattern:**\n```sql\n-- Extension table for domain-specific item attributes\nCREATE TABLE IF NOT EXISTS {prefix}_item_ext (\n    id              TEXT PRIMARY KEY,\n    item_id         TEXT NOT NULL REFERENCES item(id),\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    -- domain-specific fields --\n    category        TEXT,\n    sku_override    TEXT,\n    created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n    updated_at      TEXT NOT NULL DEFAULT (datetime('now'))\n);\n```\n\n**Action pattern:**\n```python\ndef handle_list_inventory(args, conn):\n    \"\"\"List inventory with domain-specific attributes joined to core item data.\"\"\"\n    rows = conn.execute(\n        \"\"\"SELECT i.id, i.item_name, i.item_group, ext.category,\n                  COALESCE(ws.actual_qty, '0') as qty_on_hand\n           FROM item i\n           LEFT JOIN {prefix}_item_ext ext ON i.id = ext.item_id\n           LEFT JOIN warehouse_stock ws ON i.id = ws.item_id\n           WHERE i.company_id = ?\n           ORDER BY i.item_name\"\"\",\n        (args.company_id,)\n    ).fetchall()\n    ok({\"items\": [dict(r) for r in rows], \"count\": len(rows)})\n```\n\n**Test pattern:**\n```python\ndef test_list_inventory_joins_extension(db_conn, company_id, item_id):\n    # Setup: core item exists + extension record\n    result = run_action(\"list-inventory\", company_id=company_id)\n    assert result[\"count\"] >= 1\n    assert \"category\" in result[\"items\"][0]\n```\n\n---\n\n## 9. Employee/Staff Management\n\n**Description:** Domain-specific staff roles and assignments. Extends the core ERP employee table with profession-specific fields (practitioner type, specializations, certifications).\n\n**Source modules:** healthclaw (`healthclaw_practitioner`), legalclaw (attorney profiles), constructclaw (crew assignments)\n\n**When to use:**\n- When staff have domain-specific attributes not in core employee (specialization, license, department assignment)\n- Professional credentials tracking\n\n**Schema pattern:**\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_staff_ext (\n    id              TEXT PRIMARY KEY,\n    employee_id     TEXT NOT NULL REFERENCES employee(id),\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    role            TEXT NOT NULL,\n    specialization  TEXT,\n    license_number  TEXT,\n    is_active       INTEGER DEFAULT 1,\n    created_at      TEXT NOT NULL DEFAULT (datetime('now')),\n    updated_at      TEXT NOT NULL DEFAULT (datetime('now'))\n);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_staff_ext_employee\n    ON {prefix}_staff_ext(employee_id);\n```\n\n**Action pattern:**\n```python\ndef handle_add_staff(args, conn):\n    staff_id = str(uuid.uuid4())\n    conn.execute(\n        \"\"\"INSERT INTO {prefix}_staff_ext\n           (id, employee_id, company_id, role, specialization)\n           VALUES (?, ?, ?, ?, ?)\"\"\",\n        (staff_id, args.employee_id, args.company_id, args.role, args.specialization)\n    )\n    conn.commit()\n    ok({\"message\": \"Staff profile created\", \"id\": staff_id})\n```\n\n**Test pattern:**\n```python\ndef test_add_staff_creates_profile(db_conn, company_id, employee_id):\n    result = run_action(\"add-staff\", employee_id=employee_id,\n                        company_id=company_id, role=\"attorney\")\n    assert result[\"id\"]\n    row = db_conn.execute(\"SELECT role FROM {prefix}_staff_ext WHERE id = ?\",\n                          (result[\"id\"],)).fetchone()\n    assert row[\"role\"] == \"attorney\"\n```\n\n---\n\n## 10. Customer Management (Core + Extension)\n\n**Description:** The standard pattern for customer data in vertical modules. Core customer fields (name, email, phone, address) live in the core `customer` table. The vertical adds an extension table for domain-specific attributes (donor level, client type, pet owner preferences).\n\n**Source modules:** legalclaw (`legalclaw_client_ext`), automotiveclaw (`automotiveclaw_customer_ext`), nonprofitclaw (`nonprofitclaw_donor_ext`), healthclaw (patient extension via core customer FK)\n\n**When to use:**\n- Every vertical module that has customers/clients/patients/donors\n- When you need domain-specific customer attributes beyond what core provides\n- When migrating from a standalone customer table to the extension pattern\n\n**Schema pattern:**\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_customer_ext (\n    id              TEXT PRIMARY KEY,\n    naming_series   TEXT DEFAULT '{PREFIX}-',\n    customer_id     TEXT NOT NULL REFERENCES customer(id),\n    -- Domain-specific fields (examples): --\n    customer_type   TEXT DEFAULT 'individual'\n                    CHECK(customer_type IN ('individual','business','government')),\n    domain_field_1  TEXT,\n    domain_field_2  TEXT,\n    is_active       INTEGER DEFAULT 1,\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    created_at      TEXT DEFAULT (datetime('now')),\n    updated_at      TEXT DEFAULT (datetime('now'))\n);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_customer_ext_company\n    ON {prefix}_customer_ext(company_id);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_customer_ext_customer\n    ON {prefix}_customer_ext(customer_id);\n```\n\n**Action pattern:**\n```python\ndef handle_add_client(args, conn):\n    \"\"\"Add a domain client by creating core customer + extension record in one transaction.\"\"\"\n    conn.execute(\"BEGIN\")\n    # 1. Create core customer (via cross_skill or direct if module is allowed)\n    from erpclaw_lib.cross_skill import call_skill_action\n    core_result = call_skill_action(\"erpclaw\", \"add-customer\",\n                                     name=args.name, company_id=args.company_id)\n    customer_id = core_result[\"id\"]\n\n    # 2. Create extension record\n    ext_id = str(uuid.uuid4())\n    conn.execute(\n        \"\"\"INSERT INTO {prefix}_customer_ext\n           (id, customer_id, company_id, customer_type)\n           VALUES (?, ?, ?, ?)\"\"\",\n        (ext_id, customer_id, args.company_id, args.customer_type or \"individual\")\n    )\n    conn.execute(\"COMMIT\")\n    ok({\"message\": \"Client added\", \"id\": ext_id, \"customer_id\": customer_id})\n\ndef handle_get_client(args, conn):\n    \"\"\"Get client with joined core customer data.\"\"\"\n    row = conn.execute(\n        \"\"\"SELECT ext.*, c.customer_name, c.email, c.phone\n           FROM {prefix}_customer_ext ext\n           JOIN customer c ON ext.customer_id = c.id\n           WHERE ext.id = ? AND ext.company_id = ?\"\"\",\n        (args.id, args.company_id)\n    ).fetchone()\n    if not row:\n        return err(\"Client not found\")\n    ok(dict(row))\n```\n\n**Test pattern:**\n```python\ndef test_add_client_creates_extension(db_conn, company_id, mocker):\n    mock_core = mocker.patch(\"erpclaw_lib.cross_skill.call_skill_action\",\n                              return_value={\"id\": \"cust-1\"})\n    result = run_action(\"add-client\", name=\"Jane Doe\", company_id=company_id)\n    assert result[\"customer_id\"] == \"cust-1\"\n    ext = db_conn.execute(\"SELECT * FROM {prefix}_customer_ext WHERE id = ?\",\n                          (result[\"id\"],)).fetchone()\n    assert ext[\"customer_id\"] == \"cust-1\"\n\ndef test_get_client_joins_core_data(db_conn, company_id, client_id):\n    result = run_action(\"get-client\", id=client_id, company_id=company_id)\n    assert \"customer_name\" in result  # from core customer table\n    assert \"customer_type\" in result  # from extension table\n```\n\n---\n\n## Pattern Selection Guide\n\n| Business Need | Pattern | Example Modules |\n|---------------|---------|-----------------|\n| Store and query entities | 1. CRUD Entity | All modules |\n| Schedule time-bound events | 2. Appointment/Booking | healthclaw, hospitalityclaw |\n| Document work performed | 3. Service Record | healthclaw-vet, automotiveclaw |\n| Create invoices/payments | 4. Invoice Delegation | healthclaw, hospitalityclaw, automotiveclaw |\n| Track licenses/certifications | 5. Compliance/Licensing | legalclaw, constructclaw |\n| Sell prepaid session bundles | 6. Prepaid Package | hospitalityclaw |\n| Bill on recurring schedule | 7. Recurring Billing | propertyclaw, erpclaw-billing |\n| Manage physical goods | 8. Inventory Tracking | retailclaw, automotiveclaw |\n| Extend employee profiles | 9. Employee/Staff Management | healthclaw, legalclaw |\n| Extend customer profiles | 10. Customer Management | All verticals |\n\nMost modules use 3-5 of these patterns combined. For example, a veterinary clinic module uses: CRUD Entity (pets), Appointment/Booking (visits), Service Record (treatments), Invoice Delegation (billing), and Customer Management (pet owners via core customer extension).\n\nFile v4.15.0:scripts/erpclaw-gl/assets/charts/us_gaap.json\n\n[\n  {\n    \"account_number\": \"1000\",\n    \"name\": \"Assets\",\n    \"parent_number\": null,\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1100\",\n    \"name\": \"Current Assets\",\n    \"parent_number\": \"1000\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1110\",\n    \"name\": \"Cash and Cash Equivalents\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1111\",\n    \"name\": \"Petty Cash\",\n    \"parent_number\": \"1110\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"cash\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1112\",\n    \"name\": \"Operating Checking Account\",\n    \"parent_number\": \"1110\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"bank\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1113\",\n    \"name\": \"Savings Account\",\n    \"parent_number\": \"1110\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"bank\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1120\",\n    \"name\": \"Accounts Receivable\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1121\",\n    \"name\": \"Trade Receivables\",\n    \"parent_number\": \"1120\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1122\",\n    \"name\": \"Other Receivables\",\n    \"parent_number\": \"1120\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1130\",\n    \"name\": \"Inventory\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1131\",\n    \"name\": \"Raw Materials\",\n    \"parent_number\": \"1130\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1132\",\n    \"name\": \"Work in Progress\",\n    \"parent_number\": \"1130\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1133\",\n    \"name\": \"Finished Goods\",\n    \"parent_number\": \"1130\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1140\",\n    \"name\": \"Prepaid Expenses\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1141\",\n    \"name\": \"Prepaid Insurance\",\n    \"parent_number\": \"1140\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1142\",\n    \"name\": \"Prepaid Rent\",\n    \"parent_number\": \"1140\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1150\",\n    \"name\": \"Other Current Assets\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1151\",\n    \"name\": \"Employee Advances\",\n    \"parent_number\": \"1150\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1152\",\n    \"name\": \"Security Deposits\",\n    \"parent_number\": \"1150\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1200\",\n    \"name\": \"Non-Current Assets\",\n    \"parent_number\": \"1000\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1210\",\n    \"name\": \"Property, Plant & Equipment\",\n    \"parent_number\": \"1200\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1211\",\n    \"name\": \"Land\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1212\",\n    \"name\": \"Buildings\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1213\",\n    \"name\": \"Furniture and Fixtures\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1214\",\n    \"name\": \"Office Equipment\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1215\",\n    \"name\": \"Vehicles\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1216\",\n    \"name\": \"Accumulated Depreciation - Buildings\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"accumulated_depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1217\",\n    \"name\": \"Accumulated Depreciation - FF&E\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"accumulated_depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1218\",\n    \"name\": \"Accumulated Depreciation - Vehicles\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"accumulated_depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1219\",\n    \"name\": \"Capital Work in Progress\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1220\",\n    \"name\": \"Intangible Assets\",\n    \"parent_number\": \"1200\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1221\",\n    \"name\": \"Software Licenses\",\n    \"parent_number\": \"1220\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1222\",\n    \"name\": \"Accumulated Amortization\",\n    \"parent_number\": \"1220\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"accumulated_depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1230\",\n    \"name\": \"Stock Received Not Billed\",\n    \"parent_number\": \"1200\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock_received_not_billed\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1231\",\n    \"name\": \"Asset Received Not Billed\",\n    \"parent_number\": \"1200\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"asset_received_not_billed\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"2000\",\n    \"name\": \"Liabilities\",\n    \"parent_number\": null,\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2100\",\n    \"name\": \"Current Liabilities\",\n    \"parent_number\": \"2000\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2110\",\n    \"name\": \"Accounts Payable\",\n    \"parent_number\": \"2100\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2111\",\n    \"name\": \"Trade Payables\",\n    \"parent_number\": \"2110\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2112\",\n    \"name\": \"Accrued Expenses\",\n    \"parent_number\": \"2110\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2120\",\n    \"name\": \"Taxes Payable\",\n    \"parent_number\": \"2100\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2121\",\n    \"name\": \"Sales Tax Payable\",\n    \"parent_number\": \"2120\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"tax\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2122\",\n    \"name\": \"Federal Income Tax Payable\",\n    \"parent_number\": \"2120\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"tax\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2123\",\n    \"name\": \"State Income Tax Payable\",\n    \"parent_number\": \"2120\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"tax\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2124\",\n    \"name\": \"Payroll Tax Payable\",\n    \"parent_number\": \"2120\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"tax\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2130\",\n    \"name\": \"Other Current Liabilities\",\n    \"parent_number\": \"2100\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2131\",\n    \"name\": \"Unearned Revenue\",\n    \"parent_number\": \"2130\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2132\",\n    \"name\": \"Short-Term Loans\",\n    \"parent_number\": \"2130\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2133\",\n    \"name\": \"Payroll Payable\",\n    \"parent_number\": \"2130\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payroll_payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2200\",\n    \"name\": \"Non-Current Liabilities\",\n    \"parent_number\": \"2000\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2210\",\n    \"name\": \"Long-Term Loans\",\n    \"parent_number\": \"2200\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2220\",\n    \"name\": \"Mortgage Payable\",\n    \"parent_number\": \"2200\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3000\",\n    \"name\": \"Equity\",\n    \"parent_number\": null,\n    \"root_type\": \"equity\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3100\",\n    \"name\": \"Owner's Equity\",\n    \"parent_number\": \"3000\",\n    \"root_type\": \"equity\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3110\",\n    \"name\": \"Common Stock\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"equity\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3120\",\n    \"name\": \"Additional Paid-In Capital\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"equity\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3130\",\n    \"name\": \"Retained Earnings\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"equity\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3140\",\n    \"name\": \"Dividends Paid\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"equity\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3150\",\n    \"name\": \"Opening Balance Equity\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"temporary\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4000\",\n    \"name\": \"Income\",\n    \"parent_number\": null,\n    \"root_type\": \"income\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4100\",\n    \"name\": \"Operating Revenue\",\n    \"parent_number\": \"4000\",\n    \"root_type\": \"income\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4110\",\n    \"name\": \"Sales Revenue\",\n    \"parent_number\": \"4100\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4120\",\n    \"name\": \"Service Revenue\",\n    \"parent_number\": \"4100\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4130\",\n    \"name\": \"Sales Returns & Allowances\",\n    \"parent_number\": \"4100\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4140\",\n    \"name\": \"Sales Discounts\",\n    \"parent_number\": \"4100\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4200\",\n    \"name\": \"Other Income\",\n    \"parent_number\": \"4000\",\n    \"root_type\": \"income\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4210\",\n    \"name\": \"Interest Income\",\n    \"parent_number\": \"4200\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4220\",\n    \"name\": \"Gain on Asset Disposal\",\n    \"parent_number\": \"4200\",\n    \"root_type\": \"income\",\n    \"account_type\": \"disposal_gain_loss\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4230\",\n    \"name\": \"Exchange Gain\",\n    \"parent_number\": \"4200\",\n    \"root_type\": \"income\",\n    \"account_type\": \"exchange_gain_loss\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"5000\",\n    \"name\": \"Expenses\",\n    \"parent_number\": null,\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5100\",\n    \"name\": \"Cost of Goods Sold\",\n    \"parent_number\": \"5000\",\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5110\",\n    \"name\": \"COGS - Materials\",\n    \"parent_number\": \"5100\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"cost_of_goods_sold\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5120\",\n    \"name\": \"COGS - Labor\",\n    \"parent_number\": \"5100\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"cost_of_goods_sold\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5130\",\n    \"name\": \"COGS - Overhead\",\n    \"parent_number\": \"5100\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"cost_of_goods_sold\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5140\",\n    \"name\": \"Stock Adjustment\",\n    \"parent_number\": \"5100\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"stock_adjustment\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5200\",\n    \"name\": \"Operating Expenses\",\n    \"parent_number\": \"5000\",\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5210\",\n    \"name\": \"Salaries and Wages\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5220\",\n    \"name\": \"Rent Expense\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5230\",\n    \"name\": \"Utilities\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5240\",\n    \"name\": \"Office Supplies\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5250\",\n    \"name\": \"Insurance\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5260\",\n    \"name\": \"Depreciation Expense\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5270\",\n    \"name\": \"Marketing and Advertising\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5280\",\n    \"name\": \"Professional Fees\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5290\",\n    \"name\": \"Travel and Entertainment\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5300\",\n    \"name\": \"Other Expenses\",\n    \"parent_number\": \"5000\",\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5310\",\n    \"name\": \"Interest Expense\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5320\",\n    \"name\": \"Bank Charges\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5330\",\n    \"name\": \"Exchange Loss\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"exchange_gain_loss\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5340\",\n    \"name\": \"Loss on Asset Disposal\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"disposal_gain_loss\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5350\",\n    \"name\": \"Miscellaneous Expense\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"9000\",\n    \"name\": \"Adjustments\",\n    \"parent_number\": null,\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"9100\",\n    \"name\": \"Rounding Adjustment\",\n    \"parent_number\": \"9000\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"rounding\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"9200\",\n    \"name\": \"Write Off\",\n    \"parent_number\": \"9000\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  }\n]\n\nFile v4.15.0:scripts/erpclaw-setup/assets/currencies.json\n\n[\n  {\"code\": \"USD\", \"name\": \"US Dollar\", \"symbol\": \"$\", \"decimal_places\": 2, \"enabled\": 1},\n  {\"code\": \"EUR\", \"name\": \"Euro\", \"symbol\": \"\\u20ac\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"GBP\", \"name\": \"British Pound\", \"symbol\": \"\\u00a3\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"CAD\", \"name\": \"Canadian Dollar\", \"symbol\": \"CA$\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"AUD\", \"name\": \"Australian Dollar\", \"symbol\": \"A$\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"JPY\", \"name\": \"Japanese Yen\", \"symbol\": \"\\u00a5\", \"decimal_places\": 0, \"enabled\": 0},\n  {\"code\": \"CHF\", \"name\": \"Swiss Franc\", \"symbol\": \"CHF\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"CNY\", \"name\": \"Chinese Yuan\", \"symbol\": \"\\u00a5\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"INR\", \"name\": \"Indian Rupee\", \"symbol\": \"\\u20b9\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"MXN\", \"name\": \"Mexican Peso\", \"symbol\": \"MX$\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"BRL\", \"name\": \"Brazilian Real\", \"symbol\": \"R$\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"KRW\", \"name\": \"South Korean Won\", \"symbol\": \"\\u20a9\", \"decimal_places\": 0, \"enabled\": 0},\n  {\"code\": \"SGD\", \"name\": \"Singapore Dollar\", \"symbol\": \"S$\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"HKD\", \"name\": \"Hong Kong Dollar\", \"symbol\": \"HK$\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"NZD\", \"name\": \"New Zealand Dollar\", \"symbol\": \"NZ$\", \"decimal_places\": 2, \"enabled\": 0},\n  {\"code\": \"AED\", \"name\": \"UAE Dirham\", \"symbol\": \"AED\", \"decimal_places\": 2, \"enabled\": 0}\n]\n\nFile v4.15.0:scripts/erpclaw-setup/assets/default_payment_terms.json\n\n[\n  {\n    \"name\": \"Due on Receipt\",\n    \"due_days\": 0,\n    \"discount_percentage\": null,\n    \"discount_days\": null,\n    \"description\": \"Payment due immediately upon receipt of invoice\"\n  },\n  {\n    \"name\": \"Net 15\",\n    \"due_days\": 15,\n    \"discount_percentage\": null,\n    \"discount_days\": null,\n    \"description\": \"Payment due within 15 days of invoice date\"\n  },\n  {\n    \"name\": \"Net 30\",\n    \"due_days\": 30,\n    \"discount_percentage\": null,\n    \"discount_days\": null,\n    \"description\": \"Payment due within 30 days of invoice date\"\n  },\n  {\n    \"name\": \"Net 60\",\n    \"due_days\": 60,\n    \"discount_percentage\": null,\n    \"discount_days\": null,\n    \"description\": \"Payment due within 60 days of invoice date\"\n  },\n  {\n    \"name\": \"2/10 Net 30\",\n    \"due_days\": 30,\n    \"discount_percentage\": \"2.00\",\n    \"discount_days\": 10,\n    \"description\": \"2% discount if paid within 10 days, otherwise full amount due within 30 days\"\n  },\n  {\n    \"name\": \"COD\",\n    \"due_days\": 0,\n    \"discount_percentage\": null,\n    \"discount_days\": null,\n    \"description\": \"Cash on delivery, payment collected at time of delivery\"\n  }\n]\n\nFile v4.15.0:scripts/erpclaw-setup/assets/default_uom.json\n\n[\n  {\"name\": \"Each\", \"abbr\": \"Ea\", \"must_be_whole_number\": 1},\n  {\"name\": \"Numbers\", \"abbr\": \"Nos\", \"must_be_whole_number\": 1},\n  {\"name\": \"Kilogram\", \"abbr\": \"Kg\", \"must_be_whole_number\": 0},\n  {\"name\": \"Gram\", \"abbr\": \"g\", \"must_be_whole_number\": 0},\n  {\"name\": \"Pound\", \"abbr\": \"Lb\", \"must_be_whole_number\": 0},\n  {\"name\": \"Ounce\", \"abbr\": \"Oz\", \"must_be_whole_number\": 0},\n  {\"name\": \"Litre\", \"abbr\": \"L\", \"must_be_whole_number\": 0},\n  {\"name\": \"Gallon\", \"abbr\": \"Gal\", \"must_be_whole_number\": 0},\n  {\"name\": \"Metre\", \"abbr\": \"m\", \"must_be_whole_number\": 0},\n  {\"name\": \"Feet\", \"abbr\": \"Ft\", \"must_be_whole_number\": 0},\n  {\"name\": \"Hour\", \"abbr\": \"Hr\", \"must_be_whole_number\": 0},\n  {\"name\": \"Box\", \"abbr\": \"Box\", \"must_be_whole_number\": 1},\n  {\"name\": \"Pack\", \"abbr\": \"Pk\", \"must_be_whole_number\": 1},\n  {\"name\": \"Dozen\", \"abbr\": \"Dz\", \"must_be_whole_number\": 1}\n]\n\nFile v4.15.0:scripts/erpclaw-setup/assets/us_gaap.json\n\n[\n  {\n    \"account_number\": \"1000\",\n    \"name\": \"Assets\",\n    \"parent_number\": null,\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1100\",\n    \"name\": \"Current Assets\",\n    \"parent_number\": \"1000\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1110\",\n    \"name\": \"Cash and Cash Equivalents\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1111\",\n    \"name\": \"Petty Cash\",\n    \"parent_number\": \"1110\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"cash\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1112\",\n    \"name\": \"Operating Checking Account\",\n    \"parent_number\": \"1110\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"bank\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1113\",\n    \"name\": \"Savings Account\",\n    \"parent_number\": \"1110\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"bank\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1120\",\n    \"name\": \"Accounts Receivable\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1121\",\n    \"name\": \"Trade Receivables\",\n    \"parent_number\": \"1120\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1122\",\n    \"name\": \"Other Receivables\",\n    \"parent_number\": \"1120\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1130\",\n    \"name\": \"Inventory\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1131\",\n    \"name\": \"Raw Materials\",\n    \"parent_number\": \"1130\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1132\",\n    \"name\": \"Work in Progress\",\n    \"parent_number\": \"1130\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1133\",\n    \"name\": \"Finished Goods\",\n    \"parent_number\": \"1130\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1140\",\n    \"name\": \"Prepaid Expenses\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1141\",\n    \"name\": \"Prepaid Insurance\",\n    \"parent_number\": \"1140\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1142\",\n    \"name\": \"Prepaid Rent\",\n    \"parent_number\": \"1140\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1150\",\n    \"name\": \"Other Current Assets\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1151\",\n    \"name\": \"Employee Advances\",\n    \"parent_number\": \"1150\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1152\",\n    \"name\": \"Security Deposits\",\n    \"parent_number\": \"1150\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1200\",\n    \"name\": \"Non-Current Assets\",\n    \"parent_number\": \"1000\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1210\",\n    \"name\": \"Property, Plant & Equipment\",\n    \"parent_number\": \"1200\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1211\",\n    \"name\": \"Land\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1212\",\n    \"name\": \"Buildings\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1213\",\n    \"name\": \"Furniture and Fixtures\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1214\",\n    \"name\": \"Office Equipment\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1215\",\n    \"name\": \"Vehicles\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1216\",\n    \"name\": \"Accumulated Depreciation - Buildings\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"accumulated_depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1217\",\n    \"name\": \"Accumulated Depreciation - FF&E\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"accumulated_depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1218\",\n    \"name\": \"Accumulated Depreciation - Vehicles\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"accumulated_depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1219\",\n    \"name\": \"Capital Work in Progress\",\n    \"parent_number\": \"1210\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1220\",\n    \"name\": \"Intangible Assets\",\n    \"parent_number\": \"1200\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1221\",\n    \"name\": \"Software Licenses\",\n    \"parent_number\": \"1220\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"fixed_asset\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1222\",\n    \"name\": \"Accumulated Amortization\",\n    \"parent_number\": \"1220\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"accumulated_depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1230\",\n    \"name\": \"Stock Received Not Billed\",\n    \"parent_number\": \"1200\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock_received_not_billed\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1231\",\n    \"name\": \"Asset Received Not Billed\",\n    \"parent_number\": \"1200\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"asset_received_not_billed\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"2000\",\n    \"name\": \"Liabilities\",\n    \"parent_number\": null,\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2100\",\n    \"name\": \"Current Liabilities\",\n    \"parent_number\": \"2000\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2110\",\n    \"name\": \"Accounts Payable\",\n    \"parent_number\": \"2100\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2111\",\n    \"name\": \"Trade Payables\",\n    \"parent_number\": \"2110\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2112\",\n    \"name\": \"Accrued Expenses\",\n    \"parent_number\": \"2110\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2120\",\n    \"name\": \"Taxes Payable\",\n    \"parent_number\": \"2100\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2121\",\n    \"name\": \"Sales Tax Payable\",\n    \"parent_number\": \"2120\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"tax\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2122\",\n    \"name\": \"Federal Income Tax Payable\",\n    \"parent_number\": \"2120\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"tax\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2123\",\n    \"name\": \"State Income Tax Payable\",\n    \"parent_number\": \"2120\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"tax\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2124\",\n    \"name\": \"Payroll Tax Payable\",\n    \"parent_number\": \"2120\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"tax\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2130\",\n    \"name\": \"Other Current Liabilities\",\n    \"parent_number\": \"2100\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2131\",\n    \"name\": \"Unearned Revenue\",\n    \"parent_number\": \"2130\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2132\",\n    \"name\": \"Short-Term Loans\",\n    \"parent_number\": \"2130\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2133\",\n    \"name\": \"Payroll Payable\",\n    \"parent_number\": \"2130\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payroll_payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2200\",\n    \"name\": \"Non-Current Liabilities\",\n    \"parent_number\": \"2000\",\n    \"root_type\": \"liability\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2210\",\n    \"name\": \"Long-Term Loans\",\n    \"parent_number\": \"2200\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"2220\",\n    \"name\": \"Mortgage Payable\",\n    \"parent_number\": \"2200\",\n    \"root_type\": \"liability\",\n    \"account_type\": \"payable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3000\",\n    \"name\": \"Equity\",\n    \"parent_number\": null,\n    \"root_type\": \"equity\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3100\",\n    \"name\": \"Owner's Equity\",\n    \"parent_number\": \"3000\",\n    \"root_type\": \"equity\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3110\",\n    \"name\": \"Common Stock\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"equity\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3120\",\n    \"name\": \"Additional Paid-In Capital\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"equity\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3130\",\n    \"name\": \"Retained Earnings\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"equity\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3140\",\n    \"name\": \"Dividends Paid\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"equity\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"3150\",\n    \"name\": \"Opening Balance Equity\",\n    \"parent_number\": \"3100\",\n    \"root_type\": \"equity\",\n    \"account_type\": \"temporary\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4000\",\n    \"name\": \"Income\",\n    \"parent_number\": null,\n    \"root_type\": \"income\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4100\",\n    \"name\": \"Operating Revenue\",\n    \"parent_number\": \"4000\",\n    \"root_type\": \"income\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4110\",\n    \"name\": \"Sales Revenue\",\n    \"parent_number\": \"4100\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4120\",\n    \"name\": \"Service Revenue\",\n    \"parent_number\": \"4100\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4130\",\n    \"name\": \"Sales Returns & Allowances\",\n    \"parent_number\": \"4100\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4140\",\n    \"name\": \"Sales Discounts\",\n    \"parent_number\": \"4100\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4200\",\n    \"name\": \"Other Income\",\n    \"parent_number\": \"4000\",\n    \"root_type\": \"income\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4210\",\n    \"name\": \"Interest Income\",\n    \"parent_number\": \"4200\",\n    \"root_type\": \"income\",\n    \"account_type\": \"revenue\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4220\",\n    \"name\": \"Gain on Asset Disposal\",\n    \"parent_number\": \"4200\",\n    \"root_type\": \"income\",\n    \"account_type\": \"disposal_gain_loss\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"4230\",\n    \"name\": \"Exchange Gain\",\n    \"parent_number\": \"4200\",\n    \"root_type\": \"income\",\n    \"account_type\": \"exchange_gain_loss\",\n    \"is_group\": 0,\n    \"balance_direction\": \"credit_normal\"\n  },\n  {\n    \"account_number\": \"5000\",\n    \"name\": \"Expenses\",\n    \"parent_number\": null,\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5100\",\n    \"name\": \"Cost of Goods Sold\",\n    \"parent_number\": \"5000\",\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5110\",\n    \"name\": \"COGS - Materials\",\n    \"parent_number\": \"5100\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"cost_of_goods_sold\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5120\",\n    \"name\": \"COGS - Labor\",\n    \"parent_number\": \"5100\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"cost_of_goods_sold\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5130\",\n    \"name\": \"COGS - Overhead\",\n    \"parent_number\": \"5100\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"cost_of_goods_sold\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5140\",\n    \"name\": \"Stock Adjustment\",\n    \"parent_number\": \"5100\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"stock_adjustment\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5200\",\n    \"name\": \"Operating Expenses\",\n    \"parent_number\": \"5000\",\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5210\",\n    \"name\": \"Salaries and Wages\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5220\",\n    \"name\": \"Rent Expense\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5230\",\n    \"name\": \"Utilities\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5240\",\n    \"name\": \"Office Supplies\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5250\",\n    \"name\": \"Insurance\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5260\",\n    \"name\": \"Depreciation Expense\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"depreciation\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5270\",\n    \"name\": \"Marketing and Advertising\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5280\",\n    \"name\": \"Professional Fees\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5290\",\n    \"name\": \"Travel and Entertainment\",\n    \"parent_number\": \"5200\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5300\",\n    \"name\": \"Other Expenses\",\n    \"parent_number\": \"5000\",\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5310\",\n    \"name\": \"Interest Expense\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5320\",\n    \"name\": \"Bank Charges\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5330\",\n    \"name\": \"Exchange Loss\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"exchange_gain_loss\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5340\",\n    \"name\": \"Loss on Asset Disposal\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"disposal_gain_loss\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"5350\",\n    \"name\": \"Miscellaneous Expense\",\n    \"parent_number\": \"5300\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"9000\",\n    \"name\": \"Adjustments\",\n    \"parent_number\": null,\n    \"root_type\": \"expense\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"9100\",\n    \"name\": \"Rounding Adjustment\",\n    \"parent_number\": \"9000\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"rounding\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"9200\",\n    \"name\": \"Write Off\",\n    \"parent_number\": \"9000\",\n    \"root_type\": \"expense\",\n    \"account_type\": \"expense\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  }\n]\n\nFile v4.15.0:scripts/erpclaw-setup/lib/erpclaw_lib/vendor/sqlalchemy/LICENSE\n\nCopyright 2005-2026 SQLAlchemy authors and contributors <see AUTHORS file>.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is furnished to do\nso, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\nFile v4.15.0:scripts/module_registry.json\n\n{\n  \"generated\": \"2026-08-16\",\n  \"modules\": {\n    \"agricultureclaw\": {\n      \"action_count\": 67,\n      \"category\": \"vertical\",\n      \"description\": \"Agricultural operations management: farms, fields, crops, livestock, equipment, harvest tracking, and agricultural accounting. 67 actions.\",\n      \"display_name\": \"AgricultureClaw\",\n      \"files_sha256\": {\n        \"LICENSE.txt\": \"7bb0c0eb1db8b40884cd154315e459fadacd2bff10190df3516639282608ed0c\",\n        \"README.md\": \"3bcda0750ecc815b28c0f182d09135d5583bf8ca12a4a49cd0649cc5b919fd23\",\n        \"SKILL.md\": \"4d052f8d2d9f11087360ff0e7c7924208be7a6d1c741861dd3be3a59396b98d1\",\n        \"UI.yaml\": \"a36322eab3fc5c60a990a42c6dff6a4d4b4352dac574e54b12600a969aec9fa3\",\n        \"init_db.py\": \"cb92d693d83db0b78007c76ff2628ee27f6ab8d35a23eee54b54844a1cb77a35\",\n        \"scripts/cooperative.py\": \"7510bbbee14d97d63e79fed125be3f81fb3e14371826aff82d656ee4142aef80\",\n        \"scripts/crops.py\": \"a2057f206f7fcf24d8998caaec446bda52dc715ba1fab5fc8e8bc3dddf3d1805\",\n        \"scripts/db_query.py\": \"e4eb6e1f27b633780ae5d39405428dd2bf3f9f63cbdb2783d64dc48cd4e17de4\",\n        \"scripts/field_ops.py\": \"b7bc40ecf98a504091acac85599e13408e78f95fe39cf680688613955fc6b7fc\",\n        \"scripts/harvest.py\": \"b94c9b78018accdcf97f1a3d523d005090a725da91ee22c4b713bc781037d00b\",\n        \"scripts/land.py\": \"9f4bc7b295b8e210e222b6c9881e048244f4fa1873c3bd23e9fc3d55a1a112e0\",\n        \"scripts/livestock.py\": \"eab7ba8832f25b7035a006c88a6a67dda4382b465ab0dd6964d04ebbd3dbaf0a\",\n        \"scripts/reports.py\": \"e1323bb7235bff45f0a5d3ddb7f3ef778e78e2f313b1b0afd87e3a43fe6ebc31\"\n      },\n      \"github\": \"avansaber/agricultureclaw\",\n      \"has_init_db\": true,\n      \"requires\": [\n        \"erpclaw\"\n      ],\n      \"tags\": [\n        \"agriculture\",\n        \"farming\",\n        \"crops\",\n        \"livestock\",\n        \"fields\",\n        \"harvest\",\n        \"equipment\",\n        \"irrigation\"\n      ],\n      \"version\": \"1.0.0\"\n    },\n    \"automotiveclaw\": {\n      \"action_count\": 70,\n      \"category\": \"vertical\",\n      \"description\": \"Automotive dealership and service management: vehicle inventory, sales, service...","readmeExcerpt":"Skill: Erpclaw Publish 4.15.0 20260816 103527 Owner: mailnike Summary: AI-native ERP system. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and financial reporting (including P&L / trial balance / spend grouped by department, project, cost center, location, or fund). 496 actions across 14 domains, 45 optional expansion modu","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"python3 {baseDir}/scripts/erpclaw-setup/db_query.py --action initialize-database\npython3 {baseDir}/scripts/db_query.py --action seed-defaults --company-id <id>\npython3 {baseDir}/scripts/db_query.py --action setup-chart-of-accounts --company-id <id> --template us_gaap"},{"language":"bash","snippet":"# Send queued emails — every 1 minute (erpclaw-alerts: process-email-queue)\nopenclaw cron add --name erpclaw-email-queue --cron \"* * * * *\" \\\n  --message \"Using erpclaw, run the process-email-queue action.\"\n\n# Advance drip-campaign sends — every 5 minutes (erpclaw-growth: process-drip-sends)\nopenclaw cron add --name erpclaw-drip-sends --cron \"*/5 * * * *\" \\\n  --message \"Using erpclaw, run the process-drip-sends action.\""},{"language":"text","snippet":"clawhub install erpclaw"},{"language":"text","snippet":"\"I'm opening a retail store called Sunrise Goods in Portland, Oregon. Set me up.\"\n→ creates your company, a US GAAP chart of accounts, fiscal year, and tax rates"},{"language":"text","snippet":"\"I'm a school\"           → ERPClaw pulls the education vertical and creates its tables\n\"I need manufacturing\"   → pulls Manufacturing, Projects, Assets, Quality, Support\n\"Set me up for a clinic\" → pulls clinical practice management"},{"language":"bash","snippet":"hermes skills tap add avansaber/hermes-skills\n  hermes skills install avansaber/hermes-skills/skills/erpclaw --force\n  export ERPCLAW_HOME=~/.hermes/erpclaw-home    # blank = ~/.openclaw/erpclaw\n  python3 ~/.hermes/skills/erpclaw/scripts/erpclaw-setup/db_query.py --action initialize-database"}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: erpclaw\nversion: 4.15.0\ndescription: >\n  AI-native ERP system. Full accounting, invoicing, inventory, purchasing,\n  tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation),\n  and financial reporting (including P&L / trial balance / spend grouped by department, project, cost center, location, or fund). 496 actions across 14 domains, 45 optional expansion modules (user-approved install from GitHub).\n  Double-entry GL, immutable audit trail, US GAAP compliant. Licensed under GNU GPL v3 (the marketplace \"MIT-0\" badge is a ClawHub platform default; the LICENSE.txt in the bundle is GPL v3).\nauthor: AvanSaber\nhomepage: https://github.com/avansaber/erpclaw\nsource: https://github.com/avansaber/erpclaw\nuser-invocable: true\ntags: [erp, accounting, invoicing, inventory, purchasing, tax, billing, payments, gl, reports, sales, buying, setup, hr, payroll, employees, leave, attendance, salary, revenue-recognition, lease-accounting, intercompany, consolidation]\nmetadata: {\"openclaw\":{\"type\":\"executable\",\"install\":{\"post\":\"python3 scripts/erpclaw-setup/db_query.py --action initialize-database\"},\"requires\":{\"bins\":[\"python3\",\"git\"],\"env\":[],\"optionalEnv\":[\"ERPCLAW_DB_PATH\"]},\"os\":[\"darwin\",\"linux\"]},\"hermes\":{\"category\":\"productivity\",\"config\":[{\"key\":\"erpclaw.home\",\"description\":\"ERPClaw install root; lib, install-state, and the default SQLite DB resolve under it. Unset/blank defaults to ~/.openclaw/erpclaw (byte-identical to OpenClaw).\",\"default\":\"${ERPCLAW_HOME}\",\"prompt\":\"ERPClaw home directory (blank = ~/.openclaw/erpclaw)\"}]},\"mcp\":{\"transport\":\"stdio\",\"server\":\"source/erpclaw/mcp/server.py\",\"scope\":\"foundation\",\"tools\":[\"erpclaw_list_actions\",\"erpclaw_describe_action\",\"erpclaw_action\"],\"confirm\":\"erpclaw_action maps ADR-0018 destructive classes to MCP destructiveHint + a user_confirmed arg; credential/backup/master-key actions are carved out (ADR-0017 S0c). Transport-only over db_query.py — no new write path (ADR-0024).\",\"read\":\"erpclaw_read deferred to v2\"}}\n---\n\n# erpclaw\n\n**Full-Stack ERP Controller** for ERPClaw. Company setup, chart of accounts, journal entries, payments, tax, financial reports, customers, sales, suppliers, purchasing, inventory, billing, HR, US payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and 45 optional industry modules. Local-first SQLite, double-entry GL, immutable audit trail.\n\n**Security:** Local-first. Parameterized queries. RBAC (PBKDF2). Immutable GL. Sensitive fields encrypted at the column level. Network access limited to `fetch-exchange-rates` (public API) and user-approved `install-module` from `github.com/avansaber/*`.\n\n**Runtimes:** Runs on OpenClaw (primary). Experimental support for the Hermes Agent runtime via a GitHub tap. Install root is set by the `ERPCLAW_HOME` environment variable; unset/blank defaults to `~/.openclaw/erpclaw` (zero behavior change for OpenClaw).\n\n## System of record (the ERP is authoritative)\n\nThe ERPClaw database is the sing"},{"path":"README.md","content":"<p align=\"center\">\n  <img src=\"assets/logo.svg\" alt=\"ERPClaw\" width=\"120\" height=\"120\">\n</p>\n\n<h1 align=\"center\">ERPClaw</h1>\n\n<p align=\"center\">\n  <em>The AI-native ERP. Chat with your books.</em>\n</p>\n\n<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/license-GPL--3.0-green\" alt=\"License: GPL-3.0\">\n  <!-- version badge auto-synced by release/scripts/sync_facts.py (badge URL pattern) -->\n  <img src=\"https://img.shields.io/badge/version-v4.15.0-0d9488\" alt=\"Version v4.15.0\">\n  <img src=\"https://img.shields.io/badge/OpenClaw-skill-14b8a6\" alt=\"OpenClaw Skill\">\n  <img src=\"https://img.shields.io/badge/database-SQLite%20%7C%20PostgreSQL-0d9488\" alt=\"SQLite or PostgreSQL\">\n  <a href=\"https://www.erpclaw.ai\"><img src=\"https://img.shields.io/badge/website-erpclaw.ai-14b8a6\" alt=\"Website erpclaw.ai\"></a>\n  <img src=\"https://img.shields.io/badge/Stripe%20Marketplace-Live-635bff\" alt=\"Stripe Marketplace · Live\">\n</p>\n\n<p align=\"center\">\n  <img src=\"assets/demo.svg\" alt=\"ERPClaw demo: add a customer, invoice them, record their payment, and see a P&amp;L by department, all in plain English\" width=\"760\">\n</p>\n\n**ERPClaw runs your whole business from plain English.** Tell it \"invoice Wayne\nIndustries for 10 widgets at $100\" and it writes the invoice, posts the\nbalanced journal entries, and replies with the invoice number. Record a payment,\nclose the month, pull a P&L by department, run payroll, the assistant you\nalready use does the work and the books stay audit-grade underneath. No forms to\nclick through, no screens to learn, no per-seat bill. It is open source, it is\nfree forever, and it runs on your own machine so your data never leaves it.\n\nThe difference from every other \"AI\" accounting tool is structural: ERPClaw was\nbuilt AI-native from the first commit, with the assistant as the primary\ninterface and the accounting rules as auditable code. That is not a chat sidebar\nbolted onto a forms app, and it is not something a legacy product can retrofit.\n\n<sub>The rest of this README is the technical reference for installing and\nrunning the foundation. If you just want the product overview, see\n<a href=\"https://www.erpclaw.ai\">erpclaw.ai</a>.</sub>\n\n## Quick start\n\nERPClaw is one product. You install it once, then describe your business and it\nsets itself up.\n\n```\nclawhub install erpclaw\n```\n\nThis installs the foundation and initializes the database. From there you just\ntalk to your AI assistant:\n\n```\n\"I'm opening a retail store called Sunrise Goods in Portland, Oregon. Set me up.\"\n→ creates your company, a US GAAP chart of accounts, fiscal year, and tax rates\n```\n\nIndustry coverage comes the same way, with no second install command:\n\n```\n\"I'm a school\"           → ERPClaw pulls the education vertical and creates its tables\n\"I need manufacturing\"   → pulls Manufacturing, Projects, Assets, Quality, Support\n\"Set me up for a clinic\" → pulls clinical practice management\n```\n\nVertical code lives on GitHub and is fetched on demand by sparse checkout, so\nonl"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn71eyhsjyvmm561yrd4k2dbzh80a07r\",\n  \"slug\": \"erpclaw\",\n  \"version\": \"4.15.0\",\n  \"publishedAt\": 1786896047188\n}"},{"path":"scripts/erpclaw-os/references/pattern_catalog.md","content":"# ERPClaw OS Pattern Catalog\n\nReusable patterns extracted from the existing 44-module codebase. Each pattern documents a common business entity type with schema, action, and test templates. Used by `generate_module.py` to map natural-language business descriptions to code templates.\n\n**Source of truth:** `pattern_library.py` defines the machine-readable version. This document is the human-readable reference.\n\n---\n\n## 1. CRUD Entity\n\n**Description:** The foundational pattern. A persistent business entity with create, read, update, and list operations. No financial posting, no lifecycle transitions beyond active/inactive.\n\n**Source modules:** legalclaw (`legalclaw_client_ext`), healthclaw-vet (`healthclaw_animal_patient`), automotiveclaw (`automotiveclaw_customer_ext`), nonprofitclaw (`nonprofitclaw_donor_ext`)\n\n**When to use:**\n- Any entity that needs to be stored and queried (clients, pets, vehicles, properties, employees, locations)\n- Master data tables that other records reference\n- Extension tables that augment core ERP entities (customer, item, employee)\n\n**Schema pattern:**\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_entity (\n    id          TEXT PRIMARY KEY,            -- UUID4\n    company_id  TEXT NOT NULL REFERENCES company(id),\n    name        TEXT NOT NULL,\n    status      TEXT DEFAULT 'active'\n                CHECK(status IN ('active','inactive','archived')),\n    notes       TEXT,\n    created_at  TEXT NOT NULL DEFAULT (datetime('now')),\n    updated_at  TEXT NOT NULL DEFAULT (datetime('now'))\n);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_entity_company\n    ON {prefix}_entity(company_id);\n```\n\nFor **extension tables** that link to a core entity:\n```sql\nCREATE TABLE IF NOT EXISTS {prefix}_customer_ext (\n    id              TEXT PRIMARY KEY,\n    naming_series   TEXT DEFAULT '{PREFIX}-',\n    customer_id     TEXT NOT NULL REFERENCES customer(id),\n    -- domain-specific fields here --\n    is_active       INTEGER DEFAULT 1,\n    company_id      TEXT NOT NULL REFERENCES company(id),\n    created_at      TEXT DEFAULT (datetime('now')),\n    updated_at      TEXT DEFAULT (datetime('now'))\n);\nCREATE INDEX IF NOT EXISTS idx_{prefix}_customer_ext_customer\n    ON {prefix}_customer_ext(customer_id);\n```\n\n**Action pattern:**\n```python\ndef handle_add_entity(args, conn):\n    entity_id = str(uuid.uuid4())\n    conn.execute(\n        \"INSERT INTO {prefix}_entity (id, company_id, name) VALUES (?, ?, ?)\",\n        (entity_id, args.company_id, args.name)\n    )\n    conn.commit()\n    ok({\"message\": \"Entity added\", \"id\": entity_id})\n\ndef handle_update_entity(args, conn):\n    conn.execute(\n        \"UPDATE {prefix}_entity SET name = ?, updated_at = datetime('now') WHERE id = ? AND company_id = ?\",\n        (args.name, args.id, args.company_id)\n    )\n    conn.commit()\n    ok({\"message\": \"Entity updated\"})\n\ndef handle_get_entity(args, conn):\n    row = conn.execute(\n        \"SELECT * FROM {prefix}_entity WHERE id = ? AND company_id = ?\",\n        (args.id, args.company_id)\n    )."},{"path":"scripts/erpclaw-gl/assets/charts/us_gaap.json","content":"[\n  {\n    \"account_number\": \"1000\",\n    \"name\": \"Assets\",\n    \"parent_number\": null,\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1100\",\n    \"name\": \"Current Assets\",\n    \"parent_number\": \"1000\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1110\",\n    \"name\": \"Cash and Cash Equivalents\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1111\",\n    \"name\": \"Petty Cash\",\n    \"parent_number\": \"1110\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"cash\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1112\",\n    \"name\": \"Operating Checking Account\",\n    \"parent_number\": \"1110\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"bank\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1113\",\n    \"name\": \"Savings Account\",\n    \"parent_number\": \"1110\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"bank\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1120\",\n    \"name\": \"Accounts Receivable\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1121\",\n    \"name\": \"Trade Receivables\",\n    \"parent_number\": \"1120\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1122\",\n    \"name\": \"Other Receivables\",\n    \"parent_number\": \"1120\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"receivable\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1130\",\n    \"name\": \"Inventory\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1131\",\n    \"name\": \"Raw Materials\",\n    \"parent_number\": \"1130\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1132\",\n    \"name\": \"Work in Progress\",\n    \"parent_number\": \"1130\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1133\",\n    \"name\": \"Finished Goods\",\n    \"parent_number\": \"1130\",\n    \"root_type\": \"asset\",\n    \"account_type\": \"stock\",\n    \"is_group\": 0,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_number\": \"1140\",\n    \"name\": \"Prepaid Expenses\",\n    \"parent_number\": \"1100\",\n    \"root_type\": \"asset\",\n    \"account_type\": null,\n    \"is_group\": 1,\n    \"balance_direction\": \"debit_normal\"\n  },\n  {\n    \"account_numbe"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"AI-native ERP system. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and financial reporting (including P&L / trial balance / spend grouped by department, project, cost center, location, or fund). 496 actions across 14 domains, 45 optional expansion modules (user-approved install from GitHub). Double-entry GL, immutable audit trail, US GAAP compliant. Licensed under GNU GPL v3 (the marketplace \"MIT-0\" badge is a ClawHub platform default; the LICENSE.txt in the bundle is GPL v3). Skill: Erpclaw Publish 4.15.0 20260816 103527 Owner: mailnike Summary: AI-native ERP system. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and financial reporting (including P&L / trial balance / spend grouped by department, project, cost center, location, or fund). 496 actions across 14 domains, 45 optional expansion modu","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":2205,"uniquenessScore":46,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-10-09T04:52:01.050Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-10-09T04:52:01.050Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-09T20:33:40.263Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}