fix: order creation

This commit is contained in:
2025-12-08 00:24:19 +03:00
committed by Nikita Kiselev
parent 4a3dcc11d1
commit 82ab8134e1
13 changed files with 164 additions and 96 deletions

View File

@@ -64,6 +64,11 @@ export const useCheckoutStore = defineStore('checkout', {
const pulse = usePulseStore(); const pulse = usePulseStore();
await nextTick(() => { await nextTick(() => {
pulse.ingest(TC_PULSE_EVENTS.ORDER_CREATED, {
order_id: this.order.id,
revenue: this.order?.final_total_numeric,
currency: this.order?.currency,
});
yaMetrika.reachGoal(YA_METRIKA_GOAL.ORDER_CREATED_SUCCESS, { yaMetrika.reachGoal(YA_METRIKA_GOAL.ORDER_CREATED_SUCCESS, {
price: this.order?.final_total_numeric, price: this.order?.final_total_numeric,
currency: this.order?.currency, currency: this.order?.currency,
@@ -88,11 +93,6 @@ export const useCheckoutStore = defineStore('checkout', {
} }
} }
}); });
pulse.ingest(TC_PULSE_EVENTS.ORDER_CREATED, {
order_id: this.order.id,
revenue: this.order?.final_total_numeric,
currency: this.order?.currency,
});
}); });
await window.Telegram.WebApp.HapticFeedback.notificationOccurred('success'); await window.Telegram.WebApp.HapticFeedback.notificationOccurred('success');

View File

@@ -2,6 +2,7 @@
<?php <?php
use Console\ApplicationFactory; use Console\ApplicationFactory;
use Console\Commands\PulseSendEventsCommand;
use Console\Commands\ScheduleListCommand; use Console\Commands\ScheduleListCommand;
use Console\Commands\ScheduleRunCommand; use Console\Commands\ScheduleRunCommand;
use Console\Commands\VersionCommand; use Console\Commands\VersionCommand;
@@ -92,4 +93,5 @@ $console = new Application('TeleCart', module_version());
$console->add($app->get(VersionCommand::class)); $console->add($app->get(VersionCommand::class));
$console->add($app->get(ScheduleRunCommand::class)); $console->add($app->get(ScheduleRunCommand::class));
$console->add($app->get(ScheduleListCommand::class)); $console->add($app->get(ScheduleListCommand::class));
$console->add($app->get(PulseSendEventsCommand::class));
$console->run(); $console->run();

View File

@@ -1,3 +1,3 @@
APP_DEBUG=true APP_DEBUG=true
PULSE_API_HOST=http://host.docker.internal:8086/api/ PULSE_API_HOST=https://pulse.telecart.pro/api/
PULSE_HEARTBEAT_SECRET=c5261f5d-529e-45ad-a69c-9778b755b7cb PULSE_HEARTBEAT_SECRET=c5261f5d-529e-45ad-a69c-9778b755b7cb

View File

@@ -6,7 +6,6 @@ use GuzzleHttp\Exception\GuzzleException;
use Openguru\OpenCartFramework\Cache\CacheInterface; use Openguru\OpenCartFramework\Cache\CacheInterface;
use Openguru\OpenCartFramework\Config\Settings; use Openguru\OpenCartFramework\Config\Settings;
use Openguru\OpenCartFramework\Scheduler\TaskInterface; use Openguru\OpenCartFramework\Scheduler\TaskInterface;
use Openguru\OpenCartFramework\Settings\UserSettingsInterface;
use Openguru\OpenCartFramework\TeleCartPulse\TeleCartEvent; use Openguru\OpenCartFramework\TeleCartPulse\TeleCartEvent;
use Openguru\OpenCartFramework\TeleCartPulse\TeleCartPulseEventsSender; use Openguru\OpenCartFramework\TeleCartPulse\TeleCartPulseEventsSender;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;

View File

@@ -0,0 +1,29 @@
<?php
namespace Console\Commands;
use Bastion\ScheduledTasks\TeleCartPulseSendEventsTask;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class PulseSendEventsCommand extends TeleCartCommand
{
protected static $defaultName = 'pulse:send';
protected static $defaultDescription = 'Manually send pulse events ignoring schedule.';
private TeleCartPulseSendEventsTask $teleCartPulseSendEventsTask;
public function __construct(TeleCartPulseSendEventsTask $teleCartPulseSendEventsTask)
{
parent::__construct();
$this->teleCartPulseSendEventsTask = $teleCartPulseSendEventsTask;
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$output->writeln('<info>Sending Pulse events.</info>');
$this->teleCartPulseSendEventsTask->execute();
return self::SUCCESS;
}
}

View File

@@ -4,7 +4,7 @@ namespace Console\Commands;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
class TeleCartCommand extends Command abstract class TeleCartCommand extends Command
{ {
public function __construct() public function __construct()
{ {

View File

@@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
final class Request extends SymfonyRequest final class Request extends SymfonyRequest
{ {
public function json(string $key = null, $default = null) public function json(string $key = null, $default = null)
{ {
$content = $this->getContent(); $content = $this->getContent();

View File

@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Handlers; namespace App\Handlers;
use App\Services\TelegramCustomerService; use App\Services\TelecartCustomerService;
use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\JsonResponse;
use Openguru\OpenCartFramework\Http\Request; use Openguru\OpenCartFramework\Http\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@@ -19,12 +19,12 @@ use Throwable;
class TelegramCustomerHandler class TelegramCustomerHandler
{ {
private TelegramCustomerService $telegramCustomerService; private TelecartCustomerService $telegramCustomerService;
private LoggerInterface $logger; private LoggerInterface $logger;
private TelegramInitDataDecoder $initDataDecoder; private TelegramInitDataDecoder $initDataDecoder;
public function __construct( public function __construct(
TelegramCustomerService $telegramCustomerService, TelecartCustomerService $telegramCustomerService,
LoggerInterface $logger, LoggerInterface $logger,
TelegramInitDataDecoder $initDataDecoder TelegramInitDataDecoder $initDataDecoder
) { ) {

View File

@@ -4,7 +4,7 @@ namespace App\Services;
use Openguru\OpenCartFramework\QueryBuilder\Builder; use Openguru\OpenCartFramework\QueryBuilder\Builder;
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface; use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
use Openguru\OpenCartFramework\Support\Arr; use Openguru\OpenCartFramework\QueryBuilder\JoinClause;
class OcCustomerService class OcCustomerService
{ {
@@ -17,25 +17,8 @@ class OcCustomerService
$this->database = $database; $this->database = $database;
} }
public function findOrCreate(array $orderData): ?int public function create(array $orderData, ?int $telecartCustomerId): ?int
{ {
$email = Arr::get($orderData, 'email');
$phone = Arr::get($orderData, 'telephone');
if (! $email && ! $phone) {
return null;
}
$customer = $this->builder->newQuery()
->from(db_table('customer'))
->where('email', '=', $email)
->where('telephone', '=', $phone)
->firstOrNull();
if ($customer) {
return (int) $customer['customer_id'];
}
$customerData = [ $customerData = [
'customer_group_id' => $orderData['customer_group_id'], 'customer_group_id' => $orderData['customer_group_id'],
'store_id' => $orderData['store_id'], 'store_id' => $orderData['store_id'],
@@ -56,7 +39,50 @@ class OcCustomerService
]; ];
$this->database->insert(db_table('customer'), $customerData); $this->database->insert(db_table('customer'), $customerData);
$lastInsertId = $this->database->lastInsertId();
return $this->database->lastInsertId(); if ($telecartCustomerId) {
$this->builder
->where('id', '=', $telecartCustomerId)
->update('telecart_customers', [
'oc_customer_id' => $lastInsertId,
]);
}
return $lastInsertId;
}
public function findByTelecartCustomerId(int $telegramCustomerId): ?array
{
return $this->builder->newQuery()
->select(['oc_customers.*'])
->from(db_table('customer'), 'oc_customers')
->join('telecart_customers', function (JoinClause $join) {
$join->on('telecart_customers.oc_customer_id', '=', 'oc_customers.customer_id');
})
->where('telecart_customers.id', '=', $telegramCustomerId)
->firstOrNull();
}
public function findById(int $ocCustomerId): ?array
{
return $this->builder->newQuery()
->select(['oc_customers.*'])
->from(db_table('customer'), 'oc_customers')
->where('oc_customers.customer_id', '=', $ocCustomerId)
->firstOrNull();
}
public function findOrCreateByTelecartCustomerId(int $telecartCustomerId, array $orderData): ?array
{
$ocCustomer = $this->findByTelecartCustomerId($telecartCustomerId);
if (! $ocCustomer) {
$ocCustomerId = $this->create($orderData, $telecartCustomerId);
return $this->findById($ocCustomerId);
}
return $ocCustomer;
} }
} }

View File

@@ -23,7 +23,7 @@ class OrderCreateService
private SettingsService $settings; private SettingsService $settings;
private TelegramService $telegramService; private TelegramService $telegramService;
private LoggerInterface $logger; private LoggerInterface $logger;
private TelegramCustomerService $telegramCustomerService; private TelecartCustomerService $telecartCustomerService;
private OcCustomerService $ocCustomerService; private OcCustomerService $ocCustomerService;
private OrderMetaService $orderMetaService; private OrderMetaService $orderMetaService;
@@ -34,7 +34,7 @@ class OrderCreateService
SettingsService $settings, SettingsService $settings,
TelegramService $telegramService, TelegramService $telegramService,
LoggerInterface $logger, LoggerInterface $logger,
TelegramCustomerService $telegramCustomerService, TelecartCustomerService $telegramCustomerService,
OcCustomerService $ocCustomerService, OcCustomerService $ocCustomerService,
OrderMetaService $orderMetaService OrderMetaService $orderMetaService
) { ) {
@@ -44,7 +44,7 @@ class OrderCreateService
$this->settings = $settings; $this->settings = $settings;
$this->telegramService = $telegramService; $this->telegramService = $telegramService;
$this->logger = $logger; $this->logger = $logger;
$this->telegramCustomerService = $telegramCustomerService; $this->telecartCustomerService = $telegramCustomerService;
$this->ocCustomerService = $ocCustomerService; $this->ocCustomerService = $ocCustomerService;
$this->orderMetaService = $orderMetaService; $this->orderMetaService = $orderMetaService;
} }
@@ -72,6 +72,7 @@ class OrderCreateService
// Получаем telegram_user_id из tgData // Получаем telegram_user_id из tgData
$telegramUserId = Arr::get($data['tgData'] ?? [], 'user.id'); $telegramUserId = Arr::get($data['tgData'] ?? [], 'user.id');
$telegramUserdata = Arr::get($data['tgData'] ?? [], 'user');
if (! $telegramUserId) { if (! $telegramUserId) {
throw new RuntimeException('Telegram user id is required.'); throw new RuntimeException('Telegram user id is required.');
@@ -109,16 +110,12 @@ class OrderCreateService
try { try {
$this->database->beginTransaction(); $this->database->beginTransaction();
$ocCustomerId = $this->ocCustomerService->findOrCreate($orderData); $telecartCustomer = $this->telecartCustomerService->saveOrUpdate($telegramUserdata);
$telecartCustomerId = null; $telecartCustomerId = (int) $telecartCustomer['id'];
if ($ocCustomerId) { $ocCustomer = $this->ocCustomerService->findOrCreateByTelecartCustomerId($telecartCustomerId, $orderData);
$telecartCustomerId = $this->telegramCustomerService->assignOcCustomer( $ocCustomerId = (int) $ocCustomer['customer_id'];
$telegramUserId,
$ocCustomerId
);
}
$orderData['customer_id'] = $ocCustomerId ?? 0; $orderData['customer_id'] = $ocCustomerId;
$this->database->insert(db_table('order'), $orderData); $this->database->insert(db_table('order'), $orderData);
$orderId = $this->database->lastInsertId(); $orderId = $this->database->lastInsertId();
@@ -137,6 +134,8 @@ class OrderCreateService
$this->orderMetaService->insert($orderId, $storeId, $customOrderFields, $telecartCustomerId); $this->orderMetaService->insert($orderId, $storeId, $customOrderFields, $telecartCustomerId);
} }
$this->telecartCustomerService->increaseOrdersCount($telecartCustomerId);
$this->database->commitTransaction(); $this->database->commitTransaction();
} catch (Throwable $exception) { } catch (Throwable $exception) {
$this->database->rollBackTransaction(); $this->database->rollBackTransaction();
@@ -151,10 +150,6 @@ class OrderCreateService
$this->sendNotifications($orderData, $data['tgData']); $this->sendNotifications($orderData, $data['tgData']);
if ($telecartCustomerId) {
$this->telegramCustomerService->increaseOrdersCount($telecartCustomerId);
}
$dateTimeFormatted = ''; $dateTimeFormatted = '';
try { try {
$dateTimeFormatted = $now->format('d.m.Y H:i'); $dateTimeFormatted = $now->format('d.m.Y H:i');
@@ -196,8 +191,12 @@ class OrderCreateService
$this->telegramService->sendMessage($chatId, $message); $this->telegramService->sendMessage($chatId, $message);
} catch (Throwable $exception) { } catch (Throwable $exception) {
$this->logger->error( $this->logger->error(
"Telegram sendMessage to owner error. ChatID: $chatId, Message: $message", 'Telegram sendMessage to owner error.',
['exception' => $exception], [
'exception' => $exception,
'chat_id' => $chatId,
'message' => $message,
],
); );
} }
} }
@@ -212,8 +211,12 @@ class OrderCreateService
$this->telegramService->sendMessage($customerChatId, $message); $this->telegramService->sendMessage($customerChatId, $message);
} catch (Throwable $exception) { } catch (Throwable $exception) {
$this->logger->error( $this->logger->error(
"Telegram sendMessage to customer error. ChatID: $chatId, Message: $message", "Telegram sendMessage to customer error.",
['exception' => $exception] [
'exception' => $exception,
'chat_id' => $chatId,
'message' => $message,
],
); );
} }
} }

View File

@@ -10,7 +10,7 @@ use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\Support\Utils; use Openguru\OpenCartFramework\Support\Utils;
use RuntimeException; use RuntimeException;
class TelegramCustomerService class TelecartCustomerService
{ {
private TelegramCustomer $telegramCustomer; private TelegramCustomer $telegramCustomer;

View File

@@ -88,6 +88,12 @@ class TestCase extends BaseTestCase
'oc_customer_group_id' => 99, 'oc_customer_group_id' => 99,
], ],
'pulse' => [
'api_key' => '',
'batch_size' => 50,
'max_attempts' => 3,
],
'mainpage_blocks' => [ 'mainpage_blocks' => [
[ [
'type' => 'products_feed', 'type' => 'products_feed',

View File

@@ -7,7 +7,7 @@ use App\Services\OcCustomerService;
use App\Services\OrderCreateService; use App\Services\OrderCreateService;
use App\Services\OrderMetaService; use App\Services\OrderMetaService;
use App\Services\SettingsService; use App\Services\SettingsService;
use App\Services\TelegramCustomerService; use App\Services\TelecartCustomerService;
use Carbon\Carbon; use Carbon\Carbon;
use Mockery as m; use Mockery as m;
use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator; use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator;
@@ -83,39 +83,39 @@ class OrderCreateServiceTest extends TestCase
$connection = m::mock(ConnectionInterface::class); $connection = m::mock(ConnectionInterface::class);
$connection->shouldReceive('beginTransaction')->once()->andReturnTrue(); $connection->shouldReceive('beginTransaction')->once()->andReturnTrue();
$connection->shouldReceive('commitTransaction')->once()->andReturnTrue(); $connection->shouldReceive('commitTransaction')->once()->andReturnTrue();
// $connection->shouldReceive('rollBackTransaction')->once()->andReturnTrue(); $connection->shouldReceive('rollBackTransaction')->andReturnTrue();
$connection->shouldReceive('lastInsertId')->once()->andReturn($orderId)->ordered(); $connection->shouldReceive('lastInsertId')->once()->andReturn($orderId)->ordered();
$connection->shouldReceive('lastInsertId')->once()->andReturn($orderProductId)->ordered(); $connection->shouldReceive('lastInsertId')->once()->andReturn($orderProductId)->ordered();
$connection->shouldReceive('insert')->once()->with( $connection->shouldReceive('insert')->once()->with(
db_table('order'), db_table('order'),
[ m::on(function ($orderData) use ($storeId, $data, $totalNumeric, $meta, $currencyId, $currencyCode, $currencyValue, $ocCustomerId) {
'store_id' => $storeId, return $orderData['store_id'] === $storeId
'store_name' => $this->app->getConfigValue('app.app_name'), && $orderData['store_name'] === $this->app->getConfigValue('app.app_name')
'firstname' => $data['firstname'], && $orderData['firstname'] === $data['firstname']
'lastname' => $data['lastname'], && $orderData['lastname'] === $data['lastname']
'email' => $data['email'], && $orderData['email'] === $data['email']
'telephone' => $data['telephone'], && $orderData['telephone'] === $data['telephone']
'payment_method' => $data['payment_method'], && $orderData['payment_method'] === $data['payment_method']
'comment' => $data['comment'], && $orderData['comment'] === $data['comment']
'shipping_address_1' => $data['shipping_address_1'], && $orderData['shipping_address_1'] === $data['shipping_address_1']
'shipping_city' => $data['shipping_city'], && $orderData['shipping_city'] === $data['shipping_city']
'shipping_zone' => $data['shipping_zone'], && $orderData['shipping_zone'] === $data['shipping_zone']
'shipping_postcode' => $data['shipping_postcode'], && $orderData['shipping_postcode'] === $data['shipping_postcode']
'total' => $totalNumeric, && $orderData['total'] === $totalNumeric
'order_status_id' => $this->app->getConfigValue('orders.order_default_status_id'), && $orderData['order_status_id'] === $this->app->getConfigValue('orders.order_default_status_id')
'ip' => $meta['ip'], && $orderData['ip'] === $meta['ip']
'forwarded_ip' => $meta['ip'], && $orderData['forwarded_ip'] === $meta['ip']
'user_agent' => $meta['user_agent'], && $orderData['user_agent'] === $meta['user_agent']
'date_added' => $dateAdded, && $orderData['date_added'] instanceof Carbon
'date_modified' => $dateAdded, && $orderData['date_modified'] instanceof Carbon
'language_id' => $this->app->getConfigValue('app.language_id'), && $orderData['language_id'] === $this->app->getConfigValue('app.language_id')
'currency_id' => $currencyId, && $orderData['currency_id'] === $currencyId
'currency_code' => $currencyCode, && $orderData['currency_code'] === $currencyCode
'currency_value' => $currencyValue, && $orderData['currency_value'] === $currencyValue
'customer_group_id' => $this->app->getConfigValue('orders.oc_customer_group_id'), && $orderData['customer_group_id'] === $this->app->getConfigValue('orders.oc_customer_group_id')
'customer_id' => $ocCustomerId, && $orderData['customer_id'] === $ocCustomerId;
], }),
) )
->andReturn(true); ->andReturn(true);
@@ -135,16 +135,16 @@ class OrderCreateServiceTest extends TestCase
$connection->shouldReceive('insert')->once()->with( $connection->shouldReceive('insert')->once()->with(
db_table('order_history'), db_table('order_history'),
[ m::on(function ($historyData) use ($orderId) {
'order_id' => $orderId, return $historyData['order_id'] === $orderId
'order_status_id' => $this->app->getConfigValue('orders.order_default_status_id'), && $historyData['order_status_id'] === $this->app->getConfigValue('orders.order_default_status_id')
'notify' => 0, && $historyData['notify'] === 0
'comment' => 'Заказ оформлен через Telegram Mini App.' && $historyData['comment'] === 'Заказ оформлен через Telegram Mini App.'
. "\n\nДополнительная информация по заказу:" . "\n\nДополнительная информация по заказу:"
. "\nfield_1: кирилица" . "\nfield_1: кирилица"
. "\nfield_2: hello\n", . "\nfield_2: hello\n"
'date_added' => $dateAdded, && $historyData['date_added'] instanceof Carbon;
], }),
)->andReturnTrue(); )->andReturnTrue();
$cartService = m::mock(CartService::class); $cartService = m::mock(CartService::class);
@@ -176,17 +176,19 @@ class OrderCreateServiceTest extends TestCase
$telegramServiceMock = m::mock(TelegramService::class); $telegramServiceMock = m::mock(TelegramService::class);
$loggerMock = m::mock(LoggerInterface::class); $loggerMock = m::mock(LoggerInterface::class);
$telegramCustomerService = m::mock(TelegramCustomerService::class); $telegramCustomerService = m::mock(TelecartCustomerService::class);
$telegramCustomerService->shouldReceive('assignOcCustomer')->once() $telegramCustomerService->shouldReceive('saveOrUpdate')->once()
->with($telegramUserId, $ocCustomerId) ->with($tgData['user'])
->andReturn($telecartCustomerId); ->andReturn(['id' => $telecartCustomerId]);
$telegramCustomerService->shouldReceive('increaseOrdersCount')->once() $telegramCustomerService->shouldReceive('increaseOrdersCount')->once()
->with($telecartCustomerId) ->with($telecartCustomerId)
->andReturnNull(); ->andReturnNull();
$ocCustomerService = m::mock(OcCustomerService::class); $ocCustomerService = m::mock(OcCustomerService::class);
$ocCustomerService->shouldReceive('findOrCreate')->once()->andReturn($ocCustomerId); $ocCustomerService->shouldReceive('findOrCreateByTelecartCustomerId')->once()
->with($telecartCustomerId, m::type('array'))
->andReturn(['customer_id' => $ocCustomerId]);
$orderMetaService = m::mock(OrderMetaService::class); $orderMetaService = m::mock(OrderMetaService::class);
$orderMetaService->shouldReceive('insert') $orderMetaService->shouldReceive('insert')