{"schema":"dodil.modules-index/v1","read_first":"https://blog.dodil.io/modules/lifecycle.md","module_pattern":"https://blog.dodil.io/modules/pattern.md","design_template":"https://blog.dodil.io/modules/template.md","new_module_template":"https://blog.dodil.io/modules/module.template.yaml","how_to_use":["Match what the customer said against customer_words; map capabilities to modules.","Pick the first module with first_module_fit and depends_on; install shared master data owners first.","Open the spec for each candidate: its stories, entities and customer_varies are the starting vocabulary for phase 2.","Write the customer's ERP_<MODULE>.md from the template and get sign-off before opening a package.","Build it to the module pattern (/modules/pattern.md): three artifacts, one bucket, a router per module, natural keys, idempotent writes, rules on the write path, a test per story."],"pending_means":"No reference vocabulary exists yet: do the understanding phase from scratch with the customer, using the modules named in start_from as the house style, and write their ERP_<MODULE>.md the same way. Guessing /modules/<id>/spec.yaml for a pending module returns 404.","facets":{"domain":["finance","master-data","sales","operations","it-operations","people","legal"],"maturity":["planned","reference","tested","deployed"],"first_module_fit":["foundation","high","medium","low"],"spec":["available","pending"]},"count":11,"modules":[{"id":"ar","name":"Invoicing & collections","short":"Invoicing","permission_namespace":"ar","summary":"Send invoices for what you sold, record what customers pay, and know who owes you money and for how long — without re-typing anything the sales side already captured.","maturity":"tested","spec":"available","domain":"finance","industries":[],"customer_words":["send invoices","who owes us money","chase late payments","stop re-typing quotes into the accounts","statements for customers"],"replaces":["spreadsheets","Xero","QuickBooks","Sage","a Word template and a folder"],"first_module_fit":"high","depends_on":[{"module":"business-partner","why":"an invoice is addressed to a party; sales and invoicing must mean the same customer","required":true},{"module":"crm","why":"invoicing from an accepted quote (AR-20) and showing a rep that a customer is overdue (AR-12)","required":"optional"},{"module":"gl","why":"posting invoices, receipts, credits and write-offs to the books","required":"optional"}],"feeds":["gl"],"owns_master_data":[],"references_master_data":["business_partner","product","tax_code"],"actors":["biller","collector","approver","sales","system"],"entities":["invoice","invoice_line","invoice_tax","payment","credit_note","writeoff","allocation","dunning_event","send","statement","recurring_agreement","number_series","terms","settings","account_map","tax_code"],"stories":{"person":15,"system":3,"integration":3},"jobs":["nightly","daily"],"platform_constraints":["Gap-free sequential invoice numbers are a legal requirement in many places, and DataK3 has no sequences.","ALTER TABLE … ADD COLUMN unsettles reads on that table for minutes.","No read-your-writes inside an open transaction, and rows committed seconds earlier can be briefly invisible.","DataK3 rejects CREATE VIEW."],"links":{"page":"https://blog.dodil.io/modules/ar/","spec":"https://blog.dodil.io/modules/ar/spec.yaml","packages":["https://blog.dodil.io/code/ar-core/"],"build_guide":null}},{"id":"business-partner","name":"Customers & suppliers (shared)","short":"Business partner","permission_namespace":"bp","summary":"One record per company or person you do business with, shared by every other module — so sales, invoicing and purchasing all mean the same customer.","maturity":"deployed","spec":"available","domain":"master-data","industries":[],"customer_words":["one customer list","our customers and suppliers","the same client in sales and accounting"],"replaces":["the customer tab in every spreadsheet","duplicate customer lists across tools"],"first_module_fit":"foundation","depends_on":[],"feeds":["crm","ar","purchasing","gl"],"owns_master_data":["business_partner","bp_role","bp_edges"],"references_master_data":[],"actors":["data_steward","any_module","system"],"entities":["business_partner","bp_role","bp_edge"],"stories":{"person":2,"system":1,"integration":1},"jobs":[],"platform_constraints":[],"links":{"page":"https://blog.dodil.io/modules/business-partner/","spec":"https://blog.dodil.io/modules/business-partner/spec.yaml","packages":["https://blog.dodil.io/code/erp-business-partner/"],"build_guide":null}},{"id":"crm","name":"Sales & customer relationships","short":"CRM","permission_namespace":"crm","summary":"Who your customers are, what you are trying to sell them, and where each deal stands — shared by the whole sales team instead of living in one person's spreadsheet or inbox.","maturity":"tested","spec":"available","domain":"sales","industries":["software","manufacturing","finserv","real-estate"],"customer_words":["track our customers","a sales pipeline","stop losing leads","who is talking to which client","quotes and discounts","an app for my sales team"],"replaces":["spreadsheets","shared inbox","HubSpot","Salesforce","Pipedrive"],"first_module_fit":"high","depends_on":[{"module":"business-partner","why":"the customer record is shared with invoicing and purchasing; CRM references it","required":"recommended"}],"feeds":["ar","gl"],"owns_master_data":[],"references_master_data":["business_partner","product","employee"],"actors":["rep","manager","deal_desk","marketing","system"],"entities":["account","contact","lead","opportunity","activity","pipeline_stage","product","price_book","quote","discount_policy","lead_score","campaign","task","forecast_snapshot","sequence"],"stories":{"person":12,"system":5,"integration":3},"jobs":["every 6 hours","nightly","nightly","continuous (per-step delays)","on quote decision"],"platform_constraints":[],"links":{"page":"https://blog.dodil.io/modules/crm/","spec":"https://blog.dodil.io/modules/crm/spec.yaml","packages":["https://blog.dodil.io/code/crm-suite-app/","https://blog.dodil.io/code/crm-core/"],"build_guide":"https://blog.dodil.io/library/crm-reference-build"}},{"id":"gl","name":"Accounting","short":"Accounting","permission_namespace":"gl","summary":"The books: every transaction as a balanced entry, account balances you can trust, month-end close, and the reports your accountant asks for — fed by the rest of the business rather than re-keyed from it.","maturity":"tested","spec":"available","domain":"finance","industries":[],"customer_words":["our books","the accounts","month-end","profit and loss","what does the accountant need","the auditor asked for"],"replaces":["spreadsheets","Xero","QuickBooks","Sage","NetSuite"],"first_module_fit":"medium","depends_on":[{"module":"business-partner","why":"subledger open items and party reporting need one customer/supplier record","required":"recommended"}],"feeds":["ar","purchasing"],"owns_master_data":["chart_of_accounts","period","fx_rate"],"references_master_data":["business_partner"],"actors":["bookkeeper","accountant","auditor","system"],"entities":["account","account_edge","journal","journal_line","ledger","period","journal_audit","subledger_item","reconciliation","fx_rate","fx_reval","statement"],"stories":{"person":10,"system":2,"integration":2},"jobs":["after every posting","on request, and after a close"],"platform_constraints":["There is no enforced cross-table constraint, so the period lock is an application control.","No read-your-writes inside an open transaction.","A zero DECIMAL renders as JSON 0, not \"0.00\"."],"links":{"page":"https://blog.dodil.io/modules/gl/","spec":"https://blog.dodil.io/modules/gl/spec.yaml","packages":["https://blog.dodil.io/code/gl-suite-app/","https://blog.dodil.io/code/gl-core/"],"build_guide":null}},{"id":"hire","name":"Equipment hire","short":"Hire","permission_namespace":"hire","summary":"Know what is out, what is due back, what it has earned so far, and who has not paid — for a business that hires equipment out by the day or the week.","maturity":"planned","spec":"available","domain":"operations","industries":[],"customer_words":["what's out and what's coming back","who hasn't paid","we run it off a whiteboard","hire out our kit","the yard"],"replaces":["a whiteboard and a phone","spreadsheets","MCS","inspHire"],"first_module_fit":"high","depends_on":[{"module":"business-partner","why":"the same builder must be one record for hires, bills and payments — and for invoicing later","required":true},{"module":"ar","why":"formal numbered invoices, statements and tax; hire's own billing is deliberately simpler","required":"optional"}],"feeds":["ar"],"owns_master_data":[],"references_master_data":["business_partner"],"actors":["owner","yard","system"],"entities":["equipment","rate","hire","hire_line","bill","payment","allocation","settings"],"stories":{"person":14,"system":1,"integration":1},"jobs":[],"platform_constraints":["Hire numbers come from a high-water-mark row, and concurrent inserts of the same key are currently all acknowledged with one row surviving."],"links":{"page":"https://blog.dodil.io/modules/hire/","spec":"https://blog.dodil.io/modules/hire/spec.yaml","packages":[],"build_guide":null}},{"id":"purchasing","name":"Purchasing & suppliers","short":"purchasing","permission_namespace":"purchasing","summary":"Agree what you are going to spend before you spend it, check that what a supplier bills you is what you ordered and what actually arrived, and pay it once — so nobody is chasing an invoice nobody recognises, and nothing is paid twice.","maturity":"tested","spec":"available","domain":"finance","industries":["construction","manufacturing","professional_services","retail","hospitality"],"customer_words":["purchase orders","what did we order","supplier invoices","approve spend","who authorised this","what do we owe","pay the suppliers"],"replaces":["email approvals","spreadsheets","Coupa","Ariba","a folder of PDFs"],"first_module_fit":"medium","depends_on":[{"module":"business-partner"}],"feeds":["gl"],"owns_master_data":[],"references_master_data":["business_partner","product"],"actors":["requester","spend_approver","buyer","payables_clerk","system","payables_approver"],"entities":["purchase_request","purchase_order","purchase_order_line","goods_receipt","supplier_bill","payment_run","payment_allocation","approval_limit"],"stories":{"person":14,"system":1,"integration":2},"jobs":[null],"platform_constraints":[null,null,null],"links":{"page":"https://blog.dodil.io/modules/purchasing/","spec":"https://blog.dodil.io/modules/purchasing/spec.yaml","packages":["https://blog.dodil.io/code/ap-core/"],"build_guide":null}},{"id":"itsm","name":"IT help desk & incidents","summary":null,"maturity":"tested","spec":"pending","domain":"it-operations","industries":[],"customer_words":["support tickets","help desk","incidents","outages"],"replaces":["shared inbox","Zendesk","ServiceNow","Jira Service Management"],"first_module_fit":"high","depends_on":[],"feeds":[],"owns_master_data":["configuration_item"],"references_master_data":["employee"],"start_from":["business-partner"],"note":null,"links":{"page":null,"spec":null,"packages":["https://blog.dodil.io/code/itsm-suite-app/"],"build_guide":null}},{"id":"helpdesk","name":"Help desk (small teams)","summary":null,"maturity":"planned","spec":"pending","domain":"it-operations","industries":[],"customer_words":["people email me when something breaks","track requests","nothing gets forgotten"],"replaces":["shared inbox","spreadsheets","Zendesk"],"first_module_fit":"high","depends_on":[],"feeds":[],"owns_master_data":[],"references_master_data":["employee"],"start_from":["crm"],"note":"the ITSM module is built for large IT departments (change boards, major-incident rooms); a small team wants tickets, categories and a search","links":{"page":null,"spec":null,"packages":[],"build_guide":null}},{"id":"hr","name":"People & recruiting","summary":null,"maturity":"planned","spec":"pending","domain":"people","industries":[],"customer_words":["who works here","hiring","onboarding","holiday and absence"],"replaces":["spreadsheets","BambooHR","Workday"],"first_module_fit":"medium","depends_on":[],"feeds":[],"owns_master_data":["employee","org_unit"],"references_master_data":[],"start_from":["business-partner"],"note":"owns the employee record every other module references — decide its ownership before a second module needs people","links":{"page":null,"spec":null,"packages":[],"build_guide":null}},{"id":"expenses","name":"Expenses","summary":null,"maturity":"planned","spec":"pending","domain":"finance","industries":[],"customer_words":["expense claims","receipts","reimburse the team"],"replaces":["spreadsheets","Concur","Expensify"],"first_module_fit":"low","depends_on":[{"module":"gl"}],"feeds":[],"owns_master_data":[],"references_master_data":["employee"],"start_from":["gl","hr"],"note":null,"links":{"page":null,"spec":null,"packages":[],"build_guide":null}},{"id":"contracts","name":"Contracts","summary":null,"maturity":"planned","spec":"pending","domain":"legal","industries":[],"customer_words":["where is the signed contract","renewal dates","what did we agree"],"replaces":["a shared drive","Ironclad"],"first_module_fit":"low","depends_on":[{"module":"business-partner"}],"feeds":[],"owns_master_data":[],"references_master_data":["business_partner"],"start_from":["business-partner","crm"],"note":null,"links":{"page":null,"spec":null,"packages":[],"build_guide":null}}]}