Use order delivery address when creating order from CRM

This commit is contained in:
max-baranikov 2021-07-15 14:29:21 +03:00 committed by GitHub
parent 2f4897b35f
commit 287547c212
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 530 additions and 298 deletions

View File

@ -26,35 +26,4 @@ Module allows integrate CMS Prestashop with [Simla.com](https://www.retailcrm.pr
#### Customization
##### Filters
If you want to modify data, sent between CRM and CMS you can use custom filters.
There are list of available filters:
* *RetailcrmFilterProcessOrder* - order array, which will be sent to CRM
* *RetailcrmFilterProcessCustomer* - customer array, which will be sent to CRM
* *RetailcrmFilterProcessCustomerCorporate* - corporate customer array, which will be sent to CRM
* *RetailcrmFilterProcessAddress* - address array, which will be sent to CRM
* *RetailcrmFilterProcessOffer* - offer array, which will be sent to CRM (saved into Icml file)
* *RetailcrmFilterCustomersHistory* - array with assembled history for customer, loaded from CRM
* *RetailcrmFilterOrdersHistory* - array with assembled history for order, loaded from CRM
* *RetailcrmFilterSaveCustomer* - built customer object, which will be saved to CMS
* *RetailcrmFilterSaveCustomerAddress* - built customer address object, which will be saved to CMS
* *RetailcrmFilterSaveCorporateCustomer* - built corporate customer object, which will be saved to CMS
* *RetailcrmFilterSaveCorporateCustomerAddress* - built corporate customer address object, which will be saved to CMS
To use filters you should define a new class in `<prestashop-root>/modules/retailcrm/custom/hooks`. Filename and classname must match the filter name.
Filter class should implement interface *RetailcrmFilterInterface*. In filter class you must define *filter()* function, which will take initial `$object` and return customized `$object`.
##### Classes
If you want to change the default behavior of a module classes and be sure that these changes won't be overwritten during the module upgrade process, you should **copy the original classes** that you are going to customize to the `<prestashop-root>/modules/retailcrm/custom/classes` directory.
From here you can modify the methods of the classes for your own purposes, and they will not be affected during the module upgrade process.
Keep in mind that:
* If the logic and classes of the module have changed a lot after an upgrade, your customized logic may cause the module to malfunction. **You should always check for changes after an upgrade and update your customized classes if needed.**
* This feature does not allow to customize the base class (file `retailcrm.php`). For this you can use the standard Prestashop override feature.
You can customize your module behavior using [Custom Filters](doc/3.%20Customization/Filters.md) or [Custom Classes](doc/3.%20Customization/Classes.md)

View File

@ -0,0 +1,2 @@
# Configuration

View File

@ -0,0 +1,3 @@
# Installation

6
doc/1. Setup/README.md Normal file
View File

@ -0,0 +1,6 @@
# Setup
1. [Installation](Installation.md)
2. [Configuration](Configuration.md)
3. [Upgrade](Upgrade.md)

3
doc/1. Setup/Upgrade.md Normal file
View File

@ -0,0 +1,3 @@
# Upgrade

View File

@ -0,0 +1,5 @@
# Customers
## Create
## Update

View File

@ -0,0 +1,2 @@
# Inventories

View File

@ -0,0 +1,85 @@
# Orders
## Create
### Items
### Delivery
### Addresses
Для адреса оплаты `id_address_invoice`, если заказ сделан обычным клиентом, используются данные
клиента `order[customer]`
| CRM field / value | CMS field |
|-------------------------------------------|---------------------------|
| default | `alias` |
| `order[customer][firstName]` | `firstname` |
| `order[customer][lastName]` | `lastname` |
| `order[customer][phones][0][number]` | `phone` |
| `order[customer][address][text]` | `address1`, `address2` |
| `order[customer][address][countryIso]` | `id_country` |
| `order[customer][address][city]` | `city` |
| `order[customer][address][index]` | `postcode` |
| `order[customer][address][region]` | `id_state` |
Если заказ сделан корпоративным клиентом и опция "Корпоративные клиенты" включена, то используются данные
контакта `order[contact]`, а также указываются дополнительные поля — ИНН и название компании.
| CRM field / value | CMS field |
|-------------------------------------------|---------------------------|
| -- | `alias` |
| `order[contact][firstName]` | `firstname` |
| `order[contact][lastName]` | `lastname` |
| `order[contact][phones][0][number]` | `phone` |
| `order[company][address][text]` | `address1`, `address2` |
| `order[company][address][countryIso]` | `id_country` |
| `order[company][address][city]` | `city` |
| `order[company][address][index]` | `postcode` |
| `order[company][address][region]` | `id_state` |
| `order[company][contragent][INN]` | `vat_number` |
| `order[company][name]` | `company` |
Для адреса доставки (`id_address_delivery`) используются данные заказа.
| CRM field / value | CMS field |
|-------------------------------------------|---------------------------|
| default | `alias` |
| `order[firstName]` | `firstname` |
| `order[lastName]` | `lastname` |
| `order[phone]` | `phone` |
| `order[delivery][address][text]` | `address1`, `address2` |
| `order[delivery][address][countryIso]` | `id_country` |
| `order[delivery][address][city]` | `city` |
| `order[delivery][address][index]` | `postcode` |
| `order[delivery][address][region]` | `id_state` |
После сборки объекта адреса выполняется проверка на валидность (_RetailcrmTools::validateEntity()_). Если адрес валиден,
то выполняется проверка наличия такого же адреса в CMS (_RetailcrmTools::assignAddressIdsByFields()_) и если адрес
найден, то берется его id (позволяет не создавать дубль адреса). В итоге, если id не был найден, то создается новый
адрес, иначе — сохраняется и используется существующий
### Payments
## Update
Обновление данных адреса в CMS происходит, если в заказе было изменено одно из следующих полей:
* `order[firstName]`
* `order[lastName]`
* `order[delivery][address]`
* `order[phone]`
Если были изменены поля адреса, на основе которых формируется `address1` и `address2` поля заказа, то делается запрос в
CRM для получения полной информации по заказу (проверка осуществляется в
функции `RetailcrmHistoryHelper::isAddressLineChanged()`). При этом данные, полученные по истории перезаписывают данные,
полученные по конкретному заказу — это сделано для сохранения возможности кастомизировать поля в фильтре.
Далее в зависимости от версии CMS возможны 2 варианта:
* На версии < 1.7.7 - происходит создание нового объекта адреса, после чего он присваивается заказу
* На версии \>= 1.7.7 - обновляются данные текущего адреса заказа
## Delete
При удалении заказа из Crm в CMS ничего не изменится

View File

@ -0,0 +1,8 @@
# Backward Synchronization
Обратная синхронизация — передача данных из CRM в CMS Prestashop. Реализуется в RetailcrmHistory.php посредством запросов к api методам истории. Вызывается в [Job Manager](../CLI%20&%20Job%20Manager/README.md) в команде RetailcrmSyncEvent с интервалом 7 минут
Каждый запрос получения истории изменений сопровождается параметром _sinceId_, Который хранится в конфигурации модуля
1. [Синхронизация данных клиентов](Customers.md)
2. [Синхронизация данных заказов](Orders.md)

View File

@ -0,0 +1 @@
# CLI & Job Manager

View File

@ -0,0 +1,2 @@
# Abandoned carts

View File

@ -0,0 +1 @@
# Customers

View File

@ -0,0 +1,2 @@
# Export

View File

@ -0,0 +1,2 @@
# Icml

View File

@ -0,0 +1,5 @@
# Orders
## Create
## Update

View File

@ -0,0 +1 @@
# Forward Synchronization

View File

@ -0,0 +1,2 @@
# Upload

View File

@ -0,0 +1,2 @@
# Multistore

View File

@ -0,0 +1 @@
# Workflow

View File

@ -0,0 +1,2 @@
# Templates & Views

View File

@ -0,0 +1,17 @@
# Custom classes
If you want to change the default behavior of a module classes and be sure that these changes won't be overwritten during the module upgrade process, you can create your own custom classes.
Note, that for more compatibility with future module versions it's recommended to use [custom filters](Filters.md) instead.
## Usage
To create custom class **copy the original class** that you are going to customize to the `<prestashop-root>/modules/retailcrm/custom/classes` directory.
From here you can modify the methods of the classes for your own purposes, and they will not be affected during the module upgrade process.
## Precautions
Keep in mind that:
* If the logic and classes of the module have changed a lot after an upgrade, your customized logic may cause the module to malfunction. **You should always check for changes after an upgrade and update your customized classes if needed.**
* This feature does not allow to customize the base class (file `retailcrm.php`). For this you can use the standard Prestashop override feature.

View File

@ -0,0 +1,59 @@
# Custom filters
## Usage
If you want to modify data, sent between CRM and CMS you can use custom filters.
To use filters you should define a new class in `<prestashop-root>/modules/retailcrm/custom/hooks`. Filename and classname must match the filter name.
Filter class should implement interface *RetailcrmFilterInterface*. In filter class you must define *filter()* function, which will take initial `$object` and return customized `$object`.
## Example
The example below shows you how to customize address data, loaded from CRM history during back sync:
```php
<?php
// custom/hooks/RetailcrmFilterSaveCustomerAddress.php
class RetailcrmFilterSaveCustomerAddress implements RetailcrmFilterInterface
{
/**
* @inheritDoc
*/
public static function filter($object, array $parameters)
{
/**
* @var array $dataCrm CRM address data
* @var Address $object CMS Address object
*/
$dataCrm = $parameters['dataCrm'];
if (isset($dataCrm['dni'])) {
$object->dni = $dataCrm['dni'];
}
return $object;
}
}
```
## List of filters
There are list of available filters:
* *RetailcrmFilterProcessOrder* - order array, which will be sent to CRM
* *RetailcrmFilterProcessCustomer* - customer array, which will be sent to CRM
* *RetailcrmFilterProcessCustomerCorporate* - corporate customer array, which will be sent to CRM
* *RetailcrmFilterProcessAddress* - address array, which will be sent to CRM
* *RetailcrmFilterProcessOffer* - offer array, which will be sent to CRM (saved into Icml file)
* *RetailcrmFilterCustomersHistory* - array with assembled history for customer, loaded from CRM
* *RetailcrmFilterCustomersHistoryUpdate* - array with customer info, loaded from CRM
* *RetailcrmFilterOrdersHistory* - array with assembled history for order, loaded from CRM
* *RetailcrmFilterOrdersHistoryCreate* - array with order info, loaded from CRM
* *RetailcrmFilterOrdersHistoryUpdate* - array with assembled history for order, loaded from CRM
* *RetailcrmFilterSaveCustomer* - built customer object, which will be saved to CMS
* *RetailcrmFilterSaveCustomerAddress* - built customer address object, which will be saved to CMS
* *RetailcrmFilterSaveCorporateCustomer* - built corporate customer object, which will be saved to CMS
* *RetailcrmFilterSaveCorporateCustomerAddress* - built corporate customer address object, which will be saved to CMS

View File

@ -0,0 +1 @@
# Customization

View File

@ -0,0 +1,3 @@
# Known issues
Content

1
doc/README.md Normal file
View File

@ -0,0 +1 @@
# Developers documentation

View File

@ -90,44 +90,53 @@ class RetailcrmHistory
}
$customerBuilder = new RetailcrmCustomerBuilder();
$customerBuilder->setDataCrm($customerHistory);
if (isset($customerHistory['externalId'])) {
$crmCustomerResponse = self::$api->customersGet($customerHistory['externalId'], 'externalId');
$customerData = self::$api->customersGet($customerHistory['externalId'], 'externalId');
if (isset($customerData['customer']) && $customerData['customer']) {
$foundCustomer = new Customer($customerHistory['externalId']);
$customerAddress = new Address(RetailcrmTools::searchIndividualAddress($foundCustomer));
$addressBuilder = new RetailcrmCustomerAddressBuilder();
$addressBuilder
->setCustomerAddress($customerAddress);
$customerBuilder
->setCustomer($foundCustomer)
->setAddressBuilder($addressBuilder)
->setDataCrm($customerData['customer'])
->build();
$customer = $customerBuilder->getData()->getCustomer();
$address = $customerBuilder->getData()->getCustomerAddress();
if (self::loadInCMS($customer, 'update') === false) {
continue;
}
if (!empty($address)) {
RetailcrmTools::assignAddressIdsByFields($customer, $address);
if (self::loadInCMS($address, 'update') === false) {
continue;
}
}
if (empty($crmCustomerResponse)
|| !$crmCustomerResponse->isSuccessful()
|| !$crmCustomerResponse->offsetExists('customer')
) {
continue;
}
$customerData = RetailcrmTools::filter(
'RetailcrmFilterCustomersHistoryUpdate',
$crmCustomerResponse['customer']
);
$foundCustomer = new Customer($customerHistory['externalId']);
$customerAddress = new Address(RetailcrmTools::searchIndividualAddress($foundCustomer));
$addressBuilder = new RetailcrmCustomerAddressBuilder();
$addressBuilder
->setCustomerAddress($customerAddress);
$customerBuilder
->setCustomer($foundCustomer)
->setAddressBuilder($addressBuilder)
->setDataCrm($customerData)
->build();
$customer = $customerBuilder->getData()->getCustomer();
$address = $customerBuilder->getData()->getCustomerAddress();
if (self::loadInCMS($customer, 'update') === false) {
continue;
}
if (!empty($address)) {
RetailcrmTools::assignAddressIdsByFields($customer, $address);
if (self::loadInCMS($address, 'update') === false) {
continue;
}
}
} else {
$customerBuilder->build();
$customerBuilder
->setDataCrm($customerHistory)
->build();
$customer = $customerBuilder->getData()->getCustomer();
@ -249,18 +258,17 @@ class RetailcrmHistory
continue;
}
$order = RetailcrmTools::filter(
'RetailcrmFilterOrdersHistoryCreate',
$order
);
// status
$state = $order['status'];
if (array_key_exists($state, $statuses) && $statuses[$state] != '') {
$orderStatus = $statuses[$state];
}
// delivery
$delivery = isset($order['delivery']['code']) ? $order['delivery']['code'] : false;
if ($delivery && array_key_exists($delivery, $deliveries) && $deliveries[$delivery] != '') {
$deliveryType = $deliveries[$delivery];
}
// payment
if (isset($order['payments'])) {
if (count($order['payments']) == 1) {
@ -311,6 +319,12 @@ class RetailcrmHistory
}
}
// delivery
$delivery = isset($order['delivery']['code']) ? $order['delivery']['code'] : false;
if ($delivery && array_key_exists($delivery, $deliveries) && $deliveries[$delivery] != '') {
$deliveryType = $deliveries[$delivery];
}
if (!isset($deliveryType) || !$deliveryType) {
if ($deliveryDefault) {
$deliveryType = $deliveryDefault;
@ -349,12 +363,10 @@ class RetailcrmHistory
}
} elseif (array_key_exists('externalId', $order['customer'])) {
$customerId = Customer::customerIdExistsStatic($order['customer']['externalId']);
//$customerBuilder = new RetailcrmCustomerBuilder();
//$customerBuilder->setCustomer(new Customer($customerId));
}
// address invoice
if (!empty($order['company']) && RetailcrmTools::isCorporateEnabled()) {
$corporateCustomerBuilder = new RetailcrmCorporateCustomerBuilder();
$dataOrder = array_merge(
$order['contact'],
@ -368,10 +380,9 @@ class RetailcrmHistory
->build();
$customer = $corporateCustomerBuilder->getData()->getCustomer();
$address = $corporateCustomerBuilder->getData()->getCustomerAddress();
$addressInvoice = $corporateCustomerBuilder->getData()->getCustomerAddress();
} else {
$customerBuilder = new RetailcrmCustomerBuilder();
if ($customerId) {
$customerBuilder->setCustomer(new Customer($customerId));
@ -382,7 +393,7 @@ class RetailcrmHistory
->build();
$customer = $customerBuilder->getData()->getCustomer();
$address = $customerBuilder->getData()->getCustomerAddress();
$addressInvoice = $customerBuilder->getData()->getCustomerAddress();
}
if (empty($customer->id) && !empty($customer->email)) {
@ -393,29 +404,18 @@ class RetailcrmHistory
continue;
}
if (!empty($address)) {
if (RetailcrmTools::validateEntity($addressInvoice)) {
$addressInvoice->id_customer = $customer->id;
RetailcrmTools::assignAddressIdsByFields($customer, $addressInvoice);
$address->id_customer = $customer->id;
RetailcrmTools::assignAddressIdsByFields($customer, $address);
if (empty($address->id)) {
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<Customer ID: %d> %s::%s',
$address->id_customer,
get_class($address),
'add'
)
);
$address->add();
if (empty($addressInvoice->id)) {
self::loadInCMS($addressInvoice, 'add');
if (!empty($order['company']) && RetailcrmTools::isCorporateEnabled()) {
self::$api->customersCorporateAddressesEdit(
$order['customer']['id'],
$order['company']['address']['id'],
array_merge($order['company']['address'], array('externalId' => $address->id)),
array_merge($order['company']['address'], array('externalId' => $addressInvoice->id)),
'id',
'id'
);
@ -423,12 +423,28 @@ class RetailcrmHistory
time_nanosleep(0, 20000000);
}
} else {
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf('<%d> %s::%s', $address->id, get_class($address), 'save')
);
self::loadInCMS($addressInvoice, 'save');
}
}
$address->save();
// address delivery
$addressBuilder = new RetailcrmCustomerAddressBuilder();
$addressDelivery = $addressBuilder
->setIdCustomer($customer->id)
->setDataCrm(isset($order['delivery']['address']) ? $order['delivery']['address'] : [])
->setFirstName(isset($order['firstName']) ? $order['firstName'] : null)
->setLastName(isset($order['lastName']) ? $order['lastName'] : null)
->setPhone(isset($order['phone']) ? $order['phone'] : null)
->build()
->getData();
if (RetailcrmTools::validateEntity($addressDelivery)) {
RetailcrmTools::assignAddressIdsByFields($customer, $addressDelivery);
if (empty($addressDelivery->id)) {
self::loadInCMS($addressDelivery, 'add');
} else {
self::loadInCMS($addressDelivery, 'save');
}
}
@ -439,21 +455,11 @@ class RetailcrmHistory
$cart->id_shop = Context::getContext()->shop->id;
$cart->id_shop_group = intval(Context::getContext()->shop->id_shop_group);
$cart->id_customer = $customer->id;
$cart->id_address_delivery = isset($address->id) ? (int) $address->id : 0;
$cart->id_address_invoice = isset($address->id) ? (int) $address->id : 0;
$cart->id_carrier = (int) $deliveryType;
$cart->id_address_delivery = isset($addressDelivery->id) ? (int)$addressDelivery->id : 0;
$cart->id_address_invoice = isset($addressInvoice->id) ? (int)$addressInvoice->id : 0;
$cart->id_carrier = (int)$deliveryType;
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<Customer ID: %d> %s::%s',
$cart->id_customer,
get_class($cart),
'add'
)
);
$cart->add();
self::loadInCMS($cart, 'add');
$products = array();
if (!empty($order['items'])) {
@ -465,27 +471,17 @@ class RetailcrmHistory
$productId = explode('#', $item['offer']['externalId']);
$product = array();
$product['id_product'] = (int) $productId[0];
$product['id_product'] = (int)$productId[0];
$product['id_product_attribute'] = !empty($productId[1]) ? $productId[1] : 0;
$product['quantity'] = $item['quantity'];
$product['id_address_delivery'] = isset($address->id) ? (int) $address->id : 0;
$product['id_address_delivery'] = isset($addressDelivery->id) ? (int)$addressDelivery->id : 0;
$products[] = $product;
}
}
$cart->setWsCartRows($products);
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<%d> %s::%s',
$cart->id,
get_class($cart),
'update'
)
);
$cart->update();
self::loadInCMS($cart, 'update');
/*
* Create order
@ -494,15 +490,15 @@ class RetailcrmHistory
$newOrder->id_shop = Context::getContext()->shop->id;
$newOrder->id_shop_group = intval(Context::getContext()->shop->id_shop_group);
$newOrder->reference = $newOrder->generateReference();
$newOrder->id_address_delivery = isset($address->id) ? (int) $address->id : 0;
$newOrder->id_address_invoice = isset($address->id) ? (int) $address->id : 0;
$newOrder->id_cart = (int) $cart->id;
$newOrder->id_address_delivery = isset($addressDelivery->id) ? (int)$addressDelivery->id : 0;
$newOrder->id_address_invoice = isset($addressInvoice->id) ? (int)$addressInvoice->id : 0;
$newOrder->id_cart = (int)$cart->id;
$newOrder->id_currency = $default_currency;
$newOrder->id_lang = self::$default_lang;
$newOrder->id_customer = (int) $customer->id;
$newOrder->id_customer = (int)$customer->id;
if (isset($deliveryType)) {
$newOrder->id_carrier = (int) $deliveryType;
$newOrder->id_carrier = (int)$deliveryType;
}
if (isset($paymentType)) {
@ -528,8 +524,8 @@ class RetailcrmHistory
$newOrder->total_paid_tax_excl = $totalPaid;
$newOrder->total_paid_real = $totalPaid;
$newOrder->total_products = (int) $orderTotalProducts;
$newOrder->total_products_wt = (int) $orderTotalProducts;
$newOrder->total_products = (int)$orderTotalProducts;
$newOrder->total_products_wt = (int)$orderTotalProducts;
$newOrder->total_shipping = $deliveryCost;
$newOrder->total_shipping_tax_incl = $deliveryCost;
@ -579,17 +575,7 @@ class RetailcrmHistory
$newOrderHistoryRecord->date_add = date('Y-m-d H:i:s');
$newOrderHistoryRecord->date_upd = $newOrderHistoryRecord->date_add;
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<Order ID: %d> %s::%s',
$newOrderHistoryRecord->id_order,
get_class($newOrderHistoryRecord),
'add'
)
);
$newOrderHistoryRecord->add();
self::loadInCMS($newOrderHistoryRecord, 'add');
}
} catch (\Exception $e) {
RetailcrmLogger::writeCaller(
@ -667,7 +653,7 @@ class RetailcrmHistory
if (!empty($order['items'])) {
foreach ($order['items'] as $item) {
$product = new Product((int) $item['offer']['externalId'], false, self::$default_lang);
$product = new Product((int)$item['offer']['externalId'], false, self::$default_lang);
$product_id = $item['offer']['externalId'];
$product_attribute_id = 0;
@ -698,31 +684,21 @@ class RetailcrmHistory
$orderDetail->id_order_invoice = $newOrder->invoice_number;
$orderDetail->id_shop = Context::getContext()->shop->id;
$orderDetail->product_id = (int) $product_id;
$orderDetail->product_attribute_id = (int) $product_attribute_id;
$orderDetail->product_id = (int)$product_id;
$orderDetail->product_attribute_id = (int)$product_attribute_id;
$orderDetail->product_reference = implode('', array('\'', $product->reference, '\''));
$orderDetail->product_price = $productPrice;
$orderDetail->original_product_price = $productPrice;
$orderDetail->product_quantity = (int) $item['quantity'];
$orderDetail->product_quantity_in_stock = (int) $item['quantity'];
$orderDetail->product_quantity = (int)$item['quantity'];
$orderDetail->product_quantity_in_stock = (int)$item['quantity'];
$orderDetail->total_price_tax_incl = $productPrice * $orderDetail->product_quantity;
$orderDetail->unit_price_tax_incl = $productPrice;
$orderDetail->id_warehouse = !empty($newOrder->id_warehouse) ? $newOrder->id_warehouse : 0;
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<Order ID: %d> %s::%s',
$orderDetail->id_order,
get_class($orderDetail),
'save'
)
);
if ($orderDetail->save()) {
if (self::loadInCMS($orderDetail, 'save')) {
$newItemsIds[Db::getInstance()->Insert_ID()] = $item['id'];
}
@ -755,9 +731,18 @@ class RetailcrmHistory
}
$orderToUpdate = new Order((int)$order['externalId']);
if(!Validate::isLoadedObject($orderToUpdate)) {
if (!Validate::isLoadedObject($orderToUpdate)) {
continue;
}
$order = RetailcrmTools::filter(
'RetailcrmFilterOrdersHistoryUpdate',
$order,
array(
'orderToUpdate' => $orderToUpdate
)
);
self::handleCustomerDataChange($orderToUpdate, $order);
/*
@ -771,15 +756,18 @@ class RetailcrmHistory
$addressBuilder = new RetailcrmCustomerAddressBuilder();
$orderAddressCrm = [];
// TODO: check changed fields and skip getCRMOrder() if it's possible
// It is possible if there are valid address in the database
// and if there's no address1 & address2 changed (text, street, building, etc...)
if (isset($order['delivery']['address'])) {
$orderAddressCrm = $order['delivery']['address'];
// get full order address
}
if (RetailcrmHistoryHelper::isAddressLineChanged($orderAddressCrm)) {
$infoOrder = self::getCRMOrder($order['externalId']);
if (isset($infoOrder['delivery']['address'])) {
$orderAddressCrm = $infoOrder['delivery']['address'];
// array_replace used to save changes, made by custom filters
$orderAddressCrm = array_replace(
$infoOrder['delivery']['address'],
$orderAddressCrm
);
}
}
@ -793,26 +781,19 @@ class RetailcrmHistory
->build()
->getData();
$validate = $address->validateFields(false, true);
if ($validate === true) {
if (RetailcrmTools::validateEntity($address, $orderToUpdate)) {
// Modifying an address in order creates another address
// instead of changing the original one. This issue has been fixed in PS 1.7.7
if (version_compare(_PS_VERSION_, '1.7.7', '<')) {
$address->id = null;
$address->add();
self::loadInCMS($address, 'add');
$orderToUpdate->id_address_delivery = $address->id;
$orderToUpdate->update();
self::loadInCMS($orderToUpdate, 'update');
} else {
$address->id = $orderToUpdate->id_address_delivery;
$address->update();
self::loadInCMS($address, 'update');
}
} else {
RetailcrmLogger::writeCaller(__METHOD__, sprintf(
'Error validating address for order %s: %s',
$orderToUpdate->id,
$validate
)
);
}
}
@ -853,17 +834,7 @@ class RetailcrmHistory
$orderCarrier->id_order = $orderToUpdate->id;
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<%d> %s::%s',
$orderCarrier->id,
get_class($orderCarrier),
'update'
)
);
$orderCarrier->update();
self::loadInCMS($orderCarrier, 'update');
}
}
@ -991,17 +962,7 @@ class RetailcrmHistory
$orderDetail->id_warehouse = !empty($orderToUpdate->id_warehouse)
? $orderToUpdate->id_warehouse : 0;
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<%d> %s::%s',
$orderDetail->id,
get_class($orderDetail),
'update'
)
);
$orderDetail->update();
self::loadInCMS($orderDetail, 'update');
unset($order['items'][$key]);
}
}
@ -1048,14 +1009,14 @@ class RetailcrmHistory
$orderDetail->id_order_invoice = $orderToUpdate->invoice_number;
$orderDetail->id_shop = Context::getContext()->shop->id;
$orderDetail->product_id = (int) $product_id;
$orderDetail->product_attribute_id = (int) $product_attribute_id;
$orderDetail->product_id = (int)$product_id;
$orderDetail->product_attribute_id = (int)$product_attribute_id;
$orderDetail->product_reference = implode('', array('\'', $product->reference, '\''));
$orderDetail->product_price = $productPrice;
$orderDetail->original_product_price = $productPrice;
$orderDetail->product_quantity = (int) $newItem['quantity'];
$orderDetail->product_quantity_in_stock = (int) $newItem['quantity'];
$orderDetail->product_quantity = (int)$newItem['quantity'];
$orderDetail->product_quantity_in_stock = (int)$newItem['quantity'];
$orderDetail->total_price_tax_incl = $productPrice * $orderDetail->product_quantity;
$orderDetail->unit_price_tax_incl = $productPrice;
@ -1065,17 +1026,7 @@ class RetailcrmHistory
$orderDetail->id_order_detail = !empty($parsedExtId['id_order_detail'])
? $parsedExtId['id_order_detail'] : null;
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<Order ID: %d> %s::%s',
$orderDetail->id_order,
get_class($orderDetail),
'save'
)
);
if ($orderDetail->save()) {
if (self::loadInCMS($orderDetail, 'save')) {
$newItemsIds[Db::getInstance()->Insert_ID()] = $newItem['id'];
}
@ -1118,17 +1069,7 @@ class RetailcrmHistory
$orderToUpdate->total_paid_tax_excl = $totalPaid;
$orderToUpdate->total_products_wt = $orderTotalProducts;
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<%d> %s::%s',
$orderToUpdate->id,
get_class($orderToUpdate),
'update'
)
);
$orderToUpdate->update();
self::loadInCMS($orderToUpdate, 'update');
}
/**
@ -1146,17 +1087,7 @@ class RetailcrmHistory
$orderHistory->id_order_state = $statuses[$stype];
$orderHistory->date_add = date('Y-m-d H:i:s');
RetailcrmLogger::writeDebug(
__METHOD__,
sprintf(
'<Order ID: %d> %s::%s',
$orderToUpdate->id,
get_class($orderHistory),
'save'
)
);
$orderHistory->save();
self::loadInCMS($orderHistory, 'save');
RetailcrmLogger::writeDebug(
__METHOD__,

View File

@ -307,4 +307,33 @@ class RetailcrmHistoryHelper {
return $outputArray;
}
/**
* @param array $address Crm Order address changes
*
* @return bool <b>true</b> if changed address field, which is used to generate
* <b>address1</b> and <b>address2</b> fields in CMS. <b>false</b> otherwise
*/
public static function isAddressLineChanged($address)
{
$keys = [
'street',
'building',
'flat',
'floor',
'block',
'house',
'housing',
'metro',
'notes',
];
foreach ($address as $key => $value) {
if (in_array($key, $keys)) {
return true;
}
}
return false;
}
}

View File

@ -179,6 +179,39 @@ class RetailcrmTools
return $ids;
}
/**
* @param ObjectModel $object
* @param ObjectModel|null $relatedObject
* @return bool
* @throws PrestaShopException
*/
public static function validateEntity($object, $relatedObject = null)
{
$validate = $object->validateFields(false, true);
if ($validate === true) {
return true;
}
$msg = '';
if ($relatedObject !== null) {
$msg = sprintf('for %s with id %s',
get_class($relatedObject),
$relatedObject->id
);
}
RetailcrmLogger::writeCaller(__METHOD__, sprintf(
'Error validating %s with id %s%s: %s',
get_class($object),
$object->id,
$msg,
$validate
)
);
return false;
}
/**
* Dumps entity using it's definition mapping.
*
@ -188,10 +221,10 @@ class RetailcrmTools
*/
public static function dumpEntity($object)
{
if (empty($object)) {
if (!is_object($object)) {
ob_start();
var_dump($object);
return (string) ob_get_clean();
return (string)ob_get_clean();
}
$data = array();
@ -692,7 +725,6 @@ class RetailcrmTools
*/
protected static function isAddressesEqualByFields($first, $second)
{
$equal = true;
$checkMapping = array(
'alias',
'id_country',
@ -708,21 +740,22 @@ class RetailcrmTools
foreach ($checkMapping as $field) {
if ($first->$field != $second->$field) {
$equal = false;
RetailcrmLogger::writeDebugArray(__METHOD__, array(
'first' => self::dumpEntity($first),
'second' => self::dumpEntity($second),
'field' => array(
'name' => $field,
'firstValue' => $first->$field,
'secondValue' => $second->$field
)
));
break;
RetailcrmLogger::writeDebug(__METHOD__, print_r(array(
'first' => array(
'id' => $first->id,
$field => $first->$field
),
'second' => array(
'id' => $second->id,
$field => $second->$field
),
), true));
return false;
}
}
return $equal;
return true;
}
/**
@ -747,18 +780,30 @@ class RetailcrmTools
*/
public static function filter($filter, $object, $parameters = array())
{
if (class_exists($filter) && method_exists($filter, 'filter')) {
try {
$result = call_user_func_array(
array($filter, 'filter'),
array($object, $parameters)
);
if (!class_exists($filter)) {
return $object;
}
if (!in_array(RetailcrmFilterInterface::class, class_implements($filter))) {
RetailcrmLogger::writeDebug(__METHOD__, sprintf('Filter class %s must implements %s interface',
$filter,
RetailcrmFilterInterface::class
));
return (null === $result || false === $result) ? $object : $result;
} catch (Exception $e) {
RetailcrmLogger::writeCaller(__METHOD__, 'Error in custom filter: ' . $e->getMessage());
RetailcrmLogger::writeDebug(__METHOD__, $e->getTraceAsString());
}
return $object;
}
try {
RetailcrmLogger::writeDebug($filter . '::before', print_r(self::dumpEntity($object), true));
$result = call_user_func_array(
array($filter, 'filter'),
array($object, $parameters)
);
RetailcrmLogger::writeDebug($filter . '::after', print_r(self::dumpEntity($result), true));
return (null === $result || false === $result) ? $object : $result;
} catch (Exception $e) {
RetailcrmLogger::writeCaller(__METHOD__, 'Error in custom filter: ' . $e->getMessage());
RetailcrmLogger::writeDebug(__METHOD__, $e->getTraceAsString());
}
return $object;

View File

@ -108,6 +108,7 @@
<field id="delivery_address.street" group="orderAddress">street</field>
<field id="delivery_address.building" group="orderAddress">building</field>
<field id="delivery_address.house" group="orderAddress">house</field>
<field id="delivery_address.housing" group="orderAddress">housing</field>
<field id="delivery_address.block" group="orderAddress">block</field>
<field id="delivery_address.flat" group="orderAddress">flat</field>
<field id="delivery_address.floor" group="orderAddress">floor</field>

View File

@ -84,7 +84,7 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
$this->assertEquals(true, RetailcrmHistory::customersHistory());
}
private function orderCreate($apiMock)
private function orderCreate($apiMock, $orderData)
{
RetailcrmHistory::$default_lang = (int)Configuration::get('PS_LANG_DEFAULT');
RetailcrmHistory::$api = $apiMock;
@ -98,6 +98,47 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
$order = new Order($newLastId);
$this->assertInstanceOf('Order', $order);
// delivery address
$address = new Address($order->id_address_delivery);
$this->assertEquals($orderData['firstName'],$address->firstname);
$this->assertEquals($orderData['lastName'],$address->lastname);
$builder = new RetailcrmAddressBuilder();
$addressDelivery = $builder
->setMode(RetailcrmAddressBuilder::MODE_ORDER_DELIVERY)
->setAddress($address)
->build()
->getDataArray();
$this->assertEquals($orderData['delivery']['address']['countryIso'], $addressDelivery['countryIso']);
unset($orderData['delivery']['address']['countryIso']);
$this->assertEquals($orderData['delivery']['address'], $addressDelivery['delivery']['address']);
$this->assertEquals($orderData['phone'], $addressDelivery['phone']);
// customer address
$address = new Address($order->id_address_invoice);
$this->assertEquals($orderData['customer']['firstName'],$address->firstname);
$this->assertEquals($orderData['customer']['lastName'],$address->lastname);
$addressInvoice = $builder
->setMode(RetailcrmAddressBuilder::MODE_CUSTOMER)
->setAddress($address)
->build()
->getDataArray();
if(isset($orderData['customer']['address']['id'])) {
unset($orderData['customer']['address']['id']);
}
$this->assertEquals($orderData['customer']['address'], $addressInvoice['address']);
$this->assertEquals($orderData['customer']['phones'][0]['number'], $addressInvoice['phones'][0]['number']);
// types and totals
$this->assertEquals($orderData['totalSumm'], $order->total_paid);
$this->assertEquals(10, $order->current_state);
$this->assertEquals(1, $order->id_carrier);
$this->assertEquals($orderData['payments'][0]['type'], $order->module);
}
private function switchCustomer()
@ -177,13 +218,15 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
public function testOrderCreate()
{
$orderData = $this->getApiOrder();
$this->apiMock->expects($this->any())
->method('ordersHistory')
->willReturn(
new RetailcrmApiResponse(
'200',
json_encode(
$this->getHistoryDataNewOrder($this->getApiOrder())
$this->getHistoryDataNewOrder($orderData)
)
)
);
@ -195,7 +238,7 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
'200',
json_encode(
array(
'order' => $this->getApiOrder()
'order' => $orderData
)
)
)
@ -207,23 +250,25 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
new RetailcrmApiResponse(
'200',
json_encode(
$this->getEditedOrder($this->getApiOrder())
$this->getEditedOrder($orderData)
)
)
);
$this->orderCreate($this->apiMock);
$this->orderCreate($this->apiMock, $orderData);
}
public function testOrderCreateWithCorporateCustomer()
{
$orderData = $this->getApiOrderWitchCorporateCustomer();
$this->apiMock->expects($this->any())
->method('ordersHistory')
->willReturn(
new RetailcrmApiResponse(
'200',
json_encode(
$this->getHistoryDataNewOrder($this->getApiOrderWitchCorporateCustomer())
$this->getHistoryDataNewOrder($orderData)
)
)
);
@ -235,7 +280,7 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
'200',
json_encode(
array(
'order' => $this->getApiOrderWitchCorporateCustomer()
'order' => $orderData
)
)
)
@ -247,12 +292,12 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
new RetailcrmApiResponse(
'200',
json_encode(
$this->getEditedOrder($this->getApiOrderWitchCorporateCustomer())
$this->getEditedOrder($orderData)
)
)
);
$this->orderCreate($this->apiMock);
$this->orderCreate($this->apiMock, $orderData);
}
public function testPaymentStatusUpdate()
@ -351,19 +396,6 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
)
);
$this->apiMock->expects($this->any())
->method('ordersGet')
->willReturn(
new RetailcrmApiResponse(
'200',
json_encode(
array(
'order' => $crmOrder
)
)
)
);
RetailcrmHistory::$default_lang = (int)Configuration::get('PS_LANG_DEFAULT');
RetailcrmHistory::$api = $this->apiMock;
@ -501,10 +533,9 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
)
),
'address' => array(
'id_customer' => 2222,
'index' => '111111',
'countryIso' => 'RU',
'region' => 'Moscow',
'region' => 'Buenos Aires',
'city' => 'Test',
'text' => 'Test text address'
),
@ -533,10 +564,9 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
'cost' => 100,
'netCost' => 0,
'address' => array(
'id_customer' => 2222,
'index' => '111111',
'countryIso' => 'RU',
'region' => 'Moscow',
'region' => 'Buenos Aires',
'city' => 'Test',
'text' => 'Test text address'
)
@ -620,10 +650,9 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
),
'address' => array(
'id' => 2345,
'id_customer' => 2222,
'index' => '111111',
'countryIso' => 'RU',
'region' => 'Moscow',
'region' => 'Buenos Aires',
'city' => 'Test',
'text' => 'Test text address'
),
@ -661,10 +690,9 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
'cost' => 100,
'netCost' => 0,
'address' => array(
'id_customer' => 2222,
'index' => '111111',
'countryIso' => 'RU',
'region' => 'Moscow',
'region' => 'Buenos Aires',
'city' => 'Test',
'text' => 'Test text address'
)
@ -677,10 +705,9 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
),
'address' => array(
'id' => 1,
'id_customer' => 2222,
'index' => '111111',
'countryIso' => 'RU',
'region' => 'Moscow',
'region' => 'Buenos Aires',
'city' => 'Test',
'text' => 'Test text address'
)
@ -853,11 +880,10 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
'customFields' => [],
'personalDiscount' => 0,
'address' => array(
'id_customer' => 2222,
'id' => 4053,
'countryIso' => 'RU',
'index' => '2170',
'city' => 'Moscow',
'city' => 'Buenos Aires',
'street' => 'Good',
'building' => '17',
'text' => 'Good, д. 17'
@ -906,11 +932,26 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
'site' => '127.0.0.1:8000',
'status' => 'new'
)
),
array(
'id' => 19754,
'createdAt' => '2018-01-01 00:00:00',
'source' => 'api',
'field' => 'delivery_address.street',
'apiKey' => array('current' => false),
'oldValue' => null,
'newValue' => 'Test updated address',
'order' => array(
'id' => 6025,
'externalId' => (string)$orderId,
'site' => '127.0.0.1:8000',
'status' => 'new'
)
)
),
'pagination' => array(
'limit' => 20,
'totalCount' => 2,
'totalCount' => 3,
'currentPage' => 1,
'totalPageCount' => 1
)
@ -968,9 +1009,9 @@ class RetailcrmHistoryTest extends RetailcrmTestCase
$order = $this->getApiOrder();
$order['externalId'] = (string)$orderId;
$order['delivery']['address']['region'] = 'Buenos Aires'; // todo to get real state id
$order['delivery']['address']['city'] = 'Order City new';
$order['delivery']['address']['index'] = '222';
$order['delivery']['address']['text'] = 'Test updated address';
unset($order['delivery']['address']['id_customer']);
return $order;