Pukar Ghimire
All work

Who owns a client? Designing a construction project tracker

A side project for contractors to track projects, payments and progress, with clients seeing only what concerns them. Most of the work so far is deciding who owns what.

buildledger.example.com
Role
Designing and building
Company
Independent project
Stack
TypeScript, PostgreSQL
Outcome
In design: data model and ownership rules before features

The problem

A contractor runs several projects at once, each with its own client, vendors, labour payments, materials bought on credit and an agreed total.

Clients want to know how their project is going. They shouldn't see the contractor's books to find out.

Constraints

A client can hire more than one contractor, so a client isn't simply a row under one contractor.

Materials are often paid in parts, so every transaction needs a payment status, not just an amount.

Someone has to be clearly responsible for editing client details, or two contractors will overwrite each other.

The decision

Start from the entities and ownership rules, not the screens. A project links exactly one contractor and one client, and every income and expense record belongs to a project.

Keep financial records and client-facing updates and milestones as separate models. What a client can see is decided by the structure of the data, not by remembering to hide fields.

No company or organization layer for now. Contractors are individuals until a real customer needs a team.

The trade-off

Leaving out organizations keeps the model small, but a contractor who hires an office manager will need a migration later. That cost is acceptable for a requirement that doesn't exist yet.

Still open: whether a client is one shared identity across contractors or a separate record per contractor. Shared avoids duplicates but raises who may edit them. Per-contractor is simpler but duplicates the person.

Outcome

Not shipped yet. The point of doing the model first is that adding features later shouldn't reopen who can see or change what.

NextFrom Jira ticket to pull request, with an agent doing the typing