CASE STUDY

PCyesa

Record-keeping POS for a computer shop

A system for a single computer shop that sells parts, custom PC builds and ready-made packages. It tracks every part from delivery to sale, checks builds for compatibility, and shows the owner what was sold, what is in stock and what the shop really earned. I designed and built all of it on my own, from the database schema and API to the interface and tests.

Role
Solo developer, design to testing
Type
Personal project
Status
Feature complete, not yet deployed
The PCyesa app

19

API modules

26

Database tables

2 / 27

Roles / permission codes

37

Test files

01.The problem

A computer shop sells hundreds of different parts, each with its own specs, and many sales are custom builds put together at the counter. Without proper records, it's hard to know what's really in stock, which build will actually work, which serial went to which customer, or whether a sale made money after the discount.

PCyesa keeps all of that in one place. Every part that comes in or goes out is recorded, the builder catches parts that don't fit together, and every unit sold carries its serial and warranty. The owner sees profit and inventory value, while staff only see what they need to sell.

It's deliberately a record-keeping tool for one shop. It issues no official receipts, uses no OR or SI numbers and computes no VAT, and every printed summary says NOT AN OFFICIAL RECEIPT.

02.What it does

Parts catalog

Parts are grouped by category and brand, and each category has its own typed spec fields, such as socket, RAM type, wattage or form factor. Each part has a selling price, a low-stock threshold and a weighted-average cost that staff never see.

Inventory

Deliveries are recorded as stock-ins from suppliers, and corrections as adjustments with a reason and a note. Every change writes a movement row with the balance after it, so each part has a full history, and low stock is flagged live.

PC builder

Staff pick one part per slot, with quantities on RAM and storage, and the builder checks the build as they go. A build can be saved as a draft or a quote, and a quote holds its prices for 7 days by default.

Packages

Predefined bundles show how many can be sold from current stock. At the counter, a part can be swapped for another in the same category, and the price adjusts by the difference. The sale records what actually left stock.

POS

One cart takes parts, builds, packages and service items like assembly or OS installs. It handles discounts, split payments across cash, GCash, Maya, bank transfer or card, downpayments, and serial numbers for each unit sold.

Balances, voids and returns

A balance can be collected later, and a sale can be held until it's picked up. The Owner can void a sale or return single items, down to one part of a package, either back to stock or written off as damaged.

Serials and warranty

Any serial can be looked up to see who bought it, when, and whether it's still under warranty. Serials can be corrected with an audit trail, but never deleted.

Dashboard and reports

An owner dashboard plus sales, best-seller, inventory and profit reports, all in Manila business days. Counters stay in sync over Socket.IO, so a sale on one screen updates stock on every other.

03.How a sale is written

Every sale goes through one function, in one transaction. If any check fails, nothing is saved: no stock moves, no payment is recorded and no serial is taken. Collecting a balance, releasing a held sale, voiding and returning each have their own single path too.

ONE TRANSACTION

  1. Cart
  2. Price check
  3. Lock stock
  4. Write sale
  5. Audit

WHAT A SALE RECORDS

  1. Sale
  2. Lines (what was bought)
  3. Items (what left stock)
  4. Units (serials)

STOCK

Deducted at sale, even when the order is held

So held items can't be sold to someone else. Selling more than is on hand is refused.

PROFIT

Net revenue − cost of goods, per item

Each item keeps its cost at the time of sale. A damaged return keeps the cost, so the loss shows in the report.

Stock never goes below zero. Totals always add up.

Owner and Staff map to 27 permission codes, and the database backs the key rules with CHECKs and unique indexes

04.Engineering decisions

One way to change stock

Every stock change goes through one function. It locks the part's row, refuses to go below zero with a 409, and writes the movement with its balance. A CHECK in the database backs it up, so stock can't go negative even if a bug slips past.

Cost never reaches staff

Cost, margin and profit are removed by the API, not just hidden in the UI. A contract test walks every route staff can read and fails on a cost, whether it finds it by name or by value. Staff type the cost on a delivery and never get it back.

Compatibility checks warn, not block

The builder flags conflicts, cautions and parts it can't check because a spec is missing. A shop sometimes knows better, so staff can still sell the build, and the sale records each conflict and who went ahead.

The server always sets the price

Prices come from the database, never the request. The pricing, compatibility and refund rules are pure functions with a checked copy in the UI for instant results, and both copies have to pass the same shared cases.

Discounts above the cap need the Owner

Staff can discount up to a cap the Owner sets. Above it, an Owner approves with a separate 6-digit PIN, so they never type their password in front of staff. Five wrong tries lock it for 15 minutes.

Totals always add up

A sale-wide discount is spread over lines and then items by largest remainder, so the amounts sum exactly to the centavo. A return first reduces any balance owed and only refunds the rest, and every scenario test ends by checking that stock and money reconcile.

05.Testing

Stock and money have to stay right on every route, including ones added later. Alongside its feature tests, the API's Vitest and Supertest suite has contract tests that check those rules across the whole API.

  • ▸Tests run in-process against a real PostgreSQL database and a real Redis, with nothing in the data path mocked.
  • ▸Contract tests fail the suite on a route with no permission check, a write with no audit row, a cache with no invalidator, or a cost that staff could read.
  • ▸A route coverage guard fails the suite if any route has no passing test.
  • ▸A full shop day runs through the API from start to finish, and a concurrency suite fires conflicting writes at once to prove stock and sales stay correct.
  • ▸A migration test builds a second database from the committed migrations and fails if it differs from the schema in any column, constraint or index.
  • ▸A load check rebuilds the database at full size, with 5,000 parts and 100,000 sales, and times the catalog, lists, reports and dashboard against their budgets.

06.Tech stack

  • TypeScript
  • Express 5
  • React 19 (Vite)
  • TanStack Router, Query & Table
  • Base UI
  • Tailwind CSS
  • PostgreSQL
  • Drizzle ORM
  • Redis
  • Socket.IO
  • Zod
  • Recharts
  • Vitest
  • Docker
  • Nginx
  • Sentry

The interface uses its own dark theme built for a shop counter, with large click targets, keyboard-friendly screens and every text pair checked for WCAG AA contrast in both light and dark modes. Every feature is built and tested, and deployment comes next.

Losing track of stock and sales?

I build business systems end to end: the records, the checks and the tests that keep them correct. Let's talk about yours.