Create a sales order

Learn how to create a sales order in OpenMRP.


To create a sales order in OpenMRP, you need to have created a customer and created a product. Sales orders are created as estimates and then can be: issued to start the fulfillment process, used to generate a production run for the products in the order, and used to generate payment requests for the order. In the current OpenMRP UI flow, the production run is typically created while the order is still in Estimate.

What is a sales order?

A sales order is the order that ties together:

  • the customer
  • the items/quantities/prices
  • the billing & shipping details
  • the fulfillment and invoicing workflow

Once created, it becomes the source record for picking, shipping, invoicing, and payment tracking.

Simplest steps

Creating a sales order can be done extremely simply:

  1. Select a customer
  2. Select a product and quantity
  3. Add product to the order
  4. Create the sales order

Congratulations! You have created a sales order estimate that can now be issued. It can be as simple as that. Because OpenMRP pulls details from the customer, shipping and billing data are pre-filled for you.

Let's also look in more detail at the creation of a sales order.

Required inputs

These are effectively required to create an order.

FieldWhat it definesWhat it doesWhy it’s needed
CustomerWho is buyingLoads customer defaults (addresses, terms, carrier, priority, contacts)An order must belong to a customer
Payment TermsWhen/how customer paysControls invoice/payment behaviorNeeded for billing logic
Shipping TermsFreight responsibility/rulesAffects shipping/freight handlingNeeded for fulfillment & freight logic
CarrierDefault shipping carrierUsed for shipping rate/line estimation and shipment setupNeeded to plan shipment
PriorityOrder urgencyDrives operational prioritizationNeeded for queueing/ops behavior
Bill-to addressBilling destinationUsed on invoice and financial docsRequired for valid billing record
Ship-to addressDelivery destinationUsed for shipment creation/ratingRequired for fulfillment
At least one line itemWhat is being soldCreates order value and fulfillment workloadWithout lines there is no order

Optional inputs

You can leave these blank or set them later.

FieldWhat it definesWhat it doesWhy you’d use it
Customer PO NumberCustomer’s internal PO refHelps customer-side reconciliation; duplicate warning check runsUseful for matching your SO to their PO
Promised DateCommitted ship/delivery targetUsed for scheduling and expectation settingSLA / commitment tracking
Acknowledgement email contactsWho should get order confirmationsStores notification recipients for this orderKeep customer contacts informed
Invoice email contactsWho should get invoice notificationsStores invoice notification recipientsRoute invoices to the right people
NoteInternal/custom instruction textSaved on order for ops visibilityHandling instructions, context
Order discountAn order-level discount codeAdds a negative-priced discount line to the order automaticallyPromotions, negotiated one-off concessions
Manual line price overridePer-line price edit (unlock line)Replaces the calculated price on that lineException handling on pricing

What each section is doing in the create flow

1) Basic details

  • Select customer
  • Optionally enter customer PO
  • Optionally set promised date
  • Shows customer type group (read-only context)

What happens behind the scenes: selecting a customer auto-populates terms, addresses, carrier, priority, note, and default notification contacts.

2) Customer-specific order settings

  • Payment terms
  • Shipping terms
  • Carrier & carrier option
  • Priority
  • Acknowledgement & invoice contacts
  • Note

These are the operational defaults for this specific order (even if customer defaults differ later).

3) Addresses

  • Bill-to and Ship-to can be edited per order
  • Ship-to can be matched to Bill-to quickly

These addresses are saved/linked and used directly for billing and shipment execution.

4) Line items

  • Add product & quantity
  • The unit price is calculated for you — see How line prices are set below
  • Volume discounts and customer prices apply automatically
  • Lock/unlock price per line for manual override
  • Order total updates from line values

5) Order discount

Set order_discount_id to an order discount's ID or to its unique discount code — either is accepted — and OpenMRP adds an extra negative-priced discount line to the order for you. The reduction is not a separate total field — it sits on the order as a line, so it is already reflected in the order totals and in anything computed from them, including checkout.

Changing the order discount on an existing order records the new discount but does not add, re-price, or remove the discount line. If you need the charge to change, edit that line directly.

How line prices are set

Prices are resolved server-side from the product, on both create-order and add-a-line. You do not have to compute them, and in the ordinary case you should not send them.

For each line, OpenMRP starts from the product's list price and then applies, in order:

  1. the fixed and percentage unit-conversion discount configured for the ordered unit inside the product's unit group,
  2. a conversion from the product's base unit into the unit the line was ordered in,
  3. every volume discount tier whose quantity threshold the order meets, and
  4. any contracted customer price for that customer and product, which replaces the calculated result outright.

When a line is created — with the order or added afterwards — the unit cost is always taken from the product and never from the request. Updating an existing line is the only place a unit_cost can be supplied, and the value you send is stored as-is, rounded to the nearest cent.

An explicit unit price sent on a create-order or add-a-line request is honored only for internal users. A request made on behalf of a customer account ignores it and stores the calculated price. Editing an existing line's price afterwards is a normal update and is open to anyone with permission to edit the order — that is what the UI's unlock-the-line control does.

Showing prices before an order exists

To display prices while a user is still assembling an order, call the price-quote endpoint with the customer account and the product/quantity pairs. It runs the same pricing engine and creates nothing:

curl API_HOST/v1/sales/sales-orders/price-quote \
  -X POST \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "OpenMRP-Version: CURRENT_API_VERSION" \
  -H "OpenMRP-Account: YOUR_ACCOUNT_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "buyer_account_id": "ac_opnlh43ymyee",
    "lines": [{ "product_id": "pd_07oe0r7adh2w", "quantity": { "value": "10", "unit_id": "un_82bd37dae5po" } }]
  }'

It responds with one priced line per requested line, in the order you sent them. Internal price overrides are not accepted here — the calculated price is always what comes back. See Quote Sales Order PricesAPI.

Auto-generated / system-managed values

You don’t need to enter these manually:

  • Sales Order Number: generated by system counter at create time
  • Status: the order is created as an estimate; it changes only through the issue, unissue, close, and reopen actions
  • Created by / ownership links: set from signed-in account/user
  • Sales rep assignment: resolved by internal assignment logic — the customer's default rep first, then the sales territory matching the ship-to postal code, then the ship-to state. No rep is assigned when the customer is commission-exempt, or when every ordered product belongs to a commission-exempt product line.
  • Line prices and costs: calculated from the product, as described above
  • Shipping line: a freight line carrying the estimated shipping charge is always added to the order
  • Discount line: added automatically when an order discount is supplied
  • Change log entry: creation is logged for audit trail

Validation & guardrails you should know

  • You cannot create a sales order with zero line items.
  • A customer is required before customer-specific terms and contacts can be selected.
  • The customer PO number must be unique among that customer's orders. Over the API a duplicate is rejected outright with a conflict; the UI warns you before you get there.
  • Carrier, shipping terms, and payment terms fall back to the customer's defaults when omitted, and the order is rejected if neither the request nor the customer supplies one.
  • Service level falls back to the customer's default only when carrier_id is also omitted. Supplying a carrier without a service level leaves the service level unset, and a missing service level never rejects the order.
  • Creating an order counts against your plan's invoice allowance. Once the account has reached its invoices_maximum for the current billing period, the create is rejected. Sandbox accounts, and plans with no configured limit, are exempt.
  • In the dashboard, credit card numbers are blocked in note fields. That is a UI check — the API does not validate note contents.
  • In the dashboard, a customer on hold all or hold shipment disables the Issue button and shows a credit-hold banner until the hold is cleared. Holds are not enforced by the API, so an order for a held customer can still be issued over the API.

Next steps

Next: Generate a production run