How to Prevent Overselling When Two Staff Members Create Invoices
See how final server-side stock validation prevents two simultaneous wholesale invoices from selling the same units, without locking the business to one device.
When two staff members create invoices at the same time, stock must be checked again at the final server save—not only when each person opens the product list. My Local Shops uses a Firestore transaction shared by Order Manager and Staff Bill so only invoices supported by the latest committed location stock can succeed.
This allows multiple authorised devices without locking one app to one phone. It protects critical inventory writes while keeping ordinary product selection fast.
At a glance
| Question | Behaviour |
|---|---|
| Can owner and staff bill simultaneously? | Yes |
| Is displayed availability enough protection? | No; it may change before save |
| When is stock checked again? | Inside the final server transaction |
| What if another invoice commits first? | Firestore retries against the latest document state |
| What if stock is then insufficient? | The second invoice fails; no partial invoice is created |
| Can some lines save while one line fails? | No; stock, invoice, payment and related writes are atomic |
| Is protection shop-specific? | Yes; quantity is checked at the selected billing location |
| Do custom lines deduct inventory? | No; they are explicitly outside inventory stock |
| Does this require one-device locking? | No |
| What should the second staff member do? | Review the error, reduce or replace the item, then save again |
Why a normal stock check is not enough
Suppose Shop 1 has 100 pieces of Blue / M.
- Staff A opens the product and sees 100 available.
- Staff B opens the same product and also sees 100.
- Staff A prepares an invoice for 60.
- Staff B prepares another invoice for 60.
If both apps simply subtract from the number they originally loaded, they could create sales for 120 pieces even though only 100 existed.
The product picker still checks availability for good UX. It disables zero-stock variants, shows available quantity and prevents one invoice draft from reserving the same SKU twice beyond the loaded limit. But this is only the first layer because another device can change stock afterward.
What happens at final save
After invoice review and payment selection, the shared invoice service performs one server transaction.
Inside that transaction it:
- reads the latest product documents for every inventory line;
- confirms each product belongs to the seller;
- reads current stock at the selected shop or godown;
- rejects any line whose requested quantity exceeds current availability;
- calculates stock value removed and line profit from the current landed cost;
- updates product and grouped public-listing availability;
- creates stock-movement history;
- creates payment rows;
- creates the invoice; and
- updates connected operational statistics.
Firestore may rerun this calculation when another write changes a product during the transaction. Every retry starts from the newest committed stock rather than reusing an earlier total.
Trace: two staff try to sell the same stock
Starting stock at Borivali Shop: 100 Blue / M shirts.
Staff A
- Adds 60 pieces.
- Reviews the invoice.
- Presses save.
- Server reads 100.
- 60 is allowed.
- Invoice A and stock reduction commit together.
- Remaining stock becomes 40.
Staff B
- Had already seen 100 and added 60.
- Presses save shortly after Staff A.
- The transaction reads or retries against the latest stock: 40.
- Required 60 is greater than available 40.
- Invoice B fails with a not-enough-stock message.
- No stock, invoice, payment or report row from Invoice B is saved.
Staff B can then change the quantity to 40, select another SKU, or confirm physical availability before trying again.
Why the whole invoice must fail
Assume Invoice B has three lines:
- Blue / M: 60 requested, 40 available;
- Blue / L: 20 requested, 80 available;
- Red / S: 10 requested, 50 available.
Saving only the last two lines would create a bill different from what the staff member and customer reviewed. Saving payment while the invoice failed would be worse.
The transaction therefore follows an all-or-nothing rule. If any required inventory line cannot be completed, none of the invoice's critical writes commit.
Location stock matters
Business-wide stock is not enough for billing.
If a SKU has:
- Borivali Shop: 40;
- Andheri Shop: 70; and
- Bhiwandi Godown: 200,
an invoice dispatched from Borivali can use only Borivali's 40. Stock in another shop or godown does not silently cover the invoice.
The user can deliberately change the billing location, but quantities are then checked against the newly selected location at final save.
Owner and Staff Bill use the same protection
Order Manager creates invoices under the owner's account. Staff Bill creates them under an authorised staff identity and permitted owner/location scope.
Both call the same transaction-safe invoice service. The staff invoice records who billed it, while the owner can see the resulting bill, payment, outstanding and private profit information in Order Manager.
Staff cannot bypass stock validation by using the separate app.
What about Inventory Manager changes?
The conflict may come from more than another invoice. While billing is in progress, Inventory Manager could:
- transfer stock to another location;
- remove damaged stock;
- correct an adjustment;
- add a restock; or
- complete another authorised operation.
The final invoice transaction reads the current product document. If a concurrent write changes it, Firestore's transaction mechanism detects the changed document and retries the invoice calculation against the new state.
This is why the system does not need to prohibit multiple devices. It protects the shared record where the write becomes final.
What is and is not reserved
Adding a product to a draft invoice does not reserve stock globally. A staff member may abandon the bill, change quantities or take time discussing payment.
Permanent reservation at picker time would create a different operational problem: stock could become unavailable because someone merely opened a draft and walked away.
My Local Shops currently uses:
- draft-time availability for fast guidance; and
- transaction-time validation for final truth.
The first successfully committed invoice receives the stock. Other drafts must adjust if the remaining quantity is no longer enough.
Customer-friendly staff SOP
- Select the correct dispatch location before adding items.
- Use the displayed available quantity as guidance.
- Prepare all colour-size quantities.
- Review the invoice with the customer.
- Save without unnecessary delay when stock is limited.
- If a stock error appears, do not promise the unavailable quantity.
- Check the physical shelf or ask the owner about another location.
- Reduce, replace or transfer stock through the proper workflow.
- Review and save the corrected invoice again.
Common mistakes to avoid
Trusting an old product screen indefinitely
Availability can change. The final server validation is authoritative.
Manually creating a custom line for an out-of-stock inventory SKU
Custom items do not deduct inventory. Using them to bypass stock control makes the bill and stock disagree.
Billing from the wrong location
A nearby godown's stock cannot support a shop invoice unless the operation deliberately selects or transfers from the appropriate location.
Assuming a failed invoice partially saved
The critical transaction is atomic. After failure, correct the draft and retry; do not create an unrelated adjustment for an invoice that never committed.
Sharing the owner's login
Invite staff into Staff Bill instead. This preserves staff identity, location scope and owner-only data boundaries.
Frequently asked questions
Does the app lock a product while somebody prepares a bill?
No. Drafts do not reserve stock. The final successful transaction decides which invoice receives the remaining units.
What if four people sell the same SKU?
Each final transaction is evaluated against committed stock. Successful saves reduce availability; later conflicting saves retry and fail when their requested quantity is no longer available.
Does Firestore create document versions such as V1, V2 and V3?
Not as separate product documents visible to the app. Firestore detects that a document read by a transaction changed and reruns the transaction callback against the latest state.
Will a failed save silently disappear?
No. The create-invoice screen shows the error. The user remains responsible for reviewing and correcting the draft before retrying.
Can stock become negative?
The invoice transaction refuses a requested quantity greater than stock at the selected location, preventing that invoice from taking the value below zero.
Are returns also transaction-safe?
Yes. Credit-note creation reads the source invoice and product state transactionally before restoring returned inventory and updating payment effects.
Does this protect normal Inventory Manager stock writes too?
Critical restock, transfer, removal and connected trade-import operations use their own concurrency-safe write flows. This article focuses on invoice creation.
Multiple devices without corrupted stock
Small wholesalers may have an owner and one staff member at each shop. Preventing every second device would make ordinary work unnecessarily difficult.
The safer design is to allow authorised people to work while making the final stock-changing operation concurrency-safe. My Local Shops rechecks the latest location quantity and commits the invoice, payment, stock movement and catalogue availability together, so a stale draft cannot silently oversell the same units.
Continue with How Staff Can Create Bills Without Accessing Business Profit and How to Create a Wholesale Invoice with Multiple Colours and Sizes.
