If an order is created programmtically and the state isn't set to "draft", this order never gets refreshed (in time), therefore, the adjustments are not calculated.

One solution is to change (probably more work need to be done) order->preSave()

from

    // Refresh draft orders on every save.
    if ($this->getState()->value == 'draft' && empty($this->getRefreshState())) {
      $this->setRefreshState(self::REFRESH_ON_SAVE);
    }
    $this->recalculateTotalPrice();

to

    // Refresh draft orders on every save.
    if ($this->getState()->value == 'draft' || empty($this->getRefreshState())) {
      $this->setRefreshState(self::REFRESH_ON_SAVE);
    }
    $this->recalculateTotalPrice();
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

skyredwang created an issue. See original summary.

skyredwang’s picture

Priority: Normal » Major
Status: Active » Needs review
FileSize
615 bytes

The patch has an improved solution.

mglaman’s picture

Status: Needs review » Postponed (maintainer needs more info)

That's kind of the point. You only make modifications to draft orders. Once it's not a draft it's considered immutable unless changing to a different state (validation -> complete) or some other factor (accepting payment, refunding payment.) Nothing should affect it's price.

What is the use case and why would you want to alter the price of an order when it is is no longer a draft / has been placed?

skyredwang’s picture

Status: Postponed (maintainer needs more info) » Needs review

A common use case would be: a project defines its own order states and workflow. Some orders are created and processed heedlessly and not necessarily set to "draft" when ready to be saved in Drupal commerce. Therefore, Order system wants to give those orders refresh at least once when they come in, so they will have the chance to bring in adjustments into total price.

Here is a concrete example: if a client side is a vending machine, there will be 10 machines. Each one takes care of its sales and orders, then they sync back to a central commerce system, how would you recommend to solve the problem that the order total price isn't correct when there is discount, tax involved?

bojanz’s picture

A common use case would be: a project defines its own order states and workflow.

We enforce that every order workflow must have a "draft" state so that point is moot.

The REST use case is clearly about recording a fully processed order. In which case it would be wrong for Commerce to do any kind of processing and calculation of its own. Such an order has no need for Commerce taxes or promotions.
I think it makes more sense to ensure that the total price can be set instead. That way the REST client is responsible for every amount. I believe $order->total_price is accessible directly and that we're just missing a setter, but I might be wrong.

skyredwang’s picture

FileSize
1.51 KB

This is the patch for the total price setter.

agoradesign’s picture

$order->total_price is accessible directly, I have already used that a few times

skyredwang’s picture

Status: Needs review » Needs work

I tried

$order->total_price

before called $order-save(). $order-save() will override the total again.

Then, I realize, in the REST use case, $order-save() has to be called twice anyway, the first time to generate the order id, then the second time to add in order items.

So, if we have to call save twice, then I can always set the state to 'draft' for the first time, then whatever state received for the second save.

I tested it out, it actually works.

mglaman’s picture

Status: Needs work » Closed (works as designed)

I'm going to mark this as closed because it works as designed. We do not want a :: setTotalPrice method, as this is assumed to be generated based off of order items and adjustments on each level.

As stated previously we want a draft order.

Then, I realize, in the REST use case, $order-save() has to be called twice anyway, the first time to generate the order id, then the second time to add in order items.

So, if we have to call save twice, then I can always set the state to 'draft' for the first time, then whatever state received for the second save.

This verifies it works as designed.