Groundwork Code

Source code

Fulfilment Control for Multi-Vendor Marketplaces

Order-fulfilment source code for marketplaces where one customer order splits across multiple vendors.

The demo runs on a free server and sleeps when idle, so the first load takes about a minute. Its data is wiped regularly.

Released
2026-09-04
Rules implemented
16
Process chains
6
Known limitations
9
The application, signed in
The interface is deliberately plain. This is a reference implementation you extend, not a finished product — the work sits in the rules it enforces.

What it does

This is the operational core for a multi-vendor marketplace: the part that takes a single customer order, splits it per vendor, and tracks each portion through its own fulfilment lifecycle. When an order contains items from more than one vendor, the system creates a separate order split for each, so a vendor sees and works only their own lines. It moves orders and splits through documented states — from scheduled and unfulfilled, through in_fulfillment, partial and full fulfilment, hold, cancellation and restock — and records who acted at each transition from the session, not from the request body.

It calculates shipping cost itself: billable weight is the greater of actual and dimensional weight, and DIM weight is derived from package dimensions and a divisor. It is not wired to a carrier — it computes a figure, it does not buy a label or look up tracking. Where most of the value sits is in what it will not let through: a fulfilment cannot be marked without a shipment record carrying a tracking number, and a cancellation before shipment voids the associated pick task instead of leaving a box to go out the door.

The 6 process chains

Each chain is a route a record travels. What matters as much as what it does is what it refuses — those refusals are wired into the state transitions, not offered as guidance.

F1

Approving vendors before access

Staff

A staff member reviews a vendor registration and approves it. Until that approval, the vendor account exists but cannot log in. On approval, the system records the approving staff member's name from the session, and once live the vendor manages only their own order lines after a split.

It refuses

  • Login fails while a vendor is still registered; it succeeds only after a staff member approves.
  • approved_by_name is set to the logged-in staff member even when the request body sends a different name.
  • A vendor attempting product approval receives 403; a merchant receives 200.
F2

Approving products for the storefront

Merchant

A vendor submits a product, and it stays out of the storefront until the merchant approves it. The merchant controls what goes live. On approval the approving identity is taken from the session. The same flow covers the shipping-cost calculation the product data feeds into.

It refuses

  • approved_by_name is the merchant from the session even when the body carries a different name.
  • For a 12×12×12 package with divisor 139, DIM rounds to 13: with actual weight 5 the billable weight is 13, with actual weight 20 it is 20.
F3

Driving the order lifecycle

SysteemOperations / warehouseOperations / StaffCustomer service / StaffSysteem / warehouse

The system advances an order through its states: it becomes unfulfilled when the fulfillment date is reached, moves to in_fulfillment while a warehouse picks and packs, and reaches partially_fulfilled or fulfilled as line items ship. Staff can place an order on hold and release it back to unfulfilled. Customer service can cancel before shipment, and cancelled orders move to restocked once every item is returned to available inventory.

It refuses

  • A hold blocks fulfilment until released; inventory can remain reserved during the hold.
  • Cancellation before shipment must void the associated pick task rather than leave a shipment to proceed.
F4

Splitting orders by vendor

Systeem

When a received order contains products from more than one vendor, the system splits it automatically into one order split per vendor. Each vendor then sees only their portion. An order with items from a single vendor is not split.

It refuses

  • An order with items from two vendors produces two order_splits and split_status split; an order with one vendor produces no split.
F5

Fulfilling each vendor split

Operations / warehouseCustomer service / Staff

Each vendor split runs its own fulfilment lifecycle. The warehouse moves a split into in_fulfillment, then to partially_fulfilled as some lines ship and fulfilled once all lines of that split ship with a valid tracking number. If the order is cancelled before shipment, the corresponding split pick task is voided.

It refuses

  • Shipping part of a vendor split sets it to partially_fulfilled; shipping the rest sets it to fulfilled.
  • A split reaches fulfilled only when its lines ship with a valid tracking number — a fulfilment without a shipment record is not accepted.
F6

Processing returns and restock

Operations / warehouseStaff

Operations record a requested return as received once the physical goods arrive, then move it to restocked when the returned quantities are put back into available inventory. Staff can reject a requested return. Restock and rejection are gated on permissions, and the acting identity is recorded from the session.

It refuses

  • A user without can_restock receives 403 when attempting a restock.
  • A user without the required permission receives 403 when rejecting an RMA.
  • On restock, available_qty increases by the returned quantity, restocked is set, and processed_by_name is the logged-in user.

Who uses it

Roles as the source material names them, with what each one does in the system.

RoleDoes
Admin (superuser)Runs the whole marketplace operation, sees every admin module and holds full rights over vendors, orders, shipping and returns.
StaffApproves vendor registrations, manages products and orders, places orders on hold and releases holds, and cancels orders before shipment.
Merchant (marketplace owner)Decides through approval which vendor products go live, and oversees the operation.
VendorManages its own products, submits them for approval, and sees and handles its own order lines after an order is split.
Vendor staffWorks inside the vendor's store with rights the vendor sets and can adjust.
Operations / warehouseCarries out fulfilment: picking and packing, marking partial shipments, reporting orders as fulfilled per label with a tracking number, and restocking after a cancellation.
Customer service / StaffCancels orders before shipment and makes sure the matching pick task is voided.

What this is not

This system carries no certification, audit or approval of any kind. It calculates shipping cost but is not connected to any carrier: there is no label purchase, no negotiated rate and no tracking lookup — a tracking number is a value you record, not one this system obtains. It handles no payment data, so nothing here falls in PCI-DSS scope, and it stores no addresses, email addresses or phone numbers — a marketplace that adds those takes on data-protection obligations this code does not cover. It is not certified or approved by any marketplace platform, and nothing in it has been audited against a customs or export regime. This is a starting point that implements documented rules, not a compliant system.

The rules here come from 17 business rules drawn from 42 that were examined, all 17 in scope. They are sourced from operational and marketplace documentation, not from a legal or regulatory standard, and the citations are English-language vendor and logistics references. This defines how far the implementation reaches: the state machine, the splitting logic and the shipment and cancellation guards. Anything beyond those rules is outside what this code does.

The database layer uses Node's built-in SQLite module, which is still marked experimental and prints a warning on every start. Fine for a reference implementation; you will likely want to swap it before production.

Known and accepted

  • Some screens (order detail, audit log) can be opened without signing in. They show no data. Opening a screen without a session yields nothing: all 15 read routes refuse a request without a token, and the app sends you back to the sign-in screen on a 401. What you see is an empty screen, not a data leak. Hiding those screens on the front end is an improvement, not a defect in the core promise; accepted for a prototype.
  • The shipments screen does not show which product a shipment is for: the table lists id, order, split, tracking number, weight, cost and who did it, but no product name. A shipment can cover several order lines of one order, so there is no single product to put in the table; the link runs through the order lines. A product column would be empty or misleading for an ordinary shipment. The tracking number is the carrier's number — the proof that something actually left the building, which is the core of the promise — and since it is free text you can use it to recognise your own line. Showing the product on the shipment list is an improvement, not a defect in the core promise; accepted for a prototype.
  • The inventory screen shows only a product id, not a product name or SKU, so you need to know the ids to tell which stock line belongs to which product. Same kind as the shipments screen, and the same weighing: awkward to read, but it does not touch the core promise and blocks nothing. The lesson is recorded for the pipeline: a list screen should carry a recognisable column.
  • The acceptance tests are written in Dutch, while the rest of the application is English. The three tests added during the review are in English; the thirteen acceptance tests from the build remain Dutch. Translating them by hand would touch the frozen test suite this release rests on, and a translation is not a functional improvement worth that risk. Accepted for this product; for the next products it is fixed at the source.
  • The order detail screen shows buttons the signed-in user is not allowed to use: an operations user sees 'Release hold' and gets 'insufficient permissions' when clicking it. Left as is (decision of 2 September 2026). The screen derives its buttons from the status of the order, not from the rights of the signed-in user; both are needed to know what someone may do. The server refuses correctly with a 403 and a readable message, so nothing goes wrong — it costs a user one click to find out they lack the right. Hiding buttons per right is an improvement, not a defect in the core promise.
  • Two API routes have no screen: GET /api/roles and POST /api/users would need a user-management screen that was never specified. Neither touches the core promise. The three return routes that the automated check could not assess are used: in the running app Receive, Reject and Restock were clicked and the return moved from requested to received to restocked.
  • Shipment lines can only be seen through their shipment (GET /api/shipments/:id), not as a list of their own — outside their shipment they have no meaning. Return RMAs do have their own list (GET /api/returns) and the returns screen shows them.
  • Weighed per field: name, SKU, dimensions, weight, customer name, order reference, order and order-line ids and quantity are known at creation and belong there. Only the scheduled date of an order might change later (rescheduling); that is an extension, not a defect.
  • A handful of source files use Dutch names and comments: 6 of the 44 source files outside the tests, mostly the server bootstrap, the seed and the API client. The user interface and the API are English. Accepted as is: it is confined to a few files and does not affect how the system runs. You may come across some Dutch when you read the bootstrap code. The price is unchanged.

What it rests on

16 rules the system implements, quoted from the material they were taken from. Each one carries its reference and its source in the archive, next to the module, the check and the test that enforce it. The reference in grey is the identifier the code and tests use; it comes from a larger catalogue of rules, so the numbers are not consecutive.

#Quoted from the sourceReference
1“You will not be able to log into your account ... until a marketplace staff member approves”BR-14
2“Vendors submit products for merchant approval. The merchant controls what goes live”BR-15
3“A warehouse is picking and packing the order”BR-16
4“at least one line item on the order has shipped and at least one has not”BR-17
5“every item on the order has been packed, handed to a carrier, and recorded as shipped”BR-18
6“Fulfillment is deliberately blocked. Inventory can still be reserved”BR-19
7“cannot be fulfilled until the hold is released, at which point the status returns to Unfulfilled”BR-20
8“until the fulfillment date is reached, at which point the order status updates to Unfulfilled”BR-21
9“cancellation must void the pick task”BR-22
10“all of the items on the order have been returned to available inventory”BR-23
11“Shipturtle splits it automatically so each vendor sees only their portion”BR-24
12“divide the cubic size of the package in inches by the DIM divisor to calculate the dimensional weight”BR-31
13“the first shipment fires a confirmation email, the customer assumes the order is complete”BR-34
14“marks an order Fulfilled without attaching a tracking number”BR-35
15“A storefront cancellation that never voids the pick task produces a shipped box”BR-36
16“orders split in ShipStation for Amazon ... will not update correctly on the selling channel”BR-40

Price

Both tiers contain exactly the same code. The only difference is how often you may use it. Either way you sell what you build with it, not the code itself.

Single project

$1,000

Use this code in one system, for yourself or for one client.

Unlimited

$2,500

Use it in as many systems as you like, for yourself or for clients.

Read what this is not before you buy: 9 known limitations, weighed and accepted.

Checkout opens shortly. To buy now, or to ask anything first, write to groundworkcode@outlook.com.

You may not resell this as source code, in whole or in substantial part, modified or not. What you build with it is yours to sell. The full licence ships with the archive.