<p>Your instinct to try the Adjuster API is spot-on. However, you've missed one critical aspect of how Adjusters work that may help explain why the changes you're making are unstable or invisible.</p>
<p>Modifying the <code>price</code> attribute of a <code>LineItem</code> is not a guarantor of any particular behavior. In fact, the <code>price</code> is often just the Variant's base price, memoized—not really the price that someone will pay (for a quantity of one). So, changing that property (even if you can be sure it'll be saved) may not be reflected in the various calculations that Commerce does on an Order.</p>
<p>To make consistent, persistent alterations to the price of individual <code>LineItem</code>s (or an entire <code>Order</code>), you have to go through an Adjuster.</p>
<p>At its heart, and Adjuster is a special class that returns an array of <code>OrderAdjustment</code> models based on some arbitrary, user-defined logic. Usually, that involves looping over the items in an order, or accessing some properties of the Order itself to determine what individual Adjustment(s) is/are applied. You're nearly there!</p>
<p>In your case, an Adjustment has to be returned for every <code>LineItem</code> that has a certain value set in its <code>options</code>. You've already defined the logic for this, so let's adapt your Adjuster class to return <code>OrderAdjustment</code> models instead of just mutating the price directly:</p>
<pre class="lang-php prettyprint-override"><code>// Your `use` statements will need to stay! Omitting for clarity.

class GiftWrapAdjuster extends Component implements AdjusterInterface
{
    const ADJUSTMENT_TYPE = 'discount';
    const OPTIONS_key = 'giftWrap';
    const GIFTWRAP_PRICE = 10.00;

    public function adjust(Order $order): array
    {
        // Create an array so we can push new Adjustments into it.
        $adjustments = [];

        foreach ($order-&gt;getLineItems() as $lineItem) {
            // Check if it's even of interest, and skip if not:
            if (!isset($lineItem-&gt;options[self::OPTIONS_KEY])) {
                continue;
            }

            // Same deal, if it's not the expected value, move on to the next item:
            if ($lineItem-&gt;options[self::OPTIONS_KEY] !== 'yes') {
                continue;
            }

            // Ok, so we've identified that the option exists and needs an Adjustment applied to it. Let's stub the model:
            $adjustment = new OrderAdjustment([
                // Don't worry, you can have &quot;positive&quot; discounts, and this handle doesn't show up anywhere in the front-end.
                'type' =&gt; self::ADJUSTMENT_TYPE,
                'name' =&gt; 'Gift Wrap',
                'description' =&gt; 'You’ve requested gift-wrapping for this item.',
                // Set the price, multiplying by the quantity (or not, your choice!)
                'amount' =&gt; self::GIFTWRAP_PRICE * $lineItem-&gt;qty,
            ]);

            // Make sure the Adjuster knows what Order and LineItem it's supposed to adjust. This also helps calculate stuff in-memory, prior to saving (especially for new LineItems that don't yet have an ID!):
            $adjustment-&gt;setOrder($order);
            $adjustment-&gt;setLineItem($lineItem);

            // Perhaps most importantly! Push that adjustment into the array, so it gets returned:
            $adjustments[] = $adjustment;
        }

        // Return that array—it may be empty, if nothing matched!
        return $adjustments;
    }
}

</code></pre>
<p>Defining stable logic for when adjustments are applied allows the Adjustments themselves to be almost disposable—Commerce can trash them all, and recompute them from the state of the order <em>at any time</em>. Best of all, it has a kind of paper-trail for what changes are happening on the LineItem and Order level, rather than just a single number.</p>

CC BY-SA 4.0
2021-08-07T14:02:48.033Z
Thanks a lot! i tried your code, but it does not take any action. I have it inside a module, where i have 2 other adjusters. This should`nt be a problem, should it? Or do i have to make another module, because the event is a EVENT_REGISTER_DISCOUNT_ADJUSTERS, whereas the 2 others adjusters is have the Event:  EVENT_REGISTER_ORDER_ADJUSTERS?
Henrik
CC BY-SA 4.0
2021-08-07T14:04:50.347Z
This is my Code inside the module: 
```
Event::on(
          OrderAdjustments::class,
          OrderAdjustments::EVENT_REGISTER_DISCOUNT_ADJUSTERS,
          function(RegisterComponentTypesEvent $event) {

          $event->types[] = adjusters\GiftWrapAdjuster::class;
        });
```
Henrik
CC BY-SA 4.0
2021-08-07T23:43:51.070Z
I'm not sure what the difference between these events is. I've only ever used `EVENT_REGISTER_ORDER_ADJUSTERS`. You can register multiple adjusters in one Event listener by calling `$event->types[] = MyAdjuster::class;` as many times as necessary—I'd take another look at the [official Adjuster docs](https://craftcms.com/docs/commerce/3.x/extend/adjusters.html) to make sure your files and events are set up correctly, or take a look at my [example adjuster repo](https://github.com/oof-bar/commerce-example-adjuster) for a drop-in test case.
August Miller
CC BY-SA 4.0
2021-08-06T20:39:32.073Z
2022-11-15T02:42:02.193Z
2022-11-15T02:42:02.193Z
August Miller
<p>I made a module, which should add an amount to an item, based on the chosen option.
For example, if user checks &quot;gift wrapped&quot; 5$ gets added, if &quot;Gift wrapped with Card&quot; is chosen, 10$ is added.</p>
<p>This is my code for the module:</p>
<pre><code> Event::on(
          LineItems::class,
          LineItems::EVENT_POPULATE_LINE_ITEM,
          function(LineItemEvent $event) {
              // @var LineItem $lineItem
               $lineItem = $event-&gt;lineItem;

              if(isset($lineItem-&gt;options['giftWrapped']) &amp;&amp; $lineItem-&gt;options['giftWrapped'] == 'yes'){
                $lineItem-&gt;price = $lineItem-&gt;price + 10;
              }
          }
      );
</code></pre>
<p>But the result is this:</p>
<p><img src="https://dev.henrik-hentschel.de/assets/images/craftcommercemodule.png" alt="Screenshot" /></p>
<p>The original price is 14.75 and 10 should be added.</p>
<p>What is going wrong here?
I tried different Events, next to EVENT_POPULATE_LINE_ITEM, but no luck.</p>
<p>I also tried putting it in a adjuster, but this does not work at all.</p>
<p>In General: what would be best practice? Putting it in a module, or making an adjuster?</p>
<pre><code>&lt;?php

namespace modules\terraverdemodule\adjusters;

use Craft;
use craft\base\Component;
use craft\commerce\base\AdjusterInterface;
use craft\commerce\elements\Order;
use craft\commerce\models\OrderAdjustment;
use craft\commerce\Plugin;
use craft\commerce\Model;
use craft\commerce\services\LineItems;
use craft\commerce\events\LineItemEvent;

use yii\base\Event;

class GiftWrapAdjuster extends Component implements AdjusterInterface
{
     public function adjust(Order $order): array
 {

     $adjustments = [];

     if(isset($lineItem-&gt;options['giftWrapped']) &amp;&amp; $lineItem-&gt;options['giftWrapped'] == 'yes'){
          $lineItem-&gt;price = $lineItem-&gt;price + 10;
     }

   return $adjustments;
 }
}
</code></pre>

CC BY-SA 4.0
2021-08-06T16:10:41.130Z
I'm looking for a solution to the same issue. There's a few folks saying this should still work but it doesnt. It seems to be a change in Commerce 3 that changes how the cart total is calculated from what I can tell. I've asked for [help on the official discord too](https://discord.com/channels/456442477667418113/666747438794670140/873232777066201108).
Harry Harrison
<plugin-craftcommerce><modules><adjusters>
Module: adding amounts to total item price, based on chosen option (e.g. giftWrapped)
# Module: adding amounts to total item price, based on chosen option (e.g. giftWrapped)

(Asked by: username_0 on 2021-08-05)

I made a module, which should add an amount to an item, based on the chosen option.
For example, if user checks "gift wrapped" 5$ gets added, if "Gift wrapped with Card" is chosen, 10$ is added.
This is my code for the module:
 Event::on(
          LineItems::class,
          LineItems::EVENT_POPULATE_LINE_ITEM,
          function(LineItemEvent $event) {
              // @var LineItem $lineItem
               $lineItem = $event->lineItem;

              if(isset($lineItem->options['giftWrapped']) && $lineItem->options['giftWrapped'] == 'yes'){
                $lineItem->price = $lineItem->price + 10;
              }
          }
      );

But the result is this:

The original price is 14.75 and 10 should be added.
What is going wrong here?
I tried different Events, next to EVENT_POPULATE_LINE_ITEM, but no luck.
I also tried putting it in a adjuster, but this does not work at all.
In General: what would be best practice? Putting it in a module, or making an adjuster?
options['giftWrapped']) && $lineItem->options['giftWrapped'] == 'yes'){
          $lineItem->price = $lineItem->price + 10;
     }

   return $adjustments;
 }
}

**Tags:** plugin-craftcommerce, modules, adjusters

> **Comments:**
> **username_1**: I'm looking for a solution to the same issue. There's a few folks saying this should still work but it doesnt. It seems to be a change in Commerce 3 that changes how the cart total is calculated from what I can tell. I've asked for [help on the official discord too](https://discord.com/channels/456442477667418113/666747438794670140/873232777066201108).  

---

## 1 answers

### Answer by username_2 on 2021-08-06. Score: 2

Your instinct to try the Adjuster API is spot-on. However, you've missed one critical aspect of how Adjusters work that may help explain why the changes you're making are unstable or invisible.
Modifying the price attribute of a LineItem is not a guarantor of any particular behavior. In fact, the price is often just the Variant's base price, memoized—not really the price that someone will pay (for a quantity of one). So, changing that property (even if you can be sure it'll be saved) may not be reflected in the various calculations that Commerce does on an Order.
To make consistent, persistent alterations to the price of individual LineItems (or an entire Order), you have to go through an Adjuster.
At its heart, and Adjuster is a special class that returns an array of OrderAdjustment models based on some arbitrary, user-defined logic. Usually, that involves looping over the items in an order, or accessing some properties of the Order itself to determine what individual Adjustment(s) is/are applied. You're nearly there!
In your case, an Adjustment has to be returned for every LineItem that has a certain value set in its options. You've already defined the logic for this, so let's adapt your Adjuster class to return OrderAdjustment models instead of just mutating the price directly:
// Your `use` statements will need to stay! Omitting for clarity.

class GiftWrapAdjuster extends Component implements AdjusterInterface
{
    const ADJUSTMENT_TYPE = 'discount';
    const OPTIONS_key = 'giftWrap';
    const GIFTWRAP_PRICE = 10.00;

    public function adjust(Order $order): array
    {
        // Create an array so we can push new Adjustments into it.
        $adjustments = [];

        foreach ($order->getLineItems() as $lineItem) {
            // Check if it's even of interest, and skip if not:
            if (!isset($lineItem->options[self::OPTIONS_KEY])) {
                continue;
            }

            // Same deal, if it's not the expected value, move on to the next item:
            if ($lineItem->options[self::OPTIONS_KEY] !== 'yes') {
                continue;
            }

            // Ok, so we've identified that the option exists and needs an Adjustment applied to it. Let's stub the model:
            $adjustment = new OrderAdjustment([
                // Don't worry, you can have "positive" discounts, and this handle doesn't show up anywhere in the front-end.
                'type' => self::ADJUSTMENT_TYPE,
                'name' => 'Gift Wrap',
                'description' => 'You’ve requested gift-wrapping for this item.',
                // Set the price, multiplying by the quantity (or not, your choice!)
                'amount' => self::GIFTWRAP_PRICE * $lineItem->qty,
            ]);

            // Make sure the Adjuster knows what Order and LineItem it's supposed to adjust. This also helps calculate stuff in-memory, prior to saving (especially for new LineItems that don't yet have an ID!):
            $adjustment->setOrder($order);
            $adjustment->setLineItem($lineItem);

            // Perhaps most importantly! Push that adjustment into the array, so it gets returned:
            $adjustments[] = $adjustment;
        }

        // Return that array—it may be empty, if nothing matched!
        return $adjustments;
    }
}


Defining stable logic for when adjustments are applied allows the Adjustments themselves to be almost disposable—Commerce can trash them all, and recompute them from the state of the order at any time. Best of all, it has a kind of paper-trail for what changes are happening on the LineItem and Order level, rather than just a single number.

> **Comments:**
> **username_0**: Thanks a lot! i tried your code, but it does not take any action. I have it inside a module, where i have 2 other adjusters. This should`nt be a problem, should it? Or do i have to make another module, because the event is a EVENT_REGISTER_DISCOUNT_ADJUSTERS, whereas the 2 others adjusters is have the Event:  EVENT_REGISTER_ORDER_ADJUSTERS?  
> **username_0**: This is my Code inside the module:  ``` Event::on(           OrderAdjustments::class,           OrderAdjustments::EVENT_REGISTER_DISCOUNT_ADJUSTERS,           function(RegisterComponentTypesEvent $event) {            $event->types[] = adjusters\GiftWrapAdjuster::class;         }); ```  
> **username_2**: I'm not sure what the difference between these events is. I've only ever used `EVENT_REGISTER_ORDER_ADJUSTERS`. You can register multiple adjusters in one Event listener by calling `$event->types[] = MyAdjuster::class;` as many times as necessary—I'd take another look at the [official Adjuster docs](https://craftcms.com/docs/commerce/3.x/extend/adjusters.html) to make sure your files and events are set up correctly, or take a look at my [example adjuster repo](https://github.com/oof-bar/commerce-example-adjuster) for a drop-in test case.  

---

