Back

Zelterm ERP

Multi-tenant ERP for tech-repair SMBs: repair orders, multi-currency POS, inventory with traceability, warranties/returns and SUNAT e-invoicing.

Overview

Zelterm was born for small tech-repair businesses that had no affordable system — existing options were too expensive. It's inspired by my time selling electronic components. The platform brings order — logging every repair, how the product arrived and what was done — with traceability that also protects them against potential claims. It replaces notebooks and spreadsheets with a single tool: service orders, point of sale, inventory with serial-number tracking and SUNAT e-invoicing. It doesn't stop at checkout either: post-sale warranties and returns are tracked back to the original sale, so the whole sale lifecycle stays traceable.

Key decisions

Multi-tenant from a single database

I started with one shared database to launch fast and cheap. The scaling plan is a business table with a database_id that routes each business to its own database once volume justifies it — a gradual evolution instead of day-one over-engineering.

Multi-regime Peruvian tax support

Peruvian workshops operate under different tax regimes — NRUS (boletas only, no e-invoicing obligation), RER, RMT and Régimen General — each with its own rules for issuing receipts. The product had to work even with no active e-invoicing, and price with or without IGV depending on the business's regime. Understanding the real Peruvian business landscape, not just Régimen General, shaped the data model from the start.

Multi-currency and multi-payment across the system

The system is configured in PEN or USD, but once the first invoice is issued the currency locks for accounting consistency. Propagating that rule across sales, inventory and reports was the biggest challenge: each flow could introduce conversion bugs.

SUNAT e-invoicing outsourced (Nubefact)

Instead of building the fiscal connector from scratch, I integrated an authorized provider. The complexity boils down to sending the right data, meeting Peruvian regulations with far less risk.

Dual-token JWT authentication

Short-lived access token plus a 7-day refresh token in an httpOnly cookie. Logout is invalidated statelessly: a lastLogoutAt field on the user rejects any token issued before that mark, no session table or Redis blacklist needed.

Managed hosting now, VPS later

I chose Vercel + Railway to deploy fast and validate without infrastructure friction. As the client base grows I'll move to a VPS for cost — a decision I make when demand justifies it, not before.

Observability on Postgres before Redis

Instead of adding Redis from day one for caching/observability, I used what I already had: a slow-query log plus an interceptor that flags slow requests directly in Postgres. Redis is for when real traffic justifies it — the same gradual-evolution pattern as multi-tenant and hosting.

Agent-assisted development (Claude Code + MCP)

I set up specialized agents —frontend, backend, verification, audit and testing— with skills that encode how I would work. Even a skill that challenges whether a feature is really needed, so the software scales gradually without piling on unnecessary complexity from the start.

Challenges & learnings
  • Propagating the multi-currency logic across the whole system caused conversion bugs I fixed flow by flow until they were consistent.
  • Cron jobs with long I/O (invoicing retries, sync) needed to avoid concurrent execution across instances. I started with pg_advisory_lock, but it clashed with the connection pool (pg.Pool); I solved it with my own lease lock over a control table — simpler and more reliable than Postgres's advisory lock in this context.
  • I hardened security and sales handling as I better understood the real business cases.
  • I prioritized finishing it well over fast: I only replace something when I learn a more efficient way than what I already have.