Serial Posted August 19, 2015 Share Posted August 19, 2015 Hi, I would like to know where Prestashop execute the SQL query that created a new order row in orders table (what file and function) ? Because I want to fill a field that I have created in this table. Thanks ! Link to comment Share on other sites More sharing options...
Paul C Posted August 19, 2015 Share Posted August 19, 2015 (edited) The best way of handling these are to use the hook functionality and add your code in a module attached to the correct function in your case: function actionValidateOrder(Array $params) { $id_lang = (int)Context::getContext()->language->id; $currency = $params['currency']; $order = $params['order']; $customer = $params['customer']; $delivery = new Address((int)$order->id_address_delivery); $invoice = new Address((int)$order->id_address_invoice); $order_date_text = Tools::displayDate($order->date_add, $id_lang); $carrier = new Carrier((int)$order->id_carrier); $message = $order->getFirstMessage(); // Do something cool here to insert your data } Conveniently there's also: hookdisplayAdminOrder(Array $params) { if (isset($params['id_order'])) $order = new Order($params['id_order']); if ($order) { // Fetch your data and display it by returning the html directly or via a template } } Edited August 19, 2015 by Paul C (see edit history) Link to comment Share on other sites More sharing options...
NishantVadgama Posted August 20, 2015 Share Posted August 20, 2015 Hi, I would like to know where Prestashop execute the SQL query that created a new order row in orders table (what file and function) ? Because I want to fill a field that I have created in this table. Thanks ! this is done in root/classes/PaymentModule.php class's validateOrder method. but if you want to implement this on your module then you have to override that method in your module first then only it will allow. otherwise you can change the same file as well for your store. Link to comment Share on other sites More sharing options...
Serial Posted August 20, 2015 Author Share Posted August 20, 2015 (edited) The best way of handling these are to use the hook functionality and add your code in a module attached to the correct function in your case: function actionValidateOrder(Array $params) { $id_lang = (int)Context::getContext()->language->id; $currency = $params['currency']; $order = $params['order']; $customer = $params['customer']; $delivery = new Address((int)$order->id_address_delivery); $invoice = new Address((int)$order->id_address_invoice); $order_date_text = Tools::displayDate($order->date_add, $id_lang); $carrier = new Carrier((int)$order->id_carrier); $message = $order->getFirstMessage(); // Do something cool here to insert your data } It's that I want What is the file to find this function (actionValidateOrder) ? How to do a module for this ? (please, i'm very bad in PHP ^^) I have created a list in order-carrier.tpl : {if $option.unique_carrier} <!-- Ajout liste déroulante pour sélectionner magasin seulement si Retrait en magasin --> {if $carrier.instance->id=="13"} <strong>{$carrier.instance->name|escape:'htmlall':'UTF-8'}</strong> <br/>{l s='Veuillez choisir le magasin :'} <select name="choix_magasin" id="choixmag"> <option value="0" select="selected">--</option> {foreach $stores as $store} <option value="{$store.id_store}">{$store.city}</option> {/foreach} </select> {/if} <!-- Jusqu'ici --> I have override Order-Controller.php <?php class OrderController extends OrderControllerCore { public function get_ListeMagasins() { $stores = Db::getInstance()->executeS(' SELECT s.*, cl.name country, st.iso_code state FROM '._DB_PREFIX_.'store s '.Shop::addSqlAssociation('store', 's').' LEFT JOIN '._DB_PREFIX_.'country_lang cl ON (cl.id_country = s.id_country) LEFT JOIN '._DB_PREFIX_.'state st ON (st.id_state = s.id_state) WHERE s.active = 1 AND cl.id_lang = '.(int)$this->context->language->id); $this->context->smarty->assign('stores',$stores); return $stores; } public function get_ChoixMagasin() { global $smarty; $retrait = $this->context->cart->id_carrier; if ($retrait == 13) /* Si le client a choisi le retrait en magasin (id : 13) */ { $resultat = Tools::getValue('choix_magasin'); $smarty->assign('choix_magasin', $resultat); } else /* Sinon on met la variable a 0 */ { $resultat=0; $smarty->assign('choix_magasin', $resultat); } } } Directly in OrderController.php (not in override because I don't know how we do this) : case OrderController::STEP_DELIVERY: if (Tools::isSubmit('processAddress')) $this->processAddress(); $this->autoStep(); $this->_assignCarrier(); $this->get_ListeMagasins(); /* Appelle la methode pour recuperer la liste des magasins dans le tableau Livraison */ $this->setTemplate(_PS_THEME_DIR_.'order-carrier.tpl'); break; if ($is_advanced_payment_api === true) $this->_assignAddress(); // assign some informations to display cart $this->_assignSummaryInformations(); $this->get_ChoixMagasin(); $this->setTemplate(_PS_THEME_DIR_.'order-payment.tpl'); break; And i created 2 fields : id_store and name_store in orders table. So, in fact, I want to register my smarty variable "$choix_magasin" just above in the new field id_store. Can you put me on the way ? It is the first time when I create a module ^^ Thanks a lot Edited August 20, 2015 by Serial (see edit history) Link to comment Share on other sites More sharing options...
Paul C Posted August 20, 2015 Share Posted August 20, 2015 Ok. Obviously I'm going to have to try and do this blind with no testing but here goes! The first "hack" to `OrderController.php` can be replaced with the following function in your `OrderController.php` override: public function initContent() { if ((int)$this->step === OrderController::STEP_DELIVERY) $this->get_ListeMagasins(); // Appelle la methode pour recuperer la liste des magasins dans le tableau Livraison parent::initContent(); } The above will get the list of stores available to your modified order-carrier.tpl template but we need to do something with that information to make it accessible to us later. It doesn't look like you need the store choice in order-payment.tpl so if not we can just extend the above to cover both "hacks": public function initContent() { if ((int)$this->step === OrderController::STEP_DELIVERY) $this->get_ListeMagasins(); // Appelle la methode pour recuperer la liste des magasins dans le tableau Livraison else if ((int)$this->step === OrderController::STEP_PAYMENT) $this->get_ChoixMagasin(); parent::initContent(); } You don't appear to actually do anything with this stored value though except save it in a smarty variable? In order to access it in later steps you're going to need to either save this data or postpone saving it by adding it as a hidden field in order-payment.tpl. That could be tricky as you're basically handing over control to the payment modules at that point. This is where you need to make a design choice All during the above processing we don't actually have an order - just a cart object. You could save the choice in the cart object but that would mean extending the cart class to add your additional property (id_store). You would still then need to add additional code to extend the order object as well (or modify it to access the data in the cart object in order to save it). Another option would be to store the data manually either in its own table or in the cart table. (a table of id_cart and id_store would be sufficient). I'm not 100% sure what you're trying to achieve though so it's hard at this point to advise one way or another! What is the store choice actually for? Link to comment Share on other sites More sharing options...
Serial Posted August 20, 2015 Author Share Posted August 20, 2015 (edited) Ok. Obviously I'm going to have to try and do this blind with no testing but here goes! The first "hack" to `OrderController.php` can be replaced with the following function in your `OrderController.php` override: public function initContent() { if ((int)$this->step === OrderController::STEP_DELIVERY) $this->get_ListeMagasins(); // Appelle la methode pour recuperer la liste des magasins dans le tableau Livraison parent::initContent(); } The above will get the list of stores available to your modified order-carrier.tpl template but we need to do something with that information to make it accessible to us later. It doesn't look like you need the store choice in order-payment.tpl so if not we can just extend the above to cover both "hacks": public function initContent() { if ((int)$this->step === OrderController::STEP_DELIVERY) $this->get_ListeMagasins(); // Appelle la methode pour recuperer la liste des magasins dans le tableau Livraison else if ((int)$this->step === OrderController::STEP_PAYMENT) $this->get_ChoixMagasin(); parent::initContent(); } I understand at this point. I'm going to do this override. Next, I join images for you to have an illustration of my problem. MylistFrontOffice.png : it's my front office when the customer choose his delivery (order-carrier.tpl). Debug_Order-Payment.tpl.png : it's when I'm in order-payment.tpl with {debug} tool. It's my variable that the customer has choose before in my list. The value is good because i and it's this value that I want to save in my database in ps_orders.id_store. Orders_table.png : It's my database in ps_orders. My problem is "very easy" to understand. During the delivery choice, if the customer want to take his order at a store, he select the carrier "Retreat in store" and he selects my store in the list of available stores. After, I don't want to display other informations during the cart process. I want just to be able to display his id_store that he choosen in mail confirmation order for me and him, his history of carrier, and also in my back office. I think if this id is in my database, I can to display this information everywhere. I hope you see better what I want to do to help me Edited August 20, 2015 by Serial (see edit history) Link to comment Share on other sites More sharing options...
Paul C Posted August 20, 2015 Share Posted August 20, 2015 OK. Is there a specific reason you decided not to just create your own "carrier" that allowed you to choose a store as a delivery point? Prestashop already has the ability to manage multiple stores, including addresses (distinct from multi-shop). I'm asking because I think this would require less modification! Link to comment Share on other sites More sharing options...
Serial Posted August 20, 2015 Author Share Posted August 20, 2015 (edited) Yes, but I have 35 stores. And more in the futur. So, if I add each stores that a carrier, I would have around 40 lines at the choice of delivery But my carrier "Retreat in store" exist (see mylistFrontOffice.png image). But it seems to me that Prestashop don't permits to choose a store ? Edited August 20, 2015 by Serial (see edit history) Link to comment Share on other sites More sharing options...
Paul C Posted August 20, 2015 Share Posted August 20, 2015 (edited) OK, well the next stage should be fairly easy then, since you pretty much have all the information you need. I think the key here is to look at the delivery as being to one of your stores rather than to the customer. I don't think you actually need to save the store id as a separate field - just use it instead of the id of the delivery address. Ideally we should be able to get the id of an address for your store and just use that in $this->context->cart->id_address_delivery ... but of course it isn't that easy as the Store class doesn't use an address object but uses separate fields for the address. The simplest thing to do would be to create a new address object and use the id of that - unfortunately this will create new database entries for each order and clutter up the customer address list but I guess no worse than a standard customer order. We would also have to be careful of any cart or tax rules that depend on the delivery address, but I'm assuming customers who want to collect from store are local anyway. We modify the choice function to just: public function get_ChoixMagasin() { $retrait = $this->context->cart->id_carrier; if ($retrait == 13) /* Si le client a choisi le retrait en magasin (id : 13) */ return (int)Tools::getValue('choix_magasin'); return false; } and then the override of initContent to: public function initContent() { $pickup_store = false; if ((int)$this->step === OrderController::STEP_DELIVERY) $this->get_ListeMagasins(); // Appelle la methode pour recuperer la liste des magasins dans le tableau Livraison else if ((int)$this->step === OrderController::STEP_PAYMENT) $pickup_store = $this->get_ChoixMagasin(); parent::initContent(); if ((int)$this->step === OrderController::STEP_PAYMENT) if ($pickup_store) $this->_setPickupAddress($pickup_store) } Now we need to create a function to set the pickup address (overriding the customer address set in parent::initContent() by using the data from our Store object). Any time you need to display something different for orders that are collected from your store, then you just need to check id_carrier. Be careful though as the carrier id can change! public function _setPickupAddress($pickup_store) { $store = new Store((int)$pickup_store); // Get the store object $deliveryAddress = new Address(); // Create a new address object $deliveryAddress->id_customer = $this->context->customer->id; $deliveryAddress->id_country = $store->id_country; $deliveryAddress->id_state = $store->id_state; $deliveryAddress->firstname = 'Pickup from'; // You may need to play with these $deliveryAddress->lastname = $store->name; // for it to make sense $deliveryAddress->address1 = $store->address1; $deliveryAddress->address2 = $store->address2; $deliveryAddress->postcode = $store->postcode; $deliveryAddress->city = $store->city; $deliveryAddress->phone = $store->phone; $deliveryAddress->save(); if (Validate::isLoadedObject($deliveryAddress) && ($deliveryAddress->id_customer == $customer->id)) $this->context->smarty->assign('delivery', $deliveryAddress); } Edited August 20, 2015 by Paul C (see edit history) Link to comment Share on other sites More sharing options...
Serial Posted August 21, 2015 Author Share Posted August 21, 2015 (edited) Hi ! I haven't errors in the process but my adress delivery doesn't change This is my override with your changes : <?php class OrderController extends OrderControllerCore { public function initContent() { $pickup_store = false; if ((int)$this->step === OrderController::STEP_DELIVERY) $this->get_ListeMagasins(); // Appelle la methode pour recuperer la liste des magasins dans le tableau Livraison else if ((int)$this->step === OrderController::STEP_PAYMENT) $pickup_store = $this->get_ChoixMagasin(); // Enregistre le choix du magasin du client dans la variable $pickup_store parent::initContent(); if ((int)$this->step === OrderController::STEP_DELIVERY) if ($pickup_store) $this->_setPickupAddress($pickup_store); } public function get_ListeMagasins() { $stores = Db::getInstance()->executeS(' SELECT s.*, cl.name country, st.iso_code state FROM '._DB_PREFIX_.'store s '.Shop::addSqlAssociation('store', 's').' LEFT JOIN '._DB_PREFIX_.'country_lang cl ON (cl.id_country = s.id_country) LEFT JOIN '._DB_PREFIX_.'state st ON (st.id_state = s.id_state) WHERE s.active = 1 AND cl.id_lang = '.(int)$this->context->language->id); $this->context->smarty->assign('stores',$stores); return $stores; } public function get_ChoixMagasin() { $retrait = $this->context->cart->id_carrier; if ($retrait == 13) /* Si le client a choisi le retrait en magasin (id : 13) */ return (int)Tools::getValue('choix_magasin'); return false; } public function _setPickupAddress($pickup_store) { $store = new Store((int)$pickup_store); // Get the store object $deliveryAddress = new Address(); // Create a new address object $deliveryAddress->id_customer = $this->context->customer->id; $deliveryAddress->id_country = $store->id_country; $deliveryAddress->id_state = $store->id_state; $deliveryAddress->firstname = 'Livré à : '; // You may need to play with these $deliveryAddress->lastname = $store->name; // for it to make sense $deliveryAddress->address1 = $store->address1; $deliveryAddress->address2 = $store->address2; $deliveryAddress->postcode = $store->postcode; $deliveryAddress->city = $store->city; $deliveryAddress->phone = $store->phone; $deliveryAddress->save(); if (Validate::isLoadedObject($deliveryAddress) && ($deliveryAddress->id_customer == $customer->id)) $this->context->smarty->assign('delivery', $deliveryAddress); } } Edited August 21, 2015 by Serial (see edit history) Link to comment Share on other sites More sharing options...
Paul C Posted August 21, 2015 Share Posted August 21, 2015 (edited) Ah I think I can see the error - In the initContent() override you have OrderController::STEP_DELIVERY twice. The last one (after the parent::initContent() call) should be OrderController::STEP_PAYMENT. Not saying that it will work but it definitely won't at the moment EDIT: I just noticed another error - A typo on my part! We actually don't need to check the customer id in the last of statement anyway but it was wrong. public function _setPickupAddress($pickup_store) { $store = new Store((int)$pickup_store); // Get the store object $deliveryAddress = new Address(); // Create a new address object $deliveryAddress->id_customer = $this->context->customer->id; $deliveryAddress->id_country = $store->id_country; $deliveryAddress->id_state = $store->id_state; $deliveryAddress->firstname = 'Livré à : '; // You may need to play with these $deliveryAddress->lastname = $store->name; // for it to make sense $deliveryAddress->address1 = $store->address1; $deliveryAddress->address2 = $store->address2; $deliveryAddress->postcode = $store->postcode; $deliveryAddress->city = $store->city; $deliveryAddress->phone = $store->phone; $deliveryAddress->save(); if (Validate::isLoadedObject($deliveryAddress)) $this->context->smarty->assign('delivery', $deliveryAddress); } Edited August 21, 2015 by Paul C (see edit history) Link to comment Share on other sites More sharing options...
Serial Posted August 21, 2015 Author Share Posted August 21, 2015 Thanks for your correction It's me for the first error, sorry ^^ I have an other error too after choose delivery : [PrestaShopException] Property Address->alias is empty at line 881 in file classes/ObjectModel.php 876. 877. $message = $this->validateField($field, $this->$field); 878. if ($message !== true) 879. { 880. if ($die) 881. throw new PrestaShopException($message); 882. return $error_return ? $message : false; 883. } 884. } 885. 886. return true; Link to comment Share on other sites More sharing options...
Paul C Posted August 21, 2015 Share Posted August 21, 2015 (edited) Yup. I should have checked the required fields for the address object! EDIT: The alias can only be max 32 characters, so if your store names are long you may have to change it to something else e.g. "Pickup" public function _setPickupAddress($pickup_store) { $store = new Store((int)$pickup_store); // Get the store object $deliveryAddress = new Address(); // Create a new address object $deliveryAddress->id_customer = $this->context->customer->id; $deliveryAddress->id_country = $store->id_country; $deliveryAddress->id_state = $store->id_state; $deliveryAddress->firstname = 'Livré à : '; // You may need to play with these $deliveryAddress->lastname = $store->name; // for it to make sense $deliveryAddress->alias = 'Livré à : '.$store->name; // Used in the customer's address list to identify this address $deliveryAddress->address1 = $store->address1; $deliveryAddress->address2 = $store->address2; $deliveryAddress->postcode = $store->postcode; $deliveryAddress->city = $store->city; $deliveryAddress->phone = $store->phone; $deliveryAddress->save(); if (Validate::isLoadedObject($deliveryAddress)) $this->context->smarty->assign('delivery', $deliveryAddress); } Edited August 21, 2015 by Paul C (see edit history) Link to comment Share on other sites More sharing options...
Serial Posted August 21, 2015 Author Share Posted August 21, 2015 (edited) Ok for 32 characters. I notice this. An other error in the firstname : [PrestaShopException] Property Address->firstname is not valid at line 881 in file classes/ObjectModel.php 876. 877. $message = $this->validateField($field, $this->$field); 878. if ($message !== true) 879. { 880. if ($die) 881. throw new PrestaShopException($message); 882. return $error_return ? $message : false; 883. } 884. } 885. 886. return true; Edited August 21, 2015 by Serial (see edit history) Link to comment Share on other sites More sharing options...
Paul C Posted August 21, 2015 Share Posted August 21, 2015 (edited) You probably can't have a : in the name. Unfortunately these fields are validated as "names" not just any text... It would make sense to have something relating to your store I guess in place of the first and last names? $deliveryAddress->firstname = 'Lettersonly'; // You may need to play with these $deliveryAddress->lastname = 'sameforthis'; // for it to make sense Edited August 21, 2015 by Paul C (see edit history) Link to comment Share on other sites More sharing options...
Serial Posted August 24, 2015 Author Share Posted August 24, 2015 (edited) Ok, I correct this. The order pass good. But, my delivery address doesn't change. It's always the delivery address of the customer. So, I can't see the store. EDIT : In my back office, I have new line with the store address. But, we must to go to the list field. (screenshot enclosed). For each orders pass, a new line is created. We can't set the delivery address by default ? Because, I want the store address in mails order principally. Edited August 26, 2015 by Serial (see edit history) Link to comment Share on other sites More sharing options...
Paul C Posted August 26, 2015 Share Posted August 26, 2015 Ah I think I we need to change the address in the Cart object! Doh. public function _setPickupAddress($pickup_store) { $store = new Store((int)$pickup_store); // Get the store object $deliveryAddress = new Address(); // Create a new address object $deliveryAddress->id_customer = $this->context->customer->id; $deliveryAddress->id_country = $store->id_country; $deliveryAddress->id_state = $store->id_state; $deliveryAddress->firstname = 'Livré à : '; // You may need to play with these $deliveryAddress->lastname = $store->name; // for it to make sense $deliveryAddress->alias = 'Livré à : '.$store->name; // Used in the customer's address list to identify this address $deliveryAddress->address1 = $store->address1; $deliveryAddress->address2 = $store->address2; $deliveryAddress->postcode = $store->postcode; $deliveryAddress->city = $store->city; $deliveryAddress->phone = $store->phone; $deliveryAddress->save(); if (Validate::isLoadedObject($deliveryAddress)) { $this->context->cart->id_address_delivery = $deliveryAddress->id_address; $this->context->smarty->assign('delivery', $deliveryAddress); } } Link to comment Share on other sites More sharing options...
Serial Posted August 27, 2015 Author Share Posted August 27, 2015 Sorry, but id_address is undefined property Notice: Undefined property: Address::$id_address in C:\wamp\www\PrestaTDU\override\controllers\OrderController.php on line 64 $this->context->cart->id_address_delivery = $deliveryAddress->id_address; Link to comment Share on other sites More sharing options...
Paul C Posted August 27, 2015 Share Posted August 27, 2015 Sorry. Not paying attention! $this->context->cart->id_address_delivery = $deliveryAddress->id; Link to comment Share on other sites More sharing options...
Serial Posted August 27, 2015 Author Share Posted August 27, 2015 (edited) It's the same :/ It's not the address of the store but my address who is display. I think it's about this line : $deliveryAddress = new Address(); // Create a new address object It creates a new address but no set the current address by the store address. It's not possible to do this ? : 1- Keep his own address somewhere that he entered in the register form 2- Set his address just during the order by store's address 3- After, reset his own address in his profile Edited August 27, 2015 by Serial (see edit history) Link to comment Share on other sites More sharing options...
Paul C Posted August 27, 2015 Share Posted August 27, 2015 The problem with doing it this way is that although the store has an address - it isn't an Address object that we can easily refer to (The Store object should really be consistent and use a "proper" Address object in my opinion but that's an issue for the core developers and changing it could break legacy code). The line : $this->context->cart->id_address_delivery = $deliveryAddress->id; Should set the delivery address but for some reason it isn't - it must be being overwritten somewhere later in the process. Changing it on the fly won't work as the Order refers to the Address object (via its id) so it has to be able to find it. It probably doesn't need to be associated with the specific customer but it does have to be "owned" by a customer I think (you could try setting the customer_id field to 0 later though to stop it showing in their address book - once it works as expected). I don't have easy access to the source right now to track down where the address is being changed but will have a look into it once I get the chance. I still think it would be easier if this were done in a custom carrier though. I suspect it would solve a lot of the issues! There are already modules available to do this but I understand if you don't want to pay for one Link to comment Share on other sites More sharing options...
Serial Posted August 27, 2015 Author Share Posted August 27, 2015 If I can't, I will pay for the module but yes, I must to pay other modules very important (payment etc...) 1 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now