989 lines
37 KiB
PHP
989 lines
37 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace External\FlexiBeeBundle\Synchronizers;
|
|
|
|
use AbraFlexi\ObjednavkaPrijata;
|
|
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
|
use KupShop\I18nBundle\Entity\Currency;
|
|
use KupShop\KupShopBundle\Context\CurrencyContext;
|
|
use KupShop\KupShopBundle\Util\Contexts;
|
|
use KupShop\KupShopBundle\Util\Database\QueryHint;
|
|
use KupShop\KupShopBundle\Util\Functional\Mapping;
|
|
use KupShop\OrderingBundle\Entity\Order\OrderItem;
|
|
use KupShop\OrderingBundle\Util\Order\OrderInfo;
|
|
use Query\Operator;
|
|
use Query\QueryBuilder;
|
|
|
|
class OrderSynchronizer extends BaseSynchronizer
|
|
{
|
|
protected static string $type = 'order';
|
|
protected static ?string $evidenceClass = ObjednavkaPrijata::class;
|
|
|
|
/** @required */
|
|
public UserSynchronizer $userSynchronizer;
|
|
|
|
protected array $recentlyUpdatedOrders = [];
|
|
|
|
public function syncSingleItem(int $id, ?int $flexiId = null): void
|
|
{
|
|
if ($flexiId = $this->flexiBeeUtil->getFlexiId(static::getType(), $id)) {
|
|
$items = $this->preprocessChangedItems([$this->flexiBeeApi->getOrder($flexiId)]);
|
|
$this->processItem(reset($items));
|
|
}
|
|
}
|
|
|
|
public function syncSingleItemToFlexi($item): bool
|
|
{
|
|
try {
|
|
$order = QueryHint::withRouteToMaster(fn () => \Order::get((int) $item['id']));
|
|
|
|
$orderIsPaidAtStart = $order->isPaid(true);
|
|
|
|
try {
|
|
$data = $this->getOrderData($order, $item['id_flexi'] ? (int) $item['id_flexi'] : null);
|
|
$flexiId = $this->flexiBeeApi->createOrUpdateOrder($data);
|
|
$this->logUpdate($data);
|
|
} catch (FlexiBeeException $e) {
|
|
$errorMessage = 'Objednávku se nepodařilo zapsat do FlexiBee';
|
|
if ($item['id_flexi']) {
|
|
$errorMessage = 'Objednávku se ve FlexiBee nepodařilo aktualizovat';
|
|
}
|
|
|
|
// zaznamenam neuspesnou sync
|
|
if ($errorStatus = $this->configuration->getOrderConfig()['error_status']) {
|
|
$attemptCount = $order->getData('flexiSyncAttemptCount');
|
|
$attemptCount = $attemptCount + 1;
|
|
$order->setData('flexiSyncAttemptCount', $attemptCount);
|
|
|
|
if ($attemptCount >= 10) {
|
|
$order->logHistory('[FlexiBee] Došlo k opakovanému problému se synchronizací objednávky, zkontrolujte Activity log');
|
|
$order->changeStatus($errorStatus);
|
|
}
|
|
}
|
|
|
|
// zalogovani do ActivityLogu
|
|
$this->logger->exception(
|
|
$e,
|
|
sprintf('[FlexiBee] Objednávka "%s": %s', $order->order_no, $errorMessage),
|
|
[
|
|
'orderId' => $order->id,
|
|
'flexiBeeError' => $e->getMessage(),
|
|
]
|
|
);
|
|
|
|
// nastavim orderUpdated na 0, protoze se aktualizace nepovedla, tak aby to furt nezkouselo znova
|
|
if ($item['id_flexi'] && !$errorStatus) {
|
|
$this->flexiBeeUtil->setFlexiOrderData(
|
|
(int) $order->id,
|
|
'orderUpdated',
|
|
0
|
|
);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Pokud nemam flexiId, tak pravdepodobne nastala nejaka chyba, takze preskakuju
|
|
if (!$flexiId) {
|
|
return false;
|
|
}
|
|
|
|
// objednavka jeste nema mapping a tudiz jsme ji ted poprve zapsali do FlexiBee
|
|
if (!$this->flexiBeeUtil->getMapping(OrderSynchronizer::getType(), $flexiId)) {
|
|
$this->flexiBeeUtil->createMapping(OrderSynchronizer::getType(), $flexiId, (int) $order->id);
|
|
$order->logHistory('[FlexiBee] Objednávka byla nahrána do FlexiBee; ID: '.$flexiId);
|
|
|
|
return true;
|
|
}
|
|
|
|
// pokud objednavka uz ma mapping, tak to musi znamenat, ze jsme provadeli update objednavky
|
|
// takze pouze k objednavce zaloguju, ze byla objednavka ve FlexiBee aktualizovana
|
|
$order->logHistory('[FlexiBee] Objednávka byla ve FlexiBee zaktualizována');
|
|
// aktualizuju orderUpdated stav
|
|
// skoro vzdy nastavuju na 0, ale pokud by se stalo, ze objednavka nebyla zaplacena a zaplaceni bylo provedeno v prubehu
|
|
// odesilani objednavky do Flexi, tak nastavim na 1, aby se v dalsim cyklu synchronizace poslal update objednavky do Flexi
|
|
$this->flexiBeeUtil->setFlexiOrderData(
|
|
(int) $order->id,
|
|
'orderUpdated',
|
|
!$orderIsPaidAtStart && $order->isPaid(true) ? 1 : 0
|
|
);
|
|
// updatovanout objednavku si ulozim, abych ji v sync z flexi do shopu mohl preskocit
|
|
$this->recentlyUpdatedOrders[] = $order->id;
|
|
|
|
return true;
|
|
} catch (\Throwable $e) {
|
|
$this->logger->exception($e, '[FlexiBee] Během synchronizace objednávek se vyskytla chyba!', [
|
|
'orderId' => $item['id'],
|
|
]);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected function process(?int $lastSyncTime = null): void
|
|
{
|
|
// Nahrani objednavek do FlexiBee
|
|
$this->processToFlexiBee();
|
|
// Zpracovani objednavek z FlexiBee
|
|
parent::process($lastSyncTime);
|
|
}
|
|
|
|
protected function processItem(array $item): void
|
|
{
|
|
// neznama objednavka, takze s ni nic nedelam
|
|
if (!($orderId = QueryHint::withRouteToMaster(fn () => $this->flexiBeeUtil->getMapping(static::getType(), $item['id'])))) {
|
|
return;
|
|
}
|
|
|
|
$order = QueryHint::withRouteToMaster(fn () => \Order::get($orderId));
|
|
|
|
// pokud je objednavka stornovana, tak ji neaktualizuju
|
|
if ($order->status_storno == 1) {
|
|
return;
|
|
}
|
|
|
|
// stornovani objednavky
|
|
if ($item['stavUzivK'] === 'stavDoklObch.storno') {
|
|
if ($order->status_storno == 0) {
|
|
$order->logHistory('[FlexiBee] Objednávka byla stornována ve FlexiBee');
|
|
$order->storno(false); // ORDER_STORNO event nastavi orderUpdated na 1
|
|
// nastavim orderUpdated na 0, aby synchronizace to nezkousela znovu stornovat ve FlexiBee
|
|
$this->flexiBeeUtil->setFlexiOrderData((int) $order->id, 'orderUpdated', 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// pokud mam zapnutou oboustranou synchronizaci, tak sesynchronizuju zmeny na objednavce
|
|
if (($this->configuration->getOrderConfig()['duplex_sync'] ?? false) === 'Y') {
|
|
$this->processOrderChanges($order, $item);
|
|
}
|
|
|
|
// ulozit cislo baliku z FlexiBee k objednavce
|
|
$this->saveOrderPackageNumber($order, $item);
|
|
|
|
$statuses = $this->configuration->getOrderStatusesConfig();
|
|
// zmena stavu podle konfigurace v administraci
|
|
if ($status = ($statuses[$item['stavUzivK']] ?? false)) {
|
|
if (!empty($status['status']) || !empty($status['order_message'])) {
|
|
$this->changeOrderStatus(
|
|
$order,
|
|
$item['stavUzivK'],
|
|
!empty($status['status']) ? (int) $status['status'] : null,
|
|
!empty($status['order_message']) ? $this->getOrderMessageNameById((int) $status['order_message']) : null
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function processToFlexiBee(): void
|
|
{
|
|
$this->processOrdersToFlexiBee();
|
|
}
|
|
|
|
private function processOrdersToFlexiBee(): void
|
|
{
|
|
if (isLocalDevelopment()) {
|
|
return;
|
|
}
|
|
|
|
// nacitam objednavky, ktere nejsou zapsane do FlexiBee nebo je u nich potreba provest aktualizaci
|
|
$qb = $this->getBaseOrdersQueryBuilder()
|
|
->andWhere(
|
|
Operator::andX(
|
|
Operator::orX(
|
|
// objednavka neni vyrizena
|
|
Operator::not(
|
|
Operator::inIntArray(getStatuses('handled'), 'o.status')
|
|
),
|
|
// nebo je to objednavka z poklady - pokladna ji muze vyridit hned
|
|
Operator::equals(['o.source' => OrderInfo::ORDER_SOURCE_POS])
|
|
),
|
|
Operator::orX(
|
|
Operator::andX(
|
|
Operator::isNull('fo.id_order'),
|
|
Operator::equals(['o.status_storno' => 0])
|
|
),
|
|
'JSON_VALUE(fo.data, \'$.orderUpdated\') = 1',
|
|
)
|
|
)
|
|
);
|
|
|
|
// pokud mam nastavenej error_status tak nechci porad dokola synchronizovat objednavky s chybou
|
|
$errorStatus = $this->configuration->getOrderConfig()['error_status'];
|
|
if ($errorStatus) {
|
|
$qb->andWhere(
|
|
Operator::not(
|
|
Operator::equals(['o.status' => $errorStatus]),
|
|
),
|
|
);
|
|
}
|
|
|
|
foreach ($qb->sendToMaster()->execute() as $item) {
|
|
$this->syncSingleItemToFlexi($item);
|
|
}
|
|
}
|
|
|
|
public function getOrderInvoiceData(\Order $order): array
|
|
{
|
|
$data = [
|
|
'formaUhradyCis' => $this->getPaymentTypeCode($order),
|
|
'formaDopravy' => $this->getDeliveryTypeCode($order),
|
|
'doprava' => $order->getDeliveryType()->name,
|
|
];
|
|
|
|
if ($paymentStatus = $this->getPaymentStatus($order)) {
|
|
$data['stavUhrK'] = $paymentStatus;
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
public function getOrderRealizeData(\Order $order): array
|
|
{
|
|
$items = [];
|
|
foreach ($order->fetchItems() as $item) {
|
|
$items[] = [
|
|
'id' => $this->getItemId($order, $item),
|
|
'mj' => $item['pieces'],
|
|
];
|
|
}
|
|
|
|
$datePaid = new \DateTime();
|
|
|
|
$payments = $order->getPaymentsArray();
|
|
if ($payment = reset($payments)) {
|
|
$datePaid = $payment['date'];
|
|
}
|
|
|
|
return [
|
|
'id' => 'ext:OBP:'.$this->flexiBeeUtil->getOrderNumber($order),
|
|
'realizaceObj' => [
|
|
'@type' => 'faktura-vydana',
|
|
'id' => 'ext:FAV:'.$this->flexiBeeUtil->getOrderNumber($order),
|
|
'datUhr' => $datePaid->format('Y-m-d'),
|
|
'formaUhradyCis' => $this->getPaymentTypeCode($order),
|
|
'formaDopravy' => $this->getDeliveryTypeCode($order),
|
|
'doprava' => $order->getDeliveryType()->name,
|
|
'polozkyObchDokladu' => $items,
|
|
],
|
|
];
|
|
}
|
|
|
|
public function getOrderData(\Order $order, ?int $flexiBeeId = null): array
|
|
{
|
|
$items = [];
|
|
foreach ($order->getItems() as $item) {
|
|
$items[] = $this->getOrderItemData($order, $item);
|
|
}
|
|
|
|
$orderData = [
|
|
// externi identifikator
|
|
'id' => 'ext:'.$this->getDocumentType($order).':'.$this->flexiBeeUtil->getOrderNumber($order),
|
|
// cislo objednavky
|
|
'kod' => $this->getOrderNumber($order),
|
|
'cisDosle' => $this->getOrderNumber($order),
|
|
'typDokl' => 'code:'.$this->getDocumentType($order),
|
|
'datVyst' => $this->getOrderDate($order),
|
|
'stredisko' => $this->getOrderCentral($order),
|
|
'poznam' => $this->getOrderNote($order),
|
|
|
|
'varSym' => $this->getOrderVariableSymbol($order),
|
|
|
|
'zaokrNaSumK' => $this->getOrderRounding($order),
|
|
'zaokrJakSumK' => $this->getOrderRoundDirection($order),
|
|
|
|
'formaUhradyCis' => $this->getPaymentTypeCode($order),
|
|
'formaDopravy' => $this->getDeliveryTypeCode($order),
|
|
|
|
'mena' => 'code:'.$order->getCurrency(),
|
|
'kurz' => $order->currency_rate,
|
|
'kurzMnozstvi' => 1,
|
|
|
|
'kontaktEmail' => $order->invoice_email,
|
|
'kontaktJmeno' => $order->delivery_name.' '.$order->delivery_surname,
|
|
'kontaktTel' => $order->invoice_phone,
|
|
|
|
'nazFirmy' => $this->getOrderFirmName($order),
|
|
'ulice' => $order->invoice_street,
|
|
'mesto' => $order->invoice_city,
|
|
'psc' => $order->invoice_zip,
|
|
'ic' => $order->invoice_ico,
|
|
'dic' => $order->invoice_dic,
|
|
'stat' => 'code:'.(empty($order->invoice_country) ? 'CZ' : $order->invoice_country),
|
|
'statDph' => 'code:'.(empty($order->delivery_country) ? 'CZ' : $order->delivery_country),
|
|
|
|
'postovniShodna' => $this->isOrderDeliveryAddressSame($order),
|
|
|
|
'faNazev' => $this->getOrderFirmName($order, 'delivery'),
|
|
'faUlice' => $order->delivery_street,
|
|
'faMesto' => $order->delivery_city,
|
|
'faPsc' => $order->delivery_zip,
|
|
'faStat' => 'code:'.(empty($order->delivery_country) ? 'CZ' : $order->delivery_country),
|
|
|
|
'stavUzivK' => $this->getOrderStatus($order, $flexiBeeId),
|
|
|
|
'polozkyDokladu' => $order->status_storno == 1 ? [] : $items,
|
|
'polozkyObchDokladu@removeAll' => true,
|
|
];
|
|
|
|
if ($flexiUserId = $this->getOrderUser($order)) {
|
|
$orderData['firma'] = $flexiUserId;
|
|
}
|
|
|
|
if ($orderDescription = $this->getOrderDescription($order)) {
|
|
$orderData['popis'] = $orderDescription;
|
|
}
|
|
|
|
if (null !== ($orderFlags = $this->getOrderFlags($order))) {
|
|
$orderData['stitky'] = $orderFlags;
|
|
$orderData['stitky@removeAll'] = true;
|
|
}
|
|
|
|
if ($pointId = $order->getDeliveryType()->getDelivery()->getPointId()) {
|
|
$orderData['branchId'] = $pointId;
|
|
}
|
|
|
|
return $orderData;
|
|
}
|
|
|
|
protected function processOrderChanges(\Order $order, array $flexiOrder): void
|
|
{
|
|
// pokud jsem objednavku prave updatoval, tak nema smysl se snazit ji hned zpetne updatovat z flexi
|
|
if (in_array($order->id, $this->recentlyUpdatedOrders)) {
|
|
unset($this->recentlyUpdatedOrders[$order->id]);
|
|
|
|
return;
|
|
}
|
|
|
|
// pokud je objednavka ve stavu hotovo, tak zmeny na objednavce nezpracovavame
|
|
if ($flexiOrder['stavUzivK'] === 'stavDoklObch.hotovo' || $flexiOrder['stavUzivK'] === 'stavDoklObch.storno') {
|
|
return;
|
|
}
|
|
|
|
$orderUpdate = [];
|
|
|
|
$invoiceDiff = array_diff(
|
|
[$flexiOrder['ic'], $flexiOrder['dic'], $flexiOrder['ulice'], $flexiOrder['mesto'], $flexiOrder['psc']],
|
|
[$order->invoice_ico, $order->invoice_dic, $order->invoice_street, $order->invoice_city, $order->invoice_zip]
|
|
);
|
|
|
|
if (!empty($invoiceDiff)) {
|
|
$orderUpdate = array_merge($orderUpdate, [
|
|
'invoice_ico' => $flexiOrder['ic'],
|
|
'invoice_dic' => $flexiOrder['dic'],
|
|
'invoice_street' => $flexiOrder['ulice'],
|
|
'invoice_city' => $flexiOrder['mesto'],
|
|
'invoice_zip' => $flexiOrder['psc'],
|
|
]);
|
|
}
|
|
|
|
$deliveryDiff = array_diff(
|
|
[$flexiOrder['faUlice'], $flexiOrder['faMesto'], $flexiOrder['faPsc']],
|
|
[$order->delivery_street, $order->delivery_city, $order->delivery_zip]
|
|
);
|
|
|
|
if (!empty($deliveryDiff)) {
|
|
$orderUpdate = array_merge($orderUpdate, [
|
|
'delivery_street' => $flexiOrder['faUlice'],
|
|
'delivery_city' => $flexiOrder['faMesto'],
|
|
'delivery_zip' => $flexiOrder['faPsc'],
|
|
]);
|
|
}
|
|
|
|
$activityLogChanges = [];
|
|
|
|
// pokud jsou ve FlexiBee jine fakturacni udaje, tak provadim aktualizaci
|
|
if (!empty($orderUpdate)) {
|
|
sqlQueryBuilder()
|
|
->update('orders')
|
|
->directValues($orderUpdate)
|
|
->where(Operator::equals(['id' => $order->id]))
|
|
->execute();
|
|
|
|
if (!empty($invoiceDiff)) {
|
|
$activityLogChanges['invoice'] = [
|
|
'old' => [
|
|
'invoice_ico' => $order->invoice_ico,
|
|
'invoice_dic' => $order->invoice_dic,
|
|
'invoice_street' => $order->invoice_street,
|
|
'invoice_city' => $order->invoice_city,
|
|
'invoice_zip' => $order->invoice_zip,
|
|
],
|
|
'new' => [
|
|
'invoice_ico' => $flexiOrder['ic'],
|
|
'invoice_dic' => $flexiOrder['dic'],
|
|
'invoice_street' => $flexiOrder['ulice'],
|
|
'invoice_city' => $flexiOrder['mesto'],
|
|
'invoice_zip' => $flexiOrder['psc'],
|
|
],
|
|
];
|
|
}
|
|
|
|
if (!empty($deliveryDiff)) {
|
|
$activityLogChanges['delivery'] = [
|
|
'old' => [
|
|
'delivery_street' => $order->delivery_street,
|
|
'delivery_city' => $order->delivery_city,
|
|
'delivery_zip' => $order->delivery_zip,
|
|
],
|
|
'new' => [
|
|
'delivery_street' => $flexiOrder['faUlice'],
|
|
'delivery_city' => $flexiOrder['faMesto'],
|
|
'delivery_zip' => $flexiOrder['faPsc'],
|
|
],
|
|
];
|
|
}
|
|
}
|
|
|
|
$currentOrderItems = Mapping::mapKeys($order->fetchItems(), fn ($k, $v) => [$k, false]);
|
|
foreach ($flexiOrder['items'] ?? [] as $flexiItem) {
|
|
$externalId = !empty($flexiItem['external-ids']) ? (string) $flexiItem['external-ids'] : null;
|
|
// pokud neni u polozky vyplnene externalId, tak je to polozka pridana ve FlexiBee
|
|
if (!$externalId) {
|
|
// kouknu zda ta polozka uz neni v objednavce pridana
|
|
$externalId = $order->getData('flexiBeeItems')[$flexiItem['id']] ?? null;
|
|
}
|
|
|
|
$flexiPiecePrice = \Decimal::create($flexiItem['cenaMj'])->addDiscount($flexiItem['slevaPol']);
|
|
if ($flexiItem['typCenyDphK'] === 'typCeny.sDph') {
|
|
$flexiPiecePrice = $flexiPiecePrice->removeVat($flexiItem['szbDph']);
|
|
}
|
|
|
|
// pokud nemam externalId, tak pridavam polozku
|
|
if (!$externalId) {
|
|
// pokud je to polozka s 0 ks, tak ji preskocim, protoze nema smysl ji pridavat do objednavky
|
|
if ($flexiItem['mnozMj'] == 0) {
|
|
continue;
|
|
}
|
|
|
|
$productId = null;
|
|
$variationId = null;
|
|
// pridat novou polozku do objednavky
|
|
if (!empty($flexiItem['cenik'])) {
|
|
// zkusim polozku naparovat na produkt / variantu
|
|
if (!empty($flexiItem['cenik']->ref)) {
|
|
if ($flexiCenikId = $this->flexiBeeUtil->getFlexiIdFromRef($flexiItem['cenik']->ref)) {
|
|
[$productId, $variationId] = $this->flexiBeeUtil->getItemMapping($flexiCenikId);
|
|
}
|
|
}
|
|
}
|
|
|
|
$newItemId = sqlGetConnection()->transactional(function () use ($order, $productId, $variationId, $flexiItem, $flexiPiecePrice) {
|
|
sqlQueryBuilder()
|
|
->insert('order_items')
|
|
->directValues(
|
|
[
|
|
'id_order' => $order->id,
|
|
'id_product' => $productId,
|
|
'id_variation' => $variationId,
|
|
'pieces' => $flexiItem['mnozMj'],
|
|
'pieces_reserved' => $flexiItem['mnozMj'],
|
|
'piece_price' => $flexiPiecePrice,
|
|
'total_price' => $flexiPiecePrice->mul(toDecimal($flexiItem['mnozMj'])),
|
|
'descr' => $flexiItem['nazev'],
|
|
'tax' => $flexiItem['szbDph'],
|
|
]
|
|
)->execute();
|
|
|
|
return (int) sqlInsertId();
|
|
});
|
|
|
|
// pridam mapovani pro nove pridany item
|
|
sqlGetConnection()->transactional(function () use ($order, $newItemId, $flexiItem) {
|
|
$newItems = $order->getData('flexiBeeItems');
|
|
$newItems[$flexiItem['id']] = $newItemId;
|
|
$order->setData('flexiBeeItems', $newItems);
|
|
});
|
|
|
|
$activityLogChanges['items'][$newItemId] = ['id' => $newItemId, 'name' => $flexiItem['nazev'], 'added' => true];
|
|
continue;
|
|
}
|
|
|
|
$parts = explode('-', (string) $externalId);
|
|
$itemId = (int) end($parts);
|
|
// pokud by tam ID nebylo, tak je to divny.. ale radsi fail-check a skipnu to
|
|
/** @var OrderItem $item */
|
|
if (!($item = ($order->fetchItems()[$itemId] ?? null))) {
|
|
continue;
|
|
}
|
|
|
|
// polozka je ve FlexiBee stornovana
|
|
if ($flexiItem['storno'] === true || $flexiItem['stornoPol'] === true) {
|
|
// pokracuju dal - tim polozku neoznacim v $currentOrderItems jako ze jsem ji nasel a timpadem bude smazana z objednavky
|
|
continue;
|
|
}
|
|
|
|
// oznacim si polozku, ze jsem ji nasel
|
|
$currentOrderItems[$itemId] = true;
|
|
|
|
$itemUpdate = [];
|
|
|
|
// pokud se zmenil pocet kusu
|
|
if ($item->getPieces() != $flexiItem['mnozMj']) {
|
|
$itemUpdate['pieces'] = $flexiItem['mnozMj'];
|
|
$itemUpdate['pieces_reserved'] = $flexiItem['mnozMj'];
|
|
}
|
|
|
|
// pokud se zmenilo DPH
|
|
if ($item->getVat() != $flexiItem['szbDph']) {
|
|
$itemUpdate['tax'] = $flexiItem['szbDph'];
|
|
}
|
|
|
|
// pokud se zmenila cena, nebo pocet kusu, tak potrebuju aktualizovat cenu
|
|
if ($item->getItem()['value_without_vat']->round(4)->asFloat() != $flexiPiecePrice->round(4)->asFloat() || $item->getPieces() != $flexiItem['mnozMj']) {
|
|
$itemUpdate['piece_price'] = $flexiPiecePrice->asFloat();
|
|
$itemUpdate['total_price'] = $flexiPiecePrice->mul(toDecimal($flexiItem['mnozMj']));
|
|
}
|
|
|
|
// provest aktualizaci polozky
|
|
if (!empty($itemUpdate)) {
|
|
sqlQueryBuilder()
|
|
->update('order_items')
|
|
->directValues($itemUpdate)
|
|
->where(Operator::equals(['id' => $itemId, 'id_order' => $order->id]))
|
|
->execute();
|
|
|
|
$activityLogChanges['items'][$itemId] = ['id' => $itemId, 'productId' => $item->getProductId(), 'name' => $item->getDescr(), 'updated' => true, 'changes' => $itemUpdate];
|
|
}
|
|
}
|
|
|
|
// projdu polozky, ktere ve FlexiBee odebrali z objednavky a smazu je i v e-shopove objednavce
|
|
$deletedItems = array_filter($currentOrderItems, fn ($x) => !$x);
|
|
foreach ($deletedItems as $deletedItemId => $deletedItem) {
|
|
if (!($item = ($order->fetchItems()[$deletedItemId] ?? null))) {
|
|
continue;
|
|
}
|
|
|
|
// smaznu item z objednavky
|
|
sqlQueryBuilder()
|
|
->delete('order_items')
|
|
->where(Operator::equals(['id' => $deletedItemId]))
|
|
->execute();
|
|
|
|
// zaloguju smazani do historie objednavky
|
|
$order->logHistory('[FlexiBee] Odebrána položka: '.$item->getDescr());
|
|
|
|
$activityLogChanges['items'][$deletedItemId] = ['id' => $deletedItemId, 'productId' => $item->getProductId(), 'name' => $item->getDescr(), 'deleted' => true];
|
|
}
|
|
|
|
// provest prepocitani cen, pokud se provedli nejake zmeny v polozkach
|
|
if (!empty($activityLogChanges['items'])) {
|
|
$order->recalculate();
|
|
}
|
|
|
|
// logovani
|
|
if (!empty($activityLogChanges)) {
|
|
// zaloguju zmeny do activity logu
|
|
$this->logger->activity(
|
|
sprintf('Aktualizace objednávky "%s" z FlexiBee', $order->order_no),
|
|
$activityLogChanges
|
|
);
|
|
|
|
// udelam sumarni report, ktery zaloguju primo k objednavce
|
|
$messages = [];
|
|
foreach ($activityLogChanges as $type => $changes) {
|
|
switch ($type) {
|
|
case 'invoice':
|
|
$messages[] = 'Provedena aktualizace fakturační adresy;';
|
|
break;
|
|
case 'delivery':
|
|
$messages[] = 'Provedena aktualizace dodací adresy;';
|
|
break;
|
|
case 'items':
|
|
$updated = 0;
|
|
$added = 0;
|
|
$deleted = 0;
|
|
foreach ((array) $changes as $change) {
|
|
if (($change['updated'] ?? false) === true) {
|
|
$updated++;
|
|
}
|
|
|
|
if (($change['deleted'] ?? false) === true) {
|
|
$deleted++;
|
|
}
|
|
|
|
if (($change['added'] ?? false) === true) {
|
|
$added++;
|
|
}
|
|
}
|
|
|
|
if ($updated || $added || $deleted) {
|
|
$messages[] = "Změny na položkách: přidání: {$added}x; aktualizace: {$updated}x; smazání: {$deleted}x;";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!empty($messages)) {
|
|
$order->logChange(implode('<br>', array_merge(['[FlexiBee] Provedena aktualizace objednávky podle FlexiBee:'], $messages)));
|
|
}
|
|
}
|
|
}
|
|
|
|
protected function getOrderNumber(\Order $order): string
|
|
{
|
|
return $order->order_no;
|
|
}
|
|
|
|
protected function getOrderVariableSymbol(\Order $order): string
|
|
{
|
|
return preg_replace('/\D*/', '', $order->order_no);
|
|
}
|
|
|
|
protected function getOrderStatus(\Order $order, ?int $flexiBeeId = null): string
|
|
{
|
|
if ($flexiBeeId) {
|
|
// pokud je objednavka stornovana tak stornuju i ve flexi
|
|
if ($order->status_storno == 1) {
|
|
return 'stavDoklObch.storno';
|
|
}
|
|
|
|
// pokud je to update objednavky ve FlexiBee, tak chci zachovat stav objednavky, kterej je ve FlexiBee
|
|
if ($flexiStatus = ($this->flexiBeeApi->getOrder($flexiBeeId)['stavUzivK'] ?? false)) {
|
|
return $flexiStatus;
|
|
}
|
|
}
|
|
|
|
return 'stavDoklObch.nespec';
|
|
}
|
|
|
|
protected function getOrderNote(\Order $order): string
|
|
{
|
|
return $order->note_user ?: '';
|
|
}
|
|
|
|
protected function getOrderRoundDirection(\Order $order): string
|
|
{
|
|
$currency = Contexts::get(CurrencyContext::class)->getOrDefault($order->getCurrency());
|
|
|
|
return match ($currency->getPriceRoundDirection()) {
|
|
'up' => 'zaokrJak.nahoru',
|
|
'down' => 'zaokrJak.dolu',
|
|
default => 'zaokrJak.matem',
|
|
};
|
|
}
|
|
|
|
protected function getOrderRounding(\Order $order): string
|
|
{
|
|
/** @var Currency $currency */
|
|
$currency = Contexts::get(CurrencyContext::class)->getOrDefault($order->getCurrency());
|
|
|
|
switch ($currency->getPriceRoundOrder() / 100) {
|
|
case 1:
|
|
return 'zaokrNa.jednotky';
|
|
case 0.01:
|
|
return 'zaokrNa.setiny';
|
|
case 0.001:
|
|
return 'zaokrNa.tisiciny';
|
|
case 0.05:
|
|
return 'zaokrNa.5setiny';
|
|
case 0.1:
|
|
return 'zaokrNa.desetiny';
|
|
case 0.5:
|
|
return 'zaokrNa.5desetiny';
|
|
case -0.05:
|
|
return 'zaokrNa.5jednotky';
|
|
case -0.1:
|
|
return 'zaokrNa.desitky';
|
|
}
|
|
|
|
return 'zaokrNa.zadne';
|
|
}
|
|
|
|
protected function getOrderDescription(\Order $order): ?string
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/** Stitky do FlexiBee */
|
|
protected function getOrderFlags(\Order $order): ?string
|
|
{
|
|
return null;
|
|
}
|
|
|
|
protected function getOrderItemData(\Order $order, OrderItem $item): array
|
|
{
|
|
$itemFlexiId = null;
|
|
$itemType = 'typPolozky.obecny';
|
|
if ($item->getProductId()) {
|
|
// pokud mam FlexiID, tak je to katalogova polozka
|
|
if ($itemFlexiId = $this->flexiBeeUtil->getProductFlexiId($item->getProductId(), $item->getVariationId())) {
|
|
$itemType = 'typPolozky.katalog';
|
|
}
|
|
}
|
|
|
|
// pripravim pole s datama polozky
|
|
$itemData = [
|
|
'id' => $this->getItemId($order, $item),
|
|
'typPolozkyK' => $itemType,
|
|
'kod' => $item->getCode(),
|
|
'eanKod' => $item->getEAN(),
|
|
'nazev' => $item->getDescr(),
|
|
'mnozMj' => $order->status_storno == 1 ? 0 : $item->getPieces(),
|
|
'typCenyDphK' => 'typCeny.sDph',
|
|
'cenaMj' => $item->getPiecePrice()->getPriceWithVat(false)->asFloat(),
|
|
'szbDph' => $item->getVat(),
|
|
];
|
|
|
|
// pokud mam flexi ID, tak doplnim vazbu na cenik, sklad a reknu, ze se ma rezervovat
|
|
if ($itemFlexiId) {
|
|
$itemData += $this->addFlexiBeeParams($order, $item, $itemFlexiId);
|
|
}
|
|
|
|
return $itemData;
|
|
}
|
|
|
|
protected function addFlexiBeeParams(\Order $order, OrderItem $item, $itemFlexiId): array
|
|
{
|
|
$result['cenik'] = $itemFlexiId;
|
|
$result['sklad'] = $this->getOrderStore(
|
|
$order,
|
|
$item->getProductId(),
|
|
$item->getVariationId()
|
|
);
|
|
if ($item->getPieces() > 0 && $order->status_storno != 1) {
|
|
$result['rezervovat'] = true;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
protected function saveOrderPackageNumber(\Order $order, array $item): void
|
|
{
|
|
if (empty($item['doprava'])) {
|
|
return;
|
|
}
|
|
|
|
$packageId = explode(';', $item['doprava']);
|
|
$packageId = reset($packageId);
|
|
if (!empty($packageId) && $order->package_id !== $packageId) {
|
|
// ulozim package id na objekt - napr. kvuli naslednemu odeslani mailu
|
|
$order->package_id = $packageId;
|
|
// ulozim package id k objednavce v DB
|
|
sqlQueryBuilder()
|
|
->update('orders')
|
|
->directValues(['package_id' => $packageId])
|
|
->where(Operator::equals(['id' => $order->id]))
|
|
->execute();
|
|
// zaloguju cislo baliku k objednavce
|
|
$order->logHistory(sprintf('[FlexiBee] Číslo balíku: %s', $packageId));
|
|
}
|
|
}
|
|
|
|
protected function changeOrderStatus(\Order $order, string $flexiStatus, ?int $status, ?string $orderMessage = null): void
|
|
{
|
|
// Chci odessilat jen mail, bez zmeny stavu
|
|
if ($status === null && $orderMessage !== null) {
|
|
$order->logHistory('[FlexiBee] Odeslání e-mailu: '.$orderMessage);
|
|
$order->sendEmail(null, $orderMessage);
|
|
|
|
return;
|
|
}
|
|
|
|
// Pokud neni co menit, tak jdu pryc
|
|
if ($status === null || $order->status == $status) {
|
|
return;
|
|
}
|
|
|
|
// zmenit stav a pripadne odeslat i mail
|
|
$order->logHistory('[FlexiBee] Změna stavu: '.$flexiStatus);
|
|
$order->changeStatus($status, null, null, $orderMessage);
|
|
}
|
|
|
|
private function getOrderFirmName(\Order $order, string $type = 'invoice'): string
|
|
{
|
|
if (!empty($order->{$type.'_firm'})) {
|
|
return $order->{$type.'_firm'};
|
|
}
|
|
|
|
return $order->{$type.'_name'}.' '.$order->{$type.'_surname'};
|
|
}
|
|
|
|
protected function getOrderUser(\Order $order): ?int
|
|
{
|
|
// registrovany uzivatel
|
|
if ($flexiUserId = $this->getFlexiUser($order)) {
|
|
return $flexiUserId;
|
|
}
|
|
|
|
// nejaky default uzivatel ve FlexiBee pro objednavky bez registrace
|
|
$defaultFlexiUserId = $this->configuration->getOrderConfig()['id_user'] ?? null;
|
|
if (!empty($defaultFlexiUserId)) {
|
|
return (int) $defaultFlexiUserId;
|
|
}
|
|
|
|
// pokud nechteji pouzivat default uzivatele (napr. kvuli tomu, ze by ty udaje byly videt na fakture ve FlexiBee), tak musim
|
|
// do FlexiBee zapsat i uzivatele bez registrace - primarne je to kvuli rezervacim, protoze bez uzivatele se ve FlexiBee neudela rezervace
|
|
if (!($flexiUserId = $this->flexiBeeApi->getUserIdByCode('WPJ-ORDER'.$order->id))) {
|
|
$flexiUserId = $this->userSynchronizer->processUserToFlexiBee(
|
|
$this->userSynchronizer->getOrderUserData($order)
|
|
);
|
|
}
|
|
|
|
return $flexiUserId;
|
|
}
|
|
|
|
protected function getOrderCentral(\Order $order): ?int
|
|
{
|
|
if ($centralId = ($this->configuration->getOrderConfig()['central'] ?? null)) {
|
|
return (int) $centralId;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected function getOrderStore(\Order $order, int $productId, ?int $variationId = null): ?int
|
|
{
|
|
$storeId = sqlQueryBuilder()
|
|
->select('id_store')
|
|
->from('stores_items')
|
|
->where(Operator::equalsNullable(['id_product' => $productId, 'id_variation' => $variationId]))
|
|
->execute()->fetchOne();
|
|
|
|
if (!$storeId) {
|
|
$storeId = sqlQueryBuilder()
|
|
->select('id_store')
|
|
->from('stores_items')
|
|
->where(Operator::equalsNullable(['id_product' => $productId]))
|
|
->execute()->fetchOne();
|
|
}
|
|
|
|
if ($storeId) {
|
|
return $this->flexiBeeUtil->getFlexiId(StoreSynchronizer::getType(), (int) $storeId);
|
|
}
|
|
|
|
return $this->getDefaultStoreId($order);
|
|
}
|
|
|
|
private function isOrderDeliveryAddressSame(\Order $order): bool
|
|
{
|
|
$invoice = [$order->invoice_firm, $order->invoice_name, $order->invoice_country, $order->invoice_street, $order->invoice_city, $order->invoice_zip];
|
|
$delivery = [$order->delivery_firm, $order->delivery_name, $order->delivery_country, $order->delivery_street, $order->delivery_city, $order->delivery_zip];
|
|
|
|
return $invoice === $delivery;
|
|
}
|
|
|
|
protected function getDefaultStoreId(\Order $order): ?int
|
|
{
|
|
return null;
|
|
}
|
|
|
|
protected function getFlexiUser(\Order $order): ?int
|
|
{
|
|
if ($order->id_user) {
|
|
$flexiId = sqlQueryBuilder()
|
|
->select('id_flexi')
|
|
->from('flexi_users')
|
|
->where(Operator::equals(['id_user' => $order->id_user]))
|
|
->execute()->fetchOne();
|
|
|
|
if ($flexiId) {
|
|
return (int) $flexiId;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected function getPaymentStatus(\Order $order): ?string
|
|
{
|
|
if ($deliveryType = $order->getDeliveryType()) {
|
|
$payment = $deliveryType->getPayment();
|
|
|
|
if ($payment instanceof \Hotovost) {
|
|
if ($order->isPaid()) {
|
|
return 'stavUhr.uhrazenoRucne';
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
protected function getPaymentTypeCode(\Order $order): string
|
|
{
|
|
if ($deliveryType = $order->getDeliveryType()) {
|
|
if ($code = ($this->configuration->getPaymentsConfig()[$deliveryType->id_payment] ?? false)) {
|
|
return 'code:'.$code;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
protected function getDeliveryTypeCode(\Order $order): string
|
|
{
|
|
if ($deliveryType = $order->getDeliveryType()) {
|
|
$delivery = $deliveryType->getDelivery();
|
|
|
|
if ($code = ($this->configuration->getDeliveriesConfig()[$delivery->id] ?? false)) {
|
|
return 'code:'.$code;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
protected function getBaseOrdersQueryBuilder(): QueryBuilder
|
|
{
|
|
return sqlQueryBuilder()
|
|
->select('o.id, fo.id_flexi, fo.data')
|
|
->from('orders', 'o')
|
|
->leftJoin('o', 'flexi_orders', 'fo', 'fo.id_order = o.id');
|
|
}
|
|
|
|
protected function preprocessChangedItems(array $items): array
|
|
{
|
|
// nafetchuju polozky objednavek pokud mam zapnutou obousmernou synchronizaci
|
|
if (($this->configuration->getOrderConfig()['duplex_sync'] ?? false) === 'Y') {
|
|
if (!empty($items)) {
|
|
$orderItems = $this->flexiBeeApi->getOrdersItems(array_map(fn ($x) => $x['id'], $items));
|
|
foreach ($items as &$item) {
|
|
$item['items'] = $orderItems[$item['id']] ?? [];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $items;
|
|
}
|
|
|
|
private function getOrderMessageNameById(int $orderMessageId): ?string
|
|
{
|
|
static $orderMessageCache = [];
|
|
|
|
if (($orderMessageCache[$orderMessageId] ?? false) === false) {
|
|
$orderMessageName = sqlQueryBuilder()
|
|
->select('name')
|
|
->from('emails')
|
|
->where(Operator::equals(['id' => $orderMessageId]))
|
|
->execute()->fetchOne();
|
|
|
|
if (!$orderMessageName) {
|
|
$orderMessageName = null;
|
|
}
|
|
|
|
$orderMessageCache[$orderMessageId] = $orderMessageName;
|
|
}
|
|
|
|
return $orderMessageCache[$orderMessageId];
|
|
}
|
|
|
|
protected function getItemId(\Order $order, OrderItem $item): string
|
|
{
|
|
return 'ext:OBP-POL-ESHOP:'.$order->order_no.'-'.$item->getId();
|
|
}
|
|
|
|
public function getOrderDate(\Order $order): string
|
|
{
|
|
return $order->date_created->format('Y-m-d');
|
|
}
|
|
|
|
protected function getDocumentType(\Order $order): string
|
|
{
|
|
return 'OBP';
|
|
}
|
|
}
|