$order->save($obj) does not save custom fields

  • Posts: 15
  • Thank you received: 2
  • Hikaserial Subscription Hikashop Business
5 months 2 weeks ago #356694

-- HikaShop version -- : 4.7.5
-- Joomla version -- : 4.3.4
-- PHP version -- : 8.1.23
-- Browser(s) name and version -- : chrome

Hi,

We have developped a custom plugin to integrate instalment payments through GoCardLess (SEPA) service.
It was working for 3 years using Joomla 3.
Following a recent upgrade to J4 and others extensions including hika, the following piece of code does no longer save values in the order's custom fileds used to retrieve info from GoCardLess.

			$updateOrder = hikashop_get('class.order');
			$orderObj = new stdclass ();
			$orderObj->order_id = $dbOrder->order_id;
			$orderObj->goCardLess_mandat_id = $mandateId;
			$orderObj->goCardLess_instalment_id = $instalment_schedule->id.':'.$goCardLessBaseUrl.'instalment_schedules/'.$instalment_schedule->id;
			$orderObj->goCardLess_bank_id = $bankId;
			$history = new stdClass();
//			$history->notified = 0;
			$history->history_reason = "goCardLess : $instalment_schedule->status";
			$history->history_type = 'Création abonnement';
			$history->history_data = "Ref : $instalment_schedule->id";	
			$orderObj->history = $history;

			try
			{
				$updateOrder->save($orderObj);
			}
			catch (Exception $e) {
				$this->writeToLog("Erreur order custom fields onPaymentNotification : ".$e->getMessage()."\n");					
				$app->enqueueMessage('Problème ! (référence des paiements non enregistrée. Merci de contacter nos équipes.','error');
			//	$app->redirect($cancel_url.$vars['order_id']);
			}
echo "<br /> enfin : <pre>";print_r($orderObj);echo "</pre>";exit;

I have print_r the $obj to debug and verify the object's values. There are correct. The history part is updated as expected. The 3 custom fields stay empty.

Did something change in the way to deal with updating an order ?

Thanks

Laurent p/o Grégoire

Please Log in or Create an account to join the conversation.

  • Posts: 81590
  • Thank you received: 13079
  • MODERATOR
5 months 2 weeks ago #356696

Hi,

No, the save function will save the custom fields too.
The problem must be something else.

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 2
  • Hikaserial Subscription Hikashop Business
5 months 2 weeks ago #356708

Thanks.
Any hint where to look for ?
The object to save contains the right value. History is updated. Custom fields are not.
No errors.
Field restriction parameter (no filtering) ?

Please Log in or Create an account to join the conversation.

  • Posts: 81590
  • Thank you received: 13079
  • MODERATOR
5 months 2 weeks ago #356709

Hi,

There is no restriction mechanism to prevent HikaShop from saving an attribute of the object.
For example if you add:
$orderObj->nonexistantcolumn = 1;
before the save, then you'll get a MySQL query error because the column nonexistantcolumn doesn't exist in the database.

I don't know the full code you're using, or the tests you're doing. So it's hard to say what the problem is. It could be:
1. the variables you're using to fill your fields are actually empty which make you think the code doesn't run.
-> you said the object properly has the attributes filled so it means it's not this (or your test is flawed. For example in the code you posted you output $orderObj after the try/catch. But you should do it before calling the save. It's possible there is something else filling these attributes in the onAfterOrderUpdate event and thus your output would see the attributes filled while they are empty before and during the save)
2. the code you provided doesn't actually run because of a previous condition.
-> you said the object has the attributes filled so it shouldn't be this either (or your test is flawed)
3. the code runs, but another plugin or a mass action clears these custom fields before/after the save.
-> you could modify the code in the save function of administrator/components/com_hikashop/classes/order.php, just before the parent::save() function is called to output the order data object to make sure you still have what you want to be saved in it.
4. the code runs and you think it doesn't work but it actually does.

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 2
  • Hikaserial Subscription Hikashop Business
5 months 1 week ago #356747

Hi,

Thanks for the hints.
I've checked the content of the order object along the order class "save" method and custom fileds data are there.
More precisely, I've checked just before it returns in these lines just after saving the history part around line 480 :

		} else {
			$send_email = @$order->history->history_notified;
			$app->triggerEvent('onAfterOrderUpdate', array( &$order, &$send_email) );

			$historyClass->addRecord($order);
echo "<br />après le addrecord history si existing :<pre>";print_r($order);echo "</pre>";
			if(!$send_email)
				return $order->order_id;

This function is called in my plugin's function "onPaymentNotification" and I stop it (exit) right after the save :
			$updateOrder = hikashop_get('class.order');

			$orderObj = new stdclass ();
			$orderObj->order_id = $dbOrder->order_id;
			if($this->payment_params->store_in_custom_fields)
			{
/*
				$orderObj->goCardLess_mandat_id = $mandateId;
				$orderObj->goCardLess_instalment_id = $instalment_schedule->id.':'.$goCardLessBaseUrl.'instalment_schedules/'.$instalment_schedule->id;
				$orderObj->goCardLess_bank_id = $bankId;
*/
				$orderObj->goCardLess_mandat_id = "bidon";
				$orderObj->goCardLess_instalment_id = "";
				$orderObj->goCardLess_bank_id = "TrucMuche";
				
			}
			$history = new stdClass();
//			$history->notified = 0;
			$history->history_reason = "goCardLess : $instalment_schedule->status";
			$history->history_type = 'Création abonnement';
			$history->history_data = "Ref : $instalment_schedule->id";	
			$orderObj->history = $history;

			try
			{
				$updateOrder->save($orderObj);
			}
			catch (Exception $e) {
				$this->writeToLog("Erreur order custom fields onPaymentNotification : ".$e->getMessage()."\n");					
				$app->enqueueMessage('Problème ! (référence des paiements non enregistrée. Merci de contacter nos équipes.','error');
			//	$app->redirect($cancel_url.$vars['order_id']);
			}

			if($maxDebug) $this->writeToLog("onPaiementNotification fin process 'onReflow'\n");			
			$app->enqueueMessage('Un échéancier de prélèvement a été mis en place. Différents mails d\'information vous ont été envoyés.','info');
exit;
			$app->redirect($installment_url.$vars['order_id']);				

I went directly in phpMyAdmin to verify the content of the order (in case some back-end functions) may change the DB values.... And the corresponding columns are "NULL"...

Then I added the following lines before the exit ($this->modifyOrder) :
			try
			{
				$updateOrder->save($orderObj);
			}
			catch (Exception $e) {
				$this->writeToLog("Erreur order custom fields onPaymentNotification : ".$e->getMessage()."\n");					
				$app->enqueueMessage('Problème ! (référence des paiements non enregistrée. Merci de contacter nos équipes.','error');
			//	$app->redirect($cancel_url.$vars['order_id']);
			}

			// Commenter les lignes ci-dessous pour ne pas changer le statut qui sera changé par le webhook qui valide le mandat et/ou le paiement

			$orderStatus = $this->payment_params->verified_status;
			if ($dbOrder->order_status == $orderStatus)
			{
				if($this->payment_params->debug) $this->writeToLog("Statut déjà confirmé ???\n");				
//				return true;
			}
			else
			{
				$this->modifyOrder($dbOrder->order_id, $orderStatus, true, true);
			}
/*
			$app->enqueueMessage('Un échéancier de prélèvement a été mis en place. Différents mails d\'information vous ont été envoyés.
								<br/>Votre commande sera définitivement validée dès qu\'on aura la confirmation de votre banque que le prélèvement
								a bien été mis en place. Vous serez averti et recevrez la facture correspondante.','info');
*/
			if($maxDebug) $this->writeToLog("onPaiementNotification fin process 'onReflow'\n");			
			$app->enqueueMessage('Un échéancier de prélèvement a été mis en place. Différents mails d\'information vous ont été envoyés.','info');
exit;

As expected, the order->save has been triggered once more and the object content was displayed again before the final exit.
In this situation, the order object shown has the customfields to "null" including the "old" object one.

So it really looks like the first update which trigger the parent::save in the order class does not actually save in DB the custom fields values.

By the way I have also disabled all the plugins who may interfer with the process (onBeforeUpdate). But since the values are there before and after the "parent::save", I suspect something happens during this parent's save function.

Where is made the actual save ? May you have an idea on where it could come from ? Some other hints ?

Thanks

Laurent

Please Log in or Create an account to join the conversation.

  • Posts: 81590
  • Thank you received: 13079
  • MODERATOR
5 months 1 week ago #356763

Hi,

As I said, the save is done with the call to parent::save in administrator/components/com_hikashop/classes/order.php :

$order->order_id = parent::save($order);
This parent::save function will just generate the MySQL query for the saving and run it. You don't need to debug inside it. If the MySQL query updating the order doesn't have the custom fields, it's because $order doesn't have the custom fields in the first place. There is no way around it.

A few lines before this, you have the code:
if($new) {
			$app->triggerEvent('onBeforeOrderCreate', array(&$order, &$do) );
		} else {
			$app->triggerEvent('onBeforeOrderUpdate', array(&$order, &$do) );
		}
so any plugin implementing onBeforeOrderUpdate can modify the order object between your call of the save function and the actual saving done with parent::save.
I would personnally put debug around this code in order to check what's going on, based on what you've said so far.

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 2
  • Hikaserial Subscription Hikashop Business
5 months 1 week ago #356777

Thanks for your answer.

As I said, I put plenty of $order object trace in the order class file to follow the way its content may change.
After the start of the function, the object contains the right values of the custom fields to set.

Indeed, around the lines you mention, I have :

		if($recalculate)
			$this->recalculateFullPrice($order);
 echo "<br />apres recalculate :<pre>";print_r($order);echo "</pre>";
		if(!empty($order->cart->products))
			$this->recalculateDimensions($order, $order->cart->products);
		elseif($recalculate_dimensions)
			$this->recalculateDimensions($order);

		$do = true;
		if($new) {
			$app->triggerEvent('onBeforeOrderCreate', array(&$order, &$do) );
		} else {
			$app->triggerEvent('onBeforeOrderUpdate', array(&$order, &$do) );
		}

		if(!$do)
			return false;
 echo "<br />après trigger 2 :<pre>";print_r($order);echo "</pre>";
		$unsets = array('value', 'order_current_lgid', 'order_current_locale', 'mail_status', 'total_number_of_products');
		foreach($unsets as $unset) {
			unset($order->$unset);
		}
Both dump, before and after, show the right value inside the $order object.

Around the save it self I have :
		$old = $order->old;
		unset($order->old);
		unset($order->order_url);
		unset($order->mail_params);
 echo "<br />avant save :<pre>";print_r($order);echo "</pre>";
		$order->order_id = parent::save($order);
echo "<br />après :<pre>";print_r($order);echo "</pre>";
		$order->old = $old;

And both object (before and after) show the right content.

And lines I have mentionned near history->addRecord are the final ones. Just before it returns. At this time also, the object contains the right values.

I don't see what could be triggered between the return from the save function inside the order class (where the object is correct) and the exit in my onPaymentNotification wich stops and shows the output.

In my script, I have added the modifyOrder between the call to $updateOrder->save and the exit. This last function triggers itself again the order.class function. And, at this point, I can see that the object retrieved (old) does not contain the expected values.

What could occur between the end of the hika.order.save function where the object is right and the beginning of the hika.order.save function from the modifyOrder where the object is wrong (and order_id remain the same everywhere) ?

I am not saying something is wrong with the Hika code. I'am suspecting my own code. But something has changed between Hika 4.2.2 under Joomla 3.9.28 / Php 7.3.26 which was our previous config where this plugin where able to store data in the custom fields and our current environment (Hika 4.7.5 under J! 4.3.4 / Php 8.1.23). Except the change to the J!Framework class (\use ... Factory instead of JFactory ...), I don't see what I could have change in my plugin.

That the reason why I suspected some new constraint somewhere wich prevent the values to be stored in DB.

I ran a lot of debug tests. I'll try to hack a working payment plugin to see if it save....

I'm not expecting you to debug my code. I'm just trying to exchange with the hika code experts in order to make my mind going toward the solution.

Last edit: 5 months 1 week ago by gvitry.

Please Log in or Create an account to join the conversation.

  • Posts: 81590
  • Thank you received: 13079
  • MODERATOR
5 months 1 week ago #356784

Hi,

Then, the next step is to add the exit; just after the code:

echo "<br />avant save :<pre>";print_r($order);echo "</pre>";
		$order->order_id = parent::save($order);
echo "<br />après :<pre>";print_r($order);echo "</pre>";
That way, you can verify that the data is properly set in the database by HikaShop.
If that's the case, then if you see null values in the database after the save, it probably means that something does another MySQL query to clear the values in the onAfterOrderUpdate

While many things changed in the code between your old versions of HikaShop / Joomla / PHP and the new ones, I don't see what could have changed anything to the save process.

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 2
  • Hikaserial Subscription Hikashop Business
5 months 1 week ago #356794

So I just did. Here is the modified order.class :

		$old = $order->old;
		unset($order->old);
		unset($order->order_url);
		unset($order->mail_params);
 echo "<br />avant save :<pre>";print_r($order);echo "</pre>";
		$order->order_id = parent::save($order);
echo "<br />après :<pre>";print_r($order);echo "</pre>";
		if ($order->goCardLess_mandat_id == "bidon") exit; // <------ constant set in my plugin onPaymentNotification for test purpose
		$order->old = $old;

here is the result of the output :
avant save :
stdClass Object
(
    [order_id] => 3542
    [goCardLess_mandat_id] => bidon
    [goCardLess_instalment_id] => 
    [goCardLess_bank_id] => TrucMuche
    [history] => stdClass Object
        (
            [history_reason] => goCardLess : pending
            [history_type] => Création abonnement
            [history_data] => Ref : IS000058T82BZY
        )

    [order_modified] => 1700552271
)

après :
stdClass Object
(
    [order_id] => 3542
    [goCardLess_mandat_id] => bidon
    [goCardLess_instalment_id] => 
    [goCardLess_bank_id] => TrucMuche
    [history] => stdClass Object
        (
            [history_reason] => goCardLess : pending
            [history_type] => Création abonnement
            [history_data] => Ref : IS000058T82BZY
        )

    [order_modified] => 1700552271
)
Here is the result of the query in phpmyadmin :


just after the parent::save(), all the three "go_cardless_xxxx" custom fields are set to null. None contain the right value...

We have Monetico CB payment gateway with a sandbox. I'm going to add the same "update" in onPaymentNotification of this plugin to see what happens....

Attachments:

Please Log in or Create an account to join the conversation.

  • Posts: 15
  • Thank you received: 2
  • Hikaserial Subscription Hikashop Business
5 months 1 week ago #356796

gvitry wrote: So I just did. Here is the modified order.class :

		$old = $order->old;
		unset($order->old);
		unset($order->order_url);
		unset($order->mail_params);
 echo "<br />avant save :<pre>";print_r($order);echo "</pre>";
		$order->order_id = parent::save($order);
echo "<br />après :<pre>";print_r($order);echo "</pre>";
		if ($order->goCardLess_mandat_id == "bidon") exit; // <------ constant set in my plugin onPaymentNotification for test purpose
		$order->old = $old;

here is the result of the output :
avant save :
stdClass Object
(
    [order_id] => 3542
    [goCardLess_mandat_id] => bidon
    [goCardLess_instalment_id] => 
    [goCardLess_bank_id] => TrucMuche
    [history] => stdClass Object
        (
            [history_reason] => goCardLess : pending
            [history_type] => Création abonnement
            [history_data] => Ref : IS000058T82BZY
        )

    [order_modified] => 1700552271
)

après :
stdClass Object
(
    [order_id] => 3542
    [goCardLess_mandat_id] => bidon
    [goCardLess_instalment_id] => 
    [goCardLess_bank_id] => TrucMuche
    [history] => stdClass Object
        (
            [history_reason] => goCardLess : pending
            [history_type] => Création abonnement
            [history_data] => Ref : IS000058T82BZY
        )

    [order_modified] => 1700552271
)
Here is the result of the query in phpmyadmin :


just after the parent::save(), all the three "go_cardless_xxxx" custom fields are set to null. None contain the right value...

We have Monetico CB payment gateway with a sandbox. I'm going to add the same "update" in onPaymentNotification of this plugin to see what happens....


Edit :

Reading back my message, I've realized that in my code the variable is
$orderObj->goCardLess_mandat_id
with uppercases letters "C" and "L" where the column name in the database is "gocardless_mandat_id" all lowercase.

So, instead of throwing an sql error (column does not exists) it was silently doing nothing.

I guess in some previous version there was somewhere an uppercase to lowercase conversion which changed the real name so it was working.

It was on front of my eyes since the begining.... Thank you for the talk which drive me to the solution.

The following user(s) said Thank You: nicolas

Please Log in or Create an account to join the conversation.

  • Posts: 81590
  • Thank you received: 13079
  • MODERATOR
5 months 1 week ago #356799

Hi,

Indeed, reading your last messages and seeing the screenshot of your PHPMyAdmin, I directly saw that the problem was with the case of the attributes and I was about to write about it :)
It's great your found the problem.

It could be that Joomla changed the way it process upper case characters when generating the MySQL queries.

The following user(s) said Thank You: gvitry

Please Log in or Create an account to join the conversation.

Time to create page: 0.084 seconds
Powered by Kunena Forum