Jump to content
thirty bees forum

Updating carrier delay time after call to webservice


Wartin

Recommended Posts

Hello. I'm trying to modify a carrier module to make it show the real delay for a specific order shipping.

I read a couple of modules on github and they always set '$carrier->delay' to an array of strings in different languages, like:

                'delay' => array(
                    'es' => 'Envío rapido',
                    'en' => 'Fast Shipping', // English value is required
                ),

I thought I could make something similar to what updates the price of the shipping, but I could see that the shop calls to getOrderShippingCost(), present in every module (or *External, sometimes). And there's not getOrderDelay() or similar.

I do have the real delay (in days) that comes from the same webservice that returns the shipping price. Do I have to use JS to write it in the carrier list at order-carrier.tpl or maybe I can set some smarty variable?

Thanks!

edit: I found this:

https://www.prestashop.com/forums/topic/219629-changing-carrier-delay-from-within-shipping-module/

the last post adds a hook (hookDisplayBeforeCarrier()). I'm not sure how to 'call' it.

edit2: maybe it has to be called like this (register the hook in module installation and then in template use     {$hookDisplayAfterCarrier nofilter} ?

https://github.com/PrestaShop/PrestaShop/pull/5346/commits/a7914cbbfb494c07e0142d1f1315cee06e74f01e

Edited by Wartin
Link to comment
Share on other sites

Carrier delay is stored in the database, it's not dynamic at the moment.

I guess we could create new hook that would allow modification of carrier list returned from Cart::simulateCarriersOutput.

At the moment, I suggest you to create override of this method, call the parent one and modify it's result. Something like this:

 

public function simulateCarriersOutput(Country $defaultCountry = null, $flush = false)
{
	$carriers = parent::simulateCarriersOutput(defaultCountry, flush);
	foreach ($carriers as $carrier) {
		if ($carrier['id_carrier'] === <myID>) {
			$carrier['delay'] = '<delay_from_webservice>';
		}
	}
	return $carrier;
}

 

  • Thanks 1
Link to comment
Share on other sites

22 hours ago, datakick said:

Carrier delay is stored in the database, it's not dynamic at the moment.

I guess we could create new hook that would allow modification of carrier list returned from Cart::simulateCarriersOutput.

At the moment, I suggest you to create override of this method, call the parent one and modify it's result. Something like this:

 


public function simulateCarriersOutput(Country $defaultCountry = null, $flush = false)
{
	$carriers = parent::simulateCarriersOutput(defaultCountry, flush);
	foreach ($carriers as $carrier) {
		if ($carrier['id_carrier'] === <myID>) {
			$carrier['delay'] = '<delay_from_webservice>';
		}
	}
	return $carrier;
}

 

Great, I managed to make it work (a couple of missing $, and I'm not sure if the return should return $carriers in plural like its parents.

My override looks like:

class Cart extends CartCore
{

    public function simulateCarriersOutput(Country $defaultCountry = null, $flush = false)
    {
        $carriers = parent::simulateCarriersOutput($defaultCountry, $flush);
        foreach ($carriers as $carrier) {
                if ($carrier['id_carrier'] == 264000) {
                        $carrier['delay'] = "delay_from_webservice";
                }
        }

        return $carrier;
    }
}

I'm not sure why if my carrier's ID is 64 id_carrier is 264000, but this way it 'works'. I'm trying to first print 'delay from webservice' before using the new actual value.

I don't know what's next, nor what we are doing exactly, besides reading each carrier data and changing the carrier we want to be changed.

Thanks in advance!

Link to comment
Share on other sites

I changed a little bit my code to:

    public function simulateCarriersOutput(Country $defaultCountry = null, $flush = false)
    {
        $carriers = parent::simulateCarriersOutput($defaultCountry, $flush);
        foreach ($carriers as $id => $carrier) {
                if ($carrier['id_carrier'] == 264000) {
                        $carriers[$id]['delay'] = 'my updated delay';
                        //$carrier['delay'] = "delay_from_webservice";
                }
        }
        return $carriers;
    }

If I print the smarty variable $carriers in order-carrier.tpl with:

{$carriers|@debug_print_var}

I see the list of carriers modified, with my delay.

BUT, it's not updated in carrier list

What am I missing?

 

Thanks again!!

Link to comment
Share on other sites

21 hours ago, Wartin said:

I changed a little bit my code to:


    public function simulateCarriersOutput(Country $defaultCountry = null, $flush = false)
    {
        $carriers = parent::simulateCarriersOutput($defaultCountry, $flush);
        foreach ($carriers as $id => $carrier) {
                if ($carrier['id_carrier'] == 264000) {
                        $carriers[$id]['delay'] = 'my updated delay';
                        //$carrier['delay'] = "delay_from_webservice";
                }
        }
        return $carriers;
    }

If I print the smarty variable $carriers in order-carrier.tpl with:


{$carriers|@debug_print_var}

I see the list of carriers modified, with my delay.

BUT, it's not updated in carrier list

What am I missing?

 

Thanks again!!

My solution:

class Cart extends CartCore
{
    public function getDeliveryOptionList(Country $defaultCountry = null, $flush = false)
    {
        // array lang (id_lang => text)
        $delay = [
            1 => 'My text delay!',
        ];
        $deliveryOptionList = parent::getDeliveryOptionList($defaultCountry, $flush);
        foreach ($deliveryOptionList as $idAddress => $deliveryOption) {
            foreach ($deliveryOption as $key => $value) {
                foreach ($value['carrier_list'] as $idCarrier => $data) {
                    // override Carrier
                    if (2 == $idCarrier) {
                        $deliveryOptionList[$idAddress][$key]['carrier_list'][$idCarrier]['instance']->delay = $delay;
                    }
                }
            }
        }
        return $deliveryOptionList;
    }
}

 

 

 

  • Thanks 1
Link to comment
Share on other sites

9 hours ago, Quant said:

My solution:

It works! Thanks!!

Now I have to figure out how to use the real delay calculated by the module. How do you do it?

Mine it's not saved in the database, the module makes the webservice call and use a variable 'data' with the returning values.

 

Link to comment
Share on other sites

17 hours ago, Wartin said:

It works! Thanks!!

Now I have to figure out how to use the real delay calculated by the module. How do you do it?

Mine it's not saved in the database, the module makes the webservice call and use a variable 'data' with the returning values.

 

Which database?

When viewing or printing PDF, the current one is used (TEXT_FROM_CURRENT_TB_DELIVERY -> delay).

What you want will require a lot of code editing!

Option. Save Adresse -> $ other = 'Als mein benutzerdefinierter Text'

Delay, replace Verzögerung with Adresse -> $ andere.

 

Link to comment
Share on other sites

Hello, thanks for your response. My idea is to write there the real delay, in days, returned by a webservice in a module. I thought you were doing the same.

The module use a webservice method to ask for the price. I have an array in the module with the price and the delay. I'm reading a lot of posts and documentation, and @datakick gave me a nice hint, but couldn't finish doing what I'm trying to do (I think it's basic to know how many days a client will have to wait).

I think I need a hook registered by the module, who receives the id and shows the delay for that carrier.

Option. Save Adresse -> $ other = 'Als mein benutzerdefinierter Text'
Delay, replace Verzögerung with Adresse -> $ andere. 

I'm sorry, I don't understand what did you mean here.

Thanks

Link to comment
Share on other sites

On 2/17/2021 at 2:38 PM, datakick said:

I guess we could create new hook that would allow modification of carrier list returned from Cart::simulateCarriersOutput.

At the moment, I suggest you to create override of this method, call the parent one and modify it's result. Something like this:

Hello! I'm still stuck here.

I tried both suggestions. With the override of simulateCarriersOutput I could put some string in delay, visible in .tpl with the smarty variable $carriers. With Quant's solution (getDeliveryOptionList override) my new delay string is showed in carrier list.

I couldn't pass the real delay value returned by the webservice in a module to Cart class. I need a tip here :)

If I can do it I'll write my solution here.

Thanks!

Link to comment
Share on other sites

In general, you want to 

  1. persist result of webservice call -- real delay value
  2. in override, read saved value

You will probably also need to implement some cron job to periodically fetch delay value from webservice, unless the webservice is called on every page load of your checkout (which would be crazy performance bottlenect)

Link to comment
Share on other sites

1 hour ago, datakick said:

You will probably also need to implement some cron job to periodically fetch delay value from webservice, unless the webservice is called on every page load of your checkout (which would be crazy performance bottlenect)

Delay values comes in the same answer where the price comes from carrier webservice. The module indeed executes a call in every checkout (it's open source, I didn't made it). It saves in the database some values (weight, postal codes, price, etc.) to not call it again if it already did.

Well, I thought I could use a cookie or some call to write the new delay in carrier list. Or to implement some function in my module like the one that returns the price.

Thanks for your help. I think I'll have to just write 'from 2 to 8 days' :(

Link to comment
Share on other sites

1 hour ago, Wartin said:

Delay values comes in the same answer where the price comes from carrier webservice. The module indeed executes a call in every checkout (it's open source, I didn't made it). It saves in the database some values (weight, postal codes, price, etc.) to not call it again if it already did.

Well, I thought I could use a cookie or some call to write the new delay in carrier list. Or to implement some function in my module like the one that returns the price.

Thanks for your help. I think I'll have to just write 'from 2 to 8 days' 😞

You can modify the module, and save the information to the database, for example by using something like this:

Configuration::updateGlobalValue('MY_CARRIER_DELAY', $webserviceResponse['delay']);

And in override you can load this information back by calling

$carrierDelay = Configuration::get('MY_CARRIER_DELAY');

$delay = [
  1 => $carrierDelay
];

Or something like that

  • Thanks 1
Link to comment
Share on other sites

Great, finally I tried two ways persisting the delay. First I tried with cookies, saving the value in the module with this:

$this->context->cookie->__set('newdelay', $data->PlazoEntrega);
$this->context->cookie->write();

And then in the override of the function getDeliveryOptionList this:
 

    public function getDeliveryOptionList(Country $defaultCountry = null, $flush = false)
    {
        if (Context::getContext()->cookie->__isset('newdelay')){
                $newdelay = Context::getContext()->cookie->__get('newdelay');
        }
        $delay = [
            1 => $newdelay.' días',
        ];

        $deliveryOptionList = parent::getDeliveryOptionList($defaultCountry, $flush);
        foreach ($deliveryOptionList as $idAddress => $deliveryOption) {
            foreach ($deliveryOption as $key => $value) {
                foreach ($value['carrier_list'] as $idCarrier => $data) {
                    // override Carrier
                    if (70  == $idCarrier) {
                        $deliveryOptionList[$idAddress][$key]['carrier_list'][$idCarrier]['instance']->delay = $delay;
                    }
                }
            }
        }
        return $deliveryOptionList;
    }

 

The second try, that I chose over the cookie was to add a column in module's table in the database, as it already register there other values concerning orders and prices. Then, in getDeliveryOptionList I get order values and make a query to module's table:

class Cart extends CartCore
{
    public function getDeliveryOptionList(Country $defaultCountry = null, $flush = false)
    {
        $deliveryOptionList = parent::getDeliveryOptionList($defaultCountry, $flush);

        $weight = $this->getTotalWeight();
        $address = new Address(Context::getContext()->cart->id_address_delivery);
        $postcode = $address->postcode;

        foreach ($deliveryOptionList as $idAddress => $deliveryOption) {
            foreach ($deliveryOption as $key => $value) {
                foreach ($value['carrier_list'] as $idCarrier => $data) {
                    // override Carrier
                    if (70  == $idCarrier) {
                        $query = new DbQuery();
                        $query->select('delay')
                        ->from('tb_ocae_quotes')
                        ->where('reference = 369 AND postcode ='.$postcode);
                        $newdelay = Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue($query);
                        $delay = [
                            1 => $newdelay.' días',
                        ];

                        $deliveryOptionList[$idAddress][$key]['carrier_list'][$idCarrier]['instance']->delay = $delay;
                    }
                }
            }
        }
        return $deliveryOptionList;
    }
}

I tried to get the total cart price with getDeliveryOptionList but it overflows php, no idea what am I doing wrong.

@Quant, maybe this is useful for you too.

Thanks!

Edited by Wartin
Link to comment
Share on other sites

Nice work.

I would just suggest you don't use check (70 == $idCarrier), because $idCarrier will change every time you save carrier in back office. It would be better to modify this check and base it on different information from $data, maybe carrier name or carrier reference id

 

  • Thanks 1
Link to comment
Share on other sites

58 minutes ago, datakick said:

I would just suggest you don't use check (70 == $idCarrier), because $idCarrier will change every time you save carrier in back office. It would be better to modify this check and base it on different information from $data, maybe carrier name or carrier reference id

Yes, it was something i didn't knew how to fix. Now instead of:

                    if (70  == $idCarrier) {

reads:

                    if ($data["instance"]->name == 'My carrier's name') {

 

Thanks again!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...