Sultan Kautsar

A Self-Hosted ERP for a Digital Studio's Finance Team

For a digital studio in Indonesia, I replaced its paid CRM with a self-hosted ERPNext that holds quotations, invoices, payments, and the accounting in one place, and moved eight years of client history into it, checked client by client.

ERP and accountingDigital studio

4 min read

The Client

A digital studio in Indonesia that builds and maintains websites for businesses. Its account managers send quotations and invoices, and a small finance team records payments, keeps the books, and reports to management.

The Problem

The studio ran its sales documents on Perfex CRM:

  • Perfex is paid software, and it has no accounting, so finance kept the journal by hand, outside the CRM.
  • Reports such as the general ledger, balance sheet, and profit and loss had to come from somewhere else.
  • Eight years of clients, quotations, invoices, and payments lived in Perfex, and finance needed that history in whatever came next.
  • Some clients withhold a 2% income tax (PPh 23) from their payments, which the new system had to post correctly.

What I Did

I set up ERPNext, an open-source ERP, on the studio’s existing server, imported everything from Perfex, and switched the CRM’s address over to it.

  1. Read-only dump Taken from the old CRM database with approval
  2. Local copy Restored and checked; production is never queried
  3. Importer Skips every record that already has its old ID
  4. ERPNext Invoices and payments post to the ledger
  5. Reconcile per client
    • Everything matches Ready for cutover
    • A client differs Non-zero exit; fix and run again

A scope finance agreed to
Finance confirmed they needed invoicing, quotations, and accounting: a chart of accounts, journal entries, the general ledger, a balance sheet and profit and loss, and receivables aging. Projects, payroll, a payment gateway, and multi-currency stayed out of the first version.

ERPNext, configured before coded
Of the free options I compared (Odoo Community, Invoice Ninja, Dolibarr, and ERPNext), ERPNext was the only one with full double-entry accounting and nothing behind a paid edition. Custom fields, print formats, and settings came first. The little code it needed lives in one small custom app: invoicing a quotation directly marks it Ordered without a sales order, and the studio’s branding survives every update. Finance sees only the modules they use.

Eight years of data, reconciled
A Python importer reads a local copy of the Perfex database and writes through ERPNext’s API. Every record keeps its Perfex ID, so the import can stop, resume, and run again without duplicates. A reconcile step compares each client’s invoice count, totals invoiced and paid, and quotations in both systems. The final run matched 211 of 211 clients and 974 of 974 old proposals. Withheld tax posts to a prepaid tax account, and migrated invoices keep their old numbers on the PDF.

A cutover with a written way back
ERPNext ran side by side with Perfex on a shared server before the switch. The image is built on my machine and shipped over SSH, and the deploy script shows every remote command and waits for a yes. At cutover, a final dump was compared with the imported one, Perfex was stopped, and one route file pointed the address at ERPNext. The rollback was written down first, and Perfex’s files and database stay on the server as an archive.

Backups and a manual
A nightly job backs up the database and files, and the server’s weekly offsite job copies them to S3. Finance got a manual in Indonesian that covers quotations, invoices, payments with withheld tax, journal entries, reports, and corrections.

The Stack

Layer Tool Role
ERP ERPNext v16, Frappe Quotations, invoices, payments, and accounting
Custom app Frappe app, Jinja print formats Quotation status, branding, and the PDF documents
Data MariaDB, Redis ERPNext’s database, cache, and job queue
Migration Python importer, REST API Imports Perfex history and reconciles it per client
Hosting Docker Compose, Traefik Runs next to the server’s other apps, routed by a file
Backups Cron, S3 Nightly backups on the server, weekly copy offsite

The Result

ERPNext took over the CRM’s address the day after the project started, and finance works in it directly.

Before After
A paid CRM without accounting Open-source ERPNext with double-entry books
The journal kept by hand outside the CRM Invoices and payments post to the ledger on their own
Eight years of history in the old tool Clients, quotations, invoices, and payments imported
Ledger and reports kept outside the CRM Ledger, balance sheet, and profit and loss in the same place

Finance still has to confirm the final chart of accounts and decide how to close receivables from before 2024, when the old records of payments were incomplete. The full write-up, including what broke after the cutover, is in my note on replacing a paid CRM with ERPNext.

If your team needs its sales documents and books in one place, get in touch.

Similar Problem?

New websites and landing pages, maintenance, migrations, and technical SEO for businesses and agencies.

More Work

Next step

Have a project in mind? Let's talk.

Tell me what you have and what you need. I usually reply within one working day with how I'd approach it.

Write to Me

Tell me about your project or idea. I usually reply within one working day.