iVendNextDevelopers Request a sandbox

Call-centre orders by district: send each phone order to its store — the recipe

A retailer with a call centre takes orders by phone and ships each one from the store that serves the customer's district. The agent should enter the order once and not have to know which store that is. This recipe does it with the shop's own district tree and one button. The manager sets a Serving store on each district of the Territory tree. The agent presses Send to store on the draft order. Your service writes that store's warehouse on the order and on every line, and the store then sees the order in its own list. It is for an iVendNext Partner, or the CitiXsys Consulting Team, who wants orders routed by district without code inside the shop.

Reference implementation: samples/s35_call_centre_orders/.

When to use it. The routing table belongs to the shop (district → store) and the routing is the same every time, but the shop cannot do it alone: a manifest cannot fetch into the order's own warehouse field, a save rule writes only numbers, and the shop never copies the header's warehouse down to the lines. So a service writes it, and a Desk action is how a person asks for it. For a check that only refuses a wrong warehouse at submit, use a document check (recipe) instead; it never routes.

The four rules, here. No code runs inside the shop. One address is called, your service, and only when someone presses the button. The service changes only the warehouses on a draft order: it sets no price, never touches a submitted order, and never writes over an edit someone saved after it read the order. The shop's own save may re-rate the order at the new warehouse (a store's own tax): the agent is told.

What the agent and the store see

  1. The agent enters the order as today. Its Territory comes from the customer and can be changed for this order (a gift, a second address). The agent saves it.
  2. The agent presses Send to store and confirms: "Send this order to the store that serves its territory? Its warehouse changes on every line."
  3. Sent to store — This order and its 2 lines now ship from Maadi Store, the store for Maadi. The form reloads with the store in Set Source Warehouse and on every line. A district with no store of its own says which territory's store it took: "…the store for Cairo, which Heliopolis is part of."
  4. The agent submits the order. The store's staff find it in their Sales Order list, filtered on their own Set Source Warehouse. Its delivery note ships from their warehouse.

Pressed again, the button says Already with its store and changes nothing.

Steps

  1. Add the Serving store to Territory (a link to Warehouse, shown in the Territory list and as a filter):

    "custom_fields": [
      {"dt": "Territory", "fieldname": "pp_cco_store", "fieldtype": "Link", "options": "Warehouse", "label": "Serving store",
       "insert_after": "parent_territory", "in_list_view": 1, "in_standard_filter": 1,
       "description": "The store whose warehouse ships call-centre orders for customers in this territory. Leave it empty to use the store of the territory above."}]
    
  2. Add the button on a draft Sales Order's form, for the roles that take orders, sending only the order's name and territory (your service reads the rest itself), and the service that answers it (Desk action recipe):

    "extension_subscriptions": [
      {"key": "route", "point": "desk_action", "label": "Send to store service",
       "endpoint_url": "https://your-service.example/press/route", "timeout_seconds": 10}],
    "desk_actions": [
      {"record_type": "Sales Order", "label": "Send to store", "placement": "form", "service": "route",
       "roles": ["Sales User", "Sales Manager"], "on_draft": 1, "on_submitted": 0, "send_fields": "name, territory",
       "confirm_text": "Send this order to the store that serves its territory? Its warehouse changes on every line."}]
    
  3. Add a feed registration for the app's user (it follows Sales Order; leave it switched off (it installs off): switched on, it would record every sales order's saves for the app's user). A service that serves many shops receives the user's key through it on Connect again (guide chapter 14b):

    "registered_applications": [
      {"name": "PP CCO Routing feed", "description": "the routing service reads and writes sales orders as this feed's user",
       "events": ["Sales Order"]}]
    
  4. Write the service (samples/s35_call_centre_orders/route.py and service/app.py, standard library). For each press:

    • check the signature (the subscription's secret);
    • read the order with the app's user (GET /api/resource/Sales Order/<name>); answer in words if it is submitted or has no territory;
    • walk the territory up the tree (GET /api/resource/Territory/<name>, parent_territory) to the first Serving store;
    • read that warehouse and refuse in words a group, a disabled one or another company's (the shop saves a draft with any of these and refuses it only at submit);
    • if the order already ships from that store, answer Already with its store and write nothing;
    • otherwise write {"set_warehouse": store, "items": [every line as read, its warehouse changed], "modified": <as read>} with PUT /api/resource/Sales Order/<name> (every line, a supplier-delivered one too: the shop clears the header when the lines differ; and a bundle's parts, packed_items, whose warehouse the shop fills only when it is empty), compare the saved totals with the totals as read, then answer {"refresh": true, "message": …}, saying the new total if it moved;
    • a stale write (HTTP 417 TimestampMismatchError): read once more, answer Already if routed, else ask the agent to reload; never retry the write; any other 417: The shop would not save this order, never the shop's own text.
  5. Check it before the shop: python3 tools/check/check.py samples/s35_call_centre_orders (the shop's checker and its install's two extra refusals).

What the shop does

  1. Install the app. The service and the button come in switched off. The install adds the Serving store field and makes no role.

  2. Give the app a user, as in the integration user recipe: tick Is Desk User, set no password and no User Permissions, and generate its API key. In Role Permissions, give a role of the shop's own these rights, and give the user that role alone:

    Record type Right Why
    Sales Order Read, Write the service reads the order and writes its warehouses
    Territory Read the walk up the district tree
    Customer Read the order's own save reads its customer with a rights check
    Warehouse Read the store is checked before the write

    The role reaches every sales order (read and write) and every customer, territory and warehouse (read): give it to the app's user and no one else. Sales User also works, but it reaches further than the app uses: it creates and submits orders.

  3. Choose the user as the Application User of the feed PP CCO Routing feed. A hosted service: press Connect again on the app.

  4. Switch the service on in Retail Extension Subscription (Send to store service: read what it is sent, tick the consent), then the button in Retail Desk Action (Send to store).

  5. Set a Serving store on the districts (a Sales Master Manager): a store warehouse of the same company, never a group. A governorate's store serves its districts that have none.

  6. Each store saves its list once: the Sales Order list filtered on its own Set Source Warehouse. With a User Permission on its own warehouse, a store's staff see the orders sent to their store and not those sent to another; an order whose Set Source Warehouse is empty (none set, or lines on different warehouses) is seen by every store.

Limits

Technical notes

What the service sends and writes

What was proven where

Needs this serves

This page in the kit