811 lines
28 KiB
PHP
811 lines
28 KiB
PHP
<?php
|
|
|
|
namespace KupShop\DropshipBundle\Transfer;
|
|
|
|
use KupShop\AdminBundle\Util\ActivityLog;
|
|
use KupShop\DropshipBundle\Exception\TransferException;
|
|
use KupShop\DropshipBundle\TransferInterface;
|
|
use KupShop\KupShopBundle\Config;
|
|
use KupShop\KupShopBundle\Context\CountryContext;
|
|
use KupShop\KupShopBundle\Context\CurrencyContext;
|
|
use KupShop\KupShopBundle\Context\LanguageContext;
|
|
use KupShop\KupShopBundle\Context\VatContext;
|
|
use KupShop\KupShopBundle\Query\JsonOperator;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\KupShopBundle\Util\Functional\Mapping;
|
|
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
|
|
use KupShop\KupShopBundle\Util\StringUtil;
|
|
use KupShop\OrderingBundle\Util\Order\OrderInfo;
|
|
use KupShop\OrderingBundle\Util\Order\OrderItemInfo;
|
|
use KupShop\OrderingBundle\Util\Order\OrderUtil;
|
|
use Psr\Log\LoggerInterface;
|
|
use Query\Operator;
|
|
use Query\QueryBuilder;
|
|
|
|
class ExpandoTransfer extends AbstractTransfer implements TransferInterface
|
|
{
|
|
use \DatabaseCommunication;
|
|
|
|
protected static string $type = 'expando';
|
|
protected static string $name = 'Expando';
|
|
|
|
public const URL_FULFILLMENT = 'https://app.expan.do/api/v2/fulfillment';
|
|
|
|
protected array $existsOrdersCache = [];
|
|
|
|
protected OrderUtil $orderUtil;
|
|
|
|
protected LoggerInterface $logger;
|
|
|
|
private $sentryLogger;
|
|
private $currencyContext;
|
|
private $countryContext;
|
|
private $vatContext;
|
|
|
|
public function __construct(
|
|
SentryLogger $sentryLogger,
|
|
CurrencyContext $currencyContext,
|
|
CountryContext $countryContext,
|
|
VatContext $vatContext,
|
|
LoggerInterface $logger,
|
|
) {
|
|
$this->sentryLogger = $sentryLogger;
|
|
$this->currencyContext = $currencyContext;
|
|
$this->countryContext = $countryContext;
|
|
$this->vatContext = $vatContext;
|
|
$this->logger = $logger;
|
|
}
|
|
|
|
/**
|
|
* @required
|
|
*/
|
|
public function setOrderUtil(OrderUtil $orderUtil): void
|
|
{
|
|
$this->orderUtil = $orderUtil;
|
|
}
|
|
|
|
public function in(array $config): void
|
|
{
|
|
$cfg = Config::get();
|
|
|
|
if (!($xml = $this->loadXML())) {
|
|
return;
|
|
}
|
|
|
|
$currencies = $this->currencyContext->getAll();
|
|
$update = $config['update'] ?? null;
|
|
|
|
$orders = [];
|
|
foreach ($xml->order as $order) {
|
|
$orders[] = $order;
|
|
}
|
|
|
|
$this->loadExistsOrdersCache($orders);
|
|
|
|
foreach (array_reverse($orders) as $order) {
|
|
if (!$this->isDropshipOrderValidToImport($order)) {
|
|
// objednavka uz existuje
|
|
if ($update && ($orderUpdateObj = $this->getOrderByExternalId((string) $order->orderId))) {
|
|
$this->updateDropshipOrder($orderUpdateObj, $order);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
$currencyInfo = $this->getCurrencyInfo(
|
|
$this->getCurrency($order)
|
|
);
|
|
|
|
$currencyCode = $this->isPriceConvertionEnabled() ? $this->currencyContext->getDefaultId() : $currencyInfo->getCurrencyCode();
|
|
|
|
$status = 0;
|
|
$statusStorno = 0;
|
|
|
|
if (!$this->isOrderStatusValidToImport($order)) {
|
|
continue;
|
|
}
|
|
|
|
$statusXML = strtolower((string) $order->orderStatus);
|
|
// import cancelled order
|
|
if ($statusXML === 'canceled') {
|
|
$statuses = array_filter(array_keys($cfg['Order']['Status']['global']), fn ($x) => $x < 100);
|
|
$status = end($statuses);
|
|
$statusStorno = 1;
|
|
}
|
|
|
|
$totalPrice = (string) $order->totalPrice;
|
|
$totalPrice = toDecimal($totalPrice);
|
|
|
|
$deliveryType = $this->getDeliveryType($order);
|
|
$delivery_type = $order->shippingMethod.' - '.$order->paymentMethod;
|
|
$id_delivery = null;
|
|
if ($deliveryType) {
|
|
$delivery_type = $deliveryType->name;
|
|
$id_delivery = $deliveryType->id;
|
|
}
|
|
|
|
$customer = $order->customer;
|
|
|
|
$userData = $this->getUserData($order);
|
|
|
|
$country = $userData['invoice_country'];
|
|
|
|
// Dropship Expando flag
|
|
$flags = ['DSE'];
|
|
// pokud je OSS aktivni, tak nastavit flag
|
|
if ($this->vatContext->isCountryOssActive($country) && empty($userData['invoice_dic'])) {
|
|
$flags[] = 'OSS';
|
|
}
|
|
|
|
$data = [
|
|
'currency' => $currencyCode,
|
|
'currency_rate' => $currencies[$currencyCode]->getRate(),
|
|
'date_created' => $this->getOrderDateCreated($order)->format('Y-m-d H:i:s'),
|
|
'status' => $status,
|
|
'status_storno' => $statusStorno,
|
|
'total_price' => $totalPrice,
|
|
|
|
'id_delivery' => $id_delivery,
|
|
'delivery_type' => $delivery_type,
|
|
|
|
'flags' => implode(',', $flags),
|
|
|
|
'note_invoice' => (string) $order->orderId,
|
|
|
|
'note_admin' => json_encode(
|
|
[
|
|
'expando' => [
|
|
'orderId' => (string) $order->orderId,
|
|
'marketplace' => (string) $order->marketplace,
|
|
'country' => $customer->address->country->__toString(),
|
|
],
|
|
]
|
|
),
|
|
|
|
'source' => OrderInfo::ORDER_SOURCE_DROPSHIP,
|
|
];
|
|
|
|
// nastavim jazyk objednavky
|
|
if (findModule(\Modules::TRANSLATIONS)) {
|
|
$marketplaceSettings = $this->getMarketplaceConfiguration($order)['settings'] ?? [];
|
|
$data['id_language'] = !empty($marketplaceSettings['id_language']) ? $marketplaceSettings['id_language'] : Contexts::get(LanguageContext::class)->getDefaultId();
|
|
}
|
|
|
|
$data = array_merge(
|
|
$data,
|
|
$userData
|
|
);
|
|
|
|
$orderObj = sqlGetConnection()->transactional(function () use ($order, $data, $currencyInfo) {
|
|
$orderObj = $this->createDropshipOrder($order, $data);
|
|
$orderObj->logHistory('[Dropshipment] Marketplace: '.$order->marketplace);
|
|
|
|
// Vytvorit itemy objednavky
|
|
$lastItemTax = null;
|
|
foreach ($order->items->item as $item) {
|
|
if (!$this->isOrderItemValidToImport($item)) {
|
|
continue;
|
|
}
|
|
|
|
[$productID, $variationID] = $this->getProductByItem($item);
|
|
|
|
$itemTax = $this->getItemTax($item);
|
|
// pokud je zapnuta konverze do defaultni meny, tak nastavim i defaultni DPH
|
|
if ($this->isPriceConvertionEnabled()) {
|
|
$itemTax = getAdminVat()['value'];
|
|
}
|
|
|
|
$itemPrice = $this->convertPrice($this->getItemPiecePrice($item), $currencyInfo)->removeVat(toDecimal($itemTax));
|
|
$itemQuantity = toDecimal((string) $item->itemQuantity);
|
|
|
|
if ($variationID) {
|
|
$product = new \Variation($productID, $variationID);
|
|
} else {
|
|
$product = new \Product($productID);
|
|
}
|
|
|
|
// sell product
|
|
if ($productID) {
|
|
$product->createFromDB($productID);
|
|
$product->sell($variationID, $itemQuantity->asInteger());
|
|
}
|
|
|
|
$title = $product->title;
|
|
if (!empty($title) && $variationID) {
|
|
$title .= ' ('.$product->variationTitle.')';
|
|
}
|
|
|
|
$modifiedItem = $this->modifyItem([
|
|
'id_order' => $orderObj->id,
|
|
'id_product' => $productID,
|
|
'id_variation' => $variationID,
|
|
'pieces' => $itemQuantity,
|
|
'pieces_reserved' => $itemQuantity,
|
|
'piece_price' => $itemPrice,
|
|
'total_price' => $itemPrice->mul($itemQuantity),
|
|
'descr' => (!empty($title)) ? $title : (string) $item->itemName,
|
|
'tax' => $itemTax,
|
|
'note' => json_encode(['item_type' => OrderItemInfo::TYPE_PRODUCT]),
|
|
], $item);
|
|
|
|
$this->insertSQL('order_items', $modifiedItem);
|
|
$modifiedItem['id'] = sqlInsertId();
|
|
|
|
$this->itemCreatedEvent(
|
|
product: $product,
|
|
idVariation: (int) $variationID,
|
|
piecePrice: $itemPrice,
|
|
pieces: $itemQuantity->asInteger(),
|
|
data: [
|
|
'row' => $modifiedItem,
|
|
'items_table' => 'order_items',
|
|
],
|
|
order: $orderObj
|
|
);
|
|
|
|
$lastItemTax = $itemTax;
|
|
}
|
|
|
|
// Doprava a platba
|
|
$paymentPrice = toDecimal((string) $order->paymentPrice);
|
|
$shippingPrice = toDecimal((string) $order->shippingPrice);
|
|
$shippingTaxValue = toDecimal((string) $order->price->delivery->tax);
|
|
|
|
$shippingTax = $this->calculateItemTax($shippingPrice, $shippingTaxValue)->printFloatValue(-2);
|
|
if (!$shippingTax && $lastItemTax) {
|
|
$shippingTax = $lastItemTax;
|
|
}
|
|
|
|
// pokud je zapnuta konverze do defaultni meny, tak nastavim i defaultni DPH
|
|
if ($this->isPriceConvertionEnabled()) {
|
|
$shippingTax = toDecimal(getAdminVat()['value']);
|
|
}
|
|
|
|
$deliveryItemPrice = $shippingPrice->add($paymentPrice);
|
|
$deliveryItemPrice = $this->convertPrice($deliveryItemPrice, $currencyInfo)->removeVat($shippingTax);
|
|
|
|
$this->insertDeliveryItem(
|
|
[
|
|
'id_order' => $orderObj->id,
|
|
'id_product' => null,
|
|
'id_variation' => null,
|
|
'pieces' => 1,
|
|
'pieces_reserved' => 1,
|
|
'piece_price' => $deliveryItemPrice,
|
|
'total_price' => $deliveryItemPrice,
|
|
'descr' => 'Doprava a platba',
|
|
'tax' => $shippingTax,
|
|
'note' => json_encode(['item_type' => OrderItemInfo::TYPE_DELIVERY]),
|
|
]
|
|
);
|
|
|
|
$this->contextManager->activateOrder($orderObj, function () use ($orderObj) {
|
|
$orderObj->recalculate(round: false);
|
|
});
|
|
|
|
return $orderObj;
|
|
});
|
|
|
|
$this->modifyInsertedOrder($orderObj, $order);
|
|
}
|
|
}
|
|
|
|
public function out(array $config): void
|
|
{
|
|
if (isDevelopment()) {
|
|
return;
|
|
}
|
|
|
|
if (!($accessToken = $config['api_key'] ?? null)) {
|
|
$this->addActivityLog(
|
|
'V nastavení dropshipmentu chybí API klíč!',
|
|
$config
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
$qb = $this->getBaseOutQueryBuilder();
|
|
|
|
foreach ($qb->execute() as $item) {
|
|
$order = new \Order();
|
|
$order->createFromDB($item['id']);
|
|
|
|
$balikobotData = json_decode($item['balikobot_data'] ?? '', true) ?? [];
|
|
|
|
$expandoData = $order->getData('expando');
|
|
|
|
[$carrier, $carrierName] = $this->getOutCarrier($order);
|
|
|
|
$params = [
|
|
'marketplaceOrderId' => $expandoData['orderId'],
|
|
'marketplace' => $this->getOutMarketplace($expandoData['marketplace'] ?? ''),
|
|
'shipDate' => $order->date_handle ? $order->date_handle->format('c') : (new \DateTime())->format('c'),
|
|
'status' => 'Shipped',
|
|
'carrier' => $carrier,
|
|
'carrierName' => $carrierName,
|
|
];
|
|
|
|
if ($order->package_id) {
|
|
$params['trackingNumber'] = $order->package_id;
|
|
$params['trackingUrl'] = $this->getTrackingUrl($order, $balikobotData);
|
|
}
|
|
|
|
try {
|
|
$this->sendFulfillment($accessToken, $params);
|
|
|
|
$expandoData['fulfillmentSent'] = true;
|
|
|
|
$order->setData('expando', $expandoData);
|
|
$order->logHistory('Objednávka v Expandu byla aktualizována na vyřízenou');
|
|
} catch (TransferException $e) {
|
|
addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_SYNC, $e->getMessage(), $params);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function loadExistsOrdersCache(array $orders): void
|
|
{
|
|
$ids = array_map(function ($x) {
|
|
return (string) $x->orderId;
|
|
}, $orders);
|
|
|
|
$qb = sqlQueryBuilder()
|
|
->select('id_order AS id, id_external AS expandoId')
|
|
->from('order_dropshipment')
|
|
->sendToMaster()
|
|
->where(Operator::andX(
|
|
Operator::inStringArray($ids, 'id_external'),
|
|
Operator::equals([
|
|
'id_dropshipment' => $this->dropshipment['id'],
|
|
])
|
|
));
|
|
|
|
$this->existsOrdersCache = Mapping::mapKeys($qb->execute()->fetchAllAssociative(), function ($k, $v) {
|
|
return [$v['expandoId'], $v['id']];
|
|
});
|
|
}
|
|
|
|
protected function isDropshipOrderValidToImport(\SimpleXMLElement $xml): bool
|
|
{
|
|
return parent::isDropshipOrderValidToImport($xml) && $this->isValidToImport($xml);
|
|
}
|
|
|
|
protected function isOrderItemValidToImport(\SimpleXMLElement $item): bool
|
|
{
|
|
return true;
|
|
}
|
|
|
|
protected function isValidToImport(\SimpleXMLElement $order): bool
|
|
{
|
|
// pokud objednavka uz existuje, tak vracim false, protoze ji nechci importovat znovu
|
|
if ($this->existsOrdersCache[(string) $order->orderId] ?? false) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected function getExternalData(\SimpleXMLElement $xml): array
|
|
{
|
|
return [
|
|
(string) $xml->orderId,
|
|
[
|
|
'marketplace' => (string) $xml->marketplace,
|
|
'country' => $xml->customer->address->country->__toString(),
|
|
],
|
|
];
|
|
}
|
|
|
|
protected function getOrderDateCreated(\SimpleXMLElement $order): \DateTimeInterface
|
|
{
|
|
try {
|
|
$date = new \DateTime((string) $order->purchaseDate, new \DateTimeZone('UTC'));
|
|
$date->setTimezone(new \DateTimeZone('Europe/Prague'));
|
|
} catch (\Exception) {
|
|
$date = new \DateTime();
|
|
}
|
|
|
|
return $date;
|
|
}
|
|
|
|
protected function getCurrency(\SimpleXMLElement $order): ?string
|
|
{
|
|
$currencyCode = (string) $order->currencyCode;
|
|
if (empty($currencyCode)) {
|
|
$currencyCode = 'EUR';
|
|
}
|
|
|
|
return $currencyCode;
|
|
}
|
|
|
|
protected function getDeliveryType(\SimpleXMLElement $orderItem): ?\DeliveryType
|
|
{
|
|
return $this->getDeliveryTypeByConfiguration($orderItem);
|
|
}
|
|
|
|
protected function isOrderStatusValidToImport(\SimpleXMLElement $order): bool
|
|
{
|
|
$statusXML = strtolower((string) $order->orderStatus);
|
|
|
|
$importStatuses = $this->getConfiguration()['import_statuses'] ?? [];
|
|
|
|
// default behaviour if config is not set
|
|
if (empty($importStatuses)) {
|
|
return !in_array($statusXML, $this->getIgnoredStatuses());
|
|
}
|
|
|
|
return in_array($statusXML, $importStatuses);
|
|
}
|
|
|
|
protected function getIgnoredStatuses(): array
|
|
{
|
|
return [
|
|
'canceled',
|
|
'pending',
|
|
'shipped',
|
|
];
|
|
}
|
|
|
|
protected function getItemPiecePrice(\SimpleXMLElement $item): \Decimal
|
|
{
|
|
return toDecimal((string) $item->itemPrice);
|
|
}
|
|
|
|
protected function getItemTax(\SimpleXMLElement $item): float
|
|
{
|
|
$line = $item->lineItemPrice;
|
|
|
|
$priceWithTax = toDecimal((string) $line->withTax);
|
|
$taxValue = toDecimal((string) $line->tax);
|
|
|
|
return $this->calculateItemTax($priceWithTax, $taxValue)->printFloatValue(-2);
|
|
}
|
|
|
|
protected function calculateItemTax(\Decimal $priceWithTax, \Decimal $taxValue): \Decimal
|
|
{
|
|
if ($taxValue->isZero()) {
|
|
return toDecimal(0);
|
|
}
|
|
|
|
$priceWithoutTax = $priceWithTax->sub($taxValue);
|
|
|
|
return $priceWithTax->div($priceWithoutTax)->mul(\DecimalConstants::hundred())->sub(\DecimalConstants::hundred())->round();
|
|
}
|
|
|
|
protected function getProductByItem(\SimpleXMLElement $item): array
|
|
{
|
|
[$productId, $variationId] = $this->findProduct((string) $item->itemId);
|
|
|
|
if (empty($productId) && empty($variationId)) {
|
|
return $this->getProductByCode((string) $item->itemId, null);
|
|
}
|
|
|
|
return [$productId, $variationId];
|
|
}
|
|
|
|
protected function getUserData(\SimpleXMLElement $order): array
|
|
{
|
|
$customer = $order->customer;
|
|
|
|
$country = $customer->address->country->__toString();
|
|
// validate country
|
|
if (!isset($this->countryContext->getAll()[$country])) {
|
|
$country = '';
|
|
}
|
|
|
|
return [
|
|
'invoice_name' => (string) $customer->firstname,
|
|
'invoice_surname' => (string) $customer->surname,
|
|
'invoice_email' => (string) $customer->email,
|
|
'invoice_phone' => (string) $customer->phone,
|
|
'invoice_street' => $this->prepareAddressStreet($customer->address),
|
|
'invoice_city' => (string) $customer->address->city,
|
|
'invoice_zip' => (string) $customer->address->zip,
|
|
'invoice_country' => $country,
|
|
'invoice_custom_address' => (string) $customer->address->address3,
|
|
'invoice_state' => (string) $customer->address->stateOrRegion,
|
|
'invoice_dic' => (string) $customer->taxId,
|
|
|
|
'delivery_name' => (string) $customer->firstname,
|
|
'delivery_surname' => (string) $customer->surname,
|
|
'delivery_street' => $this->prepareAddressStreet($customer->address),
|
|
'delivery_city' => (string) $customer->address->city,
|
|
'delivery_zip' => (string) $customer->address->zip,
|
|
'delivery_country' => $country,
|
|
'delivery_custom_address' => (string) $customer->address->address3,
|
|
'delivery_state' => (string) $customer->address->stateOrRegion,
|
|
];
|
|
}
|
|
|
|
protected function findProduct(string $itemId): array
|
|
{
|
|
$codes = explode('_', $itemId);
|
|
$productCode = $codes[0] ?? null;
|
|
$variationCode = $codes[1] ?? null;
|
|
|
|
if ($variationCode) {
|
|
$variation = sqlQueryBuilder()
|
|
->select('id, id_product')
|
|
->from('products_variations')
|
|
->where(Operator::equals(['code' => $variationCode]))
|
|
->execute()->fetch();
|
|
|
|
if ($variation) {
|
|
return [(int) $variation['id_product'], (int) $variation['id']];
|
|
}
|
|
|
|
$variation = sqlQueryBuilder()
|
|
->select('id, id_product')
|
|
->from('products_variations')
|
|
->where(Operator::equals(['id' => $variationCode]))
|
|
->execute()->fetch();
|
|
|
|
if ($variation) {
|
|
return [(int) $variation['id_product'], (int) $variation['id']];
|
|
}
|
|
}
|
|
|
|
if ($productCode) {
|
|
$productId = sqlQueryBuilder()
|
|
->select('id')
|
|
->from('products')
|
|
->where(Operator::equals(['code' => $productCode]))
|
|
->execute()->fetchColumn();
|
|
|
|
if ($productId) {
|
|
return [(int) $productId, null];
|
|
}
|
|
|
|
$productId = sqlQueryBuilder()
|
|
->select('id')
|
|
->from('products')
|
|
->where(Operator::equals(['id' => $productCode]))
|
|
->execute()->fetchColumn();
|
|
|
|
if ($productId) {
|
|
return [(int) $productId, null];
|
|
}
|
|
}
|
|
|
|
return [null, null];
|
|
}
|
|
|
|
protected function insertDeliveryItem(array $item): void
|
|
{
|
|
$this->insertSQL('order_items', $item);
|
|
}
|
|
|
|
protected function getTrackingUrl(\Order $order, array $balikobotData): ?string
|
|
{
|
|
if (!($trackingUrl = ($balikobotData['response'][0]['track_url'] ?? false))) {
|
|
if ($deliveryType = $order->getDeliveryType()) {
|
|
if ($delivery = $deliveryType->getDelivery()) {
|
|
$trackingUrl = $delivery->getTrackAndTraceLink($order->package_id, $order);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!$trackingUrl) {
|
|
return null;
|
|
}
|
|
|
|
return $trackingUrl;
|
|
}
|
|
|
|
protected function getOutCarrier(\Order $order): array
|
|
{
|
|
$marketplace = $order->getData('expando')['marketplace'] ?? null;
|
|
|
|
if ($deliveryType = $order->getDeliveryType()) {
|
|
if ($delivery = $deliveryType->getDelivery()) {
|
|
if ($delivery instanceof \DHL) {
|
|
return ['DHL', null];
|
|
} elseif ($delivery instanceof \DPD) {
|
|
return ['DPD', null];
|
|
} elseif ($delivery instanceof \GLS) {
|
|
return ['GLS', null];
|
|
} elseif ($delivery instanceof \PPL) {
|
|
// Amazon nezna PPL, takze to musim poslat jako "Other"
|
|
if (strpos(mb_strtolower($marketplace), 'amazon') !== false) {
|
|
return ['Other', 'PPL'];
|
|
}
|
|
|
|
return ['PPL', null];
|
|
} elseif ($delivery instanceof \Fedex) {
|
|
return ['FedEx', null];
|
|
} elseif ($delivery instanceof \UPS) {
|
|
return ['UPS', 'Standard'];
|
|
}
|
|
}
|
|
}
|
|
|
|
return ['Other', $order->getDeliveryType()->delivery ?? ''];
|
|
}
|
|
|
|
private function prepareAddressStreet(\SimpleXMLElement $address): string
|
|
{
|
|
$street = [(string) $address->address1, (string) $address->address2];
|
|
$street = implode(', ', array_filter($street));
|
|
|
|
return $street;
|
|
}
|
|
|
|
private function getOutMarketplace(string $marketplace): string
|
|
{
|
|
return preg_replace('/\s+/', '_', mb_strtolower($marketplace));
|
|
}
|
|
|
|
private function sendFulfillment(string $accessToken, array $params): void
|
|
{
|
|
$ch = curl_init();
|
|
$this->logger->notice(sprintf('EXPANDO REQUEST: ', static::getType()),
|
|
[
|
|
'Data' => $params,
|
|
'Type' => static::getType(),
|
|
]);
|
|
curl_setopt($ch, CURLOPT_URL, self::URL_FULFILLMENT);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type:application/json', 'Authorization: Bearer '.$accessToken]);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params));
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
$result = curl_exec($ch);
|
|
$this->logger->notice(sprintf('EXPANDO RESPONSE: ', static::getType()),
|
|
[
|
|
'data' => (array) $result,
|
|
'Type' => static::getType(),
|
|
]);
|
|
curl_close($ch);
|
|
|
|
$result = json_decode($result, true);
|
|
|
|
if ($result['message'] ?? null) {
|
|
if (StringUtil::startsWith($result['message'], 'ValidationError')) {
|
|
throw new TransferException(
|
|
sprintf('Send fulfillment failed with message "%s"', $result['message'])
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function getMarketplaceConfiguration(\SimpleXMLElement $order): array
|
|
{
|
|
// konfigurace dropshipmentu
|
|
$configuration = $this->getConfiguration();
|
|
|
|
// budu hledat konfiguraci podle marketplacu
|
|
$orderMarketplace = $order->marketplace->__toString();
|
|
|
|
$marketplaceConfiguration = null;
|
|
$default = null;
|
|
foreach ($configuration['marketplaces'] ?? [] as $marketplace) {
|
|
// podle nazvu zkusim najit konfiguraci pro dany marketplace
|
|
if (StringUtil::slugify($marketplace['name']) === StringUtil::slugify($orderMarketplace)) {
|
|
$marketplaceConfiguration = $marketplace;
|
|
break;
|
|
}
|
|
|
|
if (empty($marketplace['name'])) {
|
|
$default = $marketplace;
|
|
}
|
|
}
|
|
|
|
if (!$marketplaceConfiguration && $default) {
|
|
$marketplaceConfiguration = $default;
|
|
}
|
|
|
|
return $marketplaceConfiguration ?: [];
|
|
}
|
|
|
|
protected function getDeliveryTypeByConfiguration(\SimpleXMLElement $order): ?\DeliveryType
|
|
{
|
|
$config = $this->getMarketplaceConfiguration($order);
|
|
|
|
$deliveryId = null;
|
|
// najdu ID dopravy
|
|
$country = $order->customer->address->country->__toString();
|
|
foreach ($config['deliveries'] ?? [] as $item) {
|
|
if (empty($item['country']) || $item['country'] == $country) {
|
|
$deliveryId = (int) $item['id_delivery'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// nactu ID platby
|
|
$paymentId = empty($config['payments']['default']) ? null : (int) $config['payments']['default'];
|
|
|
|
return $this->findDeliveryType($deliveryId, $paymentId);
|
|
}
|
|
|
|
protected function updateDropshipOrder(\Order $order, \SimpleXMLElement $xml): void
|
|
{
|
|
$updateStatuses = $this->configuration['update'] ?? [];
|
|
|
|
$currentStatusXml = strtolower((string) $xml->orderStatus);
|
|
// pokud je povoleny update stavu
|
|
if (!in_array($currentStatusXml, $updateStatuses)) {
|
|
return;
|
|
}
|
|
|
|
// storno objednavky
|
|
if ($currentStatusXml === 'canceled') {
|
|
if (!$order->status_storno) {
|
|
$order->storno(false, '[Expando] Storno objednávky z Expanda', false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if ($currentStatusXml !== 'shipped') {
|
|
return;
|
|
}
|
|
|
|
// update stavu po expedici
|
|
$dic = (string) $xml->customer->taxId;
|
|
if ($dic != $order->invoice_dic) {
|
|
$this->updateSQL('orders', ['invoice_dic' => $dic], ['id' => $order->id]);
|
|
if ($this->vatContext->isCountryOssActive($order->invoice_country)) {
|
|
if (empty($dic)) {
|
|
$this->orderUtil->addFlag($order, 'OSS');
|
|
} else {
|
|
$this->orderUtil->removeFlag($order, 'OSS');
|
|
$this->orderUtil->removeVat($order, 0, false, true); // $keepFinalPrice = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function getBaseOutQueryBuilder(): QueryBuilder
|
|
{
|
|
$qb = sqlQueryBuilder()
|
|
->select('o.id')
|
|
->from('orders', 'o')
|
|
->where(
|
|
Operator::not(
|
|
Operator::equalsNullable([JsonOperator::value('o.note_admin', 'expando.orderId') => null])
|
|
)
|
|
)
|
|
->andWhere(
|
|
Operator::equalsNullable([JsonOperator::value('o.note_admin', 'expando.fulfillmentSent') => null])
|
|
)
|
|
->andWhere('o.status_storno = 0')
|
|
->andWhere(Operator::inIntArray(getStatuses('handled'), 'o.status'))
|
|
->andWhere('DATEDIFF(NOW(), o.date_handle) <= 30') // ne starsi nez 30 dni
|
|
->groupBy('o.id');
|
|
|
|
if (findModule(\Modules::BALIKONOS, 'provider') === 'balikobot') {
|
|
$qb->addSelect('b.data as balikobot_data')
|
|
->leftJoin('o', 'balikonos', 'b', 'b.id_order = o.id');
|
|
}
|
|
|
|
return $qb;
|
|
}
|
|
|
|
public function prepareConfigurationData(array $data): array
|
|
{
|
|
foreach ($data['marketplaces'] ?? [] as $key => $marketplace) {
|
|
// zpracovani mapovani doprav
|
|
foreach ($marketplace['deliveries'] ?? [] as $dKey => $delivery) {
|
|
$delivery = array_filter($delivery);
|
|
|
|
if (!empty($delivery['delete'])) {
|
|
unset($data['marketplaces'][$key]['deliveries'][$dKey]);
|
|
continue;
|
|
}
|
|
|
|
if ($dKey <= 0) {
|
|
if (!empty($delivery['id_delivery'])) {
|
|
$data['marketplaces'][$key]['deliveries'][] = $delivery;
|
|
}
|
|
|
|
unset($data['marketplaces'][$key]['deliveries'][$dKey]);
|
|
}
|
|
}
|
|
}
|
|
|
|
$data['marketplaces'] = array_values($data['marketplaces'] ?? []);
|
|
|
|
return $data;
|
|
}
|
|
}
|