Suppliers — Base Milestone
Status: unreleased. This walkthrough documents the code on the feat/marketplace-suppliers branch (PR #369), targeted at merging into feat/marketplace. Nothing here is live for customers yet.
What this PR is
Dentolize is growing beyond clinic/pharmacy management into a small trade platform: clinics and pharmacies (buyers) can order physical supplies from dental/medical suppliers (vendors) without leaving Dentolize, and suppliers get their own portal to manage what they sell and fulfil what gets ordered.
This PR is one of three sibling branches building on a shared foundation (feat/marketplace, see glossary for the model list). It owns two things:
- The supplier seller portal — a new standalone web app,
supplier-web, where a supplier's staff log in, list products, watch orders come in, and move them through fulfilment. - Buyer-side procurement — new screens inside the existing clinic-web and pharmacy-web apps that let clinic/pharmacy staff build a reorder list, shop the marketplace, place an order with a supplier, and receive it into stock.
The two sides meet at a shared record, the TradeOrder: a buyer places one, a supplier fulfils it, and a receipt on the buyer's side turns it into normal inventory + an expense bill — using Dentolize's existing inventory and accounting machinery rather than inventing new ones.
Why it matters
Clinics already buy consumables (gloves, anesthetic, burs, impression material) from outside suppliers by phone, WhatsApp, or in person, then hand-enter the purchase into Dentolize afterwards. This PR lets that purchase happen inside Dentolize end-to-end: browse what's on offer, place the order, and — once it arrives — one click turns it into stock plus a correctly posted supplier bill. For suppliers, it's a new, self-serve sales channel with its own storefront-style management screens and (eventually) a paid subscription tier ladder.
Scope of this milestone
Explicitly in scope (verified in code — see the feature breakdown pages for exact file:line references):
- Supplier listing CRUD, scoped to the supplier's own company, gated by a plan quota (
maxListings). - A seller-side order inbox with a fixed state machine (
PLACED → ACCEPTED → PREPARING → SHIPPED → DELIVERED, with earlyREJECTED/CANCELLEDexits). - Revenue recognition posted to the supplier's own books when an order ships or delivers, reversed if a fulfilled order is later cancelled.
- RFQ inbox + offer submission for suppliers, gated behind a paid entitlement (
RFQ_RESPOND). - A buyer-side reorder list (suggested quantities from low stock), a minimal marketplace browse + cart, placing an order, and a generic "receive into stock" bridge that creates a real
InventoryOrderandExpense.
Explicitly out of scope here (owned by the sibling feat/marketplace-core branch, per docs/trade-platform/README.md): the RFQ engine itself (creating RFQs, inviting vendors, awarding a winning offer), multi-vendor cart routing, and the richer marketplace search. This branch's buyer-side "Marketplace" tab and browseListings query are explicitly a minimal placeholder ("verifiable end-to-end before marketplace-core lands its advanced multi-vendor search" — packages/server/src/supplier/procurementService.js:24-27), not the final buyer shopping experience.
Who should read what
- Never touched this feature before → read the Walkthrough next.
- Want the mechanics (state machine, accounting entries, gating rules) → the Feature breakdown pages.
- Marketing, sales, support, training, stakeholders, QA → your dedicated page under By team.