Jump to content
thirty bees forum
  • 0

Manual zip-code to zone mapping with override


Coachonko

Question

I am working for a shop based in Italy. In italy there are "remote areas" within municipalities (states) that cost more money to deliver to. The shipping partner provided a list of ZIP codes of these locations.

I want to override TB to assign these zip codes to a zone that isn't the default state zone, so that when a customer provides the mandatory zip code at checkout (guest or registered), the carrier rates of the correct zone are applied.

What controller handles this behavior?

UPDATE: please look at the override below

Edited by Coachonko
Override code
Link to comment
Share on other sites

10 answers to this question

Recommended Posts

  • 0

Something like this:

<?php


class Address extends AddressCore
{
    // New function to get the postcode from the given address
    public static function getPostcodeByAddress($idAddress)
    {
        $idAddress = (int)$idAddress;
        if (!$idAddress) {
            return null;
        }
        return Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
            (new DbQuery())
                ->select('postcode')
                ->from('address', 'a')
                ->where('a.`id_address` = ' . $idAddress)
        );
    }

    public static function getZoneById($idAddress)
    {
        // CUSTOMIZATION
        // Use the new function
        $postcode = static::getPostcodeByAddress($idAddress);
        // Minor islands and Venice
        if (in_array($postcode, array(
            "04027", // Ponza
            "80073", // Capri
            "09014", // Carloforte
            "98000", // Eolie, Salina
            "91023", // Favignana
            "58012", // Giglio
            "80070", // Ischia
            "07024", // La Maddalena
            "92010", // Lampedusa, Linosa
            "98055", // Lipari
            "91017", // Pantelleria
            "80079", // Procida
            "71040", // Tremiti
            "90010", // Ustica
            // Venezia begin
            "30121", "30122", "30123", "30124", "30125", "30126", "30127", "30128", "30129", "30130", "30131", "30132", "30133", "30134", "30135", "30136", "30137", "30138", "30139", "30140", "30141", "30142", "30143", "30144", "30145", "30146", "30147", "30148", "30149", "30150", "30151", "30152", "30153", "30154", "30155", "30156", "30157", "30158", "30159", "30160", "30161", "30162", "30163", "30164", "30165", "30166", "30167", "30168", "30169", "30170", "30171", "30172", "30173", "30174", "30175", "30176",
            // Venezia end
            "57031", "57032", "57033", "57036", "57037", "57038", "57039" // Elba
        ))) {
            return 10; // ID of the zone for these zip codes
        }
        // Livigno is even more expensive
        if (in_array($postcode, array("23030"))) { // Array because maybe more zip codes will be added in the future
            return 11; // ID of the zone for these zip codes
        }

        // fallback to original implementation
        return parent::getZoneById($idAddress);
    }
}

 

Link to comment
Share on other sites

  • 1

My 2 cents:

Overrides should always be the last resort. They are quite brittle, and it might prevent future updates of your store.

In this case, you can use hook actionGetIDZoneByAddressID instead of override. That would require to implement very simple module that registers this hook and implements handler function. Overall, it would not be more complex than your override.

If you insist on using override, you should at least implement it in a way that utilize the parent method. Instead of

public function someMethod() 
{
   // my custom code
   ...
   // copied and pasted code from core class
   ...
}

you should use

public function someMethod() 
{
   // my custom code
   ...
   // call parent someMethod implementation
   return parent::someMethod();
}

That way, it's more likely that the override will work if method 'someMethod' is changed in core in the future 

  • Like 1
Link to comment
Share on other sites

  • 0

I've seen some posts about doing this on PS 1.4 and 1.5 so I did what I could:

1. created override in override/classes/Address.php

<?php
class Address extends AddressCore
{
    // New function to get the postcode from the given address
    public static function getPostcodeByAddress($idAddress){
        $pc = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(
            (new DbQuery())
                ->select('postcode')
                ->from('address', 'a')
                ->where('a.`id_address` = '.(int) $idAddress)
        );
        return $pc['postcode'];
    }
    public static function getZoneById($idAddress)
    {
        if (!isset($idAddress) || empty($idAddress)) {
            return false;
        }

        // Use the new function
        $postcode=self::getPostcodeByAddress($idAddress);
        // Minor islands and Venice
        if (in_array($postcode,array(
            "04027", // Ponza
            "80073", // Capri
            "09014", // Carloforte
            "98000", // Eolie, Salina
            "91023", // Favignana
            "58012", // Giglio
            "80070", // Ischia
            "07024", // La Maddalena
            "92010", // Lampedusa, Linosa
            "98055", // Lipari
            "91017", // Pantelleria
            "80079", // Procida
            "71040", // Tremiti
            "90010", // Ustica
            // Venezia begin
            "30121", "30122", "30123", "30124", "30125", "30126", "30127", "30128", "30129", "30130", "30131", "30132", "30133", "30134", "30135", "30136", "30137", "30138", "30139", "30140", "30141", "30142", "30143", "30144", "30145", "30146", "30147", "30148", "30149", "30150", "30151", "30152", "30153", "30154", "30155", "30156", "30157", "30158", "30159", "30160", "30161", "30162", "30163", "30164", "30165", "30166", "30167", "30168", "30169", "30170", "30171", "30172", "30173", "30174", "30175", "30176", 
            // Venezia end
            "57031", "57032", "57033", "57036", "57037", "57038", "57039" // Elba
            ))) {
            return 10; // ID of the zone for these zip codes
        }
        // Livigno is even more expensive
        if (in_array($postcode,array("23030"))) { // Array because maybe more zip codes will be added in the future
            return 11; // ID of the zone for these zip codes
        }

        if (isset(static::$_idZones[$idAddress])) {
            return static::$_idZones[$idAddress];
        }

        $idZone = Hook::exec('actionGetIDZoneByAddressID', ['id_address' => $idAddress]);

        if (is_numeric($idZone)) {
            static::$_idZones[$idAddress] = (int) $idZone;

            return static::$_idZones[$idAddress];
        }

        $result = Db::getInstance(_PS_USE_SQL_SLAVE_)->getRow(
            (new DbQuery())
                ->select('s.`id_zone` AS `id_zone_state`, c.`id_zone`')
                ->from('address', 'a')
                ->leftJoin('country', 'c', 'c.`id_country` = a.`id_country`')
                ->leftJoin('state', 's', 's.`id_state` = a.`id_state` AND c.`contains_states` = 1')
                ->where('a.`id_address` = '.(int) $idAddress)
        );

        static::$_idZones[$idAddress] = (int) ((int) $result['id_zone_state'] ? $result['id_zone_state'] : $result['id_zone']);

        return static::$_idZones[$idAddress];
    }
}

 2. tested

It seems to work properly on checkout. More testing should be done. Does anything look off to you?

 

UPDATE: I still want feedback on this override. From my testing it seems to work as intended both for registered and guest customers. I'll mark it as best answer so others may use this information for their shop

Edited by Coachonko
Tested
Link to comment
Share on other sites

  • 0
1 hour ago, wakabayashi said:

I am not sure about it. But isn't it possible without an override? 

I mean you can use different shipping costs for different zones. Can't you just create new zone for this zip codes?

I don't think it is possible to assign zip codes to zones without overrides no, I haven't seen that anywhere

Link to comment
Share on other sites

  • 0
On 4/21/2022 at 8:48 AM, Coachonko said:

I am working for a shop based in Italy. In italy there are "remote areas" within municipalities (states) that cost more money to deliver to. The shipping partner provided a list of ZIP codes of these locations.

I want to override TB to assign these zip codes to a zone that isn't the default state zone, so that when a customer provides the mandatory zip code at checkout (guest or registered), the carrier rates of the correct zone are applied.

What controller handles this behavior?

We used this module for a similar thing. We don't use it now, but i think it worked on TB. You could ask

https://addons.prestashop.com/en/shipping-costs/19959-shipping-costs-per-zipcode-zone-country-state-city.html

 

Link to comment
Share on other sites

  • 0
18 minutes ago, datakick said:

My 2 cents:

Overrides should always be the last resort. They are quite brittle, and it might prevent future updates of your store.

In this case, you can use hook actionGetIDZoneByAddressID instead of override. That would require to implement very simple module that registers this hook and implements handler function. Overall, it would not be more complex than your override.

If you insist on using override, you should at least implement it in a way that utilize the parent method. Instead of


public function someMethod() 
{
   // my custom code
   ...
   // copied and pasted code from core class
   ...
}

you should use


public function someMethod() 
{
   // my custom code
   ...
   // call parent someMethod implementation
   return parent::someMethod();
}

That way, it's more likely that the override will work if method 'someMethod' is changed in core in the future 

Thanks, those are interesting suggestions.

I really have no idea why I can't get any module I write to work, I wish I could find more information on how to do this. The modules I write just simply do not show up in the modules list in the backoffice, so I can't actually install them

Do you have any example of the *correct way* to override? I followed PS documentation and it was very lackluster.

Should mention that I primarily code in swift and I am not experienced with PHP.

Link to comment
Share on other sites

  • 0

Personally, I would create new database table `tb_postcode_zone_map` with two columns `postcode` and `id_zone`, and use this db table to map from postcode to zone id. The override would then look like this

 

<?php


class Address extends AddressCore
{
    // New function to get the postcode from the given address
    public static function getZoneIdByAddressPostcode($idAddress)
    {
        $idAddress = (int)$idAddress;
        if (!$idAddress) {
            return null;
        }
        return (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue(
            (new DbQuery())
                ->select('pzm.id_zone')
                ->from('address', 'a')
                ->innerJoin('postcode_zone_map', 'pzm', 'pzm.postcode = a.postcode')
                ->where('a.`id_address` = ' . $idAddress)
        );
    }

    public static function getZoneById($idAddress)
    {
        // CUSTOMIZATION
        $zoneId = static::getZoneIdByAddressPostcode($idAddress);
        if ($zoneId) {
            return $zoneId;
        }
        // fallback to original implementation
        return parent::getZoneById($idAddress);
    }
}

Again, better to do with module

Link to comment
Share on other sites

  • 0
32 minutes ago, datakick said:

Personally, I would create new database table `tb_postcode_zone_map` with two columns `postcode` and `id_zone`, and use this db table to map from postcode to zone id. 

Again, better to do with module

Is the need of setting different rates for different ZIP codes something unique to Italy? Should this feature be in the core of thirty bees? wakabayashi also assumed it would be a core feature that had to do with zones.

Adding a database table does make sense but managing a database is a bit more complex than editing cleartext. With a GUI I can see shop owners manage it fine though.

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...