Add PHPStan configuration for Symfony4 tests

- Created a new phpstan.neon file in the Symfony4 tests directory.
- Configured paths and excluded Symfony3 directory.
- Added bootstrap files for autoloading.
- Set dynamic constant names and adjusted reporting settings.
- Established PHPStan level to 6 for stricter analysis.
This commit is contained in:
2026-02-09 23:14:09 +01:00
parent 58947ad589
commit 3bd8164d3d
169 changed files with 4964 additions and 3877 deletions

View File

@@ -4,7 +4,6 @@
namespace Empik\Marketplace\API;
use Empik\Marketplace\Factory\HttpClientsFactory;
use Empik\Marketplace\Factory\HttpClientFactoryInterface;
use Empik\Marketplace\Exception\InvalidArgumentException;
class EmpikClient
@@ -23,20 +22,15 @@ class EmpikClient
private $client;
/**
* @param $apiUrl
* @param $apiKey
* @param HttpClientFactoryInterface|null $httpClientFactory
* @param string $apiUrl
* @param string $apiKey
* @throws \Exception
*/
public function __construct($apiUrl, $apiKey, HttpClientFactoryInterface $httpClientFactory = null)
public function __construct($apiUrl, $apiKey)
{
if (!$httpClientFactory) {
$httpClientFactory = new HttpClientsFactory();
}
$this->apiUrl = $apiUrl;
$this->apiKey = $apiKey;
$this->client = $httpClientFactory->createHttpClient($apiUrl);
$this->client = HttpClientsFactory::createHttpClient();
}
/**

View File

@@ -67,6 +67,8 @@ class HttpCurlClient implements HttpClientInterface
$ch = curl_init($id);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
if ($headers) {
$headersString = [];
@@ -84,10 +86,21 @@ class HttpCurlClient implements HttpClientInterface
{
$response = curl_exec($ch);
if (curl_errno($ch)) {
$errorCode = curl_errno($ch);
$errorMessage = curl_error($ch);
$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
throw new \Exception(sprintf(
'CURL error [%d]: %s (URL: %s)',
$errorCode,
$errorMessage,
$url
));
}
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// is 2xx http code
if ($httpCode < 200 || $httpCode >= 300) {
throw new \Exception('Request failed with HTTP code ' . $httpCode . '.');
}

View File

@@ -27,6 +27,7 @@ class ConfigurationAdapter
const CONF_SHIPPED_ORDER_STATE = 'EMPIK_SHIPPED_ORDER_STATE';
const CONF_CARRIER_MAP = 'EMPIK_CARRIER_MAP';
const CONF_ID_EMPLOYEE = 'EMPIK_ID_EMPLOYEE';
const CONF_DEFAULT_VAT_RATE = 'EMPIK_DEFAULT_VAT_RATE';
const ENV_TEST = 'https://stg1.marketplace.empik.com';
const ENV_PROD = 'https://marketplace.empik.com';

View File

@@ -17,16 +17,24 @@ class OrderTrackingDataProvider
$this->db = Db::getInstance();
}
public function getOrderTrackingData()
public function getOrderTrackingData($orderId = null, $limit = 50)
{
$sql = new DbQuery();
$sql->select('o.id_order, o.shipping_number, eo.empik_order_reference, eo.empik_order_carrier');
$sql->select('eo.id_empik_order, o.id_order, o.current_state, oc.id_carrier, oc.tracking_number, eo.empik_order_reference, eo.empik_order_carrier');
$sql->from('empik_orders', 'eo');
$sql->leftJoin('orders', 'o', 'eo.id_order = o.id_order');
$sql->where('o.shipping_number IS NOT NULL AND o.shipping_number != ""');
$sql->leftJoin('order_carrier', 'oc', 'oc.id_order = o.id_order');
$sql->where('oc.tracking_number != "" AND oc.tracking_number != eo.empik_tracking_number');
$sql->orderBy('o.date_add DESC');
$sql->limit(20);
return $this->db->executeS($sql);
if ($orderId) {
$sql->where('o.id_order = ' . (int)$orderId);
return $this->db->getRow($sql);
} else {
$sql->limit((int)$limit);
return $this->db->executeS($sql);
}
}
}

View File

@@ -54,7 +54,6 @@ class ProductDataProvider
pl.description_short,
pl.link_rewrite,
pl.meta_description,
pl.meta_keywords,
pl.meta_title,
pl.available_now,
pl.available_later,

View File

@@ -39,11 +39,11 @@ class EmpikAction extends ObjectModel
],
'id_import' => [
'type' => self::TYPE_INT,
'validate' => 'isCleanHtml',
'validate' => 'isUnsignedInt',
],
'import_count' => [
'type' => self::TYPE_INT,
'validate' => 'isCleanHtml',
'validate' => 'isUnsignedInt',
],
'date_start' => [
'type' => self::TYPE_DATE,
@@ -57,4 +57,4 @@ class EmpikAction extends ObjectModel
],
],
];
}
}

View File

@@ -167,7 +167,7 @@ class ExportOfferHandler
$offer['quantity'] = $calculatedQuantity;
if ($leadTimeToShip && $leadTimeToShip >= 0) {
if (is_numeric($leadTimeToShip) && $leadTimeToShip >= 0) {
$offer['leadtime-to-ship'] = $leadTimeToShip;
}

View File

@@ -246,7 +246,7 @@ class ExportProductHandler
$imageUrl = null;
if (isset($images[$i])) {
$imageUrl = $this->context->link->getImageLink(
Tools::link_rewrite($product['link_rewrite'] ? $product['link_rewrite'] : $product['name']),
Tools::str2url($product['link_rewrite'] ? $product['link_rewrite'] : $product['name']),
$images[$i]['id_image']
);
}

View File

@@ -3,7 +3,6 @@
namespace Empik\Marketplace\Hook;
use Carrier;
use Configuration;
use Context;
use Module;
@@ -12,6 +11,8 @@ use Product;
use OrderHistory;
use EmpikOrder;
use EmpikMarketplace;
use Empik\Marketplace\DataProvider\OrderTrackingDataProvider;
use Empik\Marketplace\Processor\TrackingNumberProcessor;
use Empik\Marketplace\Adapter\LoggerAdapter;
use Empik\Marketplace\Adapter\ConfigurationAdapter;
use Empik\Marketplace\Factory\EmpikClientFactory;
@@ -174,7 +175,19 @@ class HookAction
public function hookActionAdminOrdersTrackingNumberUpdate(array $params)
{
// Handled by a cron job
/** @var Order $order */
$order = $params['order'];
/** @var TrackingNumberProcessor $trackingNumberProcessor */
$trackingNumberProcessor = $this->module->getService('empik.marketplace.processor.trackingNumberProcessor');
/** @var OrderTrackingDataProvider $orderTrackingDataProvider */
$orderTrackingDataProvider = $this->module->getService('empik.marketplace.dataProvider.orderTrackingDataProvider');
$orderTrackingData = $orderTrackingDataProvider->getOrderTrackingData($order->id);
if ($orderTrackingData) {
$trackingNumberProcessor->processOrder($orderTrackingData);
}
}
public function hookActionObjectProductDeleteBefore($params)

View File

@@ -27,6 +27,7 @@ $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'empik_orders` (
`empik_order_carrier` VARCHAR(255) NOT NULL,
`empik_payment` VARCHAR(255) NOT NULL,
`empik_carrier` VARCHAR(255) NOT NULL,
`empik_tracking_number` VARCHAR(255) NOT NULL,
`empik_pickup_point` VARCHAR(255) NOT NULL,
`empik_vat_number` VARCHAR(255) NOT NULL,
`date_add` DATETIME NOT NULL,
@@ -44,6 +45,8 @@ $sql[] = 'CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'empik_product` (
`offer_export` INT(1) NOT NULL DEFAULT 0,
`offer_price` DECIMAL(20, 2) NOT NULL DEFAULT 0,
`offer_price_reduced` DECIMAL(20, 2) NOT NULL DEFAULT 0,
`condition` INT(4) NOT NULL DEFAULT 11,
`export_original_price` INT(1) NOT NULL DEFAULT 0,
UNIQUE (`id_product`, `id_product_attribute`),
PRIMARY KEY (`id_empik_product`)
)';

View File

@@ -2,8 +2,10 @@
namespace Empik\Marketplace\OrderFulfiller;
use Empik\Marketplace\Adapter\ConfigurationAdapter;
use Empik\Marketplace\Adapter\ToolsAdapter;
use Empik\Marketplace\DataProvider\ProductDataProvider;
use Empik\Marketplace\Utils\Utils;
use Order;
use OrderDetail;
use Address;
@@ -105,6 +107,8 @@ class EmpikOrder
public function add()
{
$defaultVatRate = (int)\Configuration::get(ConfigurationAdapter::CONF_DEFAULT_VAT_RATE);
$context = \Context::getContext();
if ($this->customer && !$this->customer->id) {
@@ -151,9 +155,9 @@ class EmpikOrder
$this->order->total_products = (float)$this->data['price'];
$this->order->total_products_wt = (float)$this->data['price'];
$this->order->total_shipping = (float)$this->data['shipping_price'];
$this->order->total_shipping = Utils::calculateTaxExcl($this->data['shipping_price'], $defaultVatRate);
$this->order->total_shipping_tax_incl = (float)$this->data['shipping_price'];
$this->order->total_shipping_tax_excl = (float)$this->data['shipping_price'];
$this->order->total_shipping_tax_excl = Utils::calculateTaxExcl((float)$this->data['shipping_price'], $defaultVatRate);
$this->order->conversion_rate = 1;

View File

@@ -5,9 +5,13 @@ namespace Empik\Marketplace\OrderFulfiller;
use Configuration;
use Empik\Marketplace\Adapter\ConfigurationAdapter;
use Empik\Marketplace\DataProvider\ProductDataProvider;
use Empik\Marketplace\Exception\OrderProcessException;
use Empik\Marketplace\PrestaShopContext;
use Empik\Marketplace\Provider\Tax\ProductTaxRateProvider;
use Empik\Marketplace\Repository\ProductRepository;
use Empik\Marketplace\Utils\IdentifierExtractor;
use Empik\Marketplace\Utils\Utils;
use Empik\Marketplace\Provider\Order\ProductRefResolver;
use Order;
use OrderDetail;
use Address;
@@ -17,18 +21,25 @@ use OrderHistory;
use EmpikOrder;
use Cart;
use StockAvailable;
use Carrier;
class EmpikOrderWrapper
{
/** @var ProductDataProvider */
protected $productDataProvider;
/** @var ProductTaxRateProvider */
protected $productTaxRateProvider;
/** @var ProductRepository */
protected $productRepository;
/** @var IdentifierExtractor */
protected $identifierExtractor;
/** @var ProductRefResolver */
protected $productRefResolver;
/** @var array */
protected $data;
@@ -56,13 +67,15 @@ class EmpikOrderWrapper
/** @var OrderHistory[] */
protected $orderHistory;
public function __construct()
public function __construct(ProductRefResolver $productRefResolver)
{
$this->productDataProvider = new ProductDataProvider();
$this->productTaxRateProvider = new ProductTaxRateProvider();
$this->productRepository = new ProductRepository(
new PrestaShopContext()
);
$this->identifierExtractor = new IdentifierExtractor();
$this->productRefResolver = $productRefResolver;
}
public function init($data)
@@ -70,7 +83,9 @@ class EmpikOrderWrapper
$id = EmpikOrder::getOrderIdByEmpikOrderReference($data['order_id']);
$order = new Order($id);
$this->setData($data);
$dataPrepared = $this->prepareData($data);
$this->setData($dataPrepared);
$this->setOrder($order);
}
@@ -103,20 +118,9 @@ class EmpikOrderWrapper
*/
public function getAcceptanceLines()
{
$skuType = Configuration::get(ConfigurationAdapter::CONF_SKU_TYPE);
$return = [];
foreach ($this->getData()['order_lines'] as $orderLine) {
if ($skuType === 'ID') {
$product = $this->productRepository->getProductOrCombinationById($orderLine['offer_sku']);
} elseif ($skuType === 'EAN') {
$product = $this->productRepository->getProductOrCombinationByEan($orderLine['offer_sku']);
} elseif ($skuType === 'REFERENCE') {
$product = $this->productRepository->getProductOrCombinationBySku($orderLine['offer_sku']);
}
$product = $this->productRefResolver->getProductByRef($orderLine);
$return[] = [
'id' => (string)$orderLine['order_line_id'],
'accepted' => $product && $product['active'] && $product['quantity'] >= $orderLine['quantity'],
@@ -130,6 +134,8 @@ class EmpikOrderWrapper
{
$context = \Context::getContext();
$carrierTaxRate = $this->getCarrierTaxRate();
if ($this->customer && !$this->customer->id) {
$this->customer->id_shop = $context->shop->id;
$this->customer->id_shop_group = $context->shop->id_shop_group;
@@ -167,18 +173,20 @@ class EmpikOrderWrapper
$this->order->payment = $this->data['payment_type'];
$this->order->module = 'empikmarketplace';
$this->order->total_paid = (float)$this->data['total_price'];
$this->order->total_paid_tax_incl = (float)$this->data['total_price'];
$this->order->total_paid_tax_excl = (float)$this->data['total_price'];
$this->order->total_products = (float)$this->data['total_products_tax_excl'];
$this->order->total_products_wt = (float)$this->data['total_products_tax_incl'];
$this->order->total_paid_real = (float)$this->data['total_price'];
$this->order->total_products = (float)$this->data['price'];
$this->order->total_products_wt = (float)$this->data['price'];
$this->order->total_shipping = (float)$this->data['shipping_price'];
$totalShippingTaxExcl = Utils::calculateTaxExcl($this->data['shipping_price'], $carrierTaxRate);
$this->order->total_shipping = $totalShippingTaxExcl;
$this->order->total_shipping_tax_incl = (float)$this->data['shipping_price'];
$this->order->total_shipping_tax_excl = (float)$this->data['shipping_price'];
$this->order->total_shipping_tax_excl = $totalShippingTaxExcl;
$this->order->carrier_tax_rate = $carrierTaxRate;
$this->order->total_paid_tax_incl = $this->order->total_shipping_tax_incl + $this->order->total_products_wt;
$this->order->total_paid_tax_excl = $this->order->total_shipping_tax_excl + $this->order->total_products;
$this->order->total_paid = $this->order->total_paid_tax_incl;
$this->order->total_paid_real = $this->order->total_paid_tax_incl;
$this->order->conversion_rate = 1;
@@ -195,7 +203,7 @@ class EmpikOrderWrapper
// create order
$this->order->add();
// add order carrier
// add order carrier with proper tax rate
$this->carrier->id_order = $this->order->id;
$this->carrier->shipping_cost_tax_excl = $this->order->total_shipping_tax_excl;
$this->carrier->shipping_cost_tax_incl = $this->order->total_shipping_tax_incl;
@@ -205,17 +213,17 @@ class EmpikOrderWrapper
$orderDetail->id_order = $this->order->id;
$orderDetail->add();
$this->createOrderTax($orderDetail);
if ($this->order->current_state != Configuration::get('PS_OS_CANCELED')
&& $this->order->current_state != Configuration::get('PS_OS_ERROR')) {
if (!StockAvailable::dependsOnStock($orderDetail->product_id)) {
StockAvailable::updateQuantity(
$orderDetail->product_id,
$orderDetail->product_attribute_id,
-(int) $orderDetail->product_quantity,
$orderDetail->id_shop,
true
);
}
StockAvailable::updateQuantity(
$orderDetail->product_id,
$orderDetail->product_attribute_id,
-(int) $orderDetail->product_quantity,
$orderDetail->id_shop,
true
);
}
}
@@ -227,6 +235,44 @@ class EmpikOrderWrapper
$this->createEmpikOrder();
}
protected function getCarrierTaxRate()
{
$defaultVatRate = (float)\Configuration::get(ConfigurationAdapter::CONF_DEFAULT_VAT_RATE);
if (!$this->carrier || !$this->carrier->id_carrier) {
return $defaultVatRate;
}
$carrier = new Carrier($this->carrier->id_carrier);
if (!\Validate::isLoadedObject($carrier)) {
return $defaultVatRate;
}
if ($this->shippingAddress && $this->shippingAddress->id) {
$carrierTax = $carrier->getTaxesRate($this->shippingAddress);
if ($carrierTax > 0) {
return (float) $carrierTax;
}
}
return $defaultVatRate;
}
protected function createOrderTax($orderDetail)
{
\Db::getInstance()->insert(
'order_detail_tax',
[
'id_order_detail' => $orderDetail->id,
'id_tax' => 0,
'unit_amount' => $orderDetail->unit_price_tax_incl - $orderDetail->unit_price_tax_excl,
'total_amount' => $orderDetail->total_price_tax_incl - $orderDetail->total_price_tax_excl
]
);
}
public function createEmpikOrder()
{
$empikOrder = new EmpikOrder();
@@ -245,7 +291,43 @@ class EmpikOrderWrapper
}
}
$empikOrder->add();
$empikOrder->add();
}
/**
* @param $data
* @return array
* @throws OrderProcessException
*/
private function prepareData($data)
{
$totalProductsTaxIncl = 0;
$totalProductsTaxExcl = 0;
foreach ($data['order_lines'] as $key => $orderLine) {
$productData = $this->productRefResolver->getProductByRef($orderLine);
if (!$productData) {
throw new OrderProcessException('Product not found');
}
$taxRate = $this->productTaxRateProvider->getTaxRateByTaxRuleGroup($productData['id_tax_rules_group']);
$data['order_lines'][$key]['total_price_tax_incl'] = $data['order_lines'][$key]['price'];
$data['order_lines'][$key]['total_price_tax_excl'] = $data['order_lines'][$key]['price'] / ((100 + $taxRate) / 100);
$data['order_lines'][$key]['unit_price_tax_incl'] = $data['order_lines'][$key]['price_unit'];
$data['order_lines'][$key]['unit_price_tax_excl'] = $data['order_lines'][$key]['price_unit'] / ((100 + $taxRate) / 100);
$totalProductsTaxIncl += $data['order_lines'][$key]['total_price_tax_incl'];
$totalProductsTaxExcl += $data['order_lines'][$key]['total_price_tax_excl'];
}
$data['total_products_tax_incl'] = round($totalProductsTaxIncl, 2);
$data['total_products_tax_excl'] = round($totalProductsTaxExcl, 2);
return $data;
}
/**
@@ -391,4 +473,4 @@ class EmpikOrderWrapper
{
$this->billingAddress = $billingAddress;
}
}
}

View File

@@ -18,4 +18,9 @@ class PrestaShopContext
{
return version_compare(_PS_VERSION_, '8.0', '>=');
}
public function is9()
{
return version_compare(_PS_VERSION_, '9.0', '>=');
}
}

View File

@@ -5,20 +5,21 @@ namespace Empik\Marketplace\Processor;
use Empik\Marketplace\Adapter\ConfigurationAdapter;
use Empik\Marketplace\Adapter\LoggerAdapter;
use Empik\Marketplace\API\EmpikClient;
use Empik\Marketplace\Exception\OrderProcessException;
use Empik\Marketplace\OrderFulfiller\EmpikOrderWrapper;
use Empik\Marketplace\Factory\EmpikClientFactory;
use Empik\Marketplace\Manager\ProcessManager;
use Empik\Marketplace\OrderFulfiller\OrderFulfiller;
use GuzzleHttp\Exception\ClientException;
use Exception;
use Configuration;
use Db;
use Empik\Marketplace\Provider\Order\ProductRefResolver;
class OrderProcessor
{
const CODE_WAITING_ACCEPTANCE = 'WAITING_ACCEPTANCE';
const CODE_SHIPPING = 'SHIPPING';
/** @var EmpikClientFactory */
protected $empikClientFactory;
@@ -34,6 +35,9 @@ class OrderProcessor
/** @var LoggerAdapter */
protected $logger;
/** @var ProductRefResolver */
protected $productRefResolver;
protected $allowAccept;
protected $allowImport;
@@ -41,12 +45,14 @@ class OrderProcessor
ProcessManager $processManager,
EmpikClientFactory $empikClientFactory,
OrderFulfiller $orderFulfiller,
LoggerAdapter $loggerAdapter
LoggerAdapter $loggerAdapter,
ProductRefResolver $productRefResolver
) {
$this->processManager = $processManager;
$this->empikClientFactory = $empikClientFactory;
$this->orderFulfiller = $orderFulfiller;
$this->logger = $loggerAdapter;
$this->productRefResolver = $productRefResolver;
$this->empikClient = $this->empikClientFactory->createClient();
@@ -59,24 +65,27 @@ class OrderProcessor
if (!$this->allowAccept && !$this->allowImport) {
return;
}
$response = $this->empikClient->getOrders([
'order_state_codes' => implode(',', $this->getOrderCodesForProcess()),
]);
foreach ($response['orders'] as $order) {
try {
$empikOrder = new EmpikOrderWrapper($this->productRefResolver);
$empikOrder->init($order);
$empikOrder = new EmpikOrderWrapper();
$empikOrder->init($order);
if ($this->allowAccept && $empikOrder->isAcceptable() && !$empikOrder->exist()) {
$this->accept($empikOrder);
}
if ($this->allowAccept && $empikOrder->isAcceptable() && !$empikOrder->exist()) {
$this->accept($empikOrder);
}
if ($this->allowImport) {
if (!$empikOrder->exist()) {
if ($this->allowImport && !$empikOrder->exist()) {
$this->import($empikOrder);
}
} catch (OrderProcessException $exception) {
$this->logger->logError('Empikplaces: import order process, order id ' . $order['order_id'] . ', message:' . $exception->getMessage());
} catch (Exception $exception) {
$this->logger->logError('Empikplaces: ' . $exception->getMessage());
}
}
}
@@ -102,28 +111,23 @@ class OrderProcessor
$empikOrder->getData()['order_id'],
$acceptLines
);
} catch (ClientException $e) {
$responseJson = $e->getResponse()->json();
$message = isset($responseJson['message']) ? $responseJson['message'] : null;
$this->logger->logError(sprintf('Error accepting order [%s]', $message));
} catch (Exception $e) {
$this->logger->logError(sprintf('Error accepting order [%s]', $e->getMessage()));
}
}
protected function getOrderCodesForProcess()
{
$codes = [];
if ($this->allowAccept) {
$codes[] = self::CODE_WAITING_ACCEPTANCE;
}
if ($this->allowImport) {
$codes[] = self::CODE_SHIPPING;
}
return $codes;
}
}
}

View File

@@ -5,6 +5,7 @@ namespace Empik\Marketplace\Processor;
use Empik\Marketplace\Adapter\ConfigurationAdapter;
use Empik\Marketplace\DataProvider\OrderTrackingDataProvider;
use Empik\Marketplace\Manager\CarrierMapManager;
use Empik\Marketplace\Repository\Empik\EmpikOrderRepositoryInterface;
use Empik\Marketplace\Service\Api\TrackingNumberUpdater;
use Empik\Marketplace\Service\Api\ShipOrderUpdater;
use Empik\Marketplace\Adapter\LoggerAdapter;
@@ -28,6 +29,9 @@ class TrackingNumberProcessor
/** @var LoggerAdapter */
private $logger;
/** @var EmpikOrderRepositoryInterface */
private $empikOrderRepository;
public function __construct(
OrderTrackingDataProvider $orderTrackingDataProvider,
@@ -35,7 +39,8 @@ class TrackingNumberProcessor
ShipOrderUpdater $shipOrderUpdater,
CarrierMapManager $carrierMapManager,
ConfigurationAdapter $configurationAdapter,
LoggerAdapter $loggerAdapter
LoggerAdapter $loggerAdapter,
EmpikOrderRepositoryInterface $empikOrderRepository
) {
$this->orderTrackingDataProvider = $orderTrackingDataProvider;
$this->trackingNumberUpdater = $trackingNumberUpdater;
@@ -43,45 +48,57 @@ class TrackingNumberProcessor
$this->carrierMapManager = $carrierMapManager;
$this->configurationAdapter = $configurationAdapter;
$this->logger = $loggerAdapter;
$this->empikOrderRepository = $empikOrderRepository;
}
public function process()
{
$shippedOrderStateId = (int)$this->configurationAdapter->get(ConfigurationAdapter::CONF_SHIPPED_ORDER_STATE);
$orders = $this->orderTrackingDataProvider->getOrderTrackingData();
foreach ($orders as $orderData) {
$this->processOrder($orderData);
}
}
$order = new \Order($orderData['id_order']);
$carrier = new \Carrier($order->id_carrier);
public function processOrder(array $orderData)
{
$carrier = new \Carrier($orderData['id_carrier']);
$trackingNumber = $order->shipping_number;
$empikOrderReference = $orderData['empik_order_reference'];
$trackingNumber = $orderData['tracking_number'];
$empikOrderReference = $orderData['empik_order_reference'];
$carrierCode = null;
$carrierName = null;
$carrierUrl = null;
$carrierName = null;
$carrierUrl = null;
$carrierCode = $this->carrierMapManager->getTypeByCode($orderData['empik_order_carrier']);
if (!$carrierCode) {
$carrierName = $carrier->name;
$carrierUrl = str_replace('@', $trackingNumber, $carrier->url);
$carrierCode = $this->carrierMapManager->getTypeByCode($orderData['empik_order_carrier']);
if (!$carrierCode) {
$carrierName = $carrier->name;
$carrierUrl = str_replace('@', $trackingNumber, $carrier->url);
}
try {
$this->trackingNumberUpdater->updateTrackingNumber(
$empikOrderReference,
$carrierCode,
$trackingNumber,
$carrierName,
$carrierUrl
);
$this->empikOrderRepository->updateTrackingNumber(
(int) $orderData['id_empik_order'],
$trackingNumber
);
if ($this->configurationAdapter->get(ConfigurationAdapter::CONF_SHIPPED_ORDER_STATE) == $orderData['current_state']) {
$this->shipOrderUpdater->updateOrderShipStatus($empikOrderReference);
}
try {
$this->trackingNumberUpdater->updateTrackingNumber(
$empikOrderReference,
$carrierCode,
$trackingNumber,
$carrierName,
$carrierUrl
);
if ($shippedOrderStateId && $shippedOrderStateId == $order->current_state) {
$this->shipOrderUpdater->updateOrderShipStatus($empikOrderReference);
}
} catch (\Exception $e) {
$this->logger->logError($e->getMessage());
}
} catch (\InvalidArgumentException $e) {
$this->logger->logError('[Invalid input] ' . $e->getMessage());
} catch (\RuntimeException $e) {
$this->logger->logError('[Persistence error] ' . $e->getMessage());
} catch (\Exception $e) {
$this->logger->logError($e->getMessage());
}
}
}

View File

@@ -58,6 +58,14 @@ class AddressProvider
$obj->address2 = $addressData['street_2'];
}
if (!\Validate::isName($obj->lastname)) {
$obj->lastname = self::NAME_EMPTY;
}
if (!\Validate::isName($obj->firstname)) {
$obj->firstname = self::NAME_EMPTY;
}
if ($additionalFields) {
foreach ($additionalFields as $additionalField) {
if ($additionalField['code'] === 'nip' && $additionalField['value']) {

View File

@@ -3,14 +3,13 @@
namespace Empik\Marketplace\Provider\Order;
use Configuration;
use Db;
use Cart;
use Context;
use Empik\Marketplace\Adapter\ConfigurationAdapter;
use Empik\Marketplace\Exception\OrderProcessException;
use Empik\Marketplace\Repository\ProductRepository;
use Empik\Marketplace\Utils\IdentifierExtractor;
use Empik\Marketplace\Provider\Order\ProductRefResolver;
class CartProvider
{
@@ -20,13 +19,17 @@ class CartProvider
/** @var IdentifierExtractor */
protected $identifierExtractor;
/** @var ProductRefResolver */
protected $productRefResolver;
/** @var Context */
protected $context;
public function __construct(ProductRepository $productRepository, IdentifierExtractor $identifierExtractor)
public function __construct(ProductRepository $productRepository, IdentifierExtractor $identifierExtractor, ProductRefResolver $productRefResolver)
{
$this->productRepository = $productRepository;
$this->identifierExtractor = $identifierExtractor;
$this->productRefResolver = $productRefResolver;
$this->context = Context::getContext();
}
@@ -47,7 +50,7 @@ class CartProvider
foreach ($data['order_lines'] as $orderLine) {
$productData = $this->getProductByRef($orderLine);
$productData = $this->productRefResolver->getProductByRef($orderLine);
if (!$productData) {
throw new OrderProcessException(sprintf('Products "%s" not found', $orderLine['product_title']));
@@ -65,22 +68,4 @@ class CartProvider
return $cart;
}
/**
* @param $orderLine
* @return array|bool|false|object|null
*/
protected function getProductByRef($orderLine)
{
$skuType = Configuration::get(ConfigurationAdapter::CONF_SKU_TYPE);
if ($skuType === 'ID') {
$product = $this->productRepository->getProductOrCombinationById($orderLine['offer_sku']);
} elseif ($skuType === 'EAN') {
$product = $this->productRepository->getProductOrCombinationByEan($orderLine['offer_sku']);
} elseif ($skuType === 'REFERENCE') {
$product = $this->productRepository->getProductOrCombinationBySku($orderLine['offer_sku']);
}
return !empty($product) ? $product : false;
}
}

View File

@@ -2,17 +2,16 @@
namespace Empik\Marketplace\Provider\Order;
use Empik\Marketplace\Adapter\ConfigurationAdapter;
use Empik\Marketplace\PrestaShopContext;
use Empik\Marketplace\Repository\ProductRepository;
use Empik\Marketplace\Utils\IdentifierExtractor;
use Empik\Marketplace\Provider\Tax\ProductTaxRateProvider;
use OrderDetail;
use Context;
use Configuration;
use Product;
use ProductSupplier;
use TaxRulesGroup;
use TaxCalculator;
use Empik\Marketplace\Provider\Order\ProductRefResolver;
class OrderLinesProvider
{
@@ -25,17 +24,27 @@ class OrderLinesProvider
/** @var IdentifierExtractor */
protected $identifierExtractor;
/** @var ProductTaxRateProvider */
protected $productTaxRateProvider;
/** @var ProductRefResolver */
protected $productRefResolver;
/** @var Context */
protected $context;
public function __construct(
PrestaShopContext $prestaShopContext,
ProductRepository $productRepository,
IdentifierExtractor $identifierExtractor
IdentifierExtractor $identifierExtractor,
ProductTaxRateProvider $productTaxRateProvider,
ProductRefResolver $productRefResolver
) {
$this->prestaShopContext = $prestaShopContext;
$this->productRepository = $productRepository;
$this->identifierExtractor = $identifierExtractor;
$this->productTaxRateProvider = $productTaxRateProvider;
$this->productRefResolver = $productRefResolver;
$this->context = Context::getContext();
}
@@ -46,7 +55,7 @@ class OrderLinesProvider
foreach ($data['order_lines'] as $orderLine) {
$productData = $this->getProductByRef($orderLine);
$productData = $this->productRefResolver->getProductByRef($orderLine);
if (!$productData) {
continue;
@@ -56,31 +65,33 @@ class OrderLinesProvider
$obj->id_warehouse = 0;
$obj->id_shop = $this->context->shop->id;
$obj->product_name = !empty($orderLine['product_title']) ? $orderLine['product_title'] : '--';
$obj->product_reference = !empty($orderLine['product_sku']) ? $orderLine['product_sku'] : null;
$obj->product_quantity = (int)$orderLine['quantity'];
if ($productData) {
$obj->product_id = $productData['id_product'];
$obj->product_ean13 = $productData['ean13'];
$obj->product_reference = $productData['reference'];
$obj->product_id = $productData['id_product'];
$obj->product_ean13 = $productData['ean13'];
$obj->product_reference = $productData['reference'];
$obj->product_attribute_id = 0;
if ($this->prestaShopContext->is17()) {
$obj->product_isbn = isset($productData['isbn']) ? $productData['isbn'] : null;
$obj->product_upc = isset($productData['upc']) ? $productData['upc'] : null;
$obj->product_mpn = isset($productData['mpn']) ? $productData['mpn'] : null;
}
if (!empty($productData['id_product_attribute'])) {
$obj->product_attribute_id = $productData['id_product_attribute'];
}
$obj->product_price = $orderLine['price_unit'];
if ($this->prestaShopContext->is17()) {
$obj->product_isbn = isset($productData['isbn']) ? $productData['isbn'] : null;
$obj->product_upc = isset($productData['upc']) ? $productData['upc'] : null;
$obj->product_mpn = isset($productData['mpn']) ? $productData['mpn'] : null;
}
$obj->unit_price_tax_incl = $orderLine['price_unit'];
$obj->total_price_tax_incl = $obj->unit_price_tax_incl * $obj->product_quantity;
$obj->product_price = round($orderLine['total_price_tax_excl'], 6);
$taxRate = $this->getProductTaxRateByDefaultCountry($productData);
$taxRateMultiplier = (100 + $taxRate) / 100;
$obj->total_price_tax_incl = round($orderLine['total_price_tax_incl'], 6);
$obj->total_price_tax_excl = round($orderLine['total_price_tax_excl'], 6);
$obj->unit_price_tax_incl = round($orderLine['unit_price_tax_incl'], 6);
$obj->unit_price_tax_excl = round($orderLine['unit_price_tax_excl'], 6);
$specificPriceOutput = null;
$obj->total_price_tax_excl = round($obj->total_price_tax_incl / $taxRateMultiplier, 6);
$obj->unit_price_tax_excl = round($obj->unit_price_tax_incl / $taxRateMultiplier, 6);
$obj->original_product_price = Product::getPriceStatic(
$obj->product_id,
false,
@@ -94,7 +105,7 @@ class OrderLinesProvider
null,
null,
null,
$null,
$specificPriceOutput,
true,
true,
$this->context
@@ -104,7 +115,7 @@ class OrderLinesProvider
if ($productData['id_supplier'] > 0 && ($supplierPrice = ProductSupplier::getProductPrice((int) $productData['id_supplier'], $productData['id_product'], $productData['id_product_attribute'], true)) > 0) {
$obj->purchase_supplier_price = (float) $supplierPrice;
}
$obj->tax_rate = $taxRate;
$obj->id_tax_rules_group = $productData['id_tax_rules_group'];
$lines[] = $obj;
@@ -113,39 +124,4 @@ class OrderLinesProvider
return $lines;
}
/**
* @param $orderLine
* @return array|bool|false|object|null
*/
protected function getProductByRef($orderLine)
{
$skuType = Configuration::get(ConfigurationAdapter::CONF_SKU_TYPE);
if ($skuType === 'ID') {
$product = $this->productRepository->getProductOrCombinationById($orderLine['offer_sku']);
} elseif ($skuType === 'EAN') {
$product = $this->productRepository->getProductOrCombinationByEan($orderLine['offer_sku']);
} elseif ($skuType === 'REFERENCE') {
$product = $this->productRepository->getProductOrCombinationBySku($orderLine['offer_sku']);
}
return !empty($product) ? $product : false;
}
/**
* @param $productData
* @return float
*/
protected function getProductTaxRateByDefaultCountry($productData)
{
$taxRate = 0.00;
$defaultCountryId = Configuration::get('PS_COUNTRY_DEFAULT');
$taxRates = TaxRulesGroup::getAssociatedTaxRatesByIdCountry($defaultCountryId);
if (!empty($taxRates)) {
$taxRate = reset($taxRates);
}
return $taxRate;
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace Empik\Marketplace\Provider\Order;
use Configuration;
use Empik\Marketplace\Adapter\ConfigurationAdapter;
use Empik\Marketplace\Repository\ProductRepository;
class ProductRefResolver
{
/** @var ProductRepository */
protected $productRepository;
public function __construct(ProductRepository $productRepository)
{
$this->productRepository = $productRepository;
}
/**
* Resolve product (or combination) by external offer reference according to configured SKU type.
*
* @param array $orderLine
* @return array|object|false|null
*/
public function getProductByRef($orderLine)
{
$skuType = Configuration::get(ConfigurationAdapter::CONF_SKU_TYPE);
if ($skuType === 'ID') {
$product = $this->productRepository->getProductOrCombinationById($orderLine['offer_sku']);
} elseif ($skuType === 'EAN') {
$product = $this->productRepository->getProductOrCombinationByEan($orderLine['offer_sku']);
} elseif ($skuType === 'REFERENCE') {
$product = $this->productRepository->getProductOrCombinationBySku($orderLine['offer_sku']);
}
return !empty($product) ? $product : false;
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Empik\Marketplace\Provider\Tax;
use Configuration;
use TaxRulesGroup;
class ProductTaxRateProvider
{
/**
* Resolve product tax rate using product data and default country
*
* @param int $taxRulesGroupId
*
* @return float
*/
public function getTaxRateByTaxRuleGroup($taxRulesGroupId)
{
$taxRate = 0.00;
if (empty($taxRulesGroupId)) {
return $taxRate;
}
$defaultCountryId = (int) Configuration::get('PS_COUNTRY_DEFAULT');
if ($taxRulesGroupId > 0) {
// Fetch all rates for the country, then pick the one for the product's tax rule group
$taxRatesByGroup = TaxRulesGroup::getAssociatedTaxRatesByIdCountry($defaultCountryId);
if (is_array($taxRatesByGroup) && isset($taxRatesByGroup[$taxRulesGroupId])) {
$taxRate = (float) $taxRatesByGroup[$taxRulesGroupId];
} elseif (!empty($taxRatesByGroup)) {
// Fallback: first available rate for country
$first = reset($taxRatesByGroup);
if (is_numeric($first)) {
$taxRate = (float) $first;
}
}
} else {
// Fallback to default-country based rate
$taxRate = (float) $this->getProductTaxRateByDefaultCountry();
}
return $taxRate;
}
/**
* Fallback: get tax rate for default country when product group rate can't be resolved
*
* @return float
*/
public function getProductTaxRateByDefaultCountry()
{
$taxRate = 0.00;
$defaultCountryId = Configuration::get('PS_COUNTRY_DEFAULT');
$taxRates = TaxRulesGroup::getAssociatedTaxRatesByIdCountry($defaultCountryId);
if (!empty($taxRates)) {
$taxRate = reset($taxRates);
}
return (float) $taxRate;
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace Empik\Marketplace\Repository\Empik;
class EmpikOrderRepository implements EmpikOrderRepositoryInterface
{
/**
* {@inheritdoc}
*/
public function updateTrackingNumber($empikOrderId, $trackingNumber)
{
$empikOrder = new \EmpikOrder($empikOrderId);
// Validate the AR instance actually loaded
if (!\Validate::isLoadedObject($empikOrder)) {
throw new \RuntimeException(sprintf('EmpikOrder #%d not found', $empikOrderId));
}
// Optional: normalize/validate the tracking number here
$trackingNumber = trim($trackingNumber);
if ($trackingNumber === '') {
throw new \InvalidArgumentException('Tracking number cannot be empty');
}
$empikOrder->empik_tracking_number = $trackingNumber;
if ($empikOrder->update() === false) {
throw new \RuntimeException(sprintf('Failed to update tracking number for EmpikOrder #%d', $empikOrderId));
}
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace Empik\Marketplace\Repository\Empik;
interface EmpikOrderRepositoryInterface
{
/**
* Persist tracking number for a domain Empik order id.
*
* @param int $empikOrderId
* @param string $trackingNumber
* @throws \RuntimeException when an order isn't found or an update fails
* @return void
*/
public function updateTrackingNumber($empikOrderId, $trackingNumber);
}

View File

@@ -60,7 +60,7 @@ class ProductRepository
$sql->select('IFNULL(pa.wholesale_price, p.wholesale_price) AS wholesale_price');
$sql->select('IFNULL(pa.price, (ps.price + pa.price)) AS price');
$sql->select('pl.name, pl.description, pl.description_short, pl.link_rewrite, pl.meta_description, pl.meta_keywords, pl.meta_title, pl.available_now, pl.available_later');
$sql->select('pl.name, pl.description, pl.description_short, pl.link_rewrite, pl.meta_description, pl.meta_title, pl.available_now, pl.available_later');
$sql->select('pa.id_product_attribute');
$sql->select('m.name AS manufacturer_name');
$sql->select('sav.depends_on_stock, sav.quantity');

View File

@@ -93,4 +93,18 @@ class Utils
return self::$cacheEmployeeId;
}
/**
* @param $priceTaxIncl
* @param $vatRate
* @return float
*/
public static function calculateTaxExcl($priceTaxIncl, $vatRate, $roundPrecision = 6)
{
if ($vatRate == 0) {
return (float)$priceTaxIncl;
}
return round(($priceTaxIncl / (1 + ($vatRate / 100))), $roundPrecision);
}
}