Skip to content

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 newer credit_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, and can('<action>', '<module>') in controllers. Slugs follow module___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 by order-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_orders where credit_approval_status='pending', with live debt (Σ wa_debtor_trans), order total, and any linked backend_invoice. Perm credit-invoice-approvals___view.
  • approve (:418): sets credit_approval_status='approved', credit_approved_by/at, credit_approval_notes. If a backend order → finalizeCreditApprovedBackendInvoice(); else, when AUTO_INVOICE_ON_CREDIT_APPROVAL (defaults true) → calls SalesOrderController@invoice with an internal auto-invoice flag. Perm ___approve.
  • reject (:544): credit_approval_status='rejected' + soft-deletes the sales_orders row (keeps shift/approval queues clean); deletes the linked UNAPPROVED backend_invoices/items. Perm ___reject.
  • directHistory / completeOrder / rejectOrder (:131,279,340): a merged history across the two credit flows (WaInternalReqPermission for direct/backend + sales_orders.credit_approval_status), with post-approval complete/reject actions. Perms credit-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, altered 2025_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: status enum (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_status enum (pending/approved/rejected/not_required), credit_approved_by/at, credit_approval_notes, credit_block_reason. Plus backend_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). Carries sales_order_id (mig. 2025_09_30_095615) and is_backend_invoice flag (mig. 2025_12_01_154316). Status UNAPPROVED/PENDING/APPROVED/COMPLETED/DECLINED.
  • credit_sale_invoices / credit_sale_invoice_items — new Salesman-Invoice document (CSINV-*); converted into a wa_internal_requisition at confirmation.
  • backend_invoices / backend_invoice_items (mig. 2026_08_18_100000/100001): invoice_no (BINV-*), slug, status UNAPPROVED/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-orders decides 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 blocking BLOCK_ROUTE_CUSTOMER_ON_CREDIT_LIMIT / isRouteCustomerCreditBlockingEnabled(); auto-invoice on approval AUTO_INVOICE_ON_CREDIT_APPROVAL (default on). Web invoicing uses checkCreditApprovalRequired().
  • 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_CERTIFICATE can push an order into the approval queue with a withholding reason instead of a credit reason.
  • Two invoice document families: legacy wa_internal_requisitions vs newer credit_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 to wa_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_BREAK performs breaking at confirmation when stock is short.
  • Flavor/tenant sync: dataedge-* and btl-* invoice/customer sync in processing; api_sales_order_imports for third-party order feeds — present only for certain clients.
  • Key-account deduction: enable-key-account-sales-deduction reroutes the posting account to a specific wa_customers record.
  • Customer-app orders: origin=customer_app uses a RouteCustomerRegistration JWT and a configured shift-owner user (a self-service ordering variant).
  • Legacy/commented flows: wa_sales_orders* (2023) predate the current sales_orders; the legacy sendRequisitionRequest() path on wa_internal_requisitions is superseded by the credit_sale_invoices send_request flow.

7. Open questions to confirm

  1. "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.)
  2. sales_orders.status enum discrepancy. The DB enum is PENDING/CLOSED/FULFILLED/PARTIALLY-FULFILLED/DECLINED, but the SalesOrder model's helper methods reference APPROVED (e.g. isApproved() uses is_approved boolean, not the enum). Confirm whether FULFILLED/PARTIALLY-FULFILLED are 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.)
  3. Where does the mobile order become an invoice? In the Sales-Order path, mobile capture creates a sales_orders row (PENDING) and the office invoices it via SalesOrderController@invoice. Confirm there is no path where the mobile app itself confirms/invoices when identify-sales-orders-from-normal-orders is on.
  4. ENABLE_CREDIT_APPROVAL_ORDER_FLOW vs checkCreditApprovalRequired(). The mobile submit path checks ENABLE_CREDIT_APPROVAL_ORDER_FLOW; the web invoice path uses checkCreditApprovalRequired()/BLOCK_ROUTE_CUSTOMER_ON_CREDIT_LIMIT. Confirm these are wired to the same underlying credit-block logic and can't disagree.
  5. credit_sale_invoices migration. I documented this table from controller behaviour but did not read its migration (search matched wa_internal_requisitions alterations). Confirm the table name/columns (CSINV-* numbering, status enum). (Inference from InternalRequisitionController/IssueFullfillRequisitionController usage.)
  6. AUTO_INVOICE_ON_CREDIT_APPROVAL default. Sub-agent states it defaults to true via isAutoInvoiceOnCreditApprovalEnabled(). Confirm the default and whether it's per-flavor.
  7. Confirmation enforcement. confirm()/bulkConfirm() reportedly check Setting::isSalesOrderConfirmationEnabled() but with the enforcement partly commented out. Confirm whether confirmation is truly optional/enforced.
  8. Service invoice COMPLETED (status 3). The enum includes COMPLETED but I did not trace where it's set (payment allocation?). Confirm the transition APPROVED→COMPLETED for service invoices.
  9. 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.
  10. Legacy wa_sales_orders* (2023). Confirm these are fully deprecated/unused vs the current sales_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.