first commit
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle\Admin\Tabs;
|
||||
|
||||
use KupShop\AdminBundle\Admin\WindowTab;
|
||||
|
||||
class MarketingSettingsWindowTab extends WindowTab
|
||||
{
|
||||
protected $title = 'flapMarketing';
|
||||
|
||||
protected $template = 'window/marketingSettingsWindowTab.tpl';
|
||||
|
||||
public static function getTypes()
|
||||
{
|
||||
return [
|
||||
'settings' => 1,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle\Admin\Tabs;
|
||||
|
||||
use KupShop\AdminBundle\Admin\WindowTab;
|
||||
use KupShop\AdminBundle\Util\ActivityLog;
|
||||
use KupShop\AdminBundle\Util\Filter\OrdersFilterSpecs;
|
||||
use KupShop\AdminBundle\Util\UsersFilterSpecs;
|
||||
use KupShop\CatalogBundle\Util\ProductsFilterSpecs;
|
||||
use KupShop\KupShopBundle\Util\Excel\ExcelGenerator;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\DataType;
|
||||
use Query\QueryBuilder;
|
||||
|
||||
class StatsMailerliteTab extends WindowTab
|
||||
{
|
||||
protected $title = 'flapMailerlite';
|
||||
|
||||
protected $template = 'window/tabs/statsMailerliteTab.tpl';
|
||||
|
||||
private $ordersFilterSpecs;
|
||||
private $productsFilterSpecs;
|
||||
private $usersFilterSpecs;
|
||||
|
||||
public function __construct(OrdersFilterSpecs $ordersFilterSpecs, ProductsFilterSpecs $productsFilterSpecs, UsersFilterSpecs $usersFilterSpecs)
|
||||
{
|
||||
$this->ordersFilterSpecs = $ordersFilterSpecs;
|
||||
$this->productsFilterSpecs = $productsFilterSpecs;
|
||||
$this->usersFilterSpecs = $usersFilterSpecs;
|
||||
}
|
||||
|
||||
public static function getTypes()
|
||||
{
|
||||
return [
|
||||
'stats' => 94,
|
||||
];
|
||||
}
|
||||
|
||||
public function getVars($smarty_tpl_vars)
|
||||
{
|
||||
$vars = [];
|
||||
|
||||
if (getVal('showUsersStat')) {
|
||||
$vars['rowNumber'] = $this->getCountUserData();
|
||||
$vars['usersData'] = $this->getShownData();
|
||||
}
|
||||
|
||||
return $vars;
|
||||
}
|
||||
|
||||
public function handleGetMailerlite()
|
||||
{
|
||||
set_time_limit(60 * 5);
|
||||
|
||||
$generator = new ExcelGenerator();
|
||||
|
||||
$filename = 'Users_'.date('Y-m-d_H-i').'.xlsx';
|
||||
|
||||
$generator->generateExcel($this->getHeader(), $this->loadData(), $filename);
|
||||
|
||||
addActivityLog(ActivityLog::SEVERITY_NOTICE, ActivityLog::TYPE_SECURITY, "Export uživatelů: {$filename}");
|
||||
exit;
|
||||
}
|
||||
|
||||
protected function loadData()
|
||||
{
|
||||
sqlGetConnection()->getWrappedConnection()->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
|
||||
|
||||
foreach ($this->GetUserData()->execute() as $product) {
|
||||
yield [
|
||||
$product['id'] ?? null,
|
||||
$product['name'] ?? null,
|
||||
$product['surname'] ?? null,
|
||||
$product['email'],
|
||||
$product['phone'] ?? null,
|
||||
(empty($product['date_reg']) || $product['date_reg'] == '0000-00-00 00:00:00') ? 'N' : 'Y',
|
||||
$product['count'] ?? null,
|
||||
$product['price'] ?? null,
|
||||
$product['average_price'] ?? null,
|
||||
$product['mindate'] ?? null,
|
||||
$product['maxdate'] ?? null,
|
||||
($product['get_news'] == 'Y') ? 'Y' : 'N',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
protected function getHeader(): array
|
||||
{
|
||||
return [
|
||||
['name' => 'ID', 'type' => DataType::TYPE_NUMERIC],
|
||||
['name' => 'Jméno', 'type' => DataType::TYPE_STRING],
|
||||
['name' => 'Přijmení', 'type' => DataType::TYPE_STRING],
|
||||
['name' => 'Email', 'type' => DataType::TYPE_STRING],
|
||||
['name' => 'Tel. číslo', 'type' => DataType::TYPE_STRING],
|
||||
['name' => 'Registrován', 'type' => DataType::TYPE_STRING],
|
||||
['name' => 'Počet objednávek', 'type' => DataType::TYPE_NUMERIC],
|
||||
['name' => 'Suma', 'type' => DataType::TYPE_NUMERIC, 'format' => '0.00'],
|
||||
['name' => 'Průměrná cena', 'type' => DataType::TYPE_NUMERIC, 'format' => '0.00'],
|
||||
['name' => 'První nákup', 'type' => 'datetime', 'format' => 'd.m.yyyy hh:mm:ss'],
|
||||
['name' => 'Poslední nákup', 'type' => 'datetime', 'format' => 'd.m.yyyy hh:mm:ss'],
|
||||
['name' => 'Newsletter', 'type' => DataType::TYPE_STRING],
|
||||
];
|
||||
}
|
||||
|
||||
public function GetUserData()
|
||||
{
|
||||
$actionTypeSet = getVal('actionTypeSet');
|
||||
|
||||
$query = sqlQueryBuilder()
|
||||
->select('u.id, u.name, u.surname, u.phone, u.get_news, u.date_reg, u.email, COUNT(o.invoice_email) as count, SUM(o.total_price * o.currency_rate) as price, AVG(o.total_price * o.currency_rate) as average_price, MAX(o.date_created) as maxdate, MIN(o.date_created) as mindate')
|
||||
->from('users', 'u')
|
||||
->leftJoin('u', 'orders', 'o', 'u.email = o.invoice_email')
|
||||
->andWhere('o.status_storno != 1')
|
||||
->groupBy('u.email');
|
||||
|
||||
switch ($actionTypeSet) {
|
||||
case 'newsletter':
|
||||
$query->andWhere('u.get_news = "Y"');
|
||||
|
||||
break;
|
||||
case 'just_buy':
|
||||
$query->andWhere('u.get_news != "Y" AND u.date_reg IS NULL AND u.passw = ""')
|
||||
->having('COUNT(o.invoice_email) >= 1');
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$query = $this->applySpecsToQuery($query);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function applySpecsToQuery(QueryBuilder $query)
|
||||
{
|
||||
$filter = getVal('filter', null, []);
|
||||
$specs = $this->productsFilterSpecs->getSpecs($filter);
|
||||
if ($specs) {
|
||||
$subQuery = sqlQueryBuilder()->select('oi.id_order')
|
||||
->from('order_items', 'oi')
|
||||
->leftJoin('oi', 'products', 'p', 'oi.id_product = p.id')
|
||||
->andWhere($specs)
|
||||
->groupBy('oi.id_order');
|
||||
$query->joinSubQuery('o', $subQuery, 'oi', 'o.id = oi.id_order');
|
||||
}
|
||||
$specs = $this->ordersFilterSpecs->getSpecs($filter);
|
||||
if ($specs) {
|
||||
$query->andWhere($specs);
|
||||
}
|
||||
|
||||
$specs = $this->usersFilterSpecs->getSpecs($filter);
|
||||
if ($specs) {
|
||||
$query->andWhere($specs);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function getCountUserData()
|
||||
{
|
||||
$qb = $this->GetUserData();
|
||||
|
||||
return returnSQLResult("SELECT COUNT(*) FROM ({$qb->getSQL()}) a", $qb->getParameters(), $qb->getParameterTypes());
|
||||
}
|
||||
|
||||
public function getShownData()
|
||||
{
|
||||
$query = $this->GetUserData()
|
||||
->setMaxResults(20)
|
||||
->execute()
|
||||
->fetchAll();
|
||||
|
||||
return $query;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
$txt_str['marketingSettingsWindowTab'] = [
|
||||
'flapMarketing' => 'Měřící kódy',
|
||||
|
||||
'OrderConversionMeasurement' => 'Měření konverzí objednávek',
|
||||
'VerificationString' => 'Ověřovací řetězec',
|
||||
'VerifiedByCustomers' => 'Ověřeno zákazníky',
|
||||
'VerifiedByCustomersSK' => 'Ověřeno zákazníky - SK',
|
||||
'ConversionTracking' => 'Měření konverzí',
|
||||
'SendProductsToHeureka' => 'Zahrnout produkty',
|
||||
'ConversionTrackingSK' => 'Měření konverzí - SK',
|
||||
'CountingConversions' => 'Zboží.cz - Počítání konverzí',
|
||||
'Code' => 'Kód',
|
||||
'SecretKey' => 'Tajný klíč',
|
||||
'ZboziSecretKey' => 'Zboží tajný klíč',
|
||||
'ZboziAPIKey' => 'Zboží API klíč',
|
||||
'OrderConversionMeasurementInfo' => 'Zaškrtnete-li, bude měření probíhat pouze při vytvoření objednávky!',
|
||||
'GTMPricesWithoutVAT' => 'Odesílat ceny bez DPH',
|
||||
'GTMPricesWithoutVATInfo' => 'Zaškrtnete-li, budou se do datových vrstev odesílat veškeré ceny bez DPH!',
|
||||
'GTMPurchaseWithoutDelivery' => 'Cena objednávky bez dopravy a platby',
|
||||
'GTMPurchaseWithoutDeliveryInfo' => 'Zaškrtnete-li, bude se v purchase event zasílat celková částka objednávky bez dopravy a platby!',
|
||||
'GTMPriceCurrency' => 'Měna',
|
||||
'GTMPriceCurrencyDefault' => 'Výchozí',
|
||||
'GTMReallyRun' => 'Spouštět tento kontejner?',
|
||||
'GTMReallyRunInfo' => 'Pokud jsme na beta / review verzi e-shopu, spouští se výchozí wpj kontejner. Touto funkcí lze vynutit spuštění výše vyplněného kontejneru.',
|
||||
'GTMShaSalt' => 'Salt pro SHA-256 emailu',
|
||||
'ZboziIdStore' => 'Zboží ID provozovny',
|
||||
'NewConversionTracking' => 'Sklik ID konverze',
|
||||
'SklikRetargetingId' => 'Sklik retargeting ID',
|
||||
'buttonAddValue' => 'Přidat kód',
|
||||
'note' => 'Poznámka',
|
||||
'buttonDeleteValue' => 'Smazat kód',
|
||||
'adsDescr' => 'Kód je 10-ti místné číslo nebo řetězec ve formátu XXX-XXX-XXXX',
|
||||
'GTMServerUrl' => 'Doména značkovacího serveru (server side kontejner)',
|
||||
'GTMServerUrlInfo' => 'Doména ve formátu ad4sfc.eshop.com',
|
||||
'GTMServerDebugHash' => 'Debug Hash',
|
||||
'GTM_DL_version' => 'Verze datové vrstvy',
|
||||
'feEvents' => 'Pokročilé FE eventy',
|
||||
'GTM_Domain' => 'Doména pro výdej GTM',
|
||||
'GTM_CC_FE_toggle' => 'Zapnout client-side měření na infrastruktuře WPJ',
|
||||
'GTM_CC_SS_toggle' => 'Zapnout server-side měření na infrastruktuře WPJ',
|
||||
|
||||
'backendConversionOnly' => 'Odesílat pouze backend konverze',
|
||||
'DL_opinions' => [
|
||||
0 => 'v1.0 pro všechny',
|
||||
1 => 'v2.0 pouze pro přihlášené administrátory',
|
||||
2 => 'v2.0 pro všechny',
|
||||
],
|
||||
|
||||
'active' => 'Ano',
|
||||
'inactive' => 'Ne',
|
||||
'admin_only' => 'Pouze pro administrátory',
|
||||
];
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
$txt_str['statsMailerliteTab'] = [
|
||||
'flapMailerlite' => 'Uživatelé',
|
||||
];
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
$txt_str['marketingSettingsWindowTab'] = [
|
||||
'flapMarketing' => 'Analytics',
|
||||
|
||||
'OrderConversionMeasurement' => 'Order Conversion Measurement',
|
||||
'VerificationString' => 'Verification string',
|
||||
'VerifiedByCustomers' => 'Verified by customers',
|
||||
'VerifiedByCustomersSK' => 'Verified by customers - SK',
|
||||
'ConversionTracking' => 'Conversion tracking',
|
||||
'SendProductsToHeureka' => 'Include products',
|
||||
'ConversionTrackingSK' => 'Conversion tracking - SK',
|
||||
'CountingConversions' => 'Zboží.cz - Counting conversions',
|
||||
'Code' => 'Code',
|
||||
'SecretKey' => 'Secret Key',
|
||||
'APIKey' => 'API Key',
|
||||
'OrderConversionMeasurementInfo' => 'If checked, analytics will measure orders only, not visits!',
|
||||
'GTMPricesWithoutVAT' => 'Send price without vat',
|
||||
'GTMPricesWithoutVATInfo' => 'If checked, all prices will be send without vat!',
|
||||
'GTMPriceCurrency' => 'Currency',
|
||||
'GTMReallyRun' => 'Run this container?',
|
||||
'GTMReallyRunInfo' => 'If we are on the beta / review version of the e-shop, the default wpj container is running. This function can be used to force the above-filled container to run.',
|
||||
'NewConversionTracking' => 'New conversion code',
|
||||
'sklikStoreId' => 'ID provozovny Zboží.cz',
|
||||
'buttonAddValue' => 'Add code',
|
||||
'note' => 'Note',
|
||||
'buttonDeleteValue' => 'Delete code',
|
||||
'adsDescr' => 'Code is a 10-digit number or a string in the format XXX-XXX-XXXX',
|
||||
'GTMServerUrl' => 'Tag server domain (server side container)',
|
||||
'GTMServerUrlInfo' => 'Domain in format ad4sfc.eshop.com',
|
||||
'GTMServerDebugHash' => 'Debug Hash',
|
||||
'GTM_DL_version' => 'DL version',
|
||||
'feEvents' => 'Advanced FE events',
|
||||
'GTM_Domain' => 'Domain for GTM',
|
||||
'GTM_CC_FE_toggle' => 'Enable client-side measurement on WPJ infrastructure',
|
||||
'GTM_CC_SS_toggle' => 'Enable server-side measurement on WPJ infrastructure',
|
||||
|
||||
'backendConversionOnly' => 'Send only backend conversions',
|
||||
'GTMPurchaseWithoutDelivery' => 'Order price without shipping and payment',
|
||||
'GTMPriceCurrencyDefault' => 'Default',
|
||||
'GTMShaSalt' => 'Salt (SHA-256) email',
|
||||
'ZboziIdStore' => 'Zboží ID of establishment',
|
||||
'SklikRetargetingId' => 'Sklik retargeting ID',
|
||||
'ZboziSecretKey' => 'Zboží secret key',
|
||||
'ZboziAPIKey' => 'Zboží API key',
|
||||
'DL_opinions' => [
|
||||
0 => 'v1.0 for everyone',
|
||||
1 => 'v2.0 only for logged administrators',
|
||||
2 => 'v2.0 for everyone',
|
||||
],
|
||||
|
||||
'active' => 'Yes',
|
||||
'inactive' => 'No',
|
||||
'admin_only' => 'Only for admins',
|
||||
];
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
$txt_str['statsMailerliteTab'] = [
|
||||
'flapMailerlite' => 'Users',
|
||||
];
|
||||
@@ -0,0 +1,305 @@
|
||||
<style>
|
||||
.custom-plugin-universal {
|
||||
background-color: white;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .st-logo {
|
||||
margin: 10px auto 20px auto;
|
||||
width: 114px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .pers-info {
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .pers-info img {
|
||||
position: relative;
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
bottom: -2px;
|
||||
width: 17px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .pers-info p {
|
||||
float: left;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .pers-info a {
|
||||
color: #1EA0C9;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .spent {
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
background-color: #EEF0F3;
|
||||
}
|
||||
|
||||
|
||||
.custom-plugin-universal .spent img {
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
position: relative;
|
||||
bottom: -7px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .spent .utraceno {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .spent .value {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding-left: 50px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .divider {
|
||||
border: 1px solid #E8E8E8;
|
||||
margin: 20px -10px 20px -10px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal h2 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 0px 0 5px 0;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order {
|
||||
border-bottom: 1px solid #E8E8E8;
|
||||
padding: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order .order-number {
|
||||
font-size: 14px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order .order-number a {
|
||||
color: #1EA0C9;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order .order-item-name {
|
||||
float: left;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order .order-item-price {
|
||||
text-align: right;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order .order-value {
|
||||
font-size: 14px;
|
||||
float: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order .order-calendar {
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
color: #ABABAB;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order .order-calendar img {
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 2px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order .order-car {
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
color: #ABABAB;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .order .order-car img {
|
||||
position: relative;
|
||||
left: 0;
|
||||
top: 2px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .show-rest {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.custom-plugin-universal .show-rest a {
|
||||
color: #1EA0C9;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.cleaner {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.unavailable-data {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1000px) and (max-width: 1600px) {
|
||||
#mail-ticket-plugins .custom-plugin-universal .order .order-car {
|
||||
float: left;
|
||||
width: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 1000px) and (max-width: 1450px) {
|
||||
#mail-ticket-plugins .custom-plugin-universal .order .order-value {
|
||||
float: left;
|
||||
width: 150px;
|
||||
margin: 6px 0 3px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
{if $orders || $user || $sales}
|
||||
<div class="custom-plugin-universal">
|
||||
<div class="st-logo" data-original-title="" title="">
|
||||
<img src="{$cfg.Addr.full}admin/static/images/logo.svg"
|
||||
style="background-color: #0d4db6;border-radius: 5%;padding: 7px;">
|
||||
</div>
|
||||
|
||||
<div class="available-data" style="">
|
||||
<div class="pers-info" data-original-title="" title="">
|
||||
{if $user.id} <a href="{$cfg.Addr.full|rtrim:'/'}{getAdminUrl('users', ['ID' => $user.id])}">
|
||||
<img src="https://app.supportbox.cz/assets/build/image/plugin-shoptet/person.png">{/if} <strong
|
||||
class="data-name">{$orders[0].invoice_name|default:$user->name} {$orders[0].invoice_surname|default:$user->surname}</strong></a>
|
||||
</div>
|
||||
|
||||
<div class="pers-info" data-original-title="" title="">
|
||||
<img src="https://app.supportbox.cz/assets/build/image/plugin-shoptet/phone.png">
|
||||
<span class="data-phone">{$orders[0].invoice_phone|default:$user->name}</span>
|
||||
</div>
|
||||
|
||||
<div class="pers-info" data-original-title="" title="">
|
||||
<img src="https://app.supportbox.cz/assets/build/image/plugin-shoptet/place.png">
|
||||
<span class="data-address"><p>{$orders[0].invoice_street|default:$user->street}<br>{$orders[0].invoice_city|default:$user->city}
|
||||
<br>{$orders[0].invoice_zip|default:$user->zip}</p></span>
|
||||
</div>
|
||||
|
||||
<div class="cleaner" data-original-title="" title=""></div>
|
||||
|
||||
<div class="pers-info" data-original-title="" title="">
|
||||
<strong>E-mail:</strong> {$orders[0].invoice_email|default:$user->email}
|
||||
</div>
|
||||
<div class="pers-info" data-original-title="" title="">
|
||||
<strong>Odběr k novinkám:</strong> {if $user->get_news == 'Y'}Ano{else}Ne{/if}
|
||||
</div>
|
||||
|
||||
<div class="cleaner" data-original-title="" title=""></div>
|
||||
|
||||
<div class="spent" data-original-title="" title="">
|
||||
<img src="https://app.supportbox.cz/assets/build/image/plugin-shoptet/money.png">
|
||||
|
||||
<div class="utraceno" data-original-title="" title="">Celkem utraceno</div>
|
||||
<div class="value" data-original-title="" title="">
|
||||
{$userTotals = $total_orders_price}
|
||||
{if $total_sales_price}
|
||||
{$userTotals = $userTotals + $total_sales_price}
|
||||
{/if}
|
||||
|
||||
{$userTotals|round|default:'-'} CZK<br>
|
||||
</div>
|
||||
|
||||
<div class="cleaner" data-original-title="" title=""></div>
|
||||
</div>
|
||||
|
||||
<div class="divider" data-original-title="" title=""></div>
|
||||
|
||||
<h2>Objednávky</h2>
|
||||
|
||||
|
||||
<div class="order-list">
|
||||
|
||||
{foreach $orders as $order}
|
||||
<div class="order" data-original-title="" title="">
|
||||
<div class="order-number" data-original-title="" title=""><i class="fa fa-circle red" data-original-title="" title=""
|
||||
style="color: gray"></i> <a
|
||||
href="{$cfg.Addr.full|rtrim:'/'}{getAdminUrl('orders', ['ID' => $order.id])}" data-original-title="" title=""
|
||||
class="data-id" target="_blank">{$order.order_no}</a></div>
|
||||
<div class="order-value" data-original-title=""
|
||||
title="">{$order['total_price']|round} {$order['currency']|default:'Kč'}</div>
|
||||
|
||||
<div class="cleaner" data-original-title="" title=""></div>
|
||||
{foreach $order.items as $item}
|
||||
<div>
|
||||
<div class="order-item-name">{$item.descr|substr:0:30}</div>
|
||||
<div class="order-item-price">{{$item.total_price * ( 1 + ($item.tax / 100))}|round} {$order.currency}</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
<div class="cleaner" data-original-title="" title=""></div>
|
||||
|
||||
<div class="order-calendar" data-original-title="" title="">
|
||||
<img src="https://app.supportbox.cz/assets/build/image/plugin-shoptet/calendar.png">
|
||||
<span class="order-date">{$order['date_created']|format_date:'d.m.Y'} - {$order.order_status}</span>
|
||||
</div>
|
||||
|
||||
<div class="order-car" data-original-title="" title="">
|
||||
<img src="https://app.supportbox.cz/assets/build/image/plugin-shoptet/car.png">
|
||||
<span class="delivery-date">{$order['date_handle']|format_date:'d.m.Y'}</span>
|
||||
</div>
|
||||
|
||||
<div class="cleaner" data-original-title="" title=""></div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
|
||||
{ifmodule SALES}
|
||||
<div class="divider" data-original-title="" title=""></div>
|
||||
<h2>Nákupy na prodejně</h2>
|
||||
|
||||
<div class="order-list">
|
||||
{foreach $sales as $sale}
|
||||
<div class="order" data-original-title="" title="">
|
||||
<div class="order-number" data-original-title="" title="">
|
||||
<i class="fa fa-circle red" data-original-title="" title="" style="color: gray"></i>
|
||||
<a href="{$cfg.Addr.full|rtrim:'/'}{getAdminUrl('Sales', ['ID' => $sale.id])}" data-original-title="" title=""
|
||||
class="data-id" target="_blank">{$sale.code}</a>
|
||||
</div>
|
||||
|
||||
<div class="order-value" data-original-title=""
|
||||
title="">{$sale['total_price']|round} {$order['currency']|default:'Kč'}
|
||||
</div>
|
||||
|
||||
<div class="cleaner" data-original-title="" title=""></div>
|
||||
|
||||
{foreach $sale.items as $item}
|
||||
<div>
|
||||
<div class="order-item-name">{$item.name|substr:0:30}</div>
|
||||
<div class="order-item-price">{{$item.total_price * ( 1 + ($item.tax / 100))}|round} {$order.currency}</div>
|
||||
</div>
|
||||
{/foreach}
|
||||
|
||||
<div class="cleaner" data-original-title="" title=""></div>
|
||||
|
||||
<div class="order-calendar" data-original-title="" title="">
|
||||
<img src="https://app.supportbox.cz/assets/build/image/plugin-shoptet/calendar.png">
|
||||
<span class="order-date">
|
||||
{$sale['date_created']|format_date:'d.m.Y'} - <a href="{$cfg.Addr.full|rtrim:'/'}{getAdminUrl('sellers', ['ID' => $sale.id_seller])}" target="_blank">{$sale.seller_name}</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="cleaner" data-original-title="" title=""></div>
|
||||
</div>
|
||||
{/foreach}
|
||||
</div>
|
||||
{/ifmodule}
|
||||
|
||||
{else}
|
||||
<div class="custom-plugin-universal">
|
||||
<div class="unavailable-data">
|
||||
Zákazník nebyl<br>v e-shopu nalezen
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,149 @@
|
||||
<div id="flapMailerlite" class="tab-pane fade in boxFlex">
|
||||
{insert_orders_filter filterName='flapStatsOrderMailerlite' saveFilter=true}
|
||||
{include "block.productsFilter.tpl" saveFilter=true filterName='flapStatsProductMailerlite'}
|
||||
{insert_users_filter filterName='flapStatsUsersMailerLite' saveFilter=true}
|
||||
|
||||
|
||||
{$barName = 'Mailerlite'}
|
||||
<div class="row bottom-space" data-filter-name="interval{$barName}">
|
||||
<div class="col-xs-12">
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-body">
|
||||
<div class="form-group">
|
||||
<div class="col-md-1 control-label"><label>Platnost</label></div>
|
||||
<div class="col-md-1 radio" style="width: auto; padding-right: 0px;">
|
||||
<input type="radio" class="check" name="actionTypeSet" value="registered" id="registered" checked/>
|
||||
<label for="registered"></label>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<span class="help-block">Všichni uživatelé
|
||||
<a class="help-tip" style="position:relative;" data-toggle="tooltip" title=""
|
||||
data-original-title="Exportuje množinu všech uživatelů."><i
|
||||
class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-1 radio" style="width: auto; padding-right: 0px;">
|
||||
<input type="radio" class="check" name="actionTypeSet" value="newsletter" id="newsletter"/>
|
||||
<label for="newsletter"></label>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<span class="help-block">Mají newsletter
|
||||
<a class="help-tip" style="position:relative;" data-toggle="tooltip" title=""
|
||||
data-original-title="Exportuje množinu uživatelů, kteří mají přihlášený newsletter."><i
|
||||
class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-1 radio" style="width: auto; padding-right: 0px;">
|
||||
<input type="radio" class="check" name="actionTypeSet" value="just_buy" id="just_buy"/>
|
||||
<label for="just_buy"></label>
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<span class="help-block">Pouze nakoupili
|
||||
<a class="help-tip" style="position:relative;" data-toggle="tooltip" title=""
|
||||
data-original-title="Exportuje množinu uživatelů, kteří nemají přihlášený newsletter, nemají registraci a mají alespon jednu objednávku."><i
|
||||
class="bi bi-question-circle"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-2 pull-left">
|
||||
<input type="button" class="btn btn-primary btn-block btn-sm"
|
||||
value="{'show'|translate}" data-btn-show-{$barName} data-btn-submit>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
wpj.formUtils.initFormSave($('[data-filter-name="interval{$barName}"]'), 'interval{$barName}');
|
||||
</script>
|
||||
</div>
|
||||
|
||||
{if $graphs}
|
||||
<script type="text/javascript">
|
||||
$(document).on('click', '[data-btn-show-{$barName}]', function () {
|
||||
showGraphsArea($(this));
|
||||
|
||||
{foreach $graphs as $graph}
|
||||
get{$graph}Graph($('[name=interval{$barName}]:checked').val());
|
||||
{/foreach}
|
||||
});
|
||||
</script>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row bottom-space" id="products" data-display-area="mailerlite">
|
||||
<div class="col-md-12">
|
||||
<div id="userdata_content">
|
||||
<div class="panel panel-default panel-sm">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
{'countValue'|translate} {$tab.data.rowNumber}
|
||||
</div>
|
||||
<a class="help-tip" style="position:relative; float:right;margin-top: -2px;margin-right: 10px" data-toggle="tooltip"
|
||||
title="{'userDataStats'|translate}">
|
||||
<i class="bi bi-question-circle"></i>
|
||||
</a>
|
||||
<h3 class="panel-title">{'userDataStats'|translate}</h3>
|
||||
</div>
|
||||
<table class="table">
|
||||
<tr>
|
||||
<th>{'userName'|translate}</th>
|
||||
<th>{'userSurname'|translate}</th>
|
||||
<th>{'userEmail'|translate}</th>
|
||||
<th>{'userPhone'|translate}</th>
|
||||
<th>{'isRegistered'|translate}</th>
|
||||
<th>{'countOrders'|translate}</th>
|
||||
<th>{'sumMoney'|translate}</th>
|
||||
<th>{'avgMoney'|translate}</th>
|
||||
<th>{'firstShop'|translate}</th>
|
||||
<th>{'lastShop'|translate}</th>
|
||||
<th>{'getNews'|translate}</th>
|
||||
</tr>
|
||||
{foreach $tab.data.usersData as $item}
|
||||
<tr style="background-color: #f9f9f9; border-top: 2px solid #ddd !important; ">
|
||||
<td>{$item.name}</td>
|
||||
<td>{$item.surname}</td>
|
||||
<td><a href="javascript:nw('user', '{$item.id}');">{$item.email}</a></td>
|
||||
<td>{$item.phone}</td>
|
||||
<td>{if empty($item.date_reg) || $item.date_reg == '0000-00-00 00:00:00'}N{else}Y{/if}</td>
|
||||
<td>{$item.count}</td>
|
||||
<td>{$item.price|format_price:"ceil=no;decimal=dynamic"}</td>
|
||||
<td>{$item.average_price|format_price:"ceil=no;decimal=dynamic"}</td>
|
||||
<td>{$item.mindate}</td>
|
||||
<td>{$item.maxdate}</td>
|
||||
<td>{if $item.get_news == 'Y'}Y{else}N{/if}</td>
|
||||
</tr>
|
||||
{/foreach}
|
||||
</table>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-2 pull-right">
|
||||
<input type="button" class="btn btn-primary btn-block btn-sm"
|
||||
value="{'export'|translate}" data-btn-export-Mailerlite data-btn-submit>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="application/javascript">
|
||||
$(document).on('click', '[data-btn-show-mailerlite]', function() {
|
||||
reloadDivMailerlite();
|
||||
showGraphsArea($(this));
|
||||
});
|
||||
|
||||
$(document).on('click', '[data-btn-export-mailerlite]', function() {
|
||||
var paramsString = concatParams({
|
||||
type: 'stats',
|
||||
acn: 'getMailerlite'
|
||||
}, '#flapMailerlite');
|
||||
|
||||
window.location.href = 'launch.php?s=board.php&ajax=1&' + paramsString;
|
||||
});
|
||||
|
||||
function reloadDivMailerlite() {
|
||||
reloadDiv('#userdata_content', {
|
||||
type: 'stats',
|
||||
showUsersStat: 1
|
||||
}, '#flapMailerlite');
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle\Controller;
|
||||
|
||||
use KupShop\KupShopBundle\Context\UserContext;
|
||||
use Query\Operator;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class CrispController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route(path="/getCrispData/")
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function getCrispDataAction(Request $request, UserContext $userContext)
|
||||
{
|
||||
/** @var \User $user */
|
||||
$user = $userContext->getActive();
|
||||
|
||||
if (!empty($user->id)) {
|
||||
$data = sqlQueryBuilder()
|
||||
->select("MAX(DATE(o.date_created)) 'Posledni_objednavka',
|
||||
MAX(o.order_no) 'Kod_posledni_objednavky',
|
||||
CONCAT(CEIL(SUM(o.total_price)),' ' , o.currency) 'Celkem',
|
||||
COUNT(o.id) 'Pocet_objednavek',
|
||||
SUM(o.status_storno) 'Pocet_stornovanych_objednavek'"
|
||||
)
|
||||
->from('orders', 'o')
|
||||
->where(Operator::equals(['o.id_user' => $user->id]))
|
||||
->execute()
|
||||
->fetch();
|
||||
|
||||
$data['B2B'] = $userContext->isDealer() ? 'ANO' : 'NE';
|
||||
|
||||
$data = array_map(function ($value, $key) {
|
||||
return [$key, $value];
|
||||
}, array_values($data), array_keys($data));
|
||||
} else {
|
||||
$data = [];
|
||||
}
|
||||
|
||||
$response = new JsonResponse($data);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle\Controller;
|
||||
|
||||
use KupShop\MarketingBundle\SupportBox\InfoProvider;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class SupportBoxController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route(path="/_supportbox/")
|
||||
*/
|
||||
public function accountAction(Request $request, InfoProvider $infoProvider)
|
||||
{
|
||||
$this->authorization('id', $request);
|
||||
|
||||
if (!$request->get('email') && !$request->get('phone')) {
|
||||
return new JsonResponse(['html' => '', 'error' => 'Query parameters `email` or `phone` not provided']);
|
||||
}
|
||||
|
||||
return new JsonResponse(
|
||||
$infoProvider->getAdditionalData((string) $request->get('email'), (string) $request->get('phone'))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/_supportbox/phone/{number}/")
|
||||
*/
|
||||
public function phoneAction(Request $request, InfoProvider $infoProvider, $number)
|
||||
{
|
||||
$this->authorization('id_phone', $request);
|
||||
|
||||
return new JsonResponse($infoProvider->getName($number));
|
||||
}
|
||||
|
||||
public function authorization($serviceType, $request)
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
$key = $dbcfg->analytics['supportbox'][$serviceType] ?? false;
|
||||
|
||||
if (!$key || ($key != $request->get('key') && 'Basic '.$key != $request->headers->get('Authorization'))
|
||||
) {
|
||||
throw new NotFoundHttpException('Route not found.');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
|
||||
class WebClaimVerifiyng extends AbstractController
|
||||
{
|
||||
/**
|
||||
* @Route(path="/google{hash}.html", name="google_claim_verify")
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getGoogleWebVerify(Request $request, $hash)
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
foreach ($dbcfg->analytics['google_site_verifications_file'] ?? [] as $value) {
|
||||
if ($value['value'] == $hash) {
|
||||
return new Response("google-site-verification: google{$hash}.html");
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException('Not found');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/{hash}.html", name="facebook_claim_verify", requirements={"hash"="^[a-zA-Z0-9]{30}$"})
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getFacebookWebVerify(Request $request, $hash)
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
foreach ($dbcfg->analytics['facebook_site_verifications_file'] ?? [] as $value) {
|
||||
if ($value['value'] == $hash) {
|
||||
return new Response($hash);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException('Not found');
|
||||
}
|
||||
|
||||
/**
|
||||
* @Route(path="/seznam-wmt-{hash}.txt", name="seznam_claim_verify", requirements={"slug"="^[a-zA-Z0-9]{32}$"})
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getSeznamWebVerify(Request $request, $hash)
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
foreach ($dbcfg->analytics['seznam_site_verifications_file'] ?? [] as $value) {
|
||||
if ($value['value'] == $hash) {
|
||||
return new Response($hash);
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotFoundHttpException('Not found');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle\EventListener;
|
||||
|
||||
use Heureka\ShopCertification;
|
||||
use KupShop\AdminBundle\Util\ActivityLog;
|
||||
use KupShop\KupShopBundle\Config;
|
||||
use KupShop\KupShopBundle\Context\CountryContext;
|
||||
use KupShop\KupShopBundle\Context\DomainContext;
|
||||
use KupShop\KupShopBundle\Context\LanguageContext;
|
||||
use KupShop\KupShopBundle\Util\Compat\SymfonyBridge;
|
||||
use KupShop\KupShopBundle\Util\Price\Price;
|
||||
use KupShop\KupShopBundle\Util\Price\PriceCalculator;
|
||||
use KupShop\KupShopBundle\Util\Price\PriceUtil;
|
||||
use KupShop\OrderingBundle\Event\OrderEvent;
|
||||
use KupShop\OrderingBundle\Util\Order\OrderInfo;
|
||||
use Query\Operator;
|
||||
use Soukicz\Zbozicz\CartItem;
|
||||
use Soukicz\Zbozicz\Client;
|
||||
use Soukicz\Zbozicz\Order;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
|
||||
class OrderConversionListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var PriceUtil
|
||||
*/
|
||||
private $priceUtil;
|
||||
/**
|
||||
* @var RequestStack
|
||||
*/
|
||||
private $requestStack;
|
||||
|
||||
private $countryContext;
|
||||
|
||||
/** @var DomainContext */
|
||||
private $domainContext;
|
||||
|
||||
/**
|
||||
* @var LanguageContext
|
||||
*/
|
||||
private $languageContext;
|
||||
|
||||
private $cookieConsents;
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setDomainContext(DomainContext $domainContext): void
|
||||
{
|
||||
$this->domainContext = $domainContext;
|
||||
}
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
if (isDevelopment() && !isFunctionalTests()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
OrderEvent::ORDER_FINISHED => [
|
||||
['sendZboziConversion', 200],
|
||||
['sendHeurekaConversion', 200],
|
||||
['sendCJTracking', 201],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct(PriceUtil $priceUtil, RequestStack $requestStack, CountryContext $countryContext)
|
||||
{
|
||||
$this->priceUtil = $priceUtil;
|
||||
$this->requestStack = $requestStack;
|
||||
$this->countryContext = $countryContext;
|
||||
|
||||
if ($request = $this->requestStack->getMainRequest()) {
|
||||
$cookie_bar = $request->cookies->get('cookie-bar');
|
||||
$this->cookieConsents = array_flip(explode(',', $cookie_bar ?? ''));
|
||||
}
|
||||
}
|
||||
|
||||
public function sendZboziConversion(OrderEvent $event)
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
if (empty($dbcfg->analytics['zbozi_cz_feedback']['secret_key']) || empty($dbcfg->analytics['zbozi_cz_feedback']['ID'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = $event->getOrder();
|
||||
if (empty($order->invoice_email)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
if (!empty($request) && $request->get('heurekaDisagree')) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$zbozi = $this->getZboziClient();
|
||||
|
||||
$delivery_type = $order->getDeliveryType();
|
||||
|
||||
$order->fetchItems();
|
||||
|
||||
$target = $this->priceUtil->getCurrencyById('CZK');
|
||||
$source = $this->priceUtil->getCurrencyById($order->currency);
|
||||
|
||||
// nastavení informací o objednávce
|
||||
$zboziOrder = new Order($order->order_no);
|
||||
$zboziOrder
|
||||
->setDeliveryType($delivery_type->getDelivery()->getZboziDeliveryID())
|
||||
->setDeliveryPrice(PriceCalculator::convert($delivery_type->getPrice(), $target)->getPriceWithVat()->asFloat())
|
||||
->setEmail($order->invoice_email)
|
||||
->setOtherCosts(0)
|
||||
->setPaymentType($delivery_type['payment']);
|
||||
|
||||
foreach ($order->items as $item) {
|
||||
if ($item['id_product']) {
|
||||
$id = $item['id_product'].($item['id_variation'] ? '_'.$item['id_variation'] : '');
|
||||
if (findModule('marketing', 'product_identifier') == 'code') {
|
||||
$id = $item['code'];
|
||||
}
|
||||
$zboziItem = new CartItem();
|
||||
$zboziItem
|
||||
->setId($id)
|
||||
->setUnitPrice(PriceCalculator::convert(new Price($item['piece_price']['value_with_vat'], $source, 0), $target)->getPriceWithVat()->asFloat())
|
||||
->setQuantity($item['pieces'])
|
||||
->setName($item['descr']);
|
||||
$zboziOrder->addCartItem($zboziItem);
|
||||
}
|
||||
}
|
||||
|
||||
// odeslání
|
||||
$zbozi->sendOrder($zboziOrder);
|
||||
} catch (\Exception $e) {
|
||||
addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_COMMUNICATION, 'Selhalo odeslani konverze zbozi.cz', ['error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Client
|
||||
*/
|
||||
public function getZboziClient()
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
return new Client($dbcfg->analytics['zbozi_cz_feedback']['ID'], $dbcfg->analytics['zbozi_cz_feedback']['secret_key'], isDevelopment() ? true : false);
|
||||
}
|
||||
|
||||
public function sendHeurekaConversion(OrderEvent $event)
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$order = $event->getOrder();
|
||||
|
||||
$request = $this->requestStack->getCurrentRequest();
|
||||
|
||||
if (!empty($request)) {
|
||||
$heurekaDisagree = $request->get('heurekaDisagree') ?? '0';
|
||||
$order->setData('heurekaDisagree', $heurekaDisagree);
|
||||
}
|
||||
|
||||
if (!empty($request) && $request->get('heurekaDisagree')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!empty($dbcfg->analytics['heureka_overeno']['ID']) && !empty($order->invoice_email)) {
|
||||
try {
|
||||
$options = $this->getHeurekaOptions();
|
||||
$overeno = new ShopCertification($options['heureka_id'], $options['options']);
|
||||
$overeno->setEmail($order->invoice_email);
|
||||
$overeno->setOrderId($order->order_no);
|
||||
|
||||
if (($dbcfg->analytics['heureka_overeno']['send_products'] ?? 'Y') == 'Y') {
|
||||
$order->fetchItems();
|
||||
|
||||
foreach ($order->items as $row) {
|
||||
$item = $this->getHeurekaProductItem($row);
|
||||
if (empty($item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$overeno->addProductItemId($item);
|
||||
} catch (ShopCertification\DuplicateProductItemIdException) {
|
||||
// ta knihovna nepovoluje vlozit vickrat stejny IDcka
|
||||
// takze to vyignoruju, at se to proste preskoci a jedu dal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// logError(__FILE__, __LINE__, "Heureka Overeno odesilam:".print_r($overeno, true));
|
||||
$overeno->logOrder();
|
||||
} catch (\Exception $e) {
|
||||
addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_COMMUNICATION, 'Selhalo odeslani konverze heureka.cz', [
|
||||
'orderNumber' => $order->order_no,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getHeurekaOptions()
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
$country = ShopCertification::HEUREKA_SK;
|
||||
if ($this->countryContext->getActiveId() == 'SK' && ($dbcfg->analytics['heureka_overeno']['ID_SK'] ?? false)) {
|
||||
$heureka_id = $dbcfg->analytics['heureka_overeno']['ID_SK'];
|
||||
} elseif ($this->languageContext->getActiveId() == 'sk' && ($dbcfg->analytics['heureka_overeno']['ID'] ?? false)) {
|
||||
$heureka_id = $dbcfg->analytics['heureka_overeno']['ID'];
|
||||
} else {
|
||||
$heureka_id = $dbcfg->analytics['heureka_overeno']['ID'];
|
||||
$country = ShopCertification::HEUREKA_CZ;
|
||||
}
|
||||
|
||||
return ['heureka_id' => $heureka_id, 'options' => ['service' => $country]];
|
||||
}
|
||||
|
||||
protected function getHeurekaProductItem($row)
|
||||
{
|
||||
if (findModule('marketing', 'product_identifier') == 'code') {
|
||||
return $row['code'];
|
||||
}
|
||||
|
||||
return $row['id_product'].(!empty($row['id_variation']) ? '_'.$row['id_variation'] : '');
|
||||
}
|
||||
|
||||
public function sendCJTracking(OrderEvent $event)
|
||||
{
|
||||
$cfg = Config::get();
|
||||
$dbcfg = \Settings::getDefault();
|
||||
|
||||
$config = $dbcfg->analytics['cjtracking'] ?? [];
|
||||
|
||||
if (empty($config['CID'])) {
|
||||
$config = $cfg['Modules']['CJTracking'] ?? [];
|
||||
if ($domainConfig = ($cfg['Modules']['CJTracking']['domain_config'][$this->domainContext->getActiveId()] ?? false)) {
|
||||
$config = array_merge($config, $domainConfig);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($CID = $config['CID'] ?? null) || empty($TYPE = $config['TYPE'] ?? null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$request = SymfonyBridge::getCurrentRequest();
|
||||
if (($cjevent = $request->cookies->get('cjevent')) || ($cjevent = $request->getSession()->get('cjevent'))) {
|
||||
$order = $event->getOrder();
|
||||
|
||||
$order->setData('cjevent', $cjevent);
|
||||
|
||||
$params = [
|
||||
'CID' => $CID, 'TYPE' => $TYPE, 'CJEVENT' => $cjevent, 'METHOD' => 'S2S',
|
||||
'CURRENCY' => $order->currency, 'OID' => $order->order_no,
|
||||
];
|
||||
$i = 1;
|
||||
$amountOfDiscount = \DecimalConstants::zero();
|
||||
foreach ($order->fetchItems() as $item) {
|
||||
if ($item['id_product']) {
|
||||
$id = $item['id_product'].($item['id_variation'] ? '_'.$item['id_variation'] : '');
|
||||
if (findModule('marketing', 'product_identifier') == 'code') {
|
||||
$id = $item['code'];
|
||||
}
|
||||
if (($item['note']['totalDiscount'] ?? false) && empty($item['note']['discounts'])) {
|
||||
$amountOfDiscount = $amountOfDiscount->add(toDecimal($item['note']['totalDiscount']));
|
||||
}
|
||||
$params['ITEM'.$i] = $id;
|
||||
$params['AMT'.$i] = $item['piece_price']['value_without_vat']->asFloat();
|
||||
$params['QTY'.$i] = $item['pieces'];
|
||||
$i++;
|
||||
} elseif (!empty($item['note'])) {
|
||||
$coupon = ($item['note']['coupon'] ?? $item['note']['generated_coupon']['code'] ?? null);
|
||||
$price = $item['piece_price']['value_without_vat']->asFloat();
|
||||
if ($coupon && ($price < 0)) {
|
||||
$params['COUPON'] = $coupon;
|
||||
$amountOfDiscount = $amountOfDiscount->add(toDecimal(-$price));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($coupons = OrderInfo::getUsedCoupons($order)) {
|
||||
$params['COUPON'] = join(',', $coupons);
|
||||
}
|
||||
|
||||
if (!empty($params['DISCOUNT'])) {
|
||||
$params['DISCOUNT'] = $amountOfDiscount->add(toDecimal($params['DISCOUNT']))->asFloat();
|
||||
} elseif ($amountOfDiscount > \DecimalConstants::zero()) {
|
||||
$params['DISCOUNT'] = $amountOfDiscount->asFloat();
|
||||
}
|
||||
|
||||
$returned = sqlQueryBuilder()->select('COUNT(*)')->from('orders')
|
||||
->where(Operator::equals(['invoice_email' => $order->getUserEmail()]))
|
||||
->andWhere('date_created >= DATE_SUB(current_date, INTERVAL 1 YEAR)')
|
||||
->execute()->fetchColumn();
|
||||
$params['cust_status'] = ($returned > 1 ? 'return' : 'new');
|
||||
|
||||
$url = 'https://www.kdukvh.com/u?'.http_build_query($params);
|
||||
$curl = curl_init();
|
||||
curl_setopt_array($curl, [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => 'GET',
|
||||
]);
|
||||
$response = curl_exec($curl);
|
||||
if ($error = curl_error($curl)) {
|
||||
addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_COMMUNICATION, 'CJ Tracking: curl error', ['error' => $error]);
|
||||
}
|
||||
if (trim($response) != 'received') {
|
||||
addActivityLog(ActivityLog::SEVERITY_ERROR, ActivityLog::TYPE_COMMUNICATION, 'CJ Tracking: not received', ['response' => $response]);
|
||||
}
|
||||
curl_close($curl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @required
|
||||
*/
|
||||
public function setLanguageContext(LanguageContext $languageContext): void
|
||||
{
|
||||
$this->languageContext = $languageContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle\EventListener;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\Cookie;
|
||||
use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException;
|
||||
use Symfony\Component\HttpKernel\Event\ResponseEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
|
||||
class ResponseListener implements EventSubscriberInterface
|
||||
{
|
||||
public const CJSessionKey = 'cjevent';
|
||||
|
||||
public static function getSubscribedEvents()
|
||||
{
|
||||
return [
|
||||
KernelEvents::RESPONSE => [
|
||||
['onKernelResponse', 100],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function onKernelResponse(ResponseEvent $event): void
|
||||
{
|
||||
$request = $event->getRequest();
|
||||
|
||||
try {
|
||||
$session = $request->getSession();
|
||||
} catch (SessionNotFoundException $e) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cookie_bar = $request->cookies->get('cookie-bar');
|
||||
$consents = array_flip(explode(',', $cookie_bar ?? ''));
|
||||
$isAnalyticsStorage = isset($consents['analytics_storage']);
|
||||
|
||||
if ($requestData = $request->get(self::CJSessionKey)) {
|
||||
$session->set(self::CJSessionKey, $requestData);
|
||||
}
|
||||
|
||||
if ($dmAfill = $request->get('transaction_id')) {
|
||||
if (isset($consents['analytics_storage'])) {
|
||||
$response = $event->getResponse();
|
||||
$response->headers->setCookie(new Cookie('AP_tracker_TID', $dmAfill, time() + 120 * 24 * 60 * 60));
|
||||
} else {
|
||||
$session->set('AP_tracker_TID', $dmAfill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
bundles/KupShop/MarketingBundle/MarketingBundle.php
Normal file
9
bundles/KupShop/MarketingBundle/MarketingBundle.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle;
|
||||
|
||||
use Symfony\Component\HttpKernel\Bundle\Bundle;
|
||||
|
||||
class MarketingBundle extends Bundle
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
content:
|
||||
resource: "@MarketingBundle/Controller/"
|
||||
type: annotation
|
||||
@@ -0,0 +1,19 @@
|
||||
services:
|
||||
_defaults:
|
||||
autowire: true
|
||||
autoconfigure: true
|
||||
public: true
|
||||
|
||||
KupShop\MarketingBundle\SupportBox\InfoProvider:
|
||||
class: KupShop\MarketingBundle\SupportBox\InfoProvider
|
||||
autowire: true
|
||||
|
||||
KupShop\MarketingBundle\TrustedShop\TrustedShopProvider:
|
||||
class: KupShop\MarketingBundle\TrustedShop\TrustedShopProvider
|
||||
autowire: true
|
||||
|
||||
KupShop\MarketingBundle\Admin\Tabs\:
|
||||
resource: ../../Admin/Tabs/*.php
|
||||
|
||||
KupShop\MarketingBundle\:
|
||||
resource: ../../{Controller,EventListener}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Smarty plugin
|
||||
* -------------------------------------------------------------
|
||||
* Type: function
|
||||
* Name: eval
|
||||
* Purpose: evaluate a template variable as a template
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
function smarty_function_get_cj_info($params, &$smarty)
|
||||
{
|
||||
$type = '';
|
||||
$view = $params['view'];
|
||||
if (!$view) {
|
||||
return '';
|
||||
}
|
||||
if ($view instanceof \KupShop\ContentBundle\View\HomeView) {
|
||||
$type = 'homepage';
|
||||
} elseif ($view instanceof \KupShop\CatalogBundle\View\CategoryView) {
|
||||
$type = 'category';
|
||||
} elseif ($view instanceof \KupShop\ContentBundle\View\ProductView) {
|
||||
$type = 'productDetail';
|
||||
} elseif ($view instanceof \KupShop\ContentBundle\View\CartView) {
|
||||
$type = 'cart';
|
||||
} elseif ($view instanceof \KupShop\CatalogBundle\View\SearchView) {
|
||||
$type = 'searchResults';
|
||||
} elseif ($view instanceof \KupShop\UserBundle\View\UserView && $params['view']->newUser()) {
|
||||
$type = 'accountSignup';
|
||||
} elseif ($view instanceof \KupShop\UserBundle\View\LoginView) {
|
||||
$type = 'accountCenter';
|
||||
} elseif ($view instanceof \KupShop\ContentBundle\View\OrderView && $view->getTemplate() == 'ordering.success.tpl') {
|
||||
$type = 'conversionConfirmation';
|
||||
}
|
||||
|
||||
if (!empty($params['assign'])) {
|
||||
$smarty->assign($params['assign'], $type);
|
||||
} else {
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* Smarty plugin
|
||||
* -------------------------------------------------------------
|
||||
* Type: function
|
||||
* Name: eval
|
||||
* Purpose: evaluate a template variable as a template
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
use KupShop\MarketingBundle\TrustedShop\TrustedShopProvider;
|
||||
|
||||
function smarty_function_send_trusted_shop($params, &$smarty)
|
||||
{
|
||||
if (empty($params['order']) || empty($params['apikey'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order = $params['order'];
|
||||
|
||||
if ($order->invoice_country == 'RO') {
|
||||
$url = 'https://www.compari.ro/';
|
||||
} elseif ($order->invoice_country == 'HU') {
|
||||
$url = 'https://www.arukereso.hu/';
|
||||
} else {
|
||||
$url = 'https://api.etrusted.com';
|
||||
}
|
||||
|
||||
$Client = ServiceContainer::getService(TrustedShopProvider::class);
|
||||
|
||||
$Client->setWebApiKey($params['apikey']);
|
||||
$Client->setServiceUrlSend($url);
|
||||
$Client->SetEmail($order->invoice_email);
|
||||
|
||||
foreach ($order->items as $item) {
|
||||
if ($item['id_product']) {
|
||||
$identifier = empty($item['id_variation']) ? $item['id_product'] : $item['id_product'].'_'.$item['id_variation'];
|
||||
|
||||
$Client->AddProduct($item['descr'], $identifier);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
echo $Client->Prepare();
|
||||
} catch (Exception $e) {
|
||||
$raven = getRaven();
|
||||
$raven->captureException($e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\OrderDiscountBundle\Resources\upgrade;
|
||||
|
||||
use KupShop\I18nBundle\Translations\SettingsTranslation;
|
||||
use KupShop\KupShopBundle\Context\LanguageContext;
|
||||
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
|
||||
|
||||
class MarketingUpgrade extends \UpgradeNew
|
||||
{
|
||||
public function check_GoogleVerificationTransformed()
|
||||
{
|
||||
return $this->checkDataMigration(10);
|
||||
}
|
||||
|
||||
/** Migrate google verification codes */
|
||||
public function upgrade_GoogleVerificationTransformed()
|
||||
{
|
||||
$dbcfg = \Settings::getDefault();
|
||||
$value = $dbcfg->loadValue('analytics');
|
||||
if (!$value) {
|
||||
$value = [];
|
||||
}
|
||||
$transformedCodes = [];
|
||||
if (isset($value['google_site_verification'])) {
|
||||
$explodedCode = explode(';', $value['google_site_verification']['id']);
|
||||
foreach ($explodedCode as $code) {
|
||||
if ($code != '') {
|
||||
$transformedCodes[] = ['value' => $code, 'note' => ''];
|
||||
}
|
||||
}
|
||||
// TODO: Odmazat nepotrebny settingy - google_site_verification;
|
||||
}
|
||||
|
||||
foreach ($value['google_site_verifications'] ?? [] as $item) {
|
||||
if ($item['value'] != '') {
|
||||
$transformedCodes[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$value['google_site_verifications'] = $transformedCodes;
|
||||
|
||||
$dbcfg->saveValue('analytics', $value);
|
||||
|
||||
\Settings::clearCache();
|
||||
|
||||
$this->commitDataMigration(10);
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_GoogleVerificationTransformed2()
|
||||
{
|
||||
return $this->checkDataMigration(11);
|
||||
}
|
||||
|
||||
/** Migrate google verification codes */
|
||||
public function upgrade_GoogleVerificationTransformed2()
|
||||
{
|
||||
if (findModule(\Modules::CURRENCIES)) {
|
||||
$languageContext = ServiceContainer::getService(LanguageContext::class);
|
||||
$settingsTranslations = ServiceContainer::getService(SettingsTranslation::class);
|
||||
|
||||
foreach ($languageContext->getSupported() as $language) {
|
||||
if ($language->getId() != $languageContext->getDefaultId()) {
|
||||
$data = $settingsTranslations->setLanguageId($language->getId())->get();
|
||||
|
||||
if (!isset($data['analytics'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$transformedCodes = [];
|
||||
$value = $data['analytics'];
|
||||
|
||||
if (isset($value['google_site_verification'])) {
|
||||
$explodedCode = explode(';', $value['google_site_verification']['id']);
|
||||
foreach ($explodedCode as $code) {
|
||||
if ($code != '') {
|
||||
$transformedCodes[] = ['value' => $code, 'note' => ''];
|
||||
}
|
||||
}
|
||||
// TODO: Odmazat nepotrebny settingy - google_site_verification;
|
||||
}
|
||||
|
||||
// Do not save empty array
|
||||
if (!$transformedCodes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$value['google_site_verifications'] = $transformedCodes;
|
||||
|
||||
$settingsTranslations->save(['analytics' => $value]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
\Settings::clearCache();
|
||||
|
||||
$this->commitDataMigration(11);
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\OrderDiscountBundle\Resources\upgrade;
|
||||
|
||||
use KupShop\CatalogBundle\Util\ReviewsUtil;
|
||||
|
||||
class ReviewsUpgrade extends \UpgradeNew
|
||||
{
|
||||
protected function isAllowed()
|
||||
{
|
||||
return findModule(\Modules::REVIEWS);
|
||||
}
|
||||
|
||||
public function check_heurekaIDNameType()
|
||||
{
|
||||
return !$this->checkColumnExists('reviews', 'heureka_id');
|
||||
}
|
||||
|
||||
/** Change name of column heureka_id in reviews to external_id and type from int(11) to varchar(32)*/
|
||||
public function upgrade_heurekaIDNameType()
|
||||
{
|
||||
sqlQuery('ALTER TABLE reviews CHANGE heureka_id external_id VARCHAR(32) NULL');
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_externalId()
|
||||
{
|
||||
return $this->checkColumnExists('reviews', 'external_id');
|
||||
}
|
||||
|
||||
/** Add external_id column to table reviews */
|
||||
public function upgrade_externalId()
|
||||
{
|
||||
sqlQuery('ALTER TABLE reviews ADD COLUMN external_id VARCHAR(50) NULL UNIQUE;');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_externalIdType()
|
||||
{
|
||||
return $this->checkColumnType('reviews', 'external_id', 'VARCHAR(50)');
|
||||
}
|
||||
|
||||
/** Increase reviews.external_id length */
|
||||
public function upgrade_externalIdType()
|
||||
{
|
||||
sqlQuery('ALTER TABLE reviews CHANGE COLUMN external_id external_id VARCHAR(50) NULL');
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_DataColumn()
|
||||
{
|
||||
return $this->checkColumnExists('reviews', 'data');
|
||||
}
|
||||
|
||||
/** Add data column into reviews */
|
||||
public function upgrade_DataColumn()
|
||||
{
|
||||
sqlQuery('ALTER TABLE reviews ADD COLUMN data MEDIUMTEXT DEFAULT NULL');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_reportSeverity()
|
||||
{
|
||||
return $this->checkColumnExists('reviews', 'source');
|
||||
}
|
||||
|
||||
/** Add source to reviews */
|
||||
public function upgrade_reportSeverity()
|
||||
{
|
||||
sqlQuery("alter table reviews add source enum('heureka.cz', 'heureka.sk', 'arukereso.hu', 'compari.ro', 'zbozi.cz') NULL;");
|
||||
sqlQuery('create index report_reviews_security_index ON reviews (source);');
|
||||
|
||||
$this->updateReviewSource();
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_ReviewsSource()
|
||||
{
|
||||
return $this->checkEnumOptions('reviews', 'source', array_keys(ReviewsUtil::$sources));
|
||||
}
|
||||
|
||||
/** reviews: Add 'source' column enum */
|
||||
public function upgrade_ReviewsSource()
|
||||
{
|
||||
$this->updateEnumOptions(
|
||||
'reviews',
|
||||
'source',
|
||||
array_keys(ReviewsUtil::$sources));
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_ReviewsSourceUpToDate()
|
||||
{
|
||||
return $this->checkDataMigration(21);
|
||||
}
|
||||
|
||||
/** Reviews: Update source column */
|
||||
public function upgrade_ReviewsSourceUpToDate()
|
||||
{
|
||||
$this->updateReviewSource();
|
||||
|
||||
$this->commitDataMigration(21);
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
protected function updateReviewSource(): void
|
||||
{
|
||||
if (findModule(\Modules::TRANSLATIONS)) {
|
||||
sqlQuery("UPDATE reviews SET source='heureka.cz' WHERE external_id REGEXP '^[0-9]+$' AND id_language = 'cs' AND source IS NULL");
|
||||
sqlQuery("UPDATE reviews SET source='heureka.sk' WHERE external_id REGEXP '^[0-9]+$' AND id_language = 'sk' AND source IS NULL");
|
||||
sqlQuery("UPDATE reviews SET source='zbozi.cz' WHERE external_id NOT REGEXP '^[0-9]+$' AND id_language = 'cs' AND source IS NULL");
|
||||
sqlQuery("UPDATE reviews SET source='arukereso.hu' WHERE external_id LIKE ('hu_%') AND id_language = 'hu' AND source IS NULL");
|
||||
sqlQuery("UPDATE reviews SET source='compari.ro' WHERE external_id LIKE ('ro_%') AND id_language = 'ro' AND source IS NULL");
|
||||
} else {
|
||||
sqlQuery("UPDATE reviews SET source='heureka.cz' WHERE external_id REGEXP '^[0-9]+$' AND source IS NULL");
|
||||
sqlQuery("UPDATE reviews SET source='zbozi.cz' WHERE external_id NOT REGEXP '^[0-9]+$' AND source IS NULL");
|
||||
}
|
||||
}
|
||||
|
||||
public function check_ReviewResponse()
|
||||
{
|
||||
return $this->checkColumnExists('reviews', 'response');
|
||||
}
|
||||
|
||||
/** Add review response */
|
||||
public function upgrade_ReviewResponse()
|
||||
{
|
||||
sqlQuery('ALTER TABLE reviews ADD COLUMN response MEDIUMTEXT NULL;');
|
||||
sqlQuery('ALTER TABLE reviews ADD COLUMN response_time DATETIME NULL;');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
|
||||
public function check_ReviewSaleIdColumn(): bool
|
||||
{
|
||||
return findModule(\Modules::SALES) && $this->checkColumnExists('reviews', 'id_sale');
|
||||
}
|
||||
|
||||
/** Add reviews.id_sale column */
|
||||
public function upgrade_ReviewSaleIdColumn(): void
|
||||
{
|
||||
sqlQuery('ALTER TABLE reviews ADD COLUMN id_sale INT(11) UNSIGNED NULL AFTER id_order');
|
||||
sqlQuery('ALTER TABLE reviews ADD CONSTRAINT FK_reviews_id_sale FOREIGN KEY (id_sale) REFERENCES sales(id) ON UPDATE CASCADE ON DELETE CASCADE');
|
||||
|
||||
$this->upgradeOK();
|
||||
}
|
||||
}
|
||||
146
bundles/KupShop/MarketingBundle/SupportBox/InfoProvider.php
Normal file
146
bundles/KupShop/MarketingBundle/SupportBox/InfoProvider.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle\SupportBox;
|
||||
|
||||
use Query\Operator;
|
||||
|
||||
class InfoProvider
|
||||
{
|
||||
public function getAdditionalData(?string $email, ?string $phone): array
|
||||
{
|
||||
$data = [];
|
||||
|
||||
if ($email) {
|
||||
$data['user'] = (new \User())->createFromLogin($email);
|
||||
} elseif ($phone) {
|
||||
$phone = str_replace(' ', '', $phone);
|
||||
$data['user'] = \User::createFromSpec(Operator::like(['phone' => '%'.$phone.'%']));
|
||||
}
|
||||
|
||||
$data = array_merge($data, $this->provideOrdersData($email, $phone));
|
||||
$data = array_merge($data, $this->provideSalesData($email, $phone));
|
||||
|
||||
$smarty = createSmarty(true, true);
|
||||
$smarty->assign($data);
|
||||
|
||||
$result['html'] = $smarty->fetch('supportBox/userInfo.tpl');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getName($phone)
|
||||
{
|
||||
$phone = str_replace(' ', '', $phone);
|
||||
$name = sqlQueryBuilder()
|
||||
->select('CONCAT_WS(" ", name, surname)')
|
||||
->from('users')
|
||||
->where(Operator::like(['phone' => '%'.$phone.'%']))
|
||||
->orderBy('id', 'DESC')
|
||||
->setMaxResults(1)
|
||||
->execute()->fetchColumn();
|
||||
|
||||
if (!$name) {
|
||||
$name = sqlQueryBuilder()
|
||||
->select('CONCAT_WS(" ", invoice_name, invoice_surname)')
|
||||
->from('orders')
|
||||
->where(Operator::like(['invoice_phone' => '%'.$phone.'%']))
|
||||
->orderBy('id', 'DESC')
|
||||
->setMaxResults(1)
|
||||
->execute()->fetchColumn();
|
||||
}
|
||||
|
||||
if (!$name) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $name,
|
||||
];
|
||||
}
|
||||
|
||||
protected function provideOrdersData(?string $email, ?string $phone): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
$orX = [];
|
||||
|
||||
if ($email) {
|
||||
$orX[] = Operator::like(['invoice_email' => $email]);
|
||||
}
|
||||
|
||||
if ($phone) {
|
||||
$orX[] = Operator::like(['invoice_phone' => $phone]);
|
||||
}
|
||||
|
||||
$result['total_orders_price'] = sqlQueryBuilder()->select('SUM(total_price*currency_rate)')
|
||||
->from('orders')
|
||||
->where(Operator::orX($orX))
|
||||
->andWhere('status_storno!=1')
|
||||
->execute()->fetchOne();
|
||||
|
||||
$ordersQb = sqlQueryBuilder()
|
||||
->select('*')
|
||||
->from('orders', 'o')
|
||||
->orderBy('o.id', 'DESC')
|
||||
->setMaxResults(2)
|
||||
->where(Operator::orX($orX));
|
||||
|
||||
foreach ($ordersQb->execute() as $order) {
|
||||
$order['order_status'] = getOrderStatus($order['status'])['name'];
|
||||
$order['items'] = sqlQueryBuilder()
|
||||
->select('*')
|
||||
->from('order_items')
|
||||
->where(Operator::equals(['id_order' => $order['id']]))
|
||||
->execute()->fetchAllAssociative();
|
||||
|
||||
$result['orders'][] = $order;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function provideSalesData(?string $email, ?string $phone): array
|
||||
{
|
||||
if (!findModule(\Modules::SALES)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
$orX = [];
|
||||
|
||||
if ($email) {
|
||||
$orX[] = Operator::like(['u.email' => $email]);
|
||||
}
|
||||
|
||||
if ($phone) {
|
||||
$orX[] = Operator::like(['u.phone' => $phone]);
|
||||
}
|
||||
|
||||
$baseQb = sqlQueryBuilder()
|
||||
->select('s.*, sell.title as seller_name')
|
||||
->from('sales', 's')
|
||||
->leftJoin('s', 'users', 'u', 'u.id = s.id_user')
|
||||
->leftJoin('s', 'sellers', 'sell', 'sell.id = s.id_seller');
|
||||
|
||||
$result['total_sales_price'] = (clone $baseQb)
|
||||
->select('SUM(total_price)')
|
||||
->where(Operator::orX($orX))
|
||||
->execute()->fetchOne();
|
||||
|
||||
$salesQb = (clone $baseQb)
|
||||
->where(Operator::orX($orX));
|
||||
|
||||
foreach ($salesQb->execute() as $sale) {
|
||||
$sale['items'] = sqlQueryBuilder()
|
||||
->select('*')
|
||||
->from('sales_items')
|
||||
->where(Operator::equals(['id_sale' => $sale['id']]))
|
||||
->execute()->fetchAllAssociative();
|
||||
|
||||
$result['sales'][] = $sale;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
|
||||
namespace KupShop\MarketingBundle\TrustedShop;
|
||||
|
||||
/** Trusted Shop engine class, which provides purchase data sending.
|
||||
* Requires PHP version 5.3.0 or higher.
|
||||
* Requires PHP curl package (php5-curl, libcurl, curl).
|
||||
*
|
||||
* @version 2.0
|
||||
*
|
||||
* @author Árukereső.hu 2016 */
|
||||
class TrustedShopProvider
|
||||
{
|
||||
public const VERSION = '2.0/PHP';
|
||||
|
||||
protected $service_url_send = 'https://www.compari.ro/';
|
||||
public const SERVICE_URL_AKU = 'https://assets.arukereso.com/aku.min.js';
|
||||
public const SERVICE_TOKEN_REQUEST = 't2/TokenRequest.php';
|
||||
public const SERVICE_TOKEN_PROCESS = 't2/TrustedShop.php';
|
||||
|
||||
public const ERROR_EMPTY_EMAIL = 'Customer e-mail address is empty.';
|
||||
public const ERROR_EMPTY_WEBAPIKEY = 'Partner WebApiKey is empty.';
|
||||
public const ERROR_EXAMPLE_EMAIL = 'Customer e-mail address has been not changed yet.';
|
||||
public const ERROR_EXAMPLE_PRODUCT = 'Product name has been not changed yet.';
|
||||
public const ERROR_TOKEN_REQUEST_TIMED_OUT = 'Token request timed out.';
|
||||
public const ERROR_TOKEN_REQUEST_FAILED = 'Token request failed.';
|
||||
public const ERROR_TOKEN_BAD_REQUEST = 'Bad request: ';
|
||||
|
||||
protected $WebApiKey;
|
||||
protected $Email;
|
||||
protected $Products = [];
|
||||
|
||||
/** Sets the customer's e-mail address.
|
||||
* @param string $Email - Current customer's e-mail address. */
|
||||
public function SetEmail($Email)
|
||||
{
|
||||
$this->Email = $Email;
|
||||
}
|
||||
|
||||
/** Adds a product to send. Callable multiple times.
|
||||
* @param string $ProductName - A product name from the customer's cart
|
||||
* @param string $ProductId - A product id, it must be same as in the feed. */
|
||||
public function AddProduct($ProductName, $ProductId = null)
|
||||
{
|
||||
$Content = [];
|
||||
$Content['Name'] = $ProductName;
|
||||
if (!empty($ProductId)) {
|
||||
$Content['Id'] = $ProductId;
|
||||
}
|
||||
$this->Products[] = $Content;
|
||||
}
|
||||
|
||||
/** Prepares the Trusted code, which provides data sending from the customer's browser to us.
|
||||
* @return string - Prepared Trusted code (HTML). */
|
||||
public function Prepare()
|
||||
{
|
||||
if (empty($this->WebApiKey)) {
|
||||
throw new \Exception(self::ERROR_EMPTY_WEBAPIKEY);
|
||||
}
|
||||
if (empty($this->Email)) {
|
||||
throw new \Exception(self::ERROR_EMPTY_EMAIL);
|
||||
}
|
||||
if ($this->Email == 'somebody@example.com') {
|
||||
throw new \Exception(self::ERROR_EXAMPLE_EMAIL);
|
||||
}
|
||||
$Examples = ['Name of first purchased product', 'Name of second purchased product'];
|
||||
foreach ($Examples as $Example) {
|
||||
foreach ($this->Products as $Product) {
|
||||
if ($Product['Name'] == $Example) {
|
||||
throw new \Exception(self::ERROR_EXAMPLE_PRODUCT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$Params = [];
|
||||
$Params['Version'] = self::VERSION;
|
||||
$Params['WebApiKey'] = $this->WebApiKey;
|
||||
$Params['Email'] = $this->Email;
|
||||
$Params['Products'] = json_encode($this->Products);
|
||||
|
||||
$Random = md5($this->WebApiKey.microtime());
|
||||
$Query = $this->GetQuery($Params);
|
||||
|
||||
// Sending:
|
||||
$Output = '<script type="text/javascript">window.aku_request_done = function(w, c) {';
|
||||
$Output .= 'var I = new Image(); I.src="'.$this->service_url_send.self::SERVICE_TOKEN_PROCESS.$Query.'" + c;';
|
||||
$Output .= '};</script>';
|
||||
// Include:
|
||||
$Output .= '<script type="text/javascript"> (function() {';
|
||||
$Output .= 'var a=document.createElement("script"); a.type="text/javascript"; a.src="'.self::SERVICE_URL_AKU.'"; a.async=true;';
|
||||
$Output .= '(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a);';
|
||||
$Output .= '})();</script>';
|
||||
// Fallback:
|
||||
$Output .= '<noscript>';
|
||||
$Output .= '<img src="'.$this->service_url_send.self::SERVICE_TOKEN_PROCESS.$Query.$Random.'" />';
|
||||
$Output .= '</noscript>';
|
||||
|
||||
return $Output;
|
||||
}
|
||||
|
||||
/** Performs a request on our servers to get a token and assembles query params with it.
|
||||
* @param array $Params - Parameters to send with token request
|
||||
*
|
||||
* @return string - Query string to assemble sending code snipet on client's side with it. */
|
||||
protected function GetQuery($Params)
|
||||
{
|
||||
// Prepare curl request:
|
||||
$Curl = curl_init();
|
||||
curl_setopt($Curl, CURLOPT_URL, $this->service_url_send.self::SERVICE_TOKEN_REQUEST);
|
||||
curl_setopt($Curl, CURLOPT_POST, 1);
|
||||
curl_setopt($Curl, CURLOPT_POSTFIELDS, http_build_query($Params));
|
||||
curl_setopt($Curl, CURLOPT_CONNECTTIMEOUT_MS, 500);
|
||||
curl_setopt($Curl, CURLOPT_TIMEOUT_MS, 500);
|
||||
curl_setopt($Curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($Curl, CURLOPT_HEADER, true);
|
||||
|
||||
// Execute the request:
|
||||
$Response = curl_exec($Curl);
|
||||
|
||||
if (curl_errno($Curl) === 0 && $Response !== false) {
|
||||
$Info = curl_getinfo($Curl);
|
||||
$StatusCode = $Info['http_code'];
|
||||
|
||||
$JsonBody = substr($Response, $Info['header_size']);
|
||||
$JsonArray = json_decode($JsonBody, true);
|
||||
$JsonError = json_last_error();
|
||||
|
||||
curl_close($Curl);
|
||||
|
||||
if (empty($JsonError)) {
|
||||
if ($StatusCode == 200) {
|
||||
$Query = [];
|
||||
$Query[] = 'Token='.$JsonArray['Token'];
|
||||
$Query[] = 'WebApiKey='.$this->WebApiKey;
|
||||
$Query[] = 'C=';
|
||||
|
||||
return '?'.join('&', $Query);
|
||||
} elseif ($StatusCode == 400) {
|
||||
throw new \Exception(self::ERROR_TOKEN_BAD_REQUEST.$JsonArray['ErrorCode'].' - '.$JsonArray['ErrorMessage']);
|
||||
} else {
|
||||
throw new \Exception(self::ERROR_TOKEN_REQUEST_FAILED);
|
||||
}
|
||||
} else {
|
||||
throw new \Exception('Json error: '.$JsonError);
|
||||
}
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setWebApiKey(string $WebApiKey)
|
||||
{
|
||||
$this->WebApiKey = $WebApiKey;
|
||||
}
|
||||
|
||||
public function setServiceUrlSend(string $service_url_send)
|
||||
{
|
||||
$this->service_url_send = $service_url_send;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user