commerce icon indicating copy to clipboard operation
commerce copied to clipboard

[3.x]: recalculating an order resets dynamically priced line item

Open anchovy opened this issue 1 year ago • 2 comments

What happened?

Description

We've got a credit card surcharge fee being added to the payment amount by the gateway. This is captured as a additional transaction, and then we add a dynamically priced line item (CC surcharge fee) - this is all done in the EVENT_AFTER_COMPLETE_PAYMENT

This works perfectly - but, if for some reason you edit the completed order in the CP it resets the dynamically added line item to its default price, which in our case is 0.

        Event::on(
            Payments::class,
            Payments::EVENT_AFTER_COMPLETE_PAYMENT,
            function(TransactionEvent $event) {

            $gateway = $transaction->getGateway();
            $response = $gateway->completePurchase($transaction);
            $responseData = $response->getData();

            if( !empty($responseData->AmountSurcharge) && strtolower($response->getMessage()) == 'approved'){

                // get all the existing transactions for this order
                $trans = $transaction->order->getTransactions();

                foreach($trans as $_transaction){
                    if($_transaction->note == self::SURCHARGE_TRANSACTION_NOTE) {
                        // the CC payment transaction has alreay been added.
                        return true;
                    }
                }

                $amountSurcharge = $responseData->AmountSurcharge;
                $amountSurcharge =  number_format((float)$amountSurcharge,4);

                //get the default cc fee product & it's purchasble variant
                $surchargeProduct = Product::find()->slug(self::COMMERCE_WINDCAVE_SURCHARGE_PRODUCT_SLUG)->one();
                $purchasable = $surchargeProduct->getDefaultVariant();
                $surchargeProductLineItem = CommercePlugin::getInstance()->getLineItems()->resolveLineItem($transaction->order->id, $purchasable->id, []);
                
                //set the price of this dynamically
                $surchargeProductLineItem->price = $amountSurcharge;
                $surchargeProductLineItem->salePrice = $amountSurcharge;

                //add the line item to the order.
                $transaction->order->addLineItem($surchargeProductLineItem);

                //add the transaction - this is so the order totals match!
                $ntransaction = CommercePlugin::getInstance()->getTransactions()->createTransaction($transaction->order);
                $ntransaction->status = Transaction::STATUS_SUCCESS;
                $ntransaction->type = Transaction::TYPE_PURCHASE;

                $ntransaction->reference = (string)$responseData->SurchargeDpsTxnRef;
                $ntransaction->message =  (string)$responseData->MerchantReference;
                $ntransaction->code =  (string)$responseData->AuthCode;
                $ntransaction->response = $responseData;
                $ntransaction->note = self::SURCHARGE_TRANSACTION_NOTE;
                
                //recored the total paid for this transaction as the surcharge amount only.
                $ntransaction->paymentAmount = $amountSurcharge;
                $ntransaction->amount = $amountSurcharge;
                CommercePlugin::getInstance()->getTransactions()->saveTransaction($ntransaction);

                return true;
                
            }
            
            return false;

            }
        );


### Steps to reproduce

1. dynamically add a line item with a default value of $0 to an order and with a price set to $1
2. In the CP edit the order, click 'Recalculate order' and notice the dynamically added item is reset to $0.

### Expected behavior
1: dynamically added item stays at $1.
2: snapshot should reference the dynamically set value, not the default value.



### Actual behavior
2: dynamically added item is changed to $0, it's default value.

I've put in a `EVENT_POPULATE_LINE_ITEM` event and looked at the snapshot of the lineItem, but this has the default value of $0 as well.


### Craft CMS version

Craft Pro 3.7.51

### Craft Commerce version

3.4.16

### PHP version

8.1.0

### Operating system and version

Mac Monterey

### Database type and version

MySQL 8.0.27

### Image driver and version

Imagick 3.5.1 (ImageMagick 6.9.6-2)

### Installed plugins and versions

Amazon S3 1.3.0
Asset Usage 2.3.1
AsyncQueue 2.3.0
Child Me! 1.3.1
Colour Swatches 1.7.0
Control Panel Nav 3.0.17
CP Field Inspect 1.4.4
Craft Commerce 3.4.16
Embedded Assets 2.11.3
Enupal Snapshot 1.2.9
Events 1.4.21
Feed Me 4.5.3
Field Manager 2.2.5
Forms (Freeform) 3.13.18
Imgix 2.1.0
LJ Dynamic Fields 3.0.12
Logs 3.0.5
Navigation 1.4.28
Payment Express for Craft Commerce 2 1.5.0
PDF Transform 1.0.7
Phone Number 1.5.0
Retour 3.1.73
SendGrid 1.3.1
SEOmatic 3.4.37
Smith 1.2.3
Sprig 1.13.0
Super Table 2.7.2
Table Maker 3.0.4
Template Select 2.0.2
Twigpack 1.2.17
Typed link field 1.0.25
User Group Field 2.0.2
Vizy 1.0.18
Width Fieldtype 1.1.0


anchovy avatar Aug 26 '22 00:08 anchovy

@anchovy are you editing a completed order? it may only disappear if it's a cart or you click recalculate on the order.

If you recalculate or its a cart it calls populateLineItem again and gets the price from the purchasable object (as well as updating the snapshot price).

Could you store the dynamic value in the snapshot in your own key "surchargePrice" when you originally create the line item and use EVENT_POPULATE_LINE_ITEM to retrieve it and set the salePrice every time inside that event?

That should work

lukeholder avatar Aug 29 '22 06:08 lukeholder

@lukeholder yep it's on a completed order.

I'll try the 'surchargePrice' idea and see if that works, although it still seems like a bug to me.

anchovy avatar Sep 01 '22 07:09 anchovy