first commit

This commit is contained in:
2025-08-02 16:30:27 +02:00
commit 23646bfcee
14851 changed files with 1750626 additions and 0 deletions

View File

@@ -0,0 +1,988 @@
<?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';
}
}