RentTracker: The Hidden Accounting Engine Behind Property Management SaaS
How one Next.js app turns rent, dues, deposits, and bank transfers into a precise reconciliation system that behaves more like ERP software than a landlord spreadsheet.
- RentTracker’s real innovation is an accounting model that can split one payment across many obligations without collapsing into spreadsheet logic.
- The repo behaves more like ERP software than a property dashboard because it treats accruals, allocations, and payment status as first-class financial objects.
- The data model is doing the product work by preserving relationship integrity, lease linkage, and transaction history instead of flattening everything into rows.
- The AI-oriented scaffolding hints that the codebase is meant to be understandable and extensible by both humans and agents.
Why RentTracker Is Really About Debt, Not Dashboards
Most property tools start with the tenant and end with a paid badge. RentTracker starts with the obligation. That is a deeper model, because the hard problem is not displaying rent due. It is deciding how a single incoming payment should settle rent, dues, deposits, and any leftover balance without breaking the books.
That difference matters. A dashboard can tell you who paid. An accounting engine can tell you what was paid, what remains, what was partially satisfied, and how that status changed over time. RentTracker is built around the second problem.
The Splitter Pattern That Makes Reconciliation Work
The heart of the repo is the reconciliation flow in process-accruals and reconcile/match. A bank movement does not simply flip a debt from open to closed. It becomes a transaction, then gets allocated across one or more accruals until each obligation is either partially covered or fully satisfied.
The key idea is simple: needed = amount - paid_amount. If a transaction covers only part of an accrual, the system keeps that remainder visible. If the balance reaches zero, the status becomes Paid. If not, it stays Partial. That is a small rule with large consequences, because it preserves the legal and operational truth of the account.
That is why the repo feels unusual. It does not treat reconciliation as an afterthought. It treats reconciliation as the product.
Why This Feels Like ERP, Not CRUD
The domain model is more disciplined than a typical admin app. Rent, aidat, deposits, leases, residents, funds, transactions, and allocation records are separate objects, which means the code can preserve the differences that matter in accounting. The result is less like a landlord checklist and more like a small ERP system built for one narrow financial lifecycle.
| Model | What it optimizes for | What it loses |
|---|---|---|
| Spreadsheet tracking | Fast manual entry | Allocation history, auditability, and consistent status logic |
| Generic property CRUD | Basic admin workflows | Accounting semantics and partial settlement precision |
| RentTracker | Fund-level reconciliation | Simplicity, because the model is intentionally more structured |
A flat system can answer, “Did the tenant pay?” RentTracker can answer, “What did that money settle, in what order, and what still remains open?” That is the difference between records and accounting.
The Onboarding Shortcut: Resident, Lease, and Linkage in One Move
The resident endpoint shows the product thinking clearly. When a property is present, POST /api/residents can create the resident, create the lease, and link them through the join table in one flow. That compresses onboarding and avoids the common trap of forcing users through a string of disconnected admin steps.
// Smart onboarding flow in POST /api/residents
const resident = await createResident(input)
if (propertyId) {
const lease = await createLease({ propertyId, residentId: resident.id })
await linkLeaseResident({ leaseId: lease.id, residentId: resident.id })
}
return { resident }
That is not just convenience. It is a signal that the repo optimizes for workflow compression. The system wants the user to move from intent to a valid financial relationship in as few steps as possible.
The Data Model Is Doing the Real Product Work
The schema in DATABASE_SCHEMA.md is where the design becomes visible. Join tables like lease_residents preserve many-to-many relationships. Tables like condition_report_items keep operational history intact. JSONB fields handle files and photos without forcing the relational core to become messy.
That combination matters. Normalization keeps the accounting and tenancy model honest, while JSONB gives the app enough flexibility for real-world attachments and metadata. It is a pragmatic split between structure and variation.
| Schema choice | Why it helps | Trade-off |
|---|---|---|
| Normalized relationships | Preserves leases, residents, and shared responsibility | More joins and more design discipline |
| JSONB for files and photos | Stores messy operational data without schema churn | Less rigid querying for attachments |
| Flat tables everywhere | Easy to start | Breaks down once the property lifecycle gets complex |
If the reconciliation engine is the brain, the schema is the skeleton. Without the right relationships, the rest of the product would collapse into a pile of status flags.
The Stack Is Conventional. The Product Thinking Is Not.
The stack is familiar: Next.js 15, Neon, Tailwind, Shadcn, Zod, and react-hook-form. Nothing here is exotic on purpose. That is the point. The repo uses standard parts to support an uncommon financial workflow, which is usually the right move for a B2B app that needs to be understandable and maintainable.
The interesting layer is not the framework choice. It is the way the stack is composed around accounting rules, reconciliation flows, and a polished operational interface.
What the AI-First Scaffolding Suggests About the Repo’s Future
The .agents directory, PRD-style scaffolding, and template-like structure suggest that the repository is meant to be legible to agents as well as humans. That is not a gimmick. It is a maintenance strategy. When a codebase can explain its own domain, future changes are less likely to drift away from the original accounting model.
That also makes the repo feel like a template for a class of products, not just one product. The code is trying to stay machine-readable in the same way the financial model stays auditable.