I'm working with an external system that requires me to send line items + their tax rate, and the shipping as it's own line item + any tax applied to the shipping.

The format expected looks something like this:

"items": [
        "reference": "11",
        "name": "The Fleece Awakens",
        "quantity": 13,
        "unit_price": 5250,
        "tax_rate": 2500
        "reference": "9",
        "name": "Romper for a Red Eye",
        "quantity": 3,
        "unit_price": 4000,
        "tax_rate": 2500
        "reference": "2",
        "name": "Fedex",
        "quantity": 1,
        "unit_price": 180,
        "tax_rate": 1500

The price and tax rate is multiplied by 100.

This gets even more complex depending on how the tax and shipping rules are setup.

I'm trying to cover everything, like:

  • When a shipping method has a base rate + perItemRate
  • When tax is applied either to item price or item shipping cost, or item price + shipping cost
  • When tax is included

How would I consistently get it in the format like this, while accounting for any adjusters (tax, shipping, sale/discounts)?

I noticed adjusters has optionsJson->lineItemsAffected, which might be interesting.

However, I don't know enough about the complex tax/shipping system or how adjusters work in general to foresee all the different combinations of adjusters.

1 Answer 1


Currently there is a limitation on the adjuster system, in that there is no history of every adjustment made to the order for most adjusters. This is a limitation of the current architecture. I will walk through the current architecture and explain the changes that we plan to release in Craft Commerce 2:

Let's start with an order that looks like this:

itemA price        10
      shippingCost 0
      discount     0
      tax          0
      total        10

itemA price        20
      shippingCost 0
      discount     0
      tax          0
      total        20

baseShippingCost   0
baseTax            0
baseDiscount       0

order total        30

In the above order, there has been no adjustments run "over" the order yet. Lets think of this as the state of the order at the beginning of a recalculation.

When the adjuster recalculation starts it clears all the adjustable values (shippingCost, discount, tax) from the line items, and also clears the 'base' values on the order (baseShippingCost, baseDiscount, baseTax). It also resets the totals.

The first adjuster to run is the discount adjuster. It will apply any matching discounts by modifying the amount of discount on each line item as well as the baseDiscount amount for order level discounts.

Let's say that 2 discounts were applied to the order. One that takes $1 off each line item and $10 of the total order, and the second discount taking $5 off the total order and $2 off each line item. Since there are 2 discounts, the discount adjuster is deciding to return 2 adjustments to the order, one totaling $12 and the other totaling $9. The order will then look like this:

itemA price        10
      shippingCost 0
      discount     -3
      tax          0
      total        7

itemA price        20
      shippingCost 0
      discount     -3
      tax          0
      total        17

Adjustment1 (discount) -12
Adjustment2 (discount) -9

baseShippingCost   0
baseTax            0
baseDiscount       -15

order total        9

The total of the order is now $9. Currently, there are 2 ways to get to this total:


all line item totals (which include each line item's `shippingCost`, `tax`, `discount`)
`baseShippingCost`, `baseDiscount`, `baseTax`


all line item price 

Both of these methods get to the same total, but the problem with this is that BOTH methods still do not contain all the information. For example, the line itemA's discount is -3 but it is made up of 2 discount adjustments, -1 and -2. There currently is no way to fully know how that value came to be. The same goes for the total adjustment amount, it only stored a total adjustment made to the order, with no breakdown.

This is not ideal, but we tried to combat it with an attribute on the adjustment model:

  • OrderAdjustmentModel::optionsJson

This column stores an array of line item IDs (in array key lineItemsAffected) that the adjustment modified, and it also stores information about the modification rules used like the discount's attributes.

For example, in AdjustmentA all of the attributes (pricing rules) of the discount are serialized into a json array and stored in the optionsJson.

Unfortunately the 2 ideas above do not help in understanding all adjustments made to order without a lot of work.

Solution in Craft Commerce 2:

We have been working on Commerce 2 with a solution to the above issues.

1) All adjustments made to the line items are now stored as seperate adjustments on the order. For example, in the above order AdjustmentA would create 3 adjustments, 1 to each line item for the discount, and one to the order.

All adjustments will be for single amount, not combined amounts. This means adjusters will no longer do this: $order->baseShippingCost += 10. They will not mutate the value, only add a new adjustment of 10.

2) All line item fields that an adjuster modifies are removed. This means shippingCost, tax, discounts are no longer stored on the line item. The attributes will be replaced with magic getters that add up all adjustments made to the line item for a particular adjustment type. We will probably still store them in the database but in a column called 'adjustment totals' as json (so you can still do DB queries on them).

3) All order level baseShipping, baseTax, baseDiscount attributes will be removed and replaced with order level adjustments.

This means adjusters now will return more adjustment models (with more lines added to the order), but the developer can still use the magic methods to show the total amount of shipping, discounts, tax at the line item or order level.

Let me know if you have any questions, and I will update the answer.

Note1: The migration to the new order calculation system will turn each of the line item's adjustable amounts (shippingCost, tax, discount) and baseX amounts into line item adjustments. We will archive the old adjustment totals (or zero them out so they can still show the data). This will not lose any order information, nor add any, but will be compatible with the new order recalculation adjustment system.

Note2: The issue with supplying information to 3rd parties ONLY as line items is that there can be order level adjustments that live outside of the line items. You would need to figure out how to send that additional pricing information yourself.

  • 3
    I was wondering if you could provide a longer answer...
    – Brad Bell
    Commented Aug 24, 2017 at 18:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.