first commit
This commit is contained in:
10
bundles/External/FlexiBeeBundle/AbraFlexiTypes/CenikovaSkupina.php
vendored
Normal file
10
bundles/External/FlexiBeeBundle/AbraFlexiTypes/CenikovaSkupina.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace External\FlexiBeeBundle\AbraFlexiTypes;
|
||||
|
||||
use AbraFlexi\RW;
|
||||
|
||||
class CenikovaSkupina extends RW
|
||||
{
|
||||
public ?string $evidence = 'cenikova-skupina';
|
||||
}
|
||||
15
bundles/External/FlexiBeeBundle/AbraFlexiTypes/FakturaVydanaPolozka.php
vendored
Normal file
15
bundles/External/FlexiBeeBundle/AbraFlexiTypes/FakturaVydanaPolozka.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\AbraFlexiTypes;
|
||||
|
||||
use AbraFlexi\RW;
|
||||
|
||||
/**
|
||||
* Custom typ, protoze v knihovne chybi.
|
||||
*/
|
||||
class FakturaVydanaPolozka extends RW
|
||||
{
|
||||
public ?string $evidence = 'faktura-vydana-polozka';
|
||||
}
|
||||
10
bundles/External/FlexiBeeBundle/AbraFlexiTypes/Odberatel.php
vendored
Normal file
10
bundles/External/FlexiBeeBundle/AbraFlexiTypes/Odberatel.php
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace External\FlexiBeeBundle\AbraFlexiTypes;
|
||||
|
||||
use AbraFlexi\RW;
|
||||
|
||||
class Odberatel extends RW
|
||||
{
|
||||
public ?string $evidence = 'odberatel';
|
||||
}
|
||||
15
bundles/External/FlexiBeeBundle/AbraFlexiTypes/Sklad.php
vendored
Normal file
15
bundles/External/FlexiBeeBundle/AbraFlexiTypes/Sklad.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\AbraFlexiTypes;
|
||||
|
||||
use AbraFlexi\RW;
|
||||
|
||||
/**
|
||||
* Custom typ, protoze v knihovne chybi.
|
||||
*/
|
||||
class Sklad extends RW
|
||||
{
|
||||
public ?string $evidence = 'sklad';
|
||||
}
|
||||
15
bundles/External/FlexiBeeBundle/AbraFlexiTypes/Stredisko.php
vendored
Normal file
15
bundles/External/FlexiBeeBundle/AbraFlexiTypes/Stredisko.php
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\AbraFlexiTypes;
|
||||
|
||||
use AbraFlexi\RW;
|
||||
|
||||
/**
|
||||
* Custom typ, protoze v knihovne chybi.
|
||||
*/
|
||||
class Stredisko extends RW
|
||||
{
|
||||
public ?string $evidence = 'stredisko';
|
||||
}
|
||||
53
bundles/External/FlexiBeeBundle/Admin/Actions/OrderSendToFlexiAction.php
vendored
Normal file
53
bundles/External/FlexiBeeBundle/Admin/Actions/OrderSendToFlexiAction.php
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace External\FlexiBeeBundle\Admin\Actions;
|
||||
|
||||
use External\FlexiBeeBundle\Synchronizers\OrderSynchronizer;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeUtil;
|
||||
use KupShop\AdminBundle\Admin\Actions\AbstractAction;
|
||||
use KupShop\AdminBundle\Admin\Actions\ActionResult;
|
||||
use KupShop\AdminBundle\Admin\Actions\IAction;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class OrderSendToFlexiAction extends AbstractAction implements IAction
|
||||
{
|
||||
#[Required]
|
||||
public OrderSynchronizer $orderSynchronizer;
|
||||
|
||||
#[Required]
|
||||
public FlexiBeeUtil $flexiUtil;
|
||||
|
||||
public function getTypes(): array
|
||||
{
|
||||
return ['orders'];
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '[Flexi] Odeslat do Flexi';
|
||||
}
|
||||
|
||||
public function isVisible()
|
||||
{
|
||||
return !empty($this->getId());
|
||||
}
|
||||
|
||||
public function showInMassEdit()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function execute(&$data, array $config, string $type): ActionResult
|
||||
{
|
||||
$orderId = (int) $this->getId();
|
||||
|
||||
if ($this->orderSynchronizer->syncSingleItemToFlexi([
|
||||
'id' => $orderId,
|
||||
'id_flexi' => $this->flexiUtil->getMapping('order', $orderId),
|
||||
])) {
|
||||
return new ActionResult(true);
|
||||
}
|
||||
|
||||
return new ActionResult(false, 'Odeslání do Flexi se nezdařilo.');
|
||||
}
|
||||
}
|
||||
241
bundles/External/FlexiBeeBundle/Admin/Tabs/FlexiBeeSettingsTab.php
vendored
Normal file
241
bundles/External/FlexiBeeBundle/Admin/Tabs/FlexiBeeSettingsTab.php
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Admin\Tabs;
|
||||
|
||||
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
||||
use External\FlexiBeeBundle\Synchronizers\StoreSynchronizer;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeApi;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeConfiguration;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeLocator;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeUtil;
|
||||
use KupShop\AdminBundle\Admin\WindowTab;
|
||||
use KupShop\KupShopBundle\Email\OrderMessageEmail;
|
||||
|
||||
class FlexiBeeSettingsTab extends WindowTab
|
||||
{
|
||||
protected $title = 'flapFlexiBeeSettings';
|
||||
protected $template = 'window/settings.flexibee.tpl';
|
||||
|
||||
private FlexiBeeApi $api;
|
||||
private FlexiBeeUtil $util;
|
||||
private FlexiBeeLocator $locator;
|
||||
private FlexiBeeConfiguration $configuration;
|
||||
private OrderMessageEmail $orderMessageEmail;
|
||||
|
||||
public function __construct(
|
||||
FlexiBeeApi $api,
|
||||
FlexiBeeUtil $util,
|
||||
FlexiBeeLocator $locator,
|
||||
FlexiBeeConfiguration $configuration,
|
||||
OrderMessageEmail $orderMessageEmail,
|
||||
) {
|
||||
$this->api = $api;
|
||||
$this->util = $util;
|
||||
$this->locator = $locator;
|
||||
$this->configuration = $configuration;
|
||||
$this->orderMessageEmail = $orderMessageEmail;
|
||||
}
|
||||
|
||||
public static function getTypes()
|
||||
{
|
||||
return [
|
||||
'settings' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
public function getVars($smarty_tpl_vars)
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
return [
|
||||
'flexibee' => $dbcfg->loadValue('flexibee') ?: [],
|
||||
'flexibeeData' => [
|
||||
'statuses' => $this->getFlexiBeeOrderStatuses(),
|
||||
'stores' => $this->getFlexiBeeStores(),
|
||||
'centrals' => $this->getFlexiBeeCentrals(),
|
||||
'synchronizers' => $this->getSynchronizers(),
|
||||
'pricelists' => $this->getFlexiBeePriceLists(),
|
||||
],
|
||||
'orderMessages' => $this->orderMessageEmail->getOrderMessages(),
|
||||
'deliveries' => \DeliveryType::getDeliveries(true),
|
||||
'payments' => \DeliveryType::getPayments(true),
|
||||
'changesApiButton' => $this->isConfigValid() && !$this->isChangesApiAvailable(),
|
||||
];
|
||||
}
|
||||
|
||||
public function handleUpdate()
|
||||
{
|
||||
$this->configuration->refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Otestuje připojení k FlexiBee.
|
||||
*/
|
||||
public function handleCheckConnection(): void
|
||||
{
|
||||
// kontroluju, zda je vyplněná konfigurace
|
||||
if (!$this->isConfigValid()) {
|
||||
$this->addHTMLError('<div class="alert alert-danger">Nejsou vyplněny všechny potřebné údaje!</div>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// otestuju komunikaci s API
|
||||
if (!$this->api->isConnectionValid()) {
|
||||
$this->addHTMLError('<div class="alert alert-danger">Připojení k FlexiBee se nepodařilo! Překontrolujte zadané údaje.</div>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addHTMLError('<div class="alert alert-success">Připojení k FlexiBee proběhlo úspěšné.</div>');
|
||||
|
||||
// zkontroluju, že je zapnuté ChangesAPI, které v synchronizaci používáme
|
||||
if (!$this->api->isChangesAPIEnabled()) {
|
||||
$this->addHTMLError('<div class="alert alert-danger"><strong>ChangesAPI</strong>, které je pro synchronizaci vyžadováno, není ve FlexiBee aktivní!</div>');
|
||||
}
|
||||
}
|
||||
|
||||
public function handleChangesAPIEnable(): void
|
||||
{
|
||||
// tlacitko se zobrazi jen kdyz neni changesAPI aktivni
|
||||
$this->api->changesAPIEnable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Spustí synchronizaci skladů.
|
||||
*/
|
||||
public function handleSynchronizeStores(): void
|
||||
{
|
||||
try {
|
||||
$this->util->synchronize([StoreSynchronizer::getType()]);
|
||||
} catch (FlexiBeeException $e) {
|
||||
$this->addHTMLError('Sklady se nepodařilo sesynchronizovat!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->addHTMLError('Sklady úspěšně sesynchronizovány.');
|
||||
}
|
||||
|
||||
public function getLabel()
|
||||
{
|
||||
return 'FlexiBee';
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací seznam typů synchronizací.
|
||||
*/
|
||||
protected function getSynchronizers(): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($this->locator->getTypes() as $type => $_) {
|
||||
// Sklad tady nezobrazuju, protoze ten synchronizuju zvlast podle nastaveni skladu
|
||||
if ($type === StoreSynchronizer::getType()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$type] = translate('synchronizer_'.$type, 'flexibee');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* FlexiBee stavy. Mají je ve FlexiBee natvrdo, tak by to mělo stačit takhle vydefinovaný.
|
||||
*/
|
||||
protected function getFlexiBeeOrderStatuses(): array
|
||||
{
|
||||
return [
|
||||
'stavDoklObch.pripraveno',
|
||||
'stavDoklObch.schvaleno',
|
||||
'stavDoklObch.castecneNaCeste',
|
||||
'stavDoklObch.naCeste',
|
||||
'stavDoklObch.castVydano',
|
||||
'stavDoklObch.vydano',
|
||||
'stavDoklObch.castHotovo',
|
||||
'stavDoklObch.hotovo',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací střediska ve FlexiBee.
|
||||
*/
|
||||
protected function getFlexiBeeCentrals(): array
|
||||
{
|
||||
if ($centrals = getCache('flexiBeeCentrals')) {
|
||||
return $centrals;
|
||||
}
|
||||
|
||||
$centrals = $this->api->isConnectionValid() ? $this->api->getCentrals() : [];
|
||||
|
||||
$result = [];
|
||||
foreach ($centrals as $central) {
|
||||
$result[$central['id']] = $central['nazev'];
|
||||
}
|
||||
|
||||
setCache('flexiBeeCentrals', $result, 3600);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací sklady ve FlexiBee.
|
||||
*/
|
||||
protected function getFlexiBeeStores(): array
|
||||
{
|
||||
if ($stores = getCache('flexiBeeStores')) {
|
||||
return $stores;
|
||||
}
|
||||
|
||||
$stores = $this->api->isConnectionValid() ? $this->api->getStores(true) : [];
|
||||
|
||||
$result = [];
|
||||
foreach ($stores as $store) {
|
||||
$result[$store['id']] = $store['nazev'];
|
||||
}
|
||||
|
||||
setCache('flexiBeeStores', $result, 3600);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function getFlexiBeePriceLists(): array
|
||||
{
|
||||
$pricelists = $this->api->isConnectionValid() ? $this->api->getPriceLists() : [];
|
||||
|
||||
$result[0] = 'Všechny ceníky';
|
||||
foreach ($pricelists as $pricelist) {
|
||||
$result[$pricelist['id']] = $pricelist['nazev'];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function isConfigValid(): bool
|
||||
{
|
||||
try {
|
||||
$this->configuration->getAPIConfig();
|
||||
} catch (FlexiBeeException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function isChangesApiAvailable(): bool
|
||||
{
|
||||
if (getCache('flexiBeeActiveAPI')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($result = $this->api->isConnectionValid() && $this->api->isChangesAPIEnabled()) {
|
||||
setCache('flexiBeeActiveAPI', $result, 3600);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
11
bundles/External/FlexiBeeBundle/Admin/lang/czech/flexibee.php
vendored
Normal file
11
bundles/External/FlexiBeeBundle/Admin/lang/czech/flexibee.php
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
$txt_str['flexibee'] = [
|
||||
'synchronizer_order' => 'Objednávky',
|
||||
'synchronizer_price' => 'Ceny',
|
||||
'synchronizer_product' => 'Produkty',
|
||||
'synchronizer_store' => 'Sklady',
|
||||
'synchronizer_supply' => 'Skladová zásoba',
|
||||
'synchronizer_user' => 'Uživatelé',
|
||||
'synchronizer_pricelist' => 'Ceníky',
|
||||
];
|
||||
1
bundles/External/FlexiBeeBundle/Admin/templates/actions/orderSendToFlexiAction.tpl
vendored
Normal file
1
bundles/External/FlexiBeeBundle/Admin/templates/actions/orderSendToFlexiAction.tpl
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{extends "[AdminBundle]actions/baseAction.tpl"}
|
||||
286
bundles/External/FlexiBeeBundle/Admin/templates/window/settings.flexibee.tpl
vendored
Normal file
286
bundles/External/FlexiBeeBundle/Admin/templates/window/settings.flexibee.tpl
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
<div id="flapFlexiBeeSettings" class="tab-pane fade in boxFlex">
|
||||
<div class="row bottom-space">
|
||||
<div class="col-md-12">
|
||||
<h1 class="h4 main-panel-title">Nastavení API</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="disable_autoload[flexibee]" value="1">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>Adresa FlexiBee</label>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<input type="text" class="form-control input-sm" name="data[flexibee][api][url]"
|
||||
value="{$body.data.flexibee.api.url}">
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-1">
|
||||
<button type="submit" class="btn btn-success btn-block" name="acn" value="checkConnection">Ověřit připojení</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>Firma</label>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<input type="text" class="form-control input-sm" name="data[flexibee][api][company]"
|
||||
value="{$body.data.flexibee.api.company}">
|
||||
</div>
|
||||
{if $tab.data.changesApiButton}
|
||||
<div class="col-md-3 col-md-offset-1">
|
||||
<button type="submit" class="btn btn-success btn-block" name="acn" value="changesAPIEnable">Zapnout ChangesAPI</button>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label two-lines">
|
||||
<label>Uživatelské jméno</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control input-sm" name="data[flexibee][api][user]"
|
||||
value="{$body.data.flexibee.api.user}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 control-label">
|
||||
<label>Heslo</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control input-sm" name="data[flexibee][api][password]" autocomplete="new-password"
|
||||
value="{$body.data.flexibee.api.password}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row bottom-space">
|
||||
<div class="col-md-12">
|
||||
<h1 class="h4 main-panel-title">Nastavení synchronizace</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" data-toggle="collapse" data-target="#flexibee_sync">
|
||||
<p style="margin-left: 10px;"><strong>Obecné</strong></p>
|
||||
</div>
|
||||
<div id="flexibee_sync" class="panel-collapse collapse in">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>Povolené synchronizace</label>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<select class="selecter" multiple name="data[flexibee][config][enabled_types][]">
|
||||
{foreach $tab.data.flexibeeData.synchronizers as $type => $name}
|
||||
<option value="{$type}"
|
||||
{if $body.data.flexibee.config.enabled_types && in_array($type, $body.data.flexibee.config.enabled_types)}selected{/if}>{$name}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" data-toggle="collapse" data-target="#flexibee_sync_stores">
|
||||
<p style="margin-left: 10px;"><strong>Sklady</strong></p>
|
||||
</div>
|
||||
<div id="flexibee_sync_stores" class="panel-collapse collapse in">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>Synchronizované sklady</label>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<select class="selecter" multiple name="data[flexibee][config][stores][]">
|
||||
{foreach $tab.data.flexibeeData.stores as $storeId => $store}
|
||||
<option value="{$storeId}"
|
||||
{if $body.data.flexibee.config.stores && in_array($storeId, $body.data.flexibee.config.stores)}selected{/if}>{$store}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-1">
|
||||
<button type="submit" class="btn btn-success btn-block" name="acn" value="synchronizeStores">
|
||||
Sesynchronizovat sklady
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" data-toggle="collapse" data-target="#flexibee_sync_orders">
|
||||
<p style="margin-left: 10px;"><strong>Objednávky</strong></p>
|
||||
</div>
|
||||
<div id="flexibee_sync_orders" class="panel-collapse collapse in">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>Středisko pro zápis</label>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<select class="selecter" name="data[flexibee][config][order][central]">
|
||||
<option value="">-- nevybráno --</option>
|
||||
{foreach $tab.data.flexibeeData.centrals as $centralId => $name}
|
||||
<option value="{$centralId}"
|
||||
{if $body.data.flexibee.config.order.central == $centralId}selected{/if}>{$name}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>Výchozí uživatel</label>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<input class="input-sm form-control" name="data[flexibee][config][order][id_user]"
|
||||
value="{$body.data.flexibee.config.order.id_user}" placeholder="ID uživatele ve FlexiBee">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>Chybový stav</label>
|
||||
<a class="help-tip" data-toggle="tooltip"
|
||||
title="" data-original-title="Objednávky, u kterých dojde k 10 neuspěšným pokusům o synchronizaci,
|
||||
budou přepnuty do tohoto stavu">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<select class="selecter" name="data[flexibee][config][order][error_status]">
|
||||
<option value="">-- neměnit stav --</option>
|
||||
{foreach getOrderStatuses() as $key => $orderStatus}
|
||||
<option value="{$key}"
|
||||
{if $body.data.flexibee.config.order.error_status == $key}selected{/if}>{$orderStatus.name}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>
|
||||
Obousměrná synchronizace
|
||||
<a class="help-tip" data-toggle="tooltip"
|
||||
title="" data-original-title="Zapnutím oboustranné synchronizace se začnou synchronizovat změny objednávek směrem
|
||||
z FlexiBee do e-shopu. Tzn. že pokud například přidáte k objednávce ve FlexiBee novou položku, tak se položka
|
||||
přenese i do objednávky na e-shopu. ">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
{print_toggle nameRaw='data[flexibee][config][order][duplex_sync]' value= $body.data.flexibee.config.order.duplex_sync}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="border-bottom: 1px solid #e6e9ed; margin: 5px 0 10px; padding-bottom: 6px;">
|
||||
<strong>Změny stavů</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{foreach $tab.data.flexibeeData.statuses as $status}
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>{$status}</label>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select class="selecter" name="data[flexibee][config][order][statuses][{$status}][status]">
|
||||
<option value="">-- neměnit stav --</option>
|
||||
{foreach getOrderStatuses() as $key => $orderStatus}
|
||||
<option value="{$key}" {if $body.data.flexibee.config.order.statuses[$status].status !== '' && $body.data.flexibee.config.order.statuses[$status].status !== null && $body.data.flexibee.config.order.statuses[$status].status == $key}selected{/if}>{$orderStatus.name}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<select class="selecter" name="data[flexibee][config][order][statuses][{$status}][order_message]">
|
||||
<option value="">-- neodesílat zprávu uživateli --</option>
|
||||
{foreach $tab.data.orderMessages as $orderMessage}
|
||||
<option value="{$orderMessage.id}" {if $body.data.flexibee.config.order.statuses[$status].order_message == $orderMessage.id}selected{/if}>
|
||||
{$orderMessage.name}
|
||||
</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" data-toggle="collapse" data-target="#flexibee_sync_delivery_payment">
|
||||
<p style="margin-left: 10px;"><strong>Dopravy a platby</strong></p>
|
||||
</div>
|
||||
<div id="flexibee_sync_delivery_payment" class="panel-collapse collapse in">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="border-bottom: 1px solid #e6e9ed; margin: 5px 0 10px; padding-bottom: 6px;">
|
||||
<strong>Dopravy</strong>
|
||||
</div>
|
||||
</div>
|
||||
{foreach $tab.data.deliveries as $delivery}
|
||||
<div class="form-group">
|
||||
<div class="col-md-4 control-label">
|
||||
<label>{$delivery.name}</label>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<input class="input-sm form-control" name="data[flexibee][config][delivery][{$delivery.id}]"
|
||||
value="{$body.data.flexibee.config.delivery[$delivery.id]}" placeholder="Kód ve FlexiBee">
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="row">
|
||||
<div class="col-md-12" style="border-bottom: 1px solid #e6e9ed; margin: 5px 0 10px; padding-bottom: 6px;">
|
||||
<strong>Platby</strong>
|
||||
</div>
|
||||
</div>
|
||||
{foreach $tab.data.payments as $payment}
|
||||
<div class="form-group">
|
||||
<div class="col-md-4 control-label">
|
||||
<label>{$payment.name}</label>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<input class="input-sm form-control" name="data[flexibee][config][payment][{$payment.id}]"
|
||||
value="{$body.data.flexibee.config.payment[$payment.id]}" placeholder="Kód ve FlexiBee">
|
||||
</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" data-toggle="collapse" data-target="#flexibee_sync_pricelists">
|
||||
<p style="margin-left: 10px;"><strong>Ceníky</strong></p>
|
||||
</div>
|
||||
<div id="flexibee_sync_pricelists" class="panel-collapse collapse in">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<div class="col-md-2 control-label">
|
||||
<label>Synchronizované ceníky</label>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<select class="selecter" multiple name="data[flexibee][config][pricelists][]">
|
||||
{foreach $tab.data.flexibeeData.pricelists as $pricelistId => $pricelist}
|
||||
<option value="{$pricelistId}"
|
||||
{if $body.data.flexibee.config.pricelists && in_array($pricelistId, $body.data.flexibee.config.pricelists)}selected{/if}>{$pricelist}</option>
|
||||
{/foreach}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
75
bundles/External/FlexiBeeBundle/Controller/FlexiBeeController.php
vendored
Normal file
75
bundles/External/FlexiBeeBundle/Controller/FlexiBeeController.php
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Controller;
|
||||
|
||||
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
||||
use External\FlexiBeeBundle\Synchronizers\OrderSynchronizer;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeLocator;
|
||||
use KupShop\AdminBundle\AdminRequiredControllerInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class FlexiBeeController extends AbstractController implements AdminRequiredControllerInterface
|
||||
{
|
||||
/**
|
||||
* @Route("/_flexi/dump_order/{id_order}/")
|
||||
* @Route("/_flexi/dump_order/{id_order}/{type}/", requirements={"type"="(create|realize)"})
|
||||
*/
|
||||
public function dumpOrder(OrderSynchronizer $orderSynchronizer, int $id_order, ?string $type = null): JsonResponse
|
||||
{
|
||||
try {
|
||||
$order = \Order::get($id_order);
|
||||
} catch (\Throwable $e) {
|
||||
throw new NotFoundHttpException('Order not found');
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'realize':
|
||||
$data = [
|
||||
'realize' => $orderSynchronizer->getOrderRealizeData($order),
|
||||
'invoice' => $orderSynchronizer->getOrderInvoiceData($order),
|
||||
];
|
||||
break;
|
||||
default:
|
||||
$data = $orderSynchronizer->getOrderData($order);
|
||||
}
|
||||
|
||||
return new JsonResponse(
|
||||
$data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/_flexi/{type}/")
|
||||
*/
|
||||
public function syncType(FlexiBeeLocator $locator, string $type): JsonResponse
|
||||
{
|
||||
try {
|
||||
$synchronizer = $locator->getServiceByType($type);
|
||||
$synchronizer->sync();
|
||||
} catch (FlexiBeeException $e) {
|
||||
return new JsonResponse(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
return new JsonResponse(['success' => true, 'message' => sprintf('Synchronization of "%s" done', $type)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route("/_flexi/{type}/{id}/")
|
||||
*/
|
||||
public function syncSingleItem(FlexiBeeLocator $locator, string $type, int $id): JsonResponse
|
||||
{
|
||||
try {
|
||||
$synchronizer = $locator->getServiceByType($type);
|
||||
$synchronizer->syncSingleItem($id);
|
||||
} catch (FlexiBeeException $e) {
|
||||
return new JsonResponse(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
return new JsonResponse(['success' => true, 'message' => 'Single item synchronization completed']);
|
||||
}
|
||||
}
|
||||
46
bundles/External/FlexiBeeBundle/EventSubscriber/OrderSubscriber.php
vendored
Normal file
46
bundles/External/FlexiBeeBundle/EventSubscriber/OrderSubscriber.php
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\EventSubscriber;
|
||||
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeUtil;
|
||||
use KupShop\OrderingBundle\Event\OrderEvent;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
|
||||
class OrderSubscriber implements EventSubscriberInterface
|
||||
{
|
||||
private FlexiBeeUtil $flexiBeeUtil;
|
||||
|
||||
public function __construct(FlexiBeeUtil $flexiBeeUtil)
|
||||
{
|
||||
$this->flexiBeeUtil = $flexiBeeUtil;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
OrderEvent::ORDER_PAID => [
|
||||
['updateFlexiBeeOrder', 200],
|
||||
],
|
||||
OrderEvent::ORDER_EDITED => [
|
||||
['updateFlexiBeeOrder', 200],
|
||||
],
|
||||
OrderEvent::ORDER_STORNO => [
|
||||
['updateFlexiBeeOrder', 200],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function updateFlexiBeeOrder(OrderEvent $event): void
|
||||
{
|
||||
$order = $event->getOrder();
|
||||
|
||||
// synchronizace na zaklade toho znovu odesle objednavku do FlexiBee - aktualizuje ji
|
||||
$this->flexiBeeUtil->setFlexiOrderData(
|
||||
(int) $order->id,
|
||||
'orderUpdated',
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
||||
9
bundles/External/FlexiBeeBundle/Exception/FlexiBeeException.php
vendored
Normal file
9
bundles/External/FlexiBeeBundle/Exception/FlexiBeeException.php
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Exception;
|
||||
|
||||
class FlexiBeeException extends \Exception
|
||||
{
|
||||
}
|
||||
19
bundles/External/FlexiBeeBundle/FlexiBeeBundle.php
vendored
Normal file
19
bundles/External/FlexiBeeBundle/FlexiBeeBundle.php
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle;
|
||||
|
||||
use External\FlexiBeeBundle\Synchronizers\SynchronizerInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class FlexiBeeBundle extends Bundle
|
||||
{
|
||||
public function build(ContainerBuilder $container)
|
||||
{
|
||||
$container->registerForAutoconfiguration(SynchronizerInterface::class)
|
||||
->addTag('flexibee.synchronizer')
|
||||
->setAutowired(true);
|
||||
}
|
||||
}
|
||||
40
bundles/External/FlexiBeeBundle/Inspections/FlexiBeeInspection.php
vendored
Normal file
40
bundles/External/FlexiBeeBundle/Inspections/FlexiBeeInspection.php
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Inspections;
|
||||
|
||||
use Composer\InstalledVersions;
|
||||
use KupShop\SystemInspectionBundle\Inspections\Compile\CompileInspectionInterface;
|
||||
use KupShop\SystemInspectionBundle\Inspections\Inspection;
|
||||
use KupShop\SystemInspectionBundle\InspectionWriters\MessageTypes\ComposerMissingMessage;
|
||||
use KupShop\SystemInspectionBundle\InspectionWriters\MessageTypes\SimpleMessage;
|
||||
|
||||
class FlexiBeeInspection extends Inspection implements CompileInspectionInterface
|
||||
{
|
||||
public function runInspection(): ?array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
// check for stores module
|
||||
if (!findModule(\Modules::STORES)) {
|
||||
$errors[] = new SimpleMessage('Stores module is required when FlexiBeeBundle is enabled!');
|
||||
}
|
||||
|
||||
// check for synchronization module
|
||||
if (!findModule(\Modules::SYNCHRONIZATION)) {
|
||||
$errors[] = new SimpleMessage('Synchronization module is required when FlexiBeeBundle is enabled!');
|
||||
}
|
||||
|
||||
// check for composer package
|
||||
try {
|
||||
InstalledVersions::getVersion('spojenet/flexibee');
|
||||
} catch (\Exception $e) {
|
||||
$errors[] = new ComposerMissingMessage(
|
||||
'Missing required composer dependency "spojenet/flexibee"! Run `composer require spojenet/flexibee` to fix it.'
|
||||
);
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
3
bundles/External/FlexiBeeBundle/Resources/config/routing.yml
vendored
Normal file
3
bundles/External/FlexiBeeBundle/Resources/config/routing.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
FlexiBeeBundle:
|
||||
resource: "@FlexiBeeBundle/Controller/"
|
||||
type: annotation
|
||||
10
bundles/External/FlexiBeeBundle/Resources/config/services.yml
vendored
Normal file
10
bundles/External/FlexiBeeBundle/Resources/config/services.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
services:
|
||||
_defaults:
|
||||
autoconfigure: true
|
||||
autowire: true
|
||||
|
||||
External\FlexiBeeBundle\:
|
||||
resource: ../../{Admin/Tabs,Admin/Actions,Controller,EventSubscriber,Inspections,SynchronizationRegister,Synchronizers,Util}
|
||||
|
||||
External\FlexiBeeBundle\Util\FlexiBeeLocator:
|
||||
arguments: [!tagged_locator { tag: 'flexibee.synchronizer', index_by: 'key', default_index_method: 'getType' }]
|
||||
22
bundles/External/FlexiBeeBundle/Resources/script/RunSynchronizationScript.php
vendored
Normal file
22
bundles/External/FlexiBeeBundle/Resources/script/RunSynchronizationScript.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace External\FlexiBeeBundle\Resources\script;
|
||||
|
||||
use External\FlexiBeeBundle\Synchronizers\BaseSynchronizer;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeUtil;
|
||||
use KupShop\AdminBundle\Util\Script\Script;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
|
||||
class RunSynchronizationScript extends Script
|
||||
{
|
||||
protected static $name = '[FlexiBee]RunSynchronization';
|
||||
protected static $defaultParameters = ['types' => ['product'], 'mode' => BaseSynchronizer::MODE_FULL];
|
||||
|
||||
protected function run(array $arguments)
|
||||
{
|
||||
$util = ServiceContainer::getService(FlexiBeeUtil::class);
|
||||
$util->synchronize($arguments['types'], $arguments['mode']);
|
||||
}
|
||||
}
|
||||
|
||||
return RunSynchronizationScript::class;
|
||||
96
bundles/External/FlexiBeeBundle/Resources/upgrade/FlexiBeeUpgrade.php
vendored
Normal file
96
bundles/External/FlexiBeeBundle/Resources/upgrade/FlexiBeeUpgrade.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace External\FlexiBeeBundle\Resources\upgrade;
|
||||
|
||||
class FlexiBeeUpgrade extends \UpgradeNew
|
||||
{
|
||||
protected $priority = 100;
|
||||
|
||||
public function check_FlexiProducts(): bool
|
||||
{
|
||||
return $this->checkTableExists('flexi_products');
|
||||
}
|
||||
|
||||
/** Add table flexi_products */
|
||||
public function upgrade_FlexiProducts(): void
|
||||
{
|
||||
sqlQuery('CREATE TABLE flexi_products (
|
||||
id_flexi INT(11) NOT NULL PRIMARY KEY ,
|
||||
id_product INT(11) NOT NULL,
|
||||
id_variation INT(11) DEFAULT NULL,
|
||||
CONSTRAINT FK_flexi_products_id_product FOREIGN KEY (id_product) REFERENCES products(id) ON DELETE CASCADE,
|
||||
CONSTRAINT FK_flexi_products_id_variation FOREIGN KEY (id_variation) REFERENCES products_variations(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_FlexiOrders(): bool
|
||||
{
|
||||
return $this->checkTableExists('flexi_orders');
|
||||
}
|
||||
|
||||
/** Add table flexi_orders */
|
||||
public function upgrade_FlexiOrders(): void
|
||||
{
|
||||
sqlQuery('CREATE TABLE flexi_orders (
|
||||
id_flexi INT(11) NOT NULL PRIMARY KEY ,
|
||||
id_order INT(10) UNSIGNED NOT NULL,
|
||||
data MEDIUMTEXT DEFAULT NULL,
|
||||
CONSTRAINT FK_flexi_orders_id_order FOREIGN KEY (id_order) REFERENCES orders(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_FlexiUsers(): bool
|
||||
{
|
||||
return $this->checkTableExists('flexi_users');
|
||||
}
|
||||
|
||||
/** Add table flexi_users */
|
||||
public function upgrade_FlexiUsers(): void
|
||||
{
|
||||
sqlQuery('CREATE TABLE flexi_users (
|
||||
id_flexi INT(11) NOT NULL PRIMARY KEY ,
|
||||
id_user INT(11) UNSIGNED NOT NULL,
|
||||
CONSTRAINT FK_flexi_users_id_user FOREIGN KEY (id_user) REFERENCES users(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_FlexiStores(): bool
|
||||
{
|
||||
return $this->checkTableExists('flexi_stores');
|
||||
}
|
||||
|
||||
/** Add table flexi_stores */
|
||||
public function upgrade_FlexiStores(): void
|
||||
{
|
||||
sqlQuery('CREATE TABLE flexi_stores (
|
||||
id_flexi INT(11) NOT NULL PRIMARY KEY ,
|
||||
id_store INT(11) NOT NULL,
|
||||
CONSTRAINT FK_flexi_stores_id_store FOREIGN KEY (id_store) REFERENCES stores(id) ON DELETE CASCADE
|
||||
)');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_FlexiPricelists(): bool
|
||||
{
|
||||
return $this->checkTableExists('flexi_pricelists');
|
||||
}
|
||||
|
||||
/** Add table flexi_pricelists */
|
||||
public function upgrade_FlexiPricelists(): void
|
||||
{
|
||||
sqlQuery('CREATE TABLE flexi_pricelists (
|
||||
id_flexi INT(11) NOT NULL PRIMARY KEY ,
|
||||
id_pricelist INT(11) NOT NULL,
|
||||
CONSTRAINT FK_flexi_pricelists_id_pricelist FOREIGN KEY (id_pricelist) REFERENCES pricelists(id) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
}
|
||||
65
bundles/External/FlexiBeeBundle/SynchronizationRegister/FlexiBeeSynchronizationRegister.php
vendored
Normal file
65
bundles/External/FlexiBeeBundle/SynchronizationRegister/FlexiBeeSynchronizationRegister.php
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\SynchronizationRegister;
|
||||
|
||||
use External\FlexiBeeBundle\Synchronizers\OrderSynchronizer;
|
||||
use External\FlexiBeeBundle\Synchronizers\PriceListSynchronizer;
|
||||
use External\FlexiBeeBundle\Synchronizers\PriceSynchronizer;
|
||||
use External\FlexiBeeBundle\Synchronizers\ProductSynchronizer;
|
||||
use External\FlexiBeeBundle\Synchronizers\StoreSynchronizer;
|
||||
use External\FlexiBeeBundle\Synchronizers\SupplySynchronizer;
|
||||
use External\FlexiBeeBundle\Synchronizers\UserSynchronizer;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeConfiguration;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeUtil;
|
||||
use KupShop\SynchronizationBundle\Synchronization\Job;
|
||||
use KupShop\SynchronizationBundle\Synchronization\SynchronizationRegisterInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class FlexiBeeSynchronizationRegister implements SynchronizationRegisterInterface
|
||||
{
|
||||
private FlexiBeeConfiguration $configuration;
|
||||
private FlexiBeeUtil $util;
|
||||
|
||||
public function __construct(FlexiBeeConfiguration $configuration, FlexiBeeUtil $util)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->util = $util;
|
||||
}
|
||||
|
||||
public function getJobs(): iterable
|
||||
{
|
||||
// hlavni synchronizacni thread
|
||||
yield Job::create('FlexiBee::main', fn (OutputInterface $output) => $this->synchronize($output))
|
||||
->everyMinute(5);
|
||||
// zalozeni skladu - nepotrebuju to spoustet furt, tak to oddelim od toho hlavniho threadu
|
||||
yield Job::create('FlexiBee::stores', fn () => $this->util->synchronize([StoreSynchronizer::getType()]))
|
||||
->hourly();
|
||||
}
|
||||
|
||||
private function synchronize(OutputInterface $output): void
|
||||
{
|
||||
// Tady mam serazene synchronizace tak, jak by meli jit po sobe
|
||||
$synchronizers = [
|
||||
UserSynchronizer::getType(),
|
||||
OrderSynchronizer::getType(),
|
||||
ProductSynchronizer::getType(),
|
||||
SupplySynchronizer::getType(),
|
||||
PriceSynchronizer::getType(),
|
||||
PriceListSynchronizer::getType(),
|
||||
];
|
||||
|
||||
// Projdu serazene typy synchronizaci
|
||||
foreach ($synchronizers as $synchronizer) {
|
||||
// Pokud neni typ povoleny, tak skipuju
|
||||
if (!in_array($synchronizer, $this->configuration->getEnabledSynchronizerTypes())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$output->writeln(sprintf('[FlexiBee] Running synchronization: %s...', $synchronizer));
|
||||
$this->util->synchronize([$synchronizer]);
|
||||
$output->writeln(sprintf('[FlexiBee] Completed synchronization: %s', $synchronizer));
|
||||
}
|
||||
}
|
||||
}
|
||||
229
bundles/External/FlexiBeeBundle/Synchronizers/BaseSynchronizer.php
vendored
Normal file
229
bundles/External/FlexiBeeBundle/Synchronizers/BaseSynchronizer.php
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Synchronizers;
|
||||
|
||||
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeApi;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeConfiguration;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeLogger;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeUtil;
|
||||
|
||||
abstract class BaseSynchronizer implements SynchronizerInterface
|
||||
{
|
||||
public const MODE_NORMAL = 0;
|
||||
public const MODE_FULL = 1;
|
||||
|
||||
protected static string $type;
|
||||
protected static ?string $evidenceClass = null;
|
||||
|
||||
protected bool $logging = true;
|
||||
protected int $mode = self::MODE_NORMAL;
|
||||
|
||||
protected FlexiBeeLogger $logger;
|
||||
protected FlexiBeeConfiguration $configuration;
|
||||
protected FlexiBeeApi $flexiBeeApi;
|
||||
protected FlexiBeeUtil $flexiBeeUtil;
|
||||
|
||||
protected ?int $tmpLastSync = null;
|
||||
|
||||
public function __construct(
|
||||
FlexiBeeConfiguration $configuration,
|
||||
FlexiBeeApi $flexiBeeApi,
|
||||
FlexiBeeUtil $flexiBeeUtil,
|
||||
FlexiBeeLogger $logger,
|
||||
) {
|
||||
$this->configuration = $configuration;
|
||||
$this->flexiBeeApi = $flexiBeeApi;
|
||||
$this->flexiBeeUtil = $flexiBeeUtil;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public static function getType(): string
|
||||
{
|
||||
return static::$type;
|
||||
}
|
||||
|
||||
public function sync(): void
|
||||
{
|
||||
$this->process(
|
||||
$this->getLastSyncTime()
|
||||
);
|
||||
|
||||
$this->updateLastSyncTime();
|
||||
}
|
||||
|
||||
public function syncSingleItem(int $id, ?int $flexiId = null): void
|
||||
{
|
||||
throw new FlexiBeeException(sprintf('Single item synchronization is not supported for type "%s"', static::getType()));
|
||||
}
|
||||
|
||||
protected function process(?int $lastSyncTime = null): void
|
||||
{
|
||||
foreach ($this->getItems($lastSyncTime) as $item) {
|
||||
$this->logItem((array) $item);
|
||||
$this->processItem($item);
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected function processItem(array $item): void;
|
||||
|
||||
protected function getItems(?int $lastSyncTime = null): iterable
|
||||
{
|
||||
if (!static::$evidenceClass) {
|
||||
throw new FlexiBeeException(
|
||||
sprintf('Class "%s" should overwrite "%s" method', get_class($this), __FUNCTION__)
|
||||
);
|
||||
}
|
||||
|
||||
// Pokud je nastavena evidence class, tak pro nacteni zmen pouziju ChangesAPI
|
||||
|
||||
$changesMaxVersion = $this->getLastVersion();
|
||||
|
||||
// ukladam si startTime, abych mohl limitovat maximalni dobu, kterou muze sync v ramci jednoho sync cyklu bezet
|
||||
$startTime = microtime(true);
|
||||
|
||||
do {
|
||||
// nactu si zmeny od posledni synchronizace
|
||||
$changes = $this->flexiBeeApi->getChanges($changesMaxVersion, static::$evidenceClass);
|
||||
// ulozim si maximalni verzi
|
||||
$changesMaxVersion = (int) max($changesMaxVersion, !empty($changes) ? (max(array_map(fn ($x) => $x['@in-version'], $changes)) + 1) : 0);
|
||||
// nactu si data podle ID zmen
|
||||
$changesIds = array_unique(array_map(fn ($x) => $x['id'], $changes));
|
||||
if (!empty($changesIds)) {
|
||||
// zavolam preprocess kvuli pripadnym modifikacim dat
|
||||
$changedItems = $this->preprocessChangedItems(
|
||||
$this->flexiBeeApi->getEvidenceDataByIds(static::$evidenceClass, $changesIds, filters: $this->getItemsFilter())
|
||||
);
|
||||
// zacnu vracet polozky, aby se v synchronizaci zpracovali
|
||||
foreach ($changedItems as $item) {
|
||||
yield $item;
|
||||
}
|
||||
}
|
||||
|
||||
// aktualizuju si posledni sesynchronizovanou verzi
|
||||
$this->updateLastVersion($changesMaxVersion);
|
||||
|
||||
$isTimedOut = (microtime(true) - $startTime) > 300;
|
||||
// pokud sync bezi uz dele jak 5 minut, tak ji ukoncim
|
||||
// pravdepodobne ve Flexi vzniklo hodne zmen, tak to postupne syncneme nez blokovat sync na dlouhou dobu
|
||||
if ($this->mode === self::MODE_NORMAL && $isTimedOut) {
|
||||
break;
|
||||
}
|
||||
} while (!empty($changes));
|
||||
}
|
||||
|
||||
protected function preprocessChangedItems(array $items): array
|
||||
{
|
||||
return $items;
|
||||
}
|
||||
|
||||
protected function logItem(array $item): void
|
||||
{
|
||||
if (!$this->logging) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->data(
|
||||
sprintf('[FlexiBee] Processing change of \'%s\'', static::getType()),
|
||||
[
|
||||
'Data' => $item,
|
||||
'Type' => static::getType(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
protected function logUpdate(array $item): void
|
||||
{
|
||||
if (!$this->logging) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->data(
|
||||
sprintf('[FlexiBee] Sending to Flexi change of \'%s\'', static::getType()),
|
||||
[
|
||||
'Data' => $item,
|
||||
'Type' => static::getType(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
protected function getLastVersion(?string $type = null): int
|
||||
{
|
||||
if ($this->mode === self::MODE_FULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$flexiBee = $dbcfg->loadValue('flexibee') ?: [];
|
||||
|
||||
return !empty($flexiBee['sync_versions'][$type ?: static::getType()]) ? (int) $flexiBee['sync_versions'][$type ?: static::getType()] : 0;
|
||||
}
|
||||
|
||||
protected function updateLastVersion(int $version): void
|
||||
{
|
||||
if ($this->mode === self::MODE_FULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$flexiBee = $dbcfg->loadValue('flexibee') ?: [];
|
||||
|
||||
$flexiBee['sync_versions'][static::getType()] = $version;
|
||||
|
||||
$dbcfg->saveValue('flexibee', $flexiBee);
|
||||
}
|
||||
|
||||
protected function getLastSyncTime(?string $type = null): ?int
|
||||
{
|
||||
if ($this->mode === self::MODE_FULL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$flexiBee = $dbcfg->loadValue('flexibee') ?: [];
|
||||
|
||||
$this->tmpLastSync = time() - (60 * 5);
|
||||
|
||||
return $flexiBee['timestamps'][$type ?: static::getType()] ?? null;
|
||||
}
|
||||
|
||||
protected function updateLastSyncTime(): void
|
||||
{
|
||||
if ($this->mode === self::MODE_FULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$flexiBee = $dbcfg->loadValue('flexibee') ?: [];
|
||||
|
||||
$flexiBee['timestamps'][static::getType()] = $this->tmpLastSync ?: (time() - (60 * 5));
|
||||
|
||||
$dbcfg->saveValue('flexibee', $flexiBee);
|
||||
}
|
||||
|
||||
protected function createDateTime(?int $timestamp): ?\DateTime
|
||||
{
|
||||
if (!$timestamp) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$datetime = new \DateTime();
|
||||
$datetime->setTimestamp($timestamp);
|
||||
|
||||
return $datetime;
|
||||
}
|
||||
|
||||
public function setMode(int $mode): self
|
||||
{
|
||||
$this->mode = $mode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getItemsFilter(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
988
bundles/External/FlexiBeeBundle/Synchronizers/OrderSynchronizer.php
vendored
Normal file
988
bundles/External/FlexiBeeBundle/Synchronizers/OrderSynchronizer.php
vendored
Normal 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';
|
||||
}
|
||||
}
|
||||
145
bundles/External/FlexiBeeBundle/Synchronizers/PriceListSynchronizer.php
vendored
Normal file
145
bundles/External/FlexiBeeBundle/Synchronizers/PriceListSynchronizer.php
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Synchronizers;
|
||||
|
||||
use External\FlexiBeeBundle\AbraFlexiTypes\Odberatel;
|
||||
use KupShop\PricelistBundle\Util\PriceListWorker;
|
||||
use Query\Operator;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class PriceListSynchronizer extends BaseSynchronizer
|
||||
{
|
||||
protected static string $type = 'pricelist';
|
||||
protected static ?string $evidenceClass = Odberatel::class;
|
||||
|
||||
protected PriceListWorker $priceListWorker;
|
||||
|
||||
#[Required]
|
||||
final public function setPriceListWorker(PriceListWorker $priceListWorker): void
|
||||
{
|
||||
$this->priceListWorker = $priceListWorker;
|
||||
}
|
||||
|
||||
public function syncSingleItem(int $id, ?int $flexiId = null): void
|
||||
{
|
||||
$flexiId ??= $this->flexiBeeUtil->getProductFlexiId($id);
|
||||
|
||||
if (!$flexiId) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->flexiBeeApi->getPricelistPrices(flexiProductIds: [$flexiId]) as $item) {
|
||||
$this->processItem($item);
|
||||
}
|
||||
}
|
||||
|
||||
protected function processItem(array $item): void
|
||||
{
|
||||
// zajimaji nas jen zaznamy s nastavenou Cenikovou skupinou, protoze bez toho nepozname o ktery cenik se jedna
|
||||
if (empty($item['skupCen']) || empty($item['skupCen']->ref)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$flexiId = $this->flexiBeeUtil->getFlexiIdFromRef($item['skupCen']->ref);
|
||||
|
||||
if (!in_array($flexiId, $this->configuration->getEnabledPricelists())
|
||||
&& !in_array(0, $this->configuration->getEnabledPricelists())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!($priceListId = $this->flexiBeeUtil->getMapping(static::getType(), $flexiId))) {
|
||||
$priceListId = $this->createOrAssignPriceList($flexiId, $item);
|
||||
}
|
||||
|
||||
$this->updateProductPriceListPrice($priceListId, $this->getInfo($item, 'cenik'), $item['prodejCena']);
|
||||
}
|
||||
|
||||
protected function getItems(?int $lastSyncTime = null): iterable
|
||||
{
|
||||
if ($this->mode === self::MODE_NORMAL) {
|
||||
return parent::getItems($lastSyncTime);
|
||||
}
|
||||
|
||||
if ($this->mode === self::MODE_FULL) {
|
||||
return $this->flexiBeeApi->getPricelistPrices();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function createOrAssignPriceList(int $id, array $item): int
|
||||
{
|
||||
$name = $this->getInfo($item, 'skupCen');
|
||||
|
||||
$currencyCode = $this->flexiBeeUtil->parseFlexiCode($item['mena']?->value) ?? 'CZK';
|
||||
|
||||
$priceList = $this->priceListWorker->findPriceList($name, $currencyCode);
|
||||
|
||||
if (!$priceList) {
|
||||
return sqlGetConnection()->transactional(function () use ($id, $name, $currencyCode) {
|
||||
sqlQueryBuilder()
|
||||
->insert('pricelists')
|
||||
->directValues(
|
||||
[
|
||||
'name' => $name,
|
||||
'price_history' => 0,
|
||||
'currency' => $currencyCode,
|
||||
]
|
||||
)
|
||||
->execute();
|
||||
|
||||
$priceListId = (int) sqlInsertId();
|
||||
|
||||
$this->flexiBeeUtil->createMapping(static::getType(), $id, $priceListId);
|
||||
|
||||
return $priceListId;
|
||||
});
|
||||
}
|
||||
|
||||
$this->flexiBeeUtil->createMapping(static::getType(), $id, $priceList);
|
||||
|
||||
return $priceList;
|
||||
}
|
||||
|
||||
protected function getInfo(array $item, string $key): string
|
||||
{
|
||||
return explode(':', $item[$key]->value, 2)[1];
|
||||
}
|
||||
|
||||
protected function updateProductPriceListPrice(int $priceListId, string $productCode, float $price, bool $removeVat = true): void
|
||||
{
|
||||
static $productVatCache = [];
|
||||
|
||||
if (!($mapping = $this->flexiBeeUtil->findItemByCode($productCode))) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$productId, $variationId] = $mapping;
|
||||
|
||||
$price = \Decimal::create($price, 4);
|
||||
|
||||
if ($removeVat) {
|
||||
if (!($productVatCache[$productId] ?? false)) {
|
||||
$vat = sqlQueryBuilder()
|
||||
->select('v.vat')
|
||||
->from('products', 'p')
|
||||
->join('p', 'vats', 'v', 'v.id = p.vat')
|
||||
->where(Operator::equals(['p.id' => $productId]))
|
||||
->sendToMaster()
|
||||
->execute()->fetchOne();
|
||||
|
||||
if ($vat === false) {
|
||||
$vat = getVat();
|
||||
}
|
||||
|
||||
$productVatCache[$productId] = toDecimal((float) $vat);
|
||||
}
|
||||
|
||||
$price = $price->removeVat($productVatCache[$productId]);
|
||||
}
|
||||
|
||||
$this->priceListWorker->updatePriceList($priceListId, $productId, $variationId, $price, withDiscountUpdate: false);
|
||||
}
|
||||
}
|
||||
76
bundles/External/FlexiBeeBundle/Synchronizers/PriceSynchronizer.php
vendored
Normal file
76
bundles/External/FlexiBeeBundle/Synchronizers/PriceSynchronizer.php
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Synchronizers;
|
||||
|
||||
use AbraFlexi\Cenik;
|
||||
use Query\Operator;
|
||||
|
||||
class PriceSynchronizer extends BaseSynchronizer
|
||||
{
|
||||
protected static string $type = 'price';
|
||||
protected static ?string $evidenceClass = Cenik::class;
|
||||
|
||||
protected function process(?int $lastSyncTime = null): void
|
||||
{
|
||||
parent::process($lastSyncTime);
|
||||
|
||||
$this->updateProductsPriceByVariations();
|
||||
}
|
||||
|
||||
protected function getItems(?int $lastSyncTime = null): iterable
|
||||
{
|
||||
if ($this->mode === self::MODE_NORMAL) {
|
||||
return parent::getItems($lastSyncTime);
|
||||
}
|
||||
|
||||
if ($this->mode === self::MODE_FULL) {
|
||||
return $this->flexiBeeApi->getPrices();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function syncSingleItem(int $id, ?int $flexiId = null): void
|
||||
{
|
||||
if ($flexiId = $this->flexiBeeUtil->getFlexiId(ProductSynchronizer::getType(), $id)) {
|
||||
$items = $this->preprocessChangedItems([$this->flexiBeeApi->getProductPrices($flexiId)]);
|
||||
$this->processItem(reset($items));
|
||||
}
|
||||
}
|
||||
|
||||
protected function processItem(array $item): void
|
||||
{
|
||||
if (!($mapping = $this->flexiBeeUtil->getItemMapping($item['id']))) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$productId, $variationId] = $mapping;
|
||||
|
||||
if ($variationId) {
|
||||
sqlQueryBuilder()
|
||||
->update('products_variations')
|
||||
->directValues(['price' => $item['cenaZaklBezDph']])
|
||||
->where(Operator::equals(['id' => $variationId]))
|
||||
->execute();
|
||||
} else {
|
||||
sqlQueryBuilder()
|
||||
->update('products')
|
||||
->directValues(['price' => $item['cenaZaklBezDph']])
|
||||
->where(Operator::equals(['id' => $productId]))
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
private function updateProductsPriceByVariations(): void
|
||||
{
|
||||
sqlQuery('UPDATE products p
|
||||
JOIN products_variations pv on p.id = pv.id_product
|
||||
SET p.price = (
|
||||
SELECT COALESCE(MIN(IF(pv2.price=0, null, pv2.price)), p.price)
|
||||
FROM products_variations pv2
|
||||
WHERE pv2.id_product = p.id
|
||||
);');
|
||||
}
|
||||
}
|
||||
334
bundles/External/FlexiBeeBundle/Synchronizers/ProductSynchronizer.php
vendored
Normal file
334
bundles/External/FlexiBeeBundle/Synchronizers/ProductSynchronizer.php
vendored
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Synchronizers;
|
||||
|
||||
use AbraFlexi\Cenik;
|
||||
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
||||
use External\FlexiBeeBundle\Util\FlexiBeeUtil;
|
||||
use KupShop\CatalogBundle\Product\ProductFinder;
|
||||
use KupShop\KupShopBundle\Query\JsonOperator;
|
||||
use KupShop\OSSVatsBundle\Util\VatsUtil;
|
||||
use Query\Operator;
|
||||
use Symfony\Contracts\Service\Attribute\Required;
|
||||
|
||||
class ProductSynchronizer extends BaseSynchronizer
|
||||
{
|
||||
protected static string $type = 'product';
|
||||
|
||||
protected static ?string $evidenceClass = Cenik::class;
|
||||
|
||||
private ProductFinder $productFinder;
|
||||
private VatsUtil $vatsUtil;
|
||||
private SupplySynchronizer $supplySynchronizer;
|
||||
private PriceListSynchronizer $priceListSynchronizer;
|
||||
|
||||
#[Required]
|
||||
public function setProductFinder(ProductFinder $productFinder): void
|
||||
{
|
||||
$this->productFinder = $productFinder;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function setVatsUtil(VatsUtil $vatsUtil): void
|
||||
{
|
||||
$this->vatsUtil = $vatsUtil;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
public function setFlexiBeeUtil(FlexiBeeUtil $flexiBeeUtil): void
|
||||
{
|
||||
$this->flexiBeeUtil = $flexiBeeUtil;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
final public function setSupplySynchronizer(SupplySynchronizer $supplySynchronizer): void
|
||||
{
|
||||
$this->supplySynchronizer = $supplySynchronizer;
|
||||
}
|
||||
|
||||
#[Required]
|
||||
final public function setPriceListSynchronizer(PriceListSynchronizer $priceListSynchronizer): void
|
||||
{
|
||||
$this->priceListSynchronizer = $priceListSynchronizer;
|
||||
}
|
||||
|
||||
protected function processItem(array $item): void
|
||||
{
|
||||
if (empty($item['kod'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$flexiId = (int) $item['id'];
|
||||
|
||||
// kouknout, zda uz mam pro tenhle produkt vytvoreny mapping
|
||||
if (!($mapping = $this->flexiBeeUtil->getItemMapping($flexiId))) {
|
||||
// pokud mapping nemam, tak zkusim najit variantu podle kodu zbozi
|
||||
if ($variationId = $this->flexiBeeUtil->getVariationId($item['kod'])) {
|
||||
$productId = sqlQueryBuilder()
|
||||
->select('id_product')
|
||||
->from('products_variations')
|
||||
->where(Operator::equals(['id' => $variationId]))
|
||||
->execute()->fetchColumn();
|
||||
|
||||
$mapping = [(int) $productId, $variationId];
|
||||
|
||||
// nasel jsem variantu, takze vytvorim mapping
|
||||
$this->flexiBeeUtil->createItemMapping($flexiId, $productId, $variationId);
|
||||
// zkusim najit produkt podle kodu zbozi
|
||||
} elseif ($productId = $this->flexiBeeUtil->getProductId($item['kod'])) {
|
||||
$mapping = [$productId, null];
|
||||
sqlQueryBuilder()
|
||||
->update('products')
|
||||
->directValues(['data' => json_encode(['masterCode' => $item['kod']])])
|
||||
->where(Operator::equals(['id' => $productId]))
|
||||
->execute();
|
||||
|
||||
// smazu pripadny existujici mapping, protoze se mohlo stat, ze mapping uz existoval
|
||||
// stacilo ve flexi vytvorit produkt, sesynchronizovat a pak ho smazat ve flexi
|
||||
// na eshopu uz mapping zustal, ale navic se vytvoril spravny mapping
|
||||
sqlQueryBuilder()
|
||||
->delete('flexi_products')
|
||||
->where(Operator::equals(['id_product' => $productId]))
|
||||
->execute();
|
||||
|
||||
// vytvorim mapping, protoze jsem nasel produkt
|
||||
$this->flexiBeeUtil->createItemMapping($flexiId, $productId);
|
||||
}
|
||||
|
||||
// nemam mapping a ani jsem nanasel produkt / variantu podle kodu
|
||||
$isForEshop = $this->isItemForEshop($item);
|
||||
|
||||
if (!$mapping && (is_null($isForEshop) || $isForEshop)) {
|
||||
// vytvorim product / variantu
|
||||
[$productId, $variationId] = $this->createItem($item);
|
||||
$mapping = [$productId, $variationId];
|
||||
|
||||
// vytvorim mapping
|
||||
$this->flexiBeeUtil->createItemMapping($flexiId, $productId, $variationId);
|
||||
}
|
||||
|
||||
if ($mapping) {
|
||||
$this->onItemCreated($item, ...$mapping);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$mapping) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$productId, $variationId] = $mapping;
|
||||
|
||||
// zpracuju produkt nebo variantu
|
||||
if ($variationId) {
|
||||
$this->processVariation($productId, $variationId, $item);
|
||||
} else {
|
||||
$this->processProduct($productId, $item);
|
||||
}
|
||||
}
|
||||
|
||||
public function processProduct(int $productId, array $item): void
|
||||
{
|
||||
$values = $this->getProductUpdateValues($item);
|
||||
|
||||
sqlQueryBuilder()
|
||||
->update('products')
|
||||
->directValues($values)
|
||||
->where(Operator::equals(['id' => $productId]))
|
||||
->execute();
|
||||
}
|
||||
|
||||
public function processVariation(int $productId, int $variationId, array $item): void
|
||||
{
|
||||
// update variation product
|
||||
sqlQueryBuilder()
|
||||
->update('products')
|
||||
->directValues(
|
||||
[
|
||||
'vat' => $this->productFinder->findVAT($this->getVatByType($item['typSzbDphK'])),
|
||||
]
|
||||
)
|
||||
->where(Operator::equals(['id' => $productId]))
|
||||
->execute();
|
||||
|
||||
// update variation
|
||||
$values = $this->getVariationUpdateValues($item);
|
||||
|
||||
sqlQueryBuilder()
|
||||
->update('products_variations')
|
||||
->directValues($values)
|
||||
->where(Operator::equals(['id' => $variationId]))
|
||||
->execute();
|
||||
}
|
||||
|
||||
protected function getItems(?int $lastSyncTime = null): iterable
|
||||
{
|
||||
if ($this->mode === self::MODE_NORMAL) {
|
||||
return parent::getItems($lastSyncTime);
|
||||
}
|
||||
|
||||
if ($this->mode === self::MODE_FULL) {
|
||||
return $this->flexiBeeApi->getProducts();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function createItem(array $item): array
|
||||
{
|
||||
if ($this->isVariation($item)) {
|
||||
$masterCode = $this->getVariationMasterCode($item);
|
||||
$productId = $this->getProductId($item, $masterCode);
|
||||
|
||||
[$labelId, $labelValue] = $this->getVariationParts($item);
|
||||
|
||||
$id = (int) \Variations::createProductVariation($productId, [
|
||||
$labelId => $labelValue,
|
||||
]);
|
||||
|
||||
$result = [$productId, $id];
|
||||
} else {
|
||||
$id = $this->getProductId($item);
|
||||
|
||||
$result = [$id, null];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function onItemCreated(array $item, int $productId, ?int $variationId): void
|
||||
{
|
||||
// synchronize product store
|
||||
$this->supplySynchronizer->syncSingleItem($productId, (int) $item['id']);
|
||||
// synchronize product pricelists
|
||||
$this->priceListSynchronizer->syncSingleItem($productId, (int) $item['id']);
|
||||
}
|
||||
|
||||
private function getProductId(array $item, ?string $masterCode = null): int
|
||||
{
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('id')
|
||||
->from('products');
|
||||
|
||||
if ($masterCode) {
|
||||
$qb->andWhere(
|
||||
Operator::orX(
|
||||
Operator::equals([JsonOperator::value('data', 'masterCode') => $masterCode]),
|
||||
Operator::equals([JsonOperator::value('data', 'masterCode') => $item['kod']])
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$qb->andWhere(Operator::equals(['code' => $item['kod']]));
|
||||
}
|
||||
|
||||
$id = $qb->execute()->fetchColumn();
|
||||
|
||||
if (!$id) {
|
||||
$id = sqlGetConnection()->transactional(
|
||||
function () use ($masterCode, $item) {
|
||||
$values = [
|
||||
'title' => $item['nazev'],
|
||||
'vat' => 1,
|
||||
'data' => json_encode(['masterCode' => $masterCode ? $masterCode : $item['kod']]),
|
||||
'date_added' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
sqlQueryBuilder()
|
||||
->insert('products')
|
||||
->directValues($values)
|
||||
->execute();
|
||||
|
||||
return sqlInsertId();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return (int) $id;
|
||||
}
|
||||
|
||||
protected function getVariationParts(array $item): ?array
|
||||
{
|
||||
throw new FlexiBeeException('Not implemented');
|
||||
}
|
||||
|
||||
protected function getVariationMasterCode(array $item): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function isVariation(array $item): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getVatByType(string $type): float
|
||||
{
|
||||
switch ($type) {
|
||||
case 'typSzbDph.dphZakl':
|
||||
return 21;
|
||||
case 'typSzbDph.dphOsv':
|
||||
return 0;
|
||||
case 'typSzbDph.dphSniz':
|
||||
return 12;
|
||||
case 'typSzbDph.dphSniz2':
|
||||
return 10;
|
||||
}
|
||||
|
||||
return 21;
|
||||
}
|
||||
|
||||
protected function isItemForEshop(array $item): ?bool
|
||||
{
|
||||
return $item['exportNaEshop'] ?? null;
|
||||
}
|
||||
|
||||
protected function getVariationUpdateValues(array $item): array
|
||||
{
|
||||
$values = [
|
||||
'code' => $item['kod'],
|
||||
'ean' => empty($item['eanKod']) ? null : $item['eanKod'],
|
||||
'updated' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$isForEshop = $this->isItemForEshop($item);
|
||||
if (!is_null($isForEshop)) {
|
||||
$values['figure'] = $isForEshop ? 'Y' : 'N';
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
protected function getProductUpdateValues(array $item): array
|
||||
{
|
||||
$values = [
|
||||
'code' => $item['kod'],
|
||||
'ean' => empty($item['eanKod']) ? null : $item['eanKod'],
|
||||
'title' => $item['nazev'],
|
||||
'vat' => $this->productFinder->findVAT($this->getVatByType($item['typSzbDphK'])),
|
||||
'updated' => (new \DateTime())->format('Y-m-d H:i:s'),
|
||||
];
|
||||
|
||||
$isForEshop = $this->isItemForEshop($item);
|
||||
if (!is_null($isForEshop)) {
|
||||
$values['figure'] = $isForEshop ? 'Y' : 'O';
|
||||
}
|
||||
|
||||
if (findModule(\Modules::OSS_VATS) && !empty($item['nomen']->value)) {
|
||||
$values['id_cn'] = $this->vatsUtil->getCNKey($this->flexiBeeUtil->parseFlexiCode($item['nomen']->value));
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
private function updateProductsPriceByVariations(): void
|
||||
{
|
||||
sqlQuery('UPDATE products p
|
||||
JOIN products_variations pv on p.id = pv.id_product
|
||||
SET p.price = (
|
||||
SELECT COALESCE(MIN(IF(pv2.price=0, null, pv2.price)), p.price)
|
||||
FROM products_variations pv2
|
||||
WHERE pv2.id_product = p.id
|
||||
);');
|
||||
}
|
||||
}
|
||||
59
bundles/External/FlexiBeeBundle/Synchronizers/StoreSynchronizer.php
vendored
Normal file
59
bundles/External/FlexiBeeBundle/Synchronizers/StoreSynchronizer.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Synchronizers;
|
||||
|
||||
use Query\Operator;
|
||||
|
||||
class StoreSynchronizer extends BaseSynchronizer
|
||||
{
|
||||
protected static string $type = 'store';
|
||||
|
||||
protected function processItem(array $item): void
|
||||
{
|
||||
if (!($storeId = $this->flexiBeeUtil->getMapping(static::getType(), $item['id']))) {
|
||||
$storeId = $this->createStore($item['id'], $this->getStoreName($item));
|
||||
}
|
||||
|
||||
sqlQueryBuilder()
|
||||
->update('stores')
|
||||
->directValues(
|
||||
[
|
||||
'name' => $this->getStoreName($item),
|
||||
]
|
||||
)
|
||||
->where(Operator::equals(['id' => $storeId]))
|
||||
->execute();
|
||||
}
|
||||
|
||||
protected function createStore(int $flexiId, string $name): int
|
||||
{
|
||||
return sqlGetConnection()->transactional(function () use ($flexiId, $name) {
|
||||
sqlQueryBuilder()
|
||||
->insert('stores')
|
||||
->directValues(
|
||||
[
|
||||
'name' => $name,
|
||||
]
|
||||
)
|
||||
->execute();
|
||||
|
||||
$storeId = (int) sqlInsertId();
|
||||
|
||||
$this->flexiBeeUtil->createMapping(static::getType(), $flexiId, $storeId);
|
||||
|
||||
return $storeId;
|
||||
});
|
||||
}
|
||||
|
||||
protected function getStoreName(array $item): string
|
||||
{
|
||||
return '[FlexiBee] '.$item['nazev'];
|
||||
}
|
||||
|
||||
protected function getItems(?int $lastSyncTime = null): iterable
|
||||
{
|
||||
return $this->flexiBeeApi->getStores();
|
||||
}
|
||||
}
|
||||
111
bundles/External/FlexiBeeBundle/Synchronizers/SupplySynchronizer.php
vendored
Normal file
111
bundles/External/FlexiBeeBundle/Synchronizers/SupplySynchronizer.php
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Synchronizers;
|
||||
|
||||
use AbraFlexi\SkladovaKarta;
|
||||
use KupShop\StoresBundle\Utils\StoresInStore;
|
||||
|
||||
class SupplySynchronizer extends BaseSynchronizer
|
||||
{
|
||||
protected static string $type = 'supply';
|
||||
protected static ?string $evidenceClass = SkladovaKarta::class;
|
||||
|
||||
private ?StoresInStore $storesInStore = null;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setStoresInStore(?StoresInStore $storesInStore): void
|
||||
{
|
||||
$this->storesInStore = $storesInStore;
|
||||
}
|
||||
|
||||
public function syncSingleItem(int $id, ?int $flexiId = null): void
|
||||
{
|
||||
$flexiId ??= $this->flexiBeeUtil->getProductFlexiId($id);
|
||||
|
||||
if (!$flexiId) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($this->flexiBeeApi->getStockCards(flexiProductIds: [$flexiId]) as $item) {
|
||||
$this->processItem($item);
|
||||
}
|
||||
}
|
||||
|
||||
protected function process(?int $lastSyncTime = null): void
|
||||
{
|
||||
parent::process($lastSyncTime);
|
||||
|
||||
$this->flexiBeeUtil->recalculateStores();
|
||||
}
|
||||
|
||||
protected function getItems(?int $lastSyncTime = null): iterable
|
||||
{
|
||||
if ($this->mode === self::MODE_NORMAL) {
|
||||
return parent::getItems($lastSyncTime);
|
||||
}
|
||||
|
||||
if ($this->mode === self::MODE_FULL) {
|
||||
return $this->flexiBeeApi->getStockCards();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function processItem(array $item): void
|
||||
{
|
||||
$flexiId = $this->flexiBeeUtil->getFlexiIdFromRef($item['cenik']->ref);
|
||||
|
||||
// nemame product
|
||||
if (!($mapping = $this->flexiBeeUtil->getItemMapping($flexiId))) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$productId, $variationId] = $mapping;
|
||||
|
||||
// najdu ID skladu
|
||||
$storeId = $this->getStoreId(
|
||||
$this->flexiBeeUtil->getFlexiIdFromRef($item['sklad']->ref)
|
||||
);
|
||||
|
||||
// pokud nemam ID skladu, tak ten sklad pravdepodobne nesynchronizujeme, takze muzu ignorovat
|
||||
if (!$storeId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// aktualizuju sklad produktu na danem skladu
|
||||
$this->storesInStore->updateStoreItem(
|
||||
[
|
||||
'id_store' => $storeId,
|
||||
'id_product' => $productId,
|
||||
'id_variation' => $variationId,
|
||||
// use floor so for example 0.5 is floored to 0
|
||||
'quantity' => floor((float) $item['dostupMj']),
|
||||
],
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
protected function getStoreId(int $flexiId): ?int
|
||||
{
|
||||
static $storeIdCache = [];
|
||||
|
||||
if ($storeIdCache[$flexiId] ?? false) {
|
||||
return $storeIdCache[$flexiId];
|
||||
}
|
||||
|
||||
if (!($storeId = $this->flexiBeeUtil->getMapping(StoreSynchronizer::getType(), $flexiId))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $storeIdCache[$flexiId] = $storeId;
|
||||
}
|
||||
|
||||
protected function getItemsFilter(): array
|
||||
{
|
||||
return ['ucetObdobi' => 'code:'.$this->flexiBeeApi->getCurrentAccountingPeriod()];
|
||||
}
|
||||
}
|
||||
16
bundles/External/FlexiBeeBundle/Synchronizers/SynchronizerInterface.php
vendored
Normal file
16
bundles/External/FlexiBeeBundle/Synchronizers/SynchronizerInterface.php
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Synchronizers;
|
||||
|
||||
interface SynchronizerInterface
|
||||
{
|
||||
public static function getType(): string;
|
||||
|
||||
public function sync(): void;
|
||||
|
||||
public function syncSingleItem(int $id): void;
|
||||
|
||||
public function setMode(int $mode);
|
||||
}
|
||||
207
bundles/External/FlexiBeeBundle/Synchronizers/UserSynchronizer.php
vendored
Normal file
207
bundles/External/FlexiBeeBundle/Synchronizers/UserSynchronizer.php
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Synchronizers;
|
||||
|
||||
use AbraFlexi\RO;
|
||||
use Query\Operator;
|
||||
|
||||
class UserSynchronizer extends BaseSynchronizer
|
||||
{
|
||||
protected static string $type = 'user';
|
||||
|
||||
protected function process(?int $lastSyncTime = null): void
|
||||
{
|
||||
$this->processToFlexiBee($lastSyncTime);
|
||||
}
|
||||
|
||||
protected function processItem(array $item): void
|
||||
{
|
||||
}
|
||||
|
||||
protected function getItems(?int $lastSyncTime = null): iterable
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
private function processToFlexiBee(?int $lastSyncTime): void
|
||||
{
|
||||
if (isLocalDevelopment()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('u.id, u.email, fu.id_flexi')
|
||||
->from('users', 'u')
|
||||
->leftJoin('u', 'flexi_users', 'fu', 'u.id = fu.id_user')
|
||||
->where(Operator::equals(['u.figure' => 'Y']));
|
||||
|
||||
$specs[] = 'fu.id_flexi IS NULL';
|
||||
if ($this->mode === self::MODE_NORMAL && $lastSyncTime) {
|
||||
$dateTime = $this->createDateTime($lastSyncTime);
|
||||
$specs[] = 'u.date_updated > :lastSyncTime';
|
||||
$qb->setParameter('lastSyncTime', $dateTime->format('Y-m-d H:i:s'));
|
||||
}
|
||||
|
||||
$qb->andWhere(Operator::orX($specs));
|
||||
|
||||
foreach ($qb->execute() as $item) {
|
||||
$user = \User::createFromId($item['id']);
|
||||
$user->fetchAddresses();
|
||||
|
||||
try {
|
||||
$flexiId = $item['id_flexi'] ? (int) $item['id_flexi'] : null;
|
||||
// pokud ho eshop jeste nezapsal do Flexi, tak ho zkusim ve Flexi najit
|
||||
if (!$flexiId) {
|
||||
$flexiId = $this->flexiBeeApi->getUserIdByEmail($item['email']);
|
||||
}
|
||||
|
||||
// provedu zalozeni nebo aktualizaci uzivatele ve Flexi
|
||||
$flexiId = $this->processUserToFlexiBee(
|
||||
$this->getUserData($user, $flexiId)
|
||||
);
|
||||
|
||||
// pokud uzivatel jeste nema mapping, tak ho vytvorim
|
||||
if ($flexiId && !$item['id_flexi']) {
|
||||
$this->flexiBeeUtil->createMapping(UserSynchronizer::getType(), $flexiId, (int) $user->id);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->exception($e, '[FlexiBee] Během synchronizace uživatele se vyskytla chyba!', [
|
||||
'email' => $user->email,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function processUserToFlexiBee(array $userData): ?int
|
||||
{
|
||||
try {
|
||||
if ($flexiId = $this->flexiBeeApi->createOrUpdateUser($userData)) {
|
||||
return $flexiId;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
$this->logger->exception($e, '[FlexiBee] Během synchronizace uživatele se vyskytla chyba!', [
|
||||
'email' => $userData['email'],
|
||||
]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getUserData(\User $user, ?int $flexiId = null): array
|
||||
{
|
||||
$invoiceName = $this->getUserName($user);
|
||||
if (empty($invoiceName)) {
|
||||
// invoice name cannot be empty
|
||||
$invoiceName = $user->email;
|
||||
}
|
||||
|
||||
$item = [
|
||||
'typVztahuK' => 'typVztahu.odberatel',
|
||||
'email' => $user->email,
|
||||
'canceled' => $user->figure == 'N',
|
||||
|
||||
'nazev' => $invoiceName,
|
||||
'ulice' => $user->invoice['street'],
|
||||
'mesto' => $user->invoice['city'],
|
||||
'psc' => $user->invoice['zip'],
|
||||
'ic' => $user->invoice['ico'],
|
||||
'dic' => $user->invoice['dic'],
|
||||
'stat' => $user->invoice['country'] ? 'code:'.$user->invoice['country'] : '',
|
||||
|
||||
'postovniShodna' => $this->isUserDeliveryAddressSame($user),
|
||||
|
||||
'faNazev' => $this->getUserName($user, 'delivery'),
|
||||
'faUlice' => $user->delivery['street'],
|
||||
'faMesto' => $user->delivery['city'],
|
||||
'faPsc' => $user->delivery['zip'],
|
||||
'faStat' => $user->delivery['country'] ? 'code:'.$user->delivery['country'] : '',
|
||||
];
|
||||
|
||||
if ($user->id_pricelist && ($flexiPriceListId = $this->flexiBeeUtil->getFlexiId('pricelist', $user->id_pricelist))) {
|
||||
$item['skupCen'] = $flexiPriceListId;
|
||||
}
|
||||
|
||||
if ($flexiId) {
|
||||
// pokud uzivatel uz existuje, tak vyplnim id, aby se provedl jeho update
|
||||
$item['id'] = $flexiId;
|
||||
} else {
|
||||
// pokud uzivatel jeste neexistuje, tak vyplnim jen kod, abychom ho zalozili s nejakym identifikatorem
|
||||
if ($user->id === null && !empty($user->orderId)) {
|
||||
// kvuli uzivatelum v objednavkach bez registrace
|
||||
$item['kod'] = 'WPJ-ORDER'.$user->orderId;
|
||||
} else {
|
||||
// standartni uzivatel
|
||||
$item['kod'] = 'WPJ'.$user->id;
|
||||
}
|
||||
}
|
||||
|
||||
$dateTime = !empty($user->date_reg) ? \DateTime::createFromFormat('Y-m-d H:i:s', $user->date_reg) : new \DateTime();
|
||||
if ($dateTime && $dateTime->format('Y-m-d H:i:s') !== '0000-00-00 00:00:00') {
|
||||
$item['datZaloz'] = RO::dateToFlexiDate($dateTime);
|
||||
}
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/** Vytvorim dummy uzivatele pro objednavku bez uzivatele */
|
||||
public function getOrderUserData(\Order $order): array
|
||||
{
|
||||
$user = new \User();
|
||||
|
||||
$user->id = null;
|
||||
$user->orderId = (int) $order->id;
|
||||
|
||||
$user->email = $order->invoice_email;
|
||||
$user->figure = 'Y';
|
||||
|
||||
$user->invoice = [
|
||||
'name' => $order->invoice_name,
|
||||
'surname' => $order->invoice_surname,
|
||||
'firm' => $order->invoice_firm,
|
||||
'street' => $order->invoice_street,
|
||||
'city' => $order->invoice_city,
|
||||
'zip' => $order->invoice_zip,
|
||||
'ico' => $order->invoice_ico,
|
||||
'dic' => $order->invoice_dic,
|
||||
'country' => $order->invoice_country,
|
||||
];
|
||||
|
||||
$user->delivery = [
|
||||
'name' => $order->delivery_name,
|
||||
'surname' => $order->delivery_surname,
|
||||
'firm' => $order->delivery_firm,
|
||||
'street' => $order->delivery_street,
|
||||
'city' => $order->delivery_city,
|
||||
'zip' => $order->delivery_zip,
|
||||
'country' => $order->delivery_country,
|
||||
];
|
||||
|
||||
return $this->getUserData($user);
|
||||
}
|
||||
|
||||
private function getUserName(\User $user, string $type = 'invoice'): string
|
||||
{
|
||||
$parts = [];
|
||||
if (!empty($user->{$type}['firm'])) {
|
||||
$parts[] = $user->{$type}['firm'];
|
||||
}
|
||||
|
||||
$parts[] = $user->{$type}['name'].' '.$user->{$type}['surname'];
|
||||
|
||||
return trim(implode(', ', array_filter($parts)));
|
||||
}
|
||||
|
||||
private function isUserDeliveryAddressSame(\User $user): bool
|
||||
{
|
||||
$invoice = [$user->invoice['firm'], $user->invoice['name'], $user->invoice['country'], $user->invoice['street'], $user->invoice['city'], $user->invoice['zip']];
|
||||
$delivery = array_filter([$user->delivery['firm'], $user->delivery['name'], $user->delivery['country'], $user->delivery['street'], $user->delivery['city'], $user->delivery['zip']]);
|
||||
|
||||
if (empty($delivery) || count($delivery) == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $invoice === $delivery;
|
||||
}
|
||||
}
|
||||
613
bundles/External/FlexiBeeBundle/Util/FlexiBeeApi.php
vendored
Normal file
613
bundles/External/FlexiBeeBundle/Util/FlexiBeeApi.php
vendored
Normal file
@@ -0,0 +1,613 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Util;
|
||||
|
||||
use AbraFlexi\Adresar;
|
||||
use AbraFlexi\Cenik;
|
||||
use AbraFlexi\Changes;
|
||||
use AbraFlexi\Company;
|
||||
use AbraFlexi\FakturaVydana;
|
||||
use AbraFlexi\ObjednavkaPrijata;
|
||||
use AbraFlexi\ObjednavkaPrijataPolozka;
|
||||
use AbraFlexi\SkladovaKarta;
|
||||
use AbraFlexi\StromCenik;
|
||||
use AbraFlexi\UcetniObdobi;
|
||||
use External\FlexiBeeBundle\AbraFlexiTypes\CenikovaSkupina;
|
||||
use External\FlexiBeeBundle\AbraFlexiTypes\FakturaVydanaPolozka;
|
||||
use External\FlexiBeeBundle\AbraFlexiTypes\FormaDopravy;
|
||||
use External\FlexiBeeBundle\AbraFlexiTypes\Odberatel;
|
||||
use External\FlexiBeeBundle\AbraFlexiTypes\Sklad;
|
||||
use External\FlexiBeeBundle\AbraFlexiTypes\Stredisko;
|
||||
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
||||
|
||||
class FlexiBeeApi
|
||||
{
|
||||
/**
|
||||
* Data, které u jednotlivých typů načítáme.
|
||||
*
|
||||
* https://demo.flexibee.eu/c/demo/{evidence}/properties
|
||||
*/
|
||||
protected array $defaultColumnsByEvidence = [
|
||||
'adresar' => [
|
||||
'id', 'kod', 'typVztahuK', 'email', 'canceled', 'nazev', 'ulice', 'mesto', 'psc', 'tel', 'mobil', 'ic',
|
||||
'dic', 'stat', 'postovniShodna', 'faJmenoFirmy', 'faUlice', 'faMesto', 'faPsc', 'faStat', 'stitky', 'splatDny', 'skupCen',
|
||||
],
|
||||
'skladova-karta' => [
|
||||
'lastUpdate', 'sklad', 'cenik', 'stavMJ', 'dostupMj',
|
||||
],
|
||||
'strom-cenik' => [
|
||||
'id', 'idZaznamu', 'uzel',
|
||||
],
|
||||
'cenik' => [
|
||||
'id', 'lastUpdate', 'typZasobyK', 'kod', 'eanKod', 'nazev', 'cenaZaklBezDph', 'typSzbDphK', 'sumDostupMj', 'hmotMj', 'hmotObal', 'exportNaEshop',
|
||||
],
|
||||
'objednavka-prijata' => [
|
||||
'id', 'lastUpdate', 'kod', 'cisDosle', 'mena', 'stavUzivK', 'storno', 'formaDopravy', 'formaUhradyCis',
|
||||
'kontaktEmail', 'kontaktJmeno', 'kontaktTel', 'nazFirmy', 'ulice', 'mesto', 'psc', 'ic', 'dic', 'stat',
|
||||
'postovniShodna', 'faNazev', 'faUlice', 'faMesto', 'faPsc', 'faStat', 'doprava',
|
||||
],
|
||||
'objednavka-prijata-polozka' => [
|
||||
'id', 'lastUpdate', 'doklObch', 'kod', 'nazev', 'cisRad', 'typPolozkyK', 'mnozMj', 'typCenyDphK', 'szbDph', 'cenaMj', 'cenik', 'storno', 'stornoPol', 'slevaPol',
|
||||
],
|
||||
'odberatel' => [
|
||||
'id', 'lastUpdate', 'kodIndi', 'prodejCena', 'cenik', 'skupCen', 'mena',
|
||||
],
|
||||
];
|
||||
|
||||
private FlexiBeeConfiguration $configuration;
|
||||
private FlexiBeeUtil $flexiBeeUtil;
|
||||
|
||||
public function __construct(FlexiBeeConfiguration $configuration, FlexiBeeUtil $flexiBeeUtil)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
$this->flexiBeeUtil = $flexiBeeUtil;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zkontroluje připojení k FlexiBee - ověří správnost údajů vyplněných v administraci.
|
||||
*/
|
||||
public function isConnectionValid(): bool
|
||||
{
|
||||
try {
|
||||
$company = $this->getApiWorker(Company::class);
|
||||
$data = $company->getFlexiData();
|
||||
} catch (FlexiBeeException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !empty($data);
|
||||
}
|
||||
|
||||
public function isChangesAPIEnabled(): bool
|
||||
{
|
||||
$changes = $this->getApiWorker(Changes::class);
|
||||
|
||||
return (bool) $changes->getStatus();
|
||||
}
|
||||
|
||||
public function changesAPIEnable(): bool
|
||||
{
|
||||
$changes = $this->getApiWorker(Changes::class);
|
||||
|
||||
return (bool) $changes->enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací seznam změněných objektů ve FlexiBee.
|
||||
*/
|
||||
public function getChanges(int $start, ?string $evidenceClass = null, int $limit = 500): array
|
||||
{
|
||||
$changes = $this->getApiWorker(Changes::class);
|
||||
// Musim to takhle hacknout, protoze jinak to zacne hazet chybu, ze to nezna "evidence" column
|
||||
// To changes API je asi pomerne novy a ta knihovna to nema uplne posefeny
|
||||
$changes->urlParams = array_merge($changes->urlParams, [
|
||||
'evidence' => ['type' => 'string', 'description' => 'Evidence selection'],
|
||||
]);
|
||||
|
||||
$evidence = null;
|
||||
// Pokud chci zmeny pro pouze urcitou evidenci
|
||||
if ($evidenceClass) {
|
||||
$evidence = $this->getApiWorker($evidenceClass)->getEvidence();
|
||||
}
|
||||
|
||||
$filter = array_filter([
|
||||
'start' => $start,
|
||||
'evidence' => $evidence,
|
||||
'limit' => $limit,
|
||||
]);
|
||||
|
||||
return $changes->getAllFromAbraFlexi(!empty($filter) ? $filter : null);
|
||||
}
|
||||
|
||||
public function createOrUpdateUser(array $user): int
|
||||
{
|
||||
$adresar = $this->getApiWorker(Adresar::class);
|
||||
|
||||
$adresar->insertToAbraFlexi($user);
|
||||
|
||||
return (int) $adresar->getLastInsertedId();
|
||||
}
|
||||
|
||||
public function getUserIdByEmail(string $email): ?int
|
||||
{
|
||||
$adresar = $this->getApiWorker(Adresar::class);
|
||||
|
||||
$results = $adresar->getAllFromAbraFlexi(
|
||||
$this->getFilters(['id'], ['email' => $email])
|
||||
);
|
||||
|
||||
$result = reset($results);
|
||||
|
||||
return $result ? (int) $result['id'] : null;
|
||||
}
|
||||
|
||||
public function getUserIdByCode(string $code): ?int
|
||||
{
|
||||
$adresar = $this->getApiWorker(Adresar::class);
|
||||
|
||||
$results = $adresar->getAllFromAbraFlexi(
|
||||
$this->getFilters(['id'], ['kod' => $code])
|
||||
);
|
||||
|
||||
$result = reset($results);
|
||||
|
||||
return $result ? (int) $result['id'] : null;
|
||||
}
|
||||
|
||||
public function getUsers(?\DateTime $lastUpdate = null): iterable
|
||||
{
|
||||
$adresar = $this->getApiWorker(Adresar::class);
|
||||
|
||||
return $this->withPagination(
|
||||
$adresar,
|
||||
$this->getFilters(
|
||||
$this->getColumnsByWorker($adresar),
|
||||
[],
|
||||
$lastUpdate
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí položky podle zadaných ID z dané evidence.
|
||||
*/
|
||||
public function getEvidenceDataByIds(string $evidenceClass, array $ids, ?array $columns = null, array $filters = []): array
|
||||
{
|
||||
$worker = $this->getApiWorker($evidenceClass);
|
||||
|
||||
if ($columns === null) {
|
||||
$columns = $this->getColumnsByWorker($worker);
|
||||
}
|
||||
|
||||
return $worker->getAllFromAbraFlexi(
|
||||
$this->getFilters($columns, array_merge(['limit' => 0, 'id IN ('.implode(',', $ids).')'], $filters))
|
||||
);
|
||||
}
|
||||
|
||||
public function getOrderInvoice(string $id): ?array
|
||||
{
|
||||
return $this->fetchInvoiceByField('id', 'ext:FAV:'.$id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí pdf fakturu.
|
||||
*/
|
||||
public function getOrderInvoicePdf(\Order $order): ?string
|
||||
{
|
||||
$invoice = $this->fetchInvoiceByField('cisObj', $order->order_no);
|
||||
|
||||
if (!$invoice) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$invoiceWorker = $this->getApiWorker(FakturaVydana::class);
|
||||
$invoiceWorker->processInit($invoice);
|
||||
|
||||
return $invoiceWorker->getInFormat('pdf', 'fakturaKB$$SUM', $order->getLanguage());
|
||||
}
|
||||
|
||||
private function fetchInvoiceByField(string $field, string $value): ?array
|
||||
{
|
||||
$apiWorker = $this->getApiWorker(FakturaVydana::class);
|
||||
$results = $apiWorker->getAllFromAbraFlexi(
|
||||
$this->getFilters([], [$field => $value])
|
||||
);
|
||||
|
||||
$result = reset($results);
|
||||
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
public function updateOrderInvoice(array $data): int
|
||||
{
|
||||
$fakturaVydana = $this->getApiWorker(FakturaVydana::class);
|
||||
|
||||
$fakturaVydana->insertToAbraFlexi($data);
|
||||
|
||||
if (!($flexiInvoiceId = $fakturaVydana->getLastInsertedId())) {
|
||||
throw new FlexiBeeException('Invoice was not updated');
|
||||
}
|
||||
|
||||
return (int) $flexiInvoiceId;
|
||||
}
|
||||
|
||||
public function realizeOrder(string $invoiceNumber, array $dataRealize, array $dataInvoice): int
|
||||
{
|
||||
$objednavkaPrijata = $this->getApiWorker(ObjednavkaPrijata::class);
|
||||
|
||||
$objednavkaPrijata->insertToAbraFlexi($dataRealize);
|
||||
|
||||
if (!($flexiOrderId = $objednavkaPrijata->getLastInsertedId())) {
|
||||
throw new FlexiBeeException('Order failure! Order was not successfully realized in Flexi');
|
||||
}
|
||||
|
||||
// give Flexi some time
|
||||
sleep(1);
|
||||
|
||||
if ($invoice = $this->getOrderInvoice($invoiceNumber)) {
|
||||
if (!empty($invoice['id'])) {
|
||||
$dataInvoice['id'] = $invoice['id'];
|
||||
$this->updateOrderInvoice($dataInvoice);
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $flexiOrderId;
|
||||
}
|
||||
|
||||
public function createOrUpdateOrder(array $data): int
|
||||
{
|
||||
$objednavkaPrijata = $this->getApiWorker(ObjednavkaPrijata::class);
|
||||
|
||||
$objednavkaPrijata->insertToAbraFlexi($data);
|
||||
|
||||
if (!($flexiOrderId = $objednavkaPrijata->getLastInsertedId())) {
|
||||
throw new FlexiBeeException('Something went wrong during sending order to FlexiBee! Last inserted ID was not returned.');
|
||||
}
|
||||
|
||||
return (int) $flexiOrderId;
|
||||
}
|
||||
|
||||
public function getOrder(int $flexiOrderId): ?array
|
||||
{
|
||||
$objednavkaPrijata = $this->getApiWorker(ObjednavkaPrijata::class);
|
||||
|
||||
$data = $objednavkaPrijata->getAllFromAbraFlexi(
|
||||
$this->getFilters($this->getColumnsByWorker($objednavkaPrijata), ['limit' => 0, 'id' => $flexiOrderId])
|
||||
);
|
||||
|
||||
$data = reset($data);
|
||||
|
||||
if (empty($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function getOrdersItems(array $ids): array
|
||||
{
|
||||
$objednavkaPrijataPolozka = $this->getApiWorker(ObjednavkaPrijataPolozka::class);
|
||||
|
||||
$items = $objednavkaPrijataPolozka->getAllFromAbraFlexi(
|
||||
$this->getFilters($this->getColumnsByWorker($objednavkaPrijataPolozka), ['limit' => 0, 'doklObch IN ('.implode(',', $ids).')'])
|
||||
);
|
||||
|
||||
$itemsByOrders = [];
|
||||
foreach ($items as $item) {
|
||||
$itemsByOrders[$this->flexiBeeUtil->getFlexiIdFromRef($item['doklObch']->ref)][] = $item;
|
||||
}
|
||||
|
||||
return $itemsByOrders;
|
||||
}
|
||||
|
||||
public function getOrderItems(int $flexiOrderId): array
|
||||
{
|
||||
$objednavkaPrijataPolozka = $this->getApiWorker(ObjednavkaPrijataPolozka::class);
|
||||
|
||||
return $objednavkaPrijataPolozka->getAllFromAbraFlexi(
|
||||
$this->getFilters($this->getColumnsByWorker($objednavkaPrijataPolozka), ['limit' => 0, 'doklObch' => $flexiOrderId])
|
||||
);
|
||||
}
|
||||
|
||||
public function getInvoiceItems(int $flexiOrderId): array
|
||||
{
|
||||
$fakturaVydanaPolozka = $this->getapiWorker(FakturaVydanaPolozka::class);
|
||||
|
||||
return $fakturaVydanaPolozka->getAllFromAbraFlexi(
|
||||
$this->getFilters($this->getColumnsByWorker($fakturaVydanaPolozka), ['limit' => 0, 'doklFak' => $flexiOrderId])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí všechna střediska ve FlexiBee.
|
||||
*/
|
||||
public function getCentrals(): array
|
||||
{
|
||||
return $this->getApiWorker(Stredisko::class)
|
||||
->getAllFromAbraFlexi();
|
||||
}
|
||||
|
||||
public function getDeliveries(): array
|
||||
{
|
||||
return $this->getApiWorker(FormaDopravy::class)
|
||||
->getAllFromAbraFlexi();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí seznam skladů ve FlexiBee.
|
||||
*
|
||||
* @param bool $all - pokud je false, tak se sklady filtrují podle nastavení v administraci
|
||||
*/
|
||||
public function getStores(bool $all = false): array
|
||||
{
|
||||
$store = $this->getApiWorker(Sklad::class);
|
||||
|
||||
$filter = null;
|
||||
if (!$all) {
|
||||
$filter = ['id IN ('.implode(',', $this->configuration->getEnabledStoreIds()).')'];
|
||||
}
|
||||
|
||||
return $store->getAllFromAbraFlexi($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí seznam ceníků ve FlexiBee.
|
||||
*/
|
||||
public function getPriceLists(): array
|
||||
{
|
||||
$priceList = $this->getApiWorker(CenikovaSkupina::class);
|
||||
|
||||
return $priceList->getAllFromAbraFlexi();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí skladové karty z FlexiBee.
|
||||
*/
|
||||
public function getStockCards(?\DateTime $lastUpdate = null, ?array $flexiProductIds = null): iterable
|
||||
{
|
||||
$skladovaKarta = $this->getApiWorker(SkladovaKarta::class);
|
||||
|
||||
$filter = [
|
||||
'limit' => 1000,
|
||||
'sklad in ('.implode(',', $this->configuration->getEnabledStoreIds()).')',
|
||||
'no-ext-ids' => true,
|
||||
'ucetObdobi' => 'code:'.$this->getCurrentAccountingPeriod(),
|
||||
];
|
||||
|
||||
if ($flexiProductIds !== null) {
|
||||
$filter[] = 'cenik in ('.implode(',', $flexiProductIds).')';
|
||||
}
|
||||
|
||||
return $this->withPagination(
|
||||
$skladovaKarta,
|
||||
$this->getFilters(
|
||||
$this->getColumnsByWorker($skladovaKarta),
|
||||
$filter,
|
||||
$lastUpdate
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí aktuální účetní období fe FlexiBee.
|
||||
*/
|
||||
public function getCurrentAccountingPeriod(bool $force = false): string
|
||||
{
|
||||
$cacheKey = 'flexiBee_currentAccountingPeriod';
|
||||
|
||||
if (!$force && ($currentAccountingPeriod = getCache($cacheKey))) {
|
||||
return $currentAccountingPeriod;
|
||||
}
|
||||
|
||||
$ucetniObdobi = $this->getApiWorker(UcetniObdobi::class);
|
||||
|
||||
$date = (new \DateTime())->format('Y-m-d');
|
||||
|
||||
$filters = [
|
||||
'platiOdData <= "'.$date.'" and platiDoData >= "'.$date.'"',
|
||||
];
|
||||
|
||||
$accountingPeriod = $ucetniObdobi->getAllFromAbraFlexi(
|
||||
$this->getFilters(['id', 'kod'], $filters)
|
||||
);
|
||||
$accountingPeriod = reset($accountingPeriod);
|
||||
|
||||
if (!$accountingPeriod) {
|
||||
throw new FlexiBeeException('Current accounting period not found!');
|
||||
}
|
||||
|
||||
setCache($cacheKey, $accountingPeriod['kod'], 3600);
|
||||
|
||||
return $accountingPeriod['kod'];
|
||||
}
|
||||
|
||||
public function getProductsSections(): array
|
||||
{
|
||||
$stromCenik = $this->getApiWorker(StromCenik::class);
|
||||
|
||||
$filter = [
|
||||
'limit' => 0,
|
||||
];
|
||||
|
||||
return $stromCenik->getAllFromAbraFlexi(
|
||||
$this->getFilters(
|
||||
$this->getColumnsByWorker($stromCenik),
|
||||
$filter
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí produkty z FlexiBee.
|
||||
*/
|
||||
public function getProducts(?\DateTime $lastUpdate = null): iterable
|
||||
{
|
||||
$cenik = $this->getApiWorker(Cenik::class);
|
||||
|
||||
$filter = [
|
||||
'limit' => 500,
|
||||
'typZasobyK in ("typZasoby.zbozi", "typZasoby.vyrobek")',
|
||||
];
|
||||
|
||||
return $this->withPagination(
|
||||
$cenik,
|
||||
$this->getFilters(
|
||||
$this->getColumnsByWorker($cenik),
|
||||
$filter,
|
||||
$lastUpdate
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function getPricelistPrices(?\DateTime $lastUpdate = null, ?array $flexiProductIds = null): iterable
|
||||
{
|
||||
$worker = $this->getApiWorker(Odberatel::class);
|
||||
|
||||
$filter = [
|
||||
'limit' => 500,
|
||||
'skupCen is not null',
|
||||
];
|
||||
|
||||
if ($flexiProductIds !== null) {
|
||||
$filter[] = 'cenik in ('.implode(',', $flexiProductIds).')';
|
||||
}
|
||||
|
||||
return $this->withPagination(
|
||||
$worker,
|
||||
$this->getFilters(
|
||||
$this->getColumnsByWorker($worker),
|
||||
$filter,
|
||||
$lastUpdate,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function getProductByCode(string $code): ?array
|
||||
{
|
||||
$cenik = $this->getApiWorker(Cenik::class);
|
||||
|
||||
$filter = [
|
||||
'limit' => 0,
|
||||
'kod' => $code,
|
||||
];
|
||||
|
||||
$products = $cenik->getAllFromAbraFlexi(
|
||||
$this->getFilters($this->getColumnsByWorker($cenik), $filter)
|
||||
);
|
||||
|
||||
$product = reset($products);
|
||||
|
||||
if ($product) {
|
||||
return $product;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getProductPrices(int $flexiProductId): ?array
|
||||
{
|
||||
$cenik = $this->getApiWorker(Cenik::class);
|
||||
|
||||
$data = $cenik->getAllFromAbraFlexi(
|
||||
$this->getFilters($this->getColumnsByWorker($cenik), ['limit' => 0, 'id' => $flexiProductId])
|
||||
);
|
||||
|
||||
$data = reset($data);
|
||||
|
||||
if (empty($data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrátí ceny produktů ve FlexiBee.
|
||||
*/
|
||||
public function getPrices(?\DateTime $lastUpdate = null): iterable
|
||||
{
|
||||
$cenik = $this->getApiWorker(Cenik::class);
|
||||
|
||||
$columns = [
|
||||
'id',
|
||||
'lastUpdate',
|
||||
'kod',
|
||||
'cenaZaklBezDph',
|
||||
'typSzbDphK',
|
||||
];
|
||||
|
||||
$filter = [
|
||||
'limit' => 500,
|
||||
'typZasobyK in ("typZasoby.zbozi", "typZasoby.vyrobek")',
|
||||
];
|
||||
|
||||
return $this->withPagination(
|
||||
$cenik,
|
||||
$this->getFilters($columns, $filter, $lastUpdate)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pomocná funkce pro stránkování.
|
||||
*/
|
||||
private function withPagination(FlexiBeeApiWorker $worker, array $filters = []): iterable
|
||||
{
|
||||
$totalRows = $this->getDataRowCount($worker, $filters);
|
||||
$limit = $filters['limit'] ?? 100;
|
||||
|
||||
$iterationLimit = $totalRows / $limit;
|
||||
for ($i = 0; $i < $iterationLimit; $i++) {
|
||||
// posunout offset
|
||||
$filters['start'] = $i * $limit;
|
||||
// nacist data
|
||||
foreach ($worker->getAllFromAbraFlexi($filters) as $item) {
|
||||
yield $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pomocná funkce, která vrací celkový počet položek pro daný filtr.
|
||||
*/
|
||||
private function getDataRowCount(FlexiBeeApiWorker $worker, array $filters): int
|
||||
{
|
||||
$filters['limit'] = 1;
|
||||
$filters['add-row-count'] = 'true';
|
||||
|
||||
$worker->getAllFromAbraFlexi($filters);
|
||||
|
||||
return (int) $worker->rowCount;
|
||||
}
|
||||
|
||||
protected function getColumnsByWorker(FlexiBeeApiWorker $worker): array
|
||||
{
|
||||
$evidence = $worker->getEvidence();
|
||||
$columns = $this->defaultColumnsByEvidence[$evidence] ?? [];
|
||||
|
||||
if (findModule(\Modules::OSS_VATS) && $evidence == 'cenik') {
|
||||
$columns[] = 'nomen';
|
||||
}
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
private function getFilters(array $columns = [], array $filters = [], ?\DateTime $lastUpdate = null): array
|
||||
{
|
||||
if (!empty($columns)) {
|
||||
$filters['detail'] = 'custom:'.implode(',', $columns);
|
||||
}
|
||||
|
||||
if ($lastUpdate) {
|
||||
$filters['lastUpdate'] = '> '.$lastUpdate->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
private function getApiWorker(string $worker): FlexiBeeApiWorker
|
||||
{
|
||||
return FlexiBeeApiWorker::create($worker, $this->configuration->getAPIConfig());
|
||||
}
|
||||
}
|
||||
103
bundles/External/FlexiBeeBundle/Util/FlexiBeeApiWorker.php
vendored
Normal file
103
bundles/External/FlexiBeeBundle/Util/FlexiBeeApiWorker.php
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Util;
|
||||
|
||||
use AbraFlexi\Exception;
|
||||
use AbraFlexi\RO;
|
||||
use AbraFlexi\RW;
|
||||
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
||||
|
||||
/**
|
||||
* Class FlexiBeeApiWorker.
|
||||
*
|
||||
* @property $rowCount
|
||||
* @property $urlParams
|
||||
*
|
||||
* @method updateColumnsInfo($columnsInfo = null, $evidence = null)
|
||||
* @method insertToAbraFlexi($data = null)
|
||||
* @method getAllFromAbraFlexi($conditions = null, $indexBy = null)
|
||||
* @method deleteFromAbraFlexi($id = null)
|
||||
* @method dateToFlexiDate(\DateTime $date)
|
||||
* @method getNextRecordID($conditions = [])
|
||||
* @method getEvidence()
|
||||
* @method getLastInsertedId()
|
||||
* @method getFlexiData(string $suffix = '', $conditions = null)
|
||||
* @method getFlexiRow($recordID)
|
||||
* @method getData()
|
||||
* @method processInit($init)
|
||||
* @method getInFormat($format, $reportName = null, $lang = null, $sign = false)
|
||||
*/
|
||||
class FlexiBeeApiWorker
|
||||
{
|
||||
private ?RO $worker = null;
|
||||
|
||||
public static function create(string $worker, array $config): self
|
||||
{
|
||||
$wrapper = new static();
|
||||
|
||||
return $wrapper->setWorker(
|
||||
new $worker(null, $config)
|
||||
);
|
||||
}
|
||||
|
||||
public function setWorker(RO $worker): self
|
||||
{
|
||||
$this->worker = $worker;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function __get($name)
|
||||
{
|
||||
if (property_exists($this, $name)) {
|
||||
return $this->{$name};
|
||||
}
|
||||
|
||||
if (property_exists($this->worker, $name)) {
|
||||
return $this->worker->{$name};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function __set($name, $value)
|
||||
{
|
||||
if (property_exists($this->worker, $name)) {
|
||||
return $this->worker->{$name} = $value;
|
||||
}
|
||||
|
||||
return $this->{$name} = $value;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
try {
|
||||
$result = call_user_func_array([$this->worker, $name], $arguments);
|
||||
} catch (Exception $e) {
|
||||
// ta knihovna ma nejaky rozbity message, ktere to vypisuje v exceptione, takze to tady jen catchnu
|
||||
// a vyhodim nasi FlexiBeeException se spravnou message
|
||||
foreach ($e->getErrorMessages() as $error) {
|
||||
throw new FlexiBeeException(
|
||||
sprintf('%s: %s', $error['code'] ?? 'ERROR', $error['message']),
|
||||
!empty($error['code']) && is_numeric($error['code']) ? (int) $error['code'] : 0,
|
||||
$e
|
||||
);
|
||||
}
|
||||
|
||||
throw new FlexiBeeException($e->getMessage(), $e->getCode(), $e);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->worker instanceof RW) {
|
||||
$this->worker->disconnect();
|
||||
}
|
||||
|
||||
unset($this->worker);
|
||||
}
|
||||
}
|
||||
108
bundles/External/FlexiBeeBundle/Util/FlexiBeeConfiguration.php
vendored
Normal file
108
bundles/External/FlexiBeeBundle/Util/FlexiBeeConfiguration.php
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Util;
|
||||
|
||||
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
||||
use KupShop\SynchronizationBundle\Util\SynchronizationConfiguration;
|
||||
|
||||
class FlexiBeeConfiguration
|
||||
{
|
||||
private SynchronizationConfiguration $configuration;
|
||||
|
||||
public function __construct(SynchronizationConfiguration $configuration)
|
||||
{
|
||||
$this->configuration = $configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací konfiguraci pro API potřebnou pro připojení (url, user, password, company).
|
||||
*/
|
||||
public function getAPIConfig(): array
|
||||
{
|
||||
$api = $this->getConfig()['api'] ?? [];
|
||||
|
||||
foreach (['company', 'url', 'user', 'password'] as $requiredField) {
|
||||
if (empty($api[$requiredField])) {
|
||||
throw new FlexiBeeException(
|
||||
sprintf('Missing required value in API config: %s', $requiredField)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací konfiguraci objednávek.
|
||||
*/
|
||||
public function getOrderConfig(): array
|
||||
{
|
||||
return $this->getConfig()['config']['order'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací konfiguraci doprav.
|
||||
*/
|
||||
public function getDeliveriesConfig(): array
|
||||
{
|
||||
return $this->getConfig()['config']['delivery'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací konfiguraci plateb.
|
||||
*/
|
||||
public function getPaymentsConfig(): array
|
||||
{
|
||||
return $this->getConfig()['config']['payment'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací konfiguraci stavů objednávek.
|
||||
*/
|
||||
public function getOrderStatusesConfig(): array
|
||||
{
|
||||
return $this->getOrderConfig()['statuses'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací povolené typy synchronizací, které se mají spouštět.
|
||||
*/
|
||||
public function getEnabledSynchronizerTypes(): array
|
||||
{
|
||||
return $this->getConfig()['config']['enabled_types'] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací ID skladů ve FlexiBee, které máme synchronizovat.
|
||||
*/
|
||||
public function getEnabledStoreIds(): array
|
||||
{
|
||||
return array_map(fn ($x) => (int) $x, $this->getConfig()['config']['stores'] ?? [0]);
|
||||
}
|
||||
|
||||
public function getEnabledPricelists(): array
|
||||
{
|
||||
return array_map(fn ($x) => (int) $x, $this->getConfig()['config']['pricelists'] ?? [-1]);
|
||||
}
|
||||
|
||||
public function refresh(): void
|
||||
{
|
||||
$this->configuration->clear('flexibee');
|
||||
}
|
||||
|
||||
/**
|
||||
* Vrací konfiguraci z settings tabulky.
|
||||
*/
|
||||
private function getConfig(): array
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
if (!$this->configuration->has('flexibee')) {
|
||||
$this->configuration->set('flexibee', fn () => $dbcfg->loadValue('flexibee') ?: []);
|
||||
}
|
||||
|
||||
return $this->configuration->get('flexibee');
|
||||
}
|
||||
}
|
||||
35
bundles/External/FlexiBeeBundle/Util/FlexiBeeLocator.php
vendored
Normal file
35
bundles/External/FlexiBeeBundle/Util/FlexiBeeLocator.php
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Util;
|
||||
|
||||
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
||||
use External\FlexiBeeBundle\Synchronizers\SynchronizerInterface;
|
||||
use Symfony\Component\DependencyInjection\ServiceLocator;
|
||||
|
||||
class FlexiBeeLocator
|
||||
{
|
||||
private ServiceLocator $locator;
|
||||
|
||||
public function __construct(ServiceLocator $locator)
|
||||
{
|
||||
$this->locator = $locator;
|
||||
}
|
||||
|
||||
public function getTypes(): array
|
||||
{
|
||||
return $this->locator->getProvidedServices();
|
||||
}
|
||||
|
||||
public function getServiceByType(string $type): SynchronizerInterface
|
||||
{
|
||||
if (!$this->locator->has($type)) {
|
||||
throw new FlexiBeeException(
|
||||
sprintf('Unknown synchronizer type \'%s\'!', $type)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->locator->get($type);
|
||||
}
|
||||
}
|
||||
85
bundles/External/FlexiBeeBundle/Util/FlexiBeeLogger.php
vendored
Normal file
85
bundles/External/FlexiBeeBundle/Util/FlexiBeeLogger.php
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Util;
|
||||
|
||||
use Doctrine\DBAL\Exception\DeadlockException;
|
||||
use Doctrine\DBAL\Exception\LockWaitTimeoutException;
|
||||
use External\FlexiBeeBundle\Exception\FlexiBeeException;
|
||||
use KupShop\AdminBundle\Util\ActivityLog;
|
||||
use KupShop\KupShopBundle\Util\Logging\SentryLogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class FlexiBeeLogger
|
||||
{
|
||||
private ActivityLog $activityLog;
|
||||
private SentryLogger $sentryLogger;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(ActivityLog $activityLog, SentryLogger $sentryLogger, LoggerInterface $logger)
|
||||
{
|
||||
$this->activityLog = $activityLog;
|
||||
$this->sentryLogger = $sentryLogger;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Zaloguje exceptionu do ActivityLogu, pripadne do Sentry.
|
||||
*/
|
||||
public function exception(\Throwable $e, ?string $message = null, array $data = []): void
|
||||
{
|
||||
if (isLocalDevelopment()) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!($e instanceof FlexiBeeException)) {
|
||||
$this->sentryLogger->captureException($e);
|
||||
}
|
||||
|
||||
if ($message === null) {
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
|
||||
$data = array_merge(
|
||||
[
|
||||
'exception_message' => $e->getMessage(),
|
||||
],
|
||||
$data
|
||||
);
|
||||
|
||||
// Lock wait timeout a deadlock nechci logovat do ActivityLogu
|
||||
if ($e instanceof LockWaitTimeoutException || $e instanceof DeadlockException) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->activityLog->addActivityLog(
|
||||
ActivityLog::SEVERITY_ERROR,
|
||||
ActivityLog::TYPE_SYNC,
|
||||
$message,
|
||||
array_unique($data)
|
||||
);
|
||||
}
|
||||
|
||||
public function activity(string $message, array $data = [], string $severity = ActivityLog::SEVERITY_NOTICE): void
|
||||
{
|
||||
$this->activityLog->addActivityLog(
|
||||
$severity,
|
||||
ActivityLog::TYPE_SYNC,
|
||||
'[FlexiBee] '.$message,
|
||||
$data
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Zaloguje data do kibany.
|
||||
*/
|
||||
public function data(string $message, array $data): void
|
||||
{
|
||||
if (isLocalDevelopment()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logger->notice($message, $data);
|
||||
}
|
||||
}
|
||||
293
bundles/External/FlexiBeeBundle/Util/FlexiBeeUtil.php
vendored
Normal file
293
bundles/External/FlexiBeeBundle/Util/FlexiBeeUtil.php
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace External\FlexiBeeBundle\Util;
|
||||
|
||||
use AbraFlexi\Relation;
|
||||
use External\FlexiBeeBundle\Synchronizers\BaseSynchronizer;
|
||||
use KupShop\CatalogBundle\Util\SetsUpdateStore;
|
||||
use Query\Operator;
|
||||
|
||||
class FlexiBeeUtil
|
||||
{
|
||||
public function __construct(
|
||||
private readonly FlexiBeeLocator $synchronizerLocator,
|
||||
private readonly FlexiBeeLogger $logger,
|
||||
private readonly SetsUpdateStore $setsUpdateStore,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getOrderNumber(\Order $order): string
|
||||
{
|
||||
return 'WPJ-'.$order->order_no;
|
||||
}
|
||||
|
||||
public function getMapping(string $type, int $flexiId): ?int
|
||||
{
|
||||
$id = sqlQueryBuilder()
|
||||
->select('id_'.$type)
|
||||
->from('flexi_'.$type.'s')
|
||||
->where(Operator::equals(['id_flexi' => $flexiId]))
|
||||
->execute()->fetchOne();
|
||||
|
||||
if (!$id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $id;
|
||||
}
|
||||
|
||||
public function getFlexiId(string $type, int $shopId): ?int
|
||||
{
|
||||
$flexiId = sqlQueryBuilder()
|
||||
->select('id_flexi')
|
||||
->from('flexi_'.$type.'s')
|
||||
->where(Operator::equals(['id_'.$type => $shopId]))
|
||||
->execute()->fetchOne();
|
||||
|
||||
if (!$flexiId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $flexiId;
|
||||
}
|
||||
|
||||
public function deleteMapping(string $type, int $flexiId): void
|
||||
{
|
||||
sqlQueryBuilder()
|
||||
->delete('flexi_'.$type.'s')
|
||||
->andWhere(Operator::equals(['id_flexi' => $flexiId]))
|
||||
->execute();
|
||||
}
|
||||
|
||||
public function createMapping(string $type, int $flexiId, int $shopId): void
|
||||
{
|
||||
sqlQueryBuilder()
|
||||
->insert('flexi_'.$type.'s')
|
||||
->directValues(
|
||||
[
|
||||
'id_flexi' => $flexiId,
|
||||
'id_'.$type => $shopId,
|
||||
]
|
||||
)
|
||||
->execute();
|
||||
}
|
||||
|
||||
public function createItemMapping(int $flexiId, int $productId, ?int $variationId = null): void
|
||||
{
|
||||
sqlQueryBuilder()
|
||||
->insert('flexi_products')
|
||||
->directValues(
|
||||
[
|
||||
'id_flexi' => $flexiId,
|
||||
'id_product' => $productId,
|
||||
'id_variation' => $variationId,
|
||||
]
|
||||
)->execute();
|
||||
}
|
||||
|
||||
public function setFlexiOrderData(int $orderId, string $key, $value): void
|
||||
{
|
||||
sqlGetConnection()->transactional(function () use ($orderId, $key, $value) {
|
||||
$data = sqlQueryBuilder()
|
||||
->select('data')
|
||||
->from('flexi_orders')
|
||||
->where(Operator::equalsNullable(['id_order' => $orderId]))
|
||||
->execute()->fetchOne();
|
||||
|
||||
$data = json_decode($data ?: '', true) ?? [];
|
||||
|
||||
$data[$key] = $value;
|
||||
|
||||
sqlQueryBuilder()
|
||||
->update('flexi_orders')
|
||||
->directValues(['data' => json_encode($data)])
|
||||
->where(Operator::equals(['id_order' => $orderId]))
|
||||
->execute();
|
||||
});
|
||||
}
|
||||
|
||||
public function getProductFlexiId(int $productId, ?int $variationId = null): ?int
|
||||
{
|
||||
$flexiId = sqlQueryBuilder()
|
||||
->select('id_flexi')
|
||||
->from('flexi_products')
|
||||
->where(
|
||||
Operator::equalsNullable(
|
||||
[
|
||||
'id_product' => $productId,
|
||||
'id_variation' => $variationId,
|
||||
]
|
||||
)
|
||||
)
|
||||
->execute()->fetchColumn();
|
||||
|
||||
if (!$flexiId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $flexiId;
|
||||
}
|
||||
|
||||
public function getItemMapping(int $flexiId): ?array
|
||||
{
|
||||
$data = sqlQueryBuilder()
|
||||
->select('id_product, id_variation')
|
||||
->from('flexi_products')
|
||||
->where(Operator::equals(['id_flexi' => $flexiId]))
|
||||
->execute()->fetch();
|
||||
|
||||
if (!$data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [(int) $data['id_product'], $data['id_variation'] ? (int) $data['id_variation'] : null];
|
||||
}
|
||||
|
||||
public function getProductId(string $code): ?int
|
||||
{
|
||||
$id = sqlQueryBuilder()
|
||||
->select('id')
|
||||
->from('products')
|
||||
->where(Operator::equals(['code' => $code]))
|
||||
->execute()->fetchOne();
|
||||
|
||||
if (!$id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $id;
|
||||
}
|
||||
|
||||
public function getVariationId(string $code): ?int
|
||||
{
|
||||
$id = sqlQueryBuilder()
|
||||
->select('id')
|
||||
->from('products_variations')
|
||||
->where(Operator::equals(['code' => $code]))
|
||||
->execute()->fetchOne();
|
||||
|
||||
if (!$id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $id;
|
||||
}
|
||||
|
||||
public function getFlexiIdFromRef(string $ref): int
|
||||
{
|
||||
$parts = explode('/', $ref);
|
||||
|
||||
return (int) end($parts);
|
||||
}
|
||||
|
||||
public function parseFlexiCode($itemPart): ?string
|
||||
{
|
||||
if ($itemPart instanceof Relation) {
|
||||
$itemPart = $itemPart->value;
|
||||
}
|
||||
|
||||
if (is_array($itemPart)) {
|
||||
$itemPart = reset($itemPart);
|
||||
}
|
||||
|
||||
$code = explode(':', $itemPart);
|
||||
$code = end($code);
|
||||
|
||||
return !empty($code) ? $code : null;
|
||||
}
|
||||
|
||||
public function recalculateStores(): void
|
||||
{
|
||||
$getQuantitySubQuery = function (bool $variations = false) {
|
||||
$alias = 'p';
|
||||
$productIdColumn = 'id';
|
||||
if ($variations) {
|
||||
$alias = 'pv';
|
||||
$productIdColumn = 'id_product';
|
||||
}
|
||||
|
||||
$qb = sqlQueryBuilder()
|
||||
->select('COALESCE(SUM(GREATEST(si.quantity, 0)), 0)')
|
||||
->from('stores_items', 'si')
|
||||
->where('si.id_product = '.$alias.'.'.$productIdColumn);
|
||||
|
||||
if ($variations) {
|
||||
$qb->andWhere('si.id_variation = pv.id');
|
||||
} else {
|
||||
$qb->andWhere('si.id_variation IS NULL');
|
||||
}
|
||||
|
||||
return $qb;
|
||||
};
|
||||
|
||||
$productsSubQuery = $getQuantitySubQuery();
|
||||
|
||||
$productsQuantityQb = sqlQueryBuilder()
|
||||
->update('products', 'p')
|
||||
->leftJoin('p', 'products_variations', 'pv', 'pv.id_product = p.id')
|
||||
->set('p.in_store', "({$productsSubQuery->getSQL()})")
|
||||
->andWhere('pv.id IS NULL AND p.in_store != ('.$productsSubQuery->getSQL().')')
|
||||
->addQueryBuilderParameters($productsSubQuery);
|
||||
|
||||
// ignorujeme sety - protoze ty se prepocitaji zvlast pomoci `setsUpdateStore->updateSetsStore()`
|
||||
if (findModule(\Modules::PRODUCT_SETS, \Modules::SUB_CALCULATE_STOCK)) {
|
||||
$productsQuantityQb->leftJoin('p', 'products_sets', 'ps', 'ps.id_product = p.id')
|
||||
->andWhere('ps.id_product IS NULL');
|
||||
}
|
||||
|
||||
$productsQuantityQb->execute();
|
||||
|
||||
$variationsSubQuery = $getQuantitySubQuery(true);
|
||||
sqlQuery(
|
||||
'UPDATE products_variations pv
|
||||
SET pv.in_store = ('.$variationsSubQuery->getSQL().')
|
||||
WHERE pv.in_store != ('.$variationsSubQuery->getSQL().');', $variationsSubQuery->getParameters(), $variationsSubQuery->getParameterTypes());
|
||||
|
||||
\Variations::recalcInStore();
|
||||
|
||||
if (findModule(\Modules::PRODUCT_SETS, \Modules::SUB_CALCULATE_STOCK)) {
|
||||
$this->setsUpdateStore->updateSetsStore();
|
||||
}
|
||||
}
|
||||
|
||||
public function synchronize(array $types, int $mode = BaseSynchronizer::MODE_NORMAL): void
|
||||
{
|
||||
foreach ($types as $type) {
|
||||
try {
|
||||
$synchronizer = $this->synchronizerLocator->getServiceByType($type);
|
||||
$synchronizer->setMode($mode);
|
||||
$synchronizer->sync();
|
||||
} catch (\Throwable $e) {
|
||||
if (isDevelopment()) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$this->logger->exception($e, '[FlexiBee] Během synchronizace se vyskytla chyba!', [
|
||||
'type' => $type,
|
||||
'mode' => $mode,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function findItemByCode(string $code): ?array
|
||||
{
|
||||
$result = null;
|
||||
|
||||
if ($variationId = $this->getVariationId($code)) {
|
||||
$productId = sqlQueryBuilder()
|
||||
->select('id_product')
|
||||
->from('products_variations')
|
||||
->andWhere(Operator::equals(['id' => $variationId]))
|
||||
->execute()->fetchOne();
|
||||
|
||||
$result = [(int) $productId, $variationId];
|
||||
} elseif ($productId = $this->getProductId($code)) {
|
||||
$result = [$productId, null];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user