Order Taking & Sales Invoicing¶
Bizwiz Module Guide — Sales & Receivables
1. Purpose¶
This chapter covers the journey a sale takes from the moment someone captures it until it becomes a confirmed invoice ready to be dispatched. In Bizwiz (a wholesale/distribution ERP used by Kenyan distributors), most sales are made on credit to shops on a delivery route — the salesman visits the shop, records what the shopkeeper wants, and the system turns that into a proper tax invoice that the customer owes money for.
There are several doors into this process, but they all lead to the same place — a confirmed invoice that reduces stock, creates a debt on the customer's account, and (in Kenya) gets stamped by KRA's tax device:
- Field / mobile order taking — A salesman out on the route uses the mobile app to record an order at a shop. This is the most common path. Depending on a company setting, the order either becomes an invoice straight away, or it first becomes a Sales Order that the back office reviews.
- Sales Orders — A two-stage flow: the order is captured (pending), optionally confirmed by the office, then processed into one or more invoices. If the customer is over their credit limit, the order pauses for a Credit Invoice Approval before it can be invoiced. If goods are returned, a Credit Note Approval reverses part of the invoice.
- Salesman Invoice (Sales Invoice) — A back-office screen for creating a credit sales invoice directly (the classic "internal requisition" flow). Includes an OTP (one-time PIN) safeguard when a customer is at/over their credit limit.
- Backend Invoicing — A feature-flagged office screen (
allow-backend-invoice-order-taking) that lets head-office staff raise an invoice on a route customer's behalf, with the same credit-approval safety net. - Service Invoices — Invoices for services (not stock items) — e.g. transport, repairs — with their own simple approve → KRA-sign flow and credit notes.
Once an invoice is confirmed/completed, it hands off to other modules for physical delivery, banking of the money, and reconciliation. Those are out of scope here (see Section 5).
Naming note: In Bizwiz code, a "requisition" (
wa_internal_requisitions, and the newercredit_sale_invoices) is a credit sales invoice — the word is historical. When you see "internal requisition" think "the invoice document."
2. Users & roles¶
Access is gated two ways throughout the module:
- Super-admin bypass:
$logged_user_info->role_id == config('app.allowed_role')(role 1) sees everything. - Granular permissions: the pattern
isset($my_permissions['<slug>___<action>'])in Blade, andcan('<action>', '<module>')in controllers. Slugs followmodule___action.
| Who | What they do | Key permissions (slug ___ action) |
|---|---|---|
| Field salesman (mobile) | Captures orders at shops; may submit over-limit orders for approval | Authenticated via JWT on mobile API; gated by an open shift (salesman_shifts.status = 'open') rather than a web permission |
| Sales / order clerk | Creates & confirms Sales Orders, raises Sales Invoices / Backend Invoices | sales-orders-create___view / create-orders; sales-orders-confirmation___view / confirm-orders / edit / edit-quantity / reject-orders; sales-invoice___invoices / invoices-create / edit-invoice |
| Processing officer | Invoices confirmed orders, declines, reprocesses | sales-orders-processing___view / invoice / bulk-invoice / reject / approve / edit |
| Credit controller / AR manager | Approves/rejects over-limit orders; receives OTPs | credit-invoice-approvals___view / approve / reject; credit-invoice-approvals-direct-history___view / complete-order / reject-order |
| Returns approver | Approves credit notes | credit-note-approvals___view / approve |
| Invoice confirmer / storeman | Confirms approved invoices (stock break + deduction) | confirm-invoice___view |
| Backend invoicer | Head-office invoicing on behalf of route customers | backend-order-taking___view / create / edit / edit-values / approve / archived-orders |
| Service invoicing clerk | Raises/approves service invoices, signs with KRA | service-invoices___view / edit / approve / decline / edit-approved / report / register |
Sidebar visibility is also setting-gated (sales_and_receivables.blade.php:1-5):
- Sales Orders menu appears only when setting identify-sales-orders-from-normal-orders is on.
- Backend Invoicing menu appears only when setting allow-backend-invoice-order-taking = 1.
The nav item literally labelled "Order Taking" (
sidebar...:1601) is actually shift/schedule management (Salesman Shifts, Analytics, Route Shift Overrides, Offsite Requests) — gated byorder-taking___view/order-taking-schedules___view. The actual order capture happens on the mobile app, not on this menu. See Open Questions.
3. Processes¶
3.0 The big picture¶
flowchart TD
subgraph Capture
M[Field salesman - mobile app] -->|POST record-sales-orders| API["SalesInvoiceController@create"]
W[Office clerk - web] --> SO_create["SalesOrderController@create/store"]
BE[Head office - Backend Invoicing] --> BEC[BackendOrderTakingController]
SI[Office clerk - Salesman Invoice] --> IRC[InternalRequisitionController]
end
API -->|setting identify-sales-orders ON| SOP[SalesOrderProcessing -> creates SalesOrder PENDING]
API -->|setting OFF - legacy| DIRECT[Direct invoice path]
W --> SOP2[sales_orders PENDING]
SOP --> SOP2
SOP2 --> CONF{Confirmation enabled?}
CONF -->|yes| PC[Pending Confirmation -> is_confirmed]
CONF -->|no| PROC[Processing]
PC --> PROC
PROC -->|invoice| CREDIT{Over credit limit / withholding block?}
CREDIT -->|no| INV[Create invoice = WaInternalRequisition]
CREDIT -->|yes| CIA[Credit Invoice Approval]
CIA -->|approve| INV
CIA -->|reject| REJ[Rejected / soft-deleted]
IRC --> CSI[credit_sale_invoices UNAPPROVED/PENDING]
BEC --> BINV[backend_invoices UNAPPROVED]
CSI --> APPR[Approval permissions -> APPROVED]
APPR --> CONFIRM[IssueFullfillRequisitionController - confirm]
BINV -->|no credit block| CONFIRM
BINV -->|credit block| CIA
CONFIRM --> COMPLETE[status COMPLETED - stock deducted, debtor posted]
INV --> COMPLETE
COMPLETE --> DISPATCH[Handoff: Dispatch & Delivery]
Two invoice document families coexist (see Section 6):
- Legacy: wa_internal_requisitions (+ _items) — the "internal requisition."
- New parallel: credit_sale_invoices (+ _items) — created by the newer Salesman Invoice flow, then converted into a wa_internal_requisition at confirmation time.
The lifecycle status vocabulary differs per family:
- sales_orders.status enum: PENDING, CLOSED, FULFILLED, PARTIALLY-FULFILLED, DECLINED (migration 2025_10_15_151835), plus boolean flags is_approved, is_invoiced, is_confirmed, and enum credit_approval_status (pending/approved/rejected/not_required).
- wa_internal_requisitions / credit_sale_invoices / backend_invoices.status: UNAPPROVED → PENDING → APPROVED → COMPLETED (or DECLINED).
- service_invoices.status tinyint: 0=UNAPPROVED, 1=APPROVED, 2=DECLINED, 3=COMPLETED.
3.1 Field / mobile order taking (the main entry)¶
Trigger: Salesman submits an order from the Flutter app (../bizwizorderanddelivery) → POST record-sales-orders (throttled), routed to SalesInvoiceController@create (routes/api.php:756).
The fork happens at the top of create() (SalesInvoiceController.php:90-97):
flowchart TD
A[POST record-sales-orders] --> B{Setting identify-sales-orders-from-normal-orders = 1?}
B -->|Yes| C[SalesOrderProcessing]
B -->|No| D[Legacy direct-invoice path in create]
C --> E[Validate: open shift, route/customer match, item sellable/blocked/retired]
E --> F[Geo-proximity: onsite vs offsite via MappingService]
F --> G["Create sales_orders row status=PENDING, order_number SOR-{id}"]
G --> H[Create sales_order_items with resolved pricing/UOM/discount]
H --> I[Commit -> order awaits office confirmation/processing]
Key facts from SalesOrderProcessing() (SalesInvoiceController.php:2268-2560+):
- Requires an open shift for the salesman (salesman_shifts where status='open', salesman_id).
- Resolves the credit customer account: route's shared wa_customers account, overridden by credit_customer_id, further overridden by a key-account assignment when enable-key-account-sales-deduction is on.
- Records proximity to the shop (OrderLocationLog / PreOrderLocationLog) using allowable-distance-to-mark-sale-as-onsite (default 400m) → sets shift_type = onsite|offsite.
- Creates the order as SOR-{id}, status='PENDING'. Free/promo items skipped at order level (handled at invoice level).
- Honors INVENTORY_SETUP = 'UOM BASED', ENABLE_ADDITIONAL_COST_PRICE_LIST_MODULE, per-line manual discounts, and selling-price overrides (SellingPriceOverrideService).
- Customer-app orders (origin=customer_app) authenticate a RouteCustomerRegistration JWT and post under a configured shift-owner user.
Over-limit path from mobile (submitOrderForCreditApproval, SalesInvoiceController.php:1442; completeApprovedOrder, :1834): gated by setting ENABLE_CREDIT_APPROVAL_ORDER_FLOW. When a customer is credit-blocked, the salesman submits the order for approval instead of a straight invoice; the office approves/rejects via the Credit Invoice Approvals queue (3.5).
Supporting mobile endpoints: check-salesman-proximity, check-credit-limit, sales/check-group-discount-promotions.
Legacy path (setting OFF): create() continues inline and invoices directly against the customer account (credit-blocking via isRouteCustomerCreditBlockingEnabled() and BLOCK_DEPENDENT_CUSTOMER_FROM_TAKING_ORDERS). This is the pre-Sales-Order behaviour.
3.2 Sales Orders — capture → confirmation → processing → invoice¶
Owned by SalesOrderController (app/Http/Controllers/Admin/SalesOrderController.php); routes in routes/web.php:1357-1416. Menu gated by identify-sales-orders-from-normal-orders.
stateDiagram-v2
[*] --> PENDING: create()/store() or mobile SalesOrderProcessing()
PENDING --> PENDING_confirmed: confirm()/bulkConfirm() sets is_confirmed=true
PENDING --> DECLINED: decline()/bulkReject()/bulkRejectConfirmation()
PENDING_confirmed --> credit_pending: invoice() finds over-limit -> credit_approval_status=pending
PENDING_confirmed --> INVOICED: invoice()/bulkInvoice() creates WaInternalRequisition
credit_pending --> INVOICED: CreditApproval approve (auto-invoice)
credit_pending --> rejected: CreditApproval reject (soft-delete)
INVOICED --> CLOSED: all items closed (approved_quantity == quantity)
INVOICED --> PARTIALLY_FULFILLED: some items pending
PENDING --> CLOSED: closeOrder() (no invoice)
INVOICED --> DISPATCHED: handoff to Dispatch & Delivery
Stages & routes:
| Stage | Route / method | What happens |
|---|---|---|
| Create | sales-orders.create → create(); sales-orders.store → store() |
Blank form (perm view on sales-orders-create); store (perm create-orders) writes sales_orders (PENDING) + sales_order_items, attaches/creates a shift. Also a bulk-upload sub-flow (sales-orders.bulk-upload*). |
| Confirmation | sales-orders.pending-confirmation → pendingConfirmation(); .confirm → confirm(); .bulk-confirm; .confirmation-edit/.confirmation-update/.confirmation-items.delete; .bulk-reject-confirmation |
Lists status=PENDING AND is_confirmed=false AND is_invoiced=false. Confirming sets is_confirmed/confirmed_by/confirmed_at/confirmation_notes. Editing rewrites items (respecting edit-quantity perm) and stamps confirmation_last_edited_by/at. Gated by Setting::isSalesOrderConfirmationEnabled(). |
| Processing | sales-orders.processing → processing(); .invoice → invoice(); .bulk-invoice; .reprocess-item(s); .close; .decline/.bulk-reject |
The heavy lifting. invoice() (SalesOrderController.php:5779) locks the order, validates status is PENDING, runs credit-approval + withholding checks, then creates a WaInternalRequisition (status APPROVED, or COMPLETED for customer-own-collection) plus WaInternalRequisitionItems, updates each sales_order_items.approved_quantity/status (closed/partially-closed/pending), records stock moves, and sets is_invoiced=true (and status=CLOSED when fully fulfilled). |
| Dispatched | sales-orders.dispatched → dispatched() |
Read-only list of invoiced orders (wa_internal_requisitions where sales_order_id NOT NULL). Perm view on dispatched-sales-orders. Physical dispatch is another chapter. |
| Rejected | sales-orders.rejected → rejected() |
status='DECLINED'. Perm view on rejected-sales-orders. |
| Loading sheets | sales-orders.loading-sheets*, .create-loading-sheet |
Groups invoiced orders onto loading sheets (sales_order_loading_sheets + _items) — the seam to Dispatch. |
Sync integrations (flavor-specific): processing can push to external systems — syncDataedgeInvoices/Customers, syncBtlInvoices/Customers — gated by dataedge-invoices-sync-enabled / btl-invoices-sync-enabled. There is also an API sales-order import pipeline (api_sales_order_imports table; sales-orders.api-imports*) for third-party order capture.
3.3 Salesman Invoice (Sales Invoice) — direct back-office invoice¶
InternalRequisitionController (resource sales-invoice, routes/creditSales.php:14-43) + IssueFullfillRequisitionController (confirm-invoice, creditSales.php:79-82).
flowchart TD
A[create - blank invoice form] -->|OTP gate if REQUEST_OTP_ON_CREDIT_SALES| B[store]
B --> C[credit_sale_invoices UNAPPROVED, items]
C -->|send_request| D[addCreditSaleInvoicePermissions -> PENDING; auto-APPROVED if no approvers]
D --> E[confirm-invoice/update]
E --> F[processConfirmCreditSaleInvoice]
F --> G[Auto-break stock if short + ACTIVATE_AUTO_STOCK_BREAK]
G --> H[Credit-limit re-check]
H --> I[Create WaInternalRequisition COMPLETED + items]
I --> J[Dispatch PerformPostSaleActions job -> stock deduction, GL/debtor posting]
J --> K[Print / KRA sign / handoff to dispatch]
Key facts:
- Create/store (InternalRequisitionController.php:422, 1159): builds a credit_sale_invoice (UNAPPROVED, or PENDING on send_request). Validates credit limit (credit_limit − Σ wa_debtor_trans.amount ≥ invoice total), MOQ (MinimumOrderQuantityService), per-customer daily cap (MaxSellingQtyGuard), blocked items, and price overrides. Invoice number via NumberSeriesGeneratorService (CREDIT SALE INVOICE series → CSINV-*).
- OTP over-limit safeguard (sendOtp/sendOtpOver/verifyOtp, :2704-2810; routes credit.sales.otp*): when REQUEST_OTP_ON_CREDIT_SALES is on, create() requires a verified credit_otp_verified session flag. A 6-digit OTP is cached (5-min TTL) and SMS'd to approver users/roles resolved from an Alert config (initiate-credit-sales-otp / credit-sales-otp); recipient set depends on whether the customer's balance is positive.
- Confirmation (IssueFullfillRequisitionController.php:399-517): converts the approved credit_sale_invoice into a WaInternalRequisition (COMPLETED), re-checks stock (auto-breaking via AutoBreakingService::attemptAutoBreak() when short and ACTIVATE_AUTO_STOCK_BREAK is on), re-checks credit, links the two records, registers SEVI settlement intent, and dispatches PerformPostSaleActions (queue sales) for stock deduction and debtor/GL posting.
- ESD/KRA: save_esd (confirm-invoice.save_esd) stores KRA e-signature response in wa_esd_details.
- Printing: A4 / thermal / logo templates selected by ALLOW_SYSTEM_TO_PRINT_COMPANY_LOGO_ON_INVOICE_PDF, PRINT_INVOICE_IN_THERMAL_PRINTERS; reprints tracked via WaInventoryLocationTransfer.print_count and guarded by enforce-reprint-permission.
- Archiving: sales-invoice.archive-pending / archived-orders soft-archive UNAPPROVED invoices (CreditSalesArchiveService).
3.4 Backend Invoicing (feature-flagged)¶
BackendOrderTakingController — routes routes/web.php:1333-1354, menu gated by allow-backend-invoice-order-taking. Lets head office raise an invoice on a route customer's behalf (mirrors credit_sale_invoices with BINV-* numbers).
flowchart TD
A[create - pick store/route/customer/items] --> B{Customer credit-blocked AND ENABLE_CREDIT_APPROVAL_ORDER_FLOW?}
B -->|No| C[store / send_request -> finalize backend_invoice]
C --> D[Create WaInternalRequisition APPROVED/COMPLETED]
B -->|Yes| E[submitForCreditApproval]
E --> F[backend_invoices UNAPPROVED + sales_orders credit_approval_status=pending, backend_invoice_id linked]
F --> G[Credit Invoice Approvals]
G -->|approve| H[finalizeCreditApprovedBackendInvoice -> WaInternalRequisition]
G -->|reject| I[soft-delete SalesOrder + delete UNAPPROVED backend_invoice]
Key facts (BackendOrderTakingController.php):
- getRouteCustomerCredit() computes limit/used/available and block reasons using BLOCK_ROUTE_CUSTOMER_ON_CREDIT_LIMIT.
- No credit block: store()/finalization creates a WaInternalRequisition directly (status APPROVED if it should go to a loading sheet, else COMPLETED) via stock breaking.
- Credit block: submitForCreditApproval() creates a backend_invoices draft (UNAPPROVED) and a sales_orders row (credit_approval_status='pending', backend_invoice_id linked, credit_block_reason set) so it appears in the approvals queue. On approval, finalizeCreditApprovedBackendInvoice() turns the draft into a WaInternalRequisition.
- Archived list: backend-order-taking.archive-report (ArchivedBackendInvoicesReportController).
3.5 Credit Invoice Approvals & Credit Note Approvals¶
Credit Invoice Approvals — CreditApprovalController (routes/web.php:1419-1424).
stateDiagram-v2
[*] --> pending: order/invoice over limit sets credit_approval_status=pending
pending --> approved: approve() sets approved_by/at + notes
approved --> invoiced: auto-invoice if AUTO_INVOICE_ON_CREDIT_APPROVAL (default on)
pending --> rejected: reject() soft-deletes SalesOrder (+deletes UNAPPROVED backend draft)
approved --> rejected: rejectOrder() from history (e.g. stock ran out)
approved --> invoiced_manual: completeOrder() from history
- Queue (
index):sales_orderswherecredit_approval_status='pending', with live debt (Σwa_debtor_trans), order total, and any linkedbackend_invoice. Permcredit-invoice-approvals___view. - approve (
:418): setscredit_approval_status='approved',credit_approved_by/at,credit_approval_notes. If a backend order →finalizeCreditApprovedBackendInvoice(); else, whenAUTO_INVOICE_ON_CREDIT_APPROVAL(defaults true) → callsSalesOrderController@invoicewith an internal auto-invoice flag. Perm___approve. - reject (
:544):credit_approval_status='rejected'+ soft-deletes thesales_ordersrow (keeps shift/approval queues clean); deletes the linked UNAPPROVEDbackend_invoices/items. Perm___reject. - directHistory / completeOrder / rejectOrder (
:131,279,340): a merged history across the two credit flows (WaInternalReqPermissionfor direct/backend +sales_orders.credit_approval_status), with post-approval complete/reject actions. Permscredit-invoice-approvals-direct-history___view/complete-order/reject-order.
How an order lands in this queue (web): during SalesOrderController@invoice/bulkInvoice, checkCreditApprovalRequired() (and withholding check BLOCK_SALES_ON_PENDING_INVOICE_WITHHOLDING_TAX_CERTIFICATE) sets credit_approval_status='pending' + credit_block_reason and aborts the invoice (SalesOrderController.php:4740-4749, 5886-5924).
Credit Note Approvals — CreditNoteApprovalController (routes/web.php:1427-1429).
- index: sales_invoice_returns filtered by processed (pending vs approved), resolving the parent sales_order via CreditNoteIntegratedReturnService. Perm credit-note-approvals___view.
- bulkApprove: sets processed=true, processed_by/at, and calls CreditNoteIntegratedReturnService::processOnCreditNoteApproval() (GL/customer-credit effects). Perm ___approve.
Credit notes reverse invoiced sales (returns). The mechanics of returns themselves (Sales Invoice Returns) live in the returns/dispatch area; this queue is only the approval gate for the credit-note side.
3.6 Service Invoices¶
ServiceInvoicingController + ServiceTypeController (routes/modules/service_invoicing.php). For services (not stock) — e.g. haulage, repairs.
flowchart TD
A[create - pick service type, customer, vehicle?] --> B[store: service_invoices status=0 UNAPPROVED]
B -->|save_and_approve + can approve| C[status=1 APPROVED]
B --> D[approveInvoice -> status=1 APPROVED]
C --> E[signWithKra -> cu_invoice_number + wa_esd_details]
D --> E
C --> F[createCreditNote/storeCreditNote -> reversal doc]
C --> G[declineInvoice -> status=2 DECLINED]
Key facts:
- service_invoices.status: 0 UNAPPROVED / 1 APPROVED / 2 DECLINED / 3 COMPLETED.
- store/update validate customer (is_invoice_customer, not blocked), line items (service_type, qty, unit_price, cost_type, optional images & vehicle_id when trackVehicleForServiceInvoicing() is on). save_and_approve needs can('approve','service-invoices').
- signWithKra submits to KRA (ETIMS/ESD), stores cu_invoice_number and wa_esd_details (or wa_esd_details_return for credit notes).
- Service Types (service-types.*) carry minimum_charge, tax assignment, and — in ETIMS mode — KRA registration (kra_vscu_item_code) gated by service-invoices___register.
- Reports & customer remap under service-invoicing.report* / remap-customers*.
4. Tables touched & key data¶
Sales Orders¶
sales_orders(mig.2025_09_30_091701, altered2025_10_15_151835,2025_12_31_075437,2026_04_29_092718,2026_08_27_140000):order_number(SOR-{id}),slug,customer_id→wa_customers,customer/customer_name/customer_phone,user_id,route+route_id,wa_route_customer_id,restaurant_id,wa_location_and_store_id,order_date,wa_shift_id,shift_type(onsite/offsite). Status:statusenum (PENDING/CLOSED/FULFILLED/PARTIALLY-FULFILLED/DECLINED),is_approved,is_invoiced,is_confirmed,confirmed_by/at,confirmation_notes,confirmation_last_edited_by/at,invoiced_by/at,invoice_notes,approved_by/at,declined_by/at,decline_reason. Credit:credit_approval_statusenum (pending/approved/rejected/not_required),credit_approved_by/at,credit_approval_notes,credit_block_reason. Plusbackend_invoice_id,payment_code,settle_with_sevi,customer_own_collection,has_free_line_items,third_party_system_doc_number. Soft-deletes.sales_order_items(mig.2025_09_30_091751+ many alters):sales_order_id,wa_inventory_item_id,wa_location_and_store_id,quantity,approved_quantity,standard_cost,total_cost,discount_percentage/amount/description,exclusive_discount_amount,selling_price,tax_manager_id,vat_rate/amount,total_cost_with_vat,uom_id+ UOM/conversion columns,status(item-level closed/partially-closed/pending),is_free_item, group-promo columns,notes. Soft-deletes.sales_order_loading_sheets/sales_order_loading_sheet_items— grouping for dispatch (seam).api_sales_order_imports— third-party imported orders.
Invoices (the "requisition" families)¶
wa_internal_requisitions/wa_internal_requisition_items— the confirmed invoice document (legacy + target of conversions). Carriessales_order_id(mig.2025_09_30_095615) andis_backend_invoiceflag (mig.2025_12_01_154316). StatusUNAPPROVED/PENDING/APPROVED/COMPLETED/DECLINED.credit_sale_invoices/credit_sale_invoice_items— new Salesman-Invoice document (CSINV-*); converted into awa_internal_requisitionat confirmation.backend_invoices/backend_invoice_items(mig.2026_08_18_100000/100001):invoice_no(BINV-*),slug, statusUNAPPROVED/PENDING/PROCESSING/APPROVED/DECLINED/COMPLETED,wa_internal_requisition_id,internal_requisition_no,wa_route_customer_id,payment_code,add_to_loading_sheet,is_backend_invoice_order.
Service Invoices¶
service_invoices(mig.2026_06_15_100100):invoice_number,invoice_type(tinyint),customer_id,route_customer_id,route_id,cu_invoice_number,invoice_date,branch_id,sub_total/vat_amount/withholding_amount/amount/cost,status(0–3),memo,created_by,print_count, credit-note fields.service_invoice_items/service_invoice_payments/service_invoice_payment_line_allocations.
Shared / cross-cutting¶
salesman_shifts— the open shift gating mobile capture (owner: Order-Taking/Shifts area).wa_debtor_trans— customer ledger (used for live credit balance; written by post-sale, owned by Banking/Receivables chapter).wa_stock_moves,wa_stock_breakings(+items),route_auto_breaks— inventory movement & auto-breaking (Inventory chapter).wa_esd_details/wa_esd_details_return— KRA e-signature records.sales_invoice_returns(+ items) — credit-note source (Returns/Dispatch chapter).settings— feature flags (below).wa_route_customers,wa_customers,routes,restaurants,wa_location_and_stores,wa_inventory_items,tax_managers— masters.
5. Interactions with other modules¶
Inbound (into this chapter):
- Mobile app (../bizwizorderanddelivery) → POST record-sales-orders (SalesInvoiceController@create). This is the primary origin of field orders.
- Customer Management — route customers, credit limits, blocking, key-account assignment, KRA PIN. This chapter reads those; masters are Customer Management chapter.
- Inventory — item sellability, blocked/retired items, pricing (branch/route/additional-cost), stock levels, and auto-breaking (AutoBreakingService, wa_stock_moves). Managed by the Inventory chapter.
- Order Taking / Shifts — the open salesman_shifts requirement (nav "Order Taking" = shift management).
Outbound (out of this chapter):
- Dispatch & Delivery — once invoiced (WaInternalRequisition COMPLETED / on a loading sheet), physical picking/dispatch/delivery is handled there. sales-orders.dispatched, loading sheets, and delivery schedules are the seam.
- Banking & Receivables Reconciliation — the debt created (wa_debtor_trans) and the money-in against the invoice (payment_code) reconcile there. PerformPostSaleActions posts the debtor/GL entries.
- POS / Cash Sales — cash-at-till sales are a separate document family (wa_pos_cash_sales) and chapter.
- SEVI settlement — settle_with_sevi / sevi-initiate hands invoices to the SEVI settlement flow (separate area).
- KRA / ESD — e-signing (wa_esd_details) is invoked from confirmation and service-invoice signing.
6. Alternatives & variants¶
- Two capture models, one setting:
identify-sales-orders-from-normal-ordersdecides whether a mobile order becomes a Sales Order (review-then-invoice) or invoices directly (legacy). This is the biggest behavioural switch in the chapter. - Confirmation step is optional:
Setting::isSalesOrderConfirmationEnabled()toggles the pending-confirmation stage. When off, orders go straight to processing. (Note:confirm()/bulkConfirm()reference this flag but the sub-agent flagged the enforcement as partly commented out — see Q7.) - Backend Invoicing is entirely gated by
allow-backend-invoice-order-taking. - Credit-approval flows are triple-gated: mobile
ENABLE_CREDIT_APPROVAL_ORDER_FLOW; customer blockingBLOCK_ROUTE_CUSTOMER_ON_CREDIT_LIMIT/isRouteCustomerCreditBlockingEnabled(); auto-invoice on approvalAUTO_INVOICE_ON_CREDIT_APPROVAL(default on). Web invoicing usescheckCreditApprovalRequired(). - OTP over-limit on the Salesman Invoice path:
REQUEST_OTP_ON_CREDIT_SALES(with recipient logic keyed on customer balance and branch). - Withholding-tax block:
BLOCK_SALES_ON_PENDING_INVOICE_WITHHOLDING_TAX_CERTIFICATEcan push an order into the approval queue with a withholding reason instead of a credit reason. - Two invoice document families: legacy
wa_internal_requisitionsvs newercredit_sale_invoices; controllers resolve polymorphically by slug and the new form is converted into the legacy form at confirmation. Backend invoices (backend_invoices) are a third parallel that also converts towa_internal_requisitions. - UOM-based vs quantity-based setup:
INVENTORY_SETUP = 'UOM BASED'changes item/qty handling across capture and invoicing. - Auto stock breaking:
ACTIVATE_AUTO_STOCK_BREAKperforms breaking at confirmation when stock is short. - Flavor/tenant sync:
dataedge-*andbtl-*invoice/customer sync in processing;api_sales_order_importsfor third-party order feeds — present only for certain clients. - Key-account deduction:
enable-key-account-sales-deductionreroutes the posting account to a specificwa_customersrecord. - Customer-app orders:
origin=customer_appuses aRouteCustomerRegistrationJWT and a configured shift-owner user (a self-service ordering variant). - Legacy/commented flows:
wa_sales_orders*(2023) predate the currentsales_orders; the legacysendRequisitionRequest()path onwa_internal_requisitionsis superseded by thecredit_sale_invoicessend_requestflow.
7. Open questions to confirm¶
- "Order Taking" nav ≠ order capture. The menu labelled "Order Taking" is shift/schedule management (Salesman Shifts etc.); actual capture is the mobile app. Confirm this is the intended IA and whether any web "capture" screen exists that I missed. (Inference from
sidebar...:1601-1645.) sales_orders.statusenum discrepancy. The DB enum isPENDING/CLOSED/FULFILLED/PARTIALLY-FULFILLED/DECLINED, but theSalesOrdermodel's helper methods referenceAPPROVED(e.g.isApproved()usesis_approvedboolean, not the enum). Confirm whetherFULFILLED/PARTIALLY-FULFILLEDare actually written anywhere, or if fulfillment is tracked purely via the boolean flags + item-level status. (The sub-agent found only PENDING/CLOSED/DECLINED being written.)- Where does the mobile order become an invoice? In the Sales-Order path, mobile capture creates a
sales_ordersrow (PENDING) and the office invoices it viaSalesOrderController@invoice. Confirm there is no path where the mobile app itself confirms/invoices whenidentify-sales-orders-from-normal-ordersis on. ENABLE_CREDIT_APPROVAL_ORDER_FLOWvscheckCreditApprovalRequired(). The mobile submit path checksENABLE_CREDIT_APPROVAL_ORDER_FLOW; the web invoice path usescheckCreditApprovalRequired()/BLOCK_ROUTE_CUSTOMER_ON_CREDIT_LIMIT. Confirm these are wired to the same underlying credit-block logic and can't disagree.credit_sale_invoicesmigration. I documented this table from controller behaviour but did not read its migration (search matchedwa_internal_requisitionsalterations). Confirm the table name/columns (CSINV-*numbering, status enum). (Inference fromInternalRequisitionController/IssueFullfillRequisitionControllerusage.)AUTO_INVOICE_ON_CREDIT_APPROVALdefault. Sub-agent states it defaults to true viaisAutoInvoiceOnCreditApprovalEnabled(). Confirm the default and whether it's per-flavor.- Confirmation enforcement.
confirm()/bulkConfirm()reportedly checkSetting::isSalesOrderConfirmationEnabled()but with the enforcement partly commented out. Confirm whether confirmation is truly optional/enforced. - Service invoice
COMPLETED(status 3). The enum includesCOMPLETEDbut I did not trace where it's set (payment allocation?). Confirm the transition APPROVED→COMPLETED for service invoices. - Withholding block reason routing. Confirm a withholding-blocked order surfaces in the same Credit Invoice Approvals queue (it sets
credit_approval_status='pending'with a withholding message) and how approvers distinguish credit vs withholding blocks. - Legacy
wa_sales_orders*(2023). Confirm these are fully deprecated/unused vs the currentsales_orders.
8. Source references¶
Navigation / IA
- resources/views/admin/includes/sidebar_includes/sales_and_receivables.blade.php — menu spine; Sales Orders :983-1029+, Backend Invoicing :900-927, Service Invoices :929-981, "Order Taking" (shifts) :1601-1645, Salesman Invoice :1400+.
Routes
- routes/web.php:1333-1416 — Sales Orders + Backend Invoicing; :1419-1429 — Credit Invoice / Credit Note approvals; :1446-1450 — internal-requisition invoice views.
- routes/creditSales.php:14-93 — Sales Invoice (sales-invoice), Confirm Invoice (confirm-invoice), OTP routes.
- routes/modules/service_invoicing.php — Service Invoices.
- routes/api.php:756 — mobile record-sales-orders; :757-761 — proximity/credit/promotions/credit-approval submit.
Controllers
- app/Http/Controllers/Admin/SalesInvoiceController.php — mobile capture: create() :87, SalesOrderProcessing() :2268, submitOrderForCreditApproval() :1442, completeApprovedOrder() :1834, checkCreditLimit() :2239.
- app/Http/Controllers/Admin/SalesOrderController.php — web Sales Orders: create/store, pendingConfirmation/confirm/bulkConfirm, confirmationEdit/Update, processing, invoice() :5779, bulkInvoice() :4668, credit-approval trigger :4740-4749 / 5886-5924, dispatched/rejected/decline/closeOrder/reprocessItem(s).
- app/Http/Controllers/Admin/BackendOrderTakingController.php — store, submitForCreditApproval :478, finalizeCreditApprovedBackendInvoice :742, getRouteCustomerCredit :809.
- app/Http/Controllers/Admin/CreditApprovalController.php — index :31, approve :418, reject :544, directHistory/completeOrder/rejectOrder :131/279/340.
- app/Http/Controllers/Admin/CreditNoteApprovalController.php — index :18, bulkApprove :181.
- app/Http/Controllers/Admin/InternalRequisitionController.php — Salesman/Sales Invoice: store :1159, OTP :2704-2810, searchInventory :2462.
- app/Http/Controllers/Admin/IssueFullfillRequisitionController.php — confirmation: processConfirmCreditSaleInvoice :399, processConfirmWaInternalRequisition :240, save_esd :121.
- app/Http/Controllers/Admin/ServiceInvoicing/ServiceInvoicingController.php — store :306, approveInvoice :472, signWithKra :533, storeCreditNote :591; ServiceTypeController.php.
Models
- app/Models/SalesOrder.php, app/Models/SalesOrderItem.php, app/Model/BackendInvoice.php, app/Model/WaInternalRequisition.php.
Migrations (schema)
- database/migrations/2025_09_30_091701_create_sales_orders_table.php, ..._091751_create_sales_order_items_table.php, 2025_10_15_151835_alter_slaes_orders_table_to_edit_status.php, 2025_12_31_075437_add_credit_approval_fields_to_sales_orders_table.php, 2026_04_29_092718_add_confirmation_to_sales_orders_table.php, 2026_08_27_140000_add_backend_invoice_id_to_sales_orders_table.php.
- 2026_08_18_100000_create_backend_invoices_table.php (+ _items), 2025_12_01_154929_add_setting_to_allow_backend_invoice_order_taking.php, 2025_12_01_154316_add_flag_for_backend_invoice_identification_on_internal_requisitions.php.
- 2026_06_15_100100_create_service_invoices_table.php (+ items/payments).
- 2025_09_30_094719_add_setting_to_identify_sales_orders_from_nornal_orders.php, 2025_09_30_095615_add_sales_order_reference_to_wa_internal_requisitions.php.