From 82ab8134e19f2cc4066de5241e7ff29905d79b17 Mon Sep 17 00:00:00 2001 From: Nikita Kiselev Date: Mon, 8 Dec 2025 00:24:19 +0300 Subject: [PATCH] fix: order creation --- frontend/spa/src/stores/CheckoutStore.js | 10 +-- module/oc_telegram_shop/upload/cli.php | 2 + .../upload/oc_telegram_shop/.env | 2 +- .../TeleCartPulseSendEventsTask.php | 1 - .../Commands/PulseSendEventsCommand.php | 29 ++++++ .../console/Commands/TeleCartCommand.php | 2 +- .../framework/Http/Request.php | 1 + .../src/Handlers/TelegramCustomerHandler.php | 6 +- .../src/Services/OcCustomerService.php | 66 +++++++++----- .../src/Services/OrderCreateService.php | 43 ++++----- ...ervice.php => TelecartCustomerService.php} | 2 +- .../oc_telegram_shop/tests/TestCase.php | 6 ++ .../Unit/Services/OrderCreateServiceTest.php | 90 ++++++++++--------- 13 files changed, 164 insertions(+), 96 deletions(-) create mode 100644 module/oc_telegram_shop/upload/oc_telegram_shop/console/Commands/PulseSendEventsCommand.php rename module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/{TelegramCustomerService.php => TelecartCustomerService.php} (99%) diff --git a/frontend/spa/src/stores/CheckoutStore.js b/frontend/spa/src/stores/CheckoutStore.js index 45fd3e5..1649465 100644 --- a/frontend/spa/src/stores/CheckoutStore.js +++ b/frontend/spa/src/stores/CheckoutStore.js @@ -64,6 +64,11 @@ export const useCheckoutStore = defineStore('checkout', { const pulse = usePulseStore(); 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, { price: this.order?.final_total_numeric, 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'); diff --git a/module/oc_telegram_shop/upload/cli.php b/module/oc_telegram_shop/upload/cli.php index 901df93..a536c7d 100755 --- a/module/oc_telegram_shop/upload/cli.php +++ b/module/oc_telegram_shop/upload/cli.php @@ -2,6 +2,7 @@ add($app->get(VersionCommand::class)); $console->add($app->get(ScheduleRunCommand::class)); $console->add($app->get(ScheduleListCommand::class)); +$console->add($app->get(PulseSendEventsCommand::class)); $console->run(); diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/.env b/module/oc_telegram_shop/upload/oc_telegram_shop/.env index 4717692..ff0cf1f 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/.env +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/.env @@ -1,3 +1,3 @@ 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 diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/ScheduledTasks/TeleCartPulseSendEventsTask.php b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/ScheduledTasks/TeleCartPulseSendEventsTask.php index 352572f..670a9fa 100644 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/ScheduledTasks/TeleCartPulseSendEventsTask.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/bastion/ScheduledTasks/TeleCartPulseSendEventsTask.php @@ -6,7 +6,6 @@ use GuzzleHttp\Exception\GuzzleException; use Openguru\OpenCartFramework\Cache\CacheInterface; use Openguru\OpenCartFramework\Config\Settings; use Openguru\OpenCartFramework\Scheduler\TaskInterface; -use Openguru\OpenCartFramework\Settings\UserSettingsInterface; use Openguru\OpenCartFramework\TeleCartPulse\TeleCartEvent; use Openguru\OpenCartFramework\TeleCartPulse\TeleCartPulseEventsSender; use Psr\Log\LoggerInterface; diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/console/Commands/PulseSendEventsCommand.php b/module/oc_telegram_shop/upload/oc_telegram_shop/console/Commands/PulseSendEventsCommand.php new file mode 100644 index 0000000..1b6b5e0 --- /dev/null +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/console/Commands/PulseSendEventsCommand.php @@ -0,0 +1,29 @@ +teleCartPulseSendEventsTask = $teleCartPulseSendEventsTask; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $output->writeln('Sending Pulse events.'); + $this->teleCartPulseSendEventsTask->execute(); + + return self::SUCCESS; + } +} diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/console/Commands/TeleCartCommand.php b/module/oc_telegram_shop/upload/oc_telegram_shop/console/Commands/TeleCartCommand.php index b2b7b83..b17726e 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/console/Commands/TeleCartCommand.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/console/Commands/TeleCartCommand.php @@ -4,7 +4,7 @@ namespace Console\Commands; use Symfony\Component\Console\Command\Command; -class TeleCartCommand extends Command +abstract class TeleCartCommand extends Command { public function __construct() { diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php index 126f952..f4cf60b 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/framework/Http/Request.php @@ -8,6 +8,7 @@ use Symfony\Component\HttpFoundation\Request as SymfonyRequest; final class Request extends SymfonyRequest { + public function json(string $key = null, $default = null) { $content = $this->getContent(); diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/TelegramCustomerHandler.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/TelegramCustomerHandler.php index fe1a86f..46cbb01 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/TelegramCustomerHandler.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Handlers/TelegramCustomerHandler.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace App\Handlers; -use App\Services\TelegramCustomerService; +use App\Services\TelecartCustomerService; use Symfony\Component\HttpFoundation\JsonResponse; use Openguru\OpenCartFramework\Http\Request; use Symfony\Component\HttpFoundation\Response; @@ -19,12 +19,12 @@ use Throwable; class TelegramCustomerHandler { - private TelegramCustomerService $telegramCustomerService; + private TelecartCustomerService $telegramCustomerService; private LoggerInterface $logger; private TelegramInitDataDecoder $initDataDecoder; public function __construct( - TelegramCustomerService $telegramCustomerService, + TelecartCustomerService $telegramCustomerService, LoggerInterface $logger, TelegramInitDataDecoder $initDataDecoder ) { diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OcCustomerService.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OcCustomerService.php index 6177457..56e0932 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OcCustomerService.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OcCustomerService.php @@ -4,7 +4,7 @@ namespace App\Services; use Openguru\OpenCartFramework\QueryBuilder\Builder; use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface; -use Openguru\OpenCartFramework\Support\Arr; +use Openguru\OpenCartFramework\QueryBuilder\JoinClause; class OcCustomerService { @@ -17,25 +17,8 @@ class OcCustomerService $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 = [ 'customer_group_id' => $orderData['customer_group_id'], 'store_id' => $orderData['store_id'], @@ -56,7 +39,50 @@ class OcCustomerService ]; $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; } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OrderCreateService.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OrderCreateService.php index 26fd543..df4ec13 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OrderCreateService.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/OrderCreateService.php @@ -23,7 +23,7 @@ class OrderCreateService private SettingsService $settings; private TelegramService $telegramService; private LoggerInterface $logger; - private TelegramCustomerService $telegramCustomerService; + private TelecartCustomerService $telecartCustomerService; private OcCustomerService $ocCustomerService; private OrderMetaService $orderMetaService; @@ -34,7 +34,7 @@ class OrderCreateService SettingsService $settings, TelegramService $telegramService, LoggerInterface $logger, - TelegramCustomerService $telegramCustomerService, + TelecartCustomerService $telegramCustomerService, OcCustomerService $ocCustomerService, OrderMetaService $orderMetaService ) { @@ -44,7 +44,7 @@ class OrderCreateService $this->settings = $settings; $this->telegramService = $telegramService; $this->logger = $logger; - $this->telegramCustomerService = $telegramCustomerService; + $this->telecartCustomerService = $telegramCustomerService; $this->ocCustomerService = $ocCustomerService; $this->orderMetaService = $orderMetaService; } @@ -72,6 +72,7 @@ class OrderCreateService // Получаем telegram_user_id из tgData $telegramUserId = Arr::get($data['tgData'] ?? [], 'user.id'); + $telegramUserdata = Arr::get($data['tgData'] ?? [], 'user'); if (! $telegramUserId) { throw new RuntimeException('Telegram user id is required.'); @@ -109,16 +110,12 @@ class OrderCreateService try { $this->database->beginTransaction(); - $ocCustomerId = $this->ocCustomerService->findOrCreate($orderData); - $telecartCustomerId = null; - if ($ocCustomerId) { - $telecartCustomerId = $this->telegramCustomerService->assignOcCustomer( - $telegramUserId, - $ocCustomerId - ); - } + $telecartCustomer = $this->telecartCustomerService->saveOrUpdate($telegramUserdata); + $telecartCustomerId = (int) $telecartCustomer['id']; + $ocCustomer = $this->ocCustomerService->findOrCreateByTelecartCustomerId($telecartCustomerId, $orderData); + $ocCustomerId = (int) $ocCustomer['customer_id']; - $orderData['customer_id'] = $ocCustomerId ?? 0; + $orderData['customer_id'] = $ocCustomerId; $this->database->insert(db_table('order'), $orderData); $orderId = $this->database->lastInsertId(); @@ -137,6 +134,8 @@ class OrderCreateService $this->orderMetaService->insert($orderId, $storeId, $customOrderFields, $telecartCustomerId); } + $this->telecartCustomerService->increaseOrdersCount($telecartCustomerId); + $this->database->commitTransaction(); } catch (Throwable $exception) { $this->database->rollBackTransaction(); @@ -151,10 +150,6 @@ class OrderCreateService $this->sendNotifications($orderData, $data['tgData']); - if ($telecartCustomerId) { - $this->telegramCustomerService->increaseOrdersCount($telecartCustomerId); - } - $dateTimeFormatted = ''; try { $dateTimeFormatted = $now->format('d.m.Y H:i'); @@ -196,8 +191,12 @@ class OrderCreateService $this->telegramService->sendMessage($chatId, $message); } catch (Throwable $exception) { $this->logger->error( - "Telegram sendMessage to owner error. ChatID: $chatId, Message: $message", - ['exception' => $exception], + 'Telegram sendMessage to owner error.', + [ + 'exception' => $exception, + 'chat_id' => $chatId, + 'message' => $message, + ], ); } } @@ -212,8 +211,12 @@ class OrderCreateService $this->telegramService->sendMessage($customerChatId, $message); } catch (Throwable $exception) { $this->logger->error( - "Telegram sendMessage to customer error. ChatID: $chatId, Message: $message", - ['exception' => $exception] + "Telegram sendMessage to customer error.", + [ + 'exception' => $exception, + 'chat_id' => $chatId, + 'message' => $message, + ], ); } } diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/TelegramCustomerService.php b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/TelecartCustomerService.php similarity index 99% rename from module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/TelegramCustomerService.php rename to module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/TelecartCustomerService.php index 3123788..e18dc93 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/TelegramCustomerService.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/src/Services/TelecartCustomerService.php @@ -10,7 +10,7 @@ use Openguru\OpenCartFramework\Support\Arr; use Openguru\OpenCartFramework\Support\Utils; use RuntimeException; -class TelegramCustomerService +class TelecartCustomerService { private TelegramCustomer $telegramCustomer; diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/TestCase.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/TestCase.php index 98074f5..f8e4aa8 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/TestCase.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/TestCase.php @@ -88,6 +88,12 @@ class TestCase extends BaseTestCase 'oc_customer_group_id' => 99, ], + 'pulse' => [ + 'api_key' => '', + 'batch_size' => 50, + 'max_attempts' => 3, + ], + 'mainpage_blocks' => [ [ 'type' => 'products_feed', diff --git a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/Services/OrderCreateServiceTest.php b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/Services/OrderCreateServiceTest.php index 5697cb3..e3e3e4b 100755 --- a/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/Services/OrderCreateServiceTest.php +++ b/module/oc_telegram_shop/upload/oc_telegram_shop/tests/Unit/Services/OrderCreateServiceTest.php @@ -7,7 +7,7 @@ use App\Services\OcCustomerService; use App\Services\OrderCreateService; use App\Services\OrderMetaService; use App\Services\SettingsService; -use App\Services\TelegramCustomerService; +use App\Services\TelecartCustomerService; use Carbon\Carbon; use Mockery as m; use Openguru\OpenCartFramework\OpenCart\Decorators\OcRegistryDecorator; @@ -83,39 +83,39 @@ class OrderCreateServiceTest extends TestCase $connection = m::mock(ConnectionInterface::class); $connection->shouldReceive('beginTransaction')->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($orderProductId)->ordered(); $connection->shouldReceive('insert')->once()->with( db_table('order'), - [ - 'store_id' => $storeId, - 'store_name' => $this->app->getConfigValue('app.app_name'), - 'firstname' => $data['firstname'], - 'lastname' => $data['lastname'], - 'email' => $data['email'], - 'telephone' => $data['telephone'], - 'payment_method' => $data['payment_method'], - 'comment' => $data['comment'], - 'shipping_address_1' => $data['shipping_address_1'], - 'shipping_city' => $data['shipping_city'], - 'shipping_zone' => $data['shipping_zone'], - 'shipping_postcode' => $data['shipping_postcode'], - 'total' => $totalNumeric, - 'order_status_id' => $this->app->getConfigValue('orders.order_default_status_id'), - 'ip' => $meta['ip'], - 'forwarded_ip' => $meta['ip'], - 'user_agent' => $meta['user_agent'], - 'date_added' => $dateAdded, - 'date_modified' => $dateAdded, - 'language_id' => $this->app->getConfigValue('app.language_id'), - 'currency_id' => $currencyId, - 'currency_code' => $currencyCode, - 'currency_value' => $currencyValue, - 'customer_group_id' => $this->app->getConfigValue('orders.oc_customer_group_id'), - 'customer_id' => $ocCustomerId, - ], + m::on(function ($orderData) use ($storeId, $data, $totalNumeric, $meta, $currencyId, $currencyCode, $currencyValue, $ocCustomerId) { + return $orderData['store_id'] === $storeId + && $orderData['store_name'] === $this->app->getConfigValue('app.app_name') + && $orderData['firstname'] === $data['firstname'] + && $orderData['lastname'] === $data['lastname'] + && $orderData['email'] === $data['email'] + && $orderData['telephone'] === $data['telephone'] + && $orderData['payment_method'] === $data['payment_method'] + && $orderData['comment'] === $data['comment'] + && $orderData['shipping_address_1'] === $data['shipping_address_1'] + && $orderData['shipping_city'] === $data['shipping_city'] + && $orderData['shipping_zone'] === $data['shipping_zone'] + && $orderData['shipping_postcode'] === $data['shipping_postcode'] + && $orderData['total'] === $totalNumeric + && $orderData['order_status_id'] === $this->app->getConfigValue('orders.order_default_status_id') + && $orderData['ip'] === $meta['ip'] + && $orderData['forwarded_ip'] === $meta['ip'] + && $orderData['user_agent'] === $meta['user_agent'] + && $orderData['date_added'] instanceof Carbon + && $orderData['date_modified'] instanceof Carbon + && $orderData['language_id'] === $this->app->getConfigValue('app.language_id') + && $orderData['currency_id'] === $currencyId + && $orderData['currency_code'] === $currencyCode + && $orderData['currency_value'] === $currencyValue + && $orderData['customer_group_id'] === $this->app->getConfigValue('orders.oc_customer_group_id') + && $orderData['customer_id'] === $ocCustomerId; + }), ) ->andReturn(true); @@ -135,16 +135,16 @@ class OrderCreateServiceTest extends TestCase $connection->shouldReceive('insert')->once()->with( db_table('order_history'), - [ - 'order_id' => $orderId, - 'order_status_id' => $this->app->getConfigValue('orders.order_default_status_id'), - 'notify' => 0, - 'comment' => 'Заказ оформлен через Telegram Mini App.' - . "\n\nДополнительная информация по заказу:" - . "\nfield_1: кирилица" - . "\nfield_2: hello\n", - 'date_added' => $dateAdded, - ], + m::on(function ($historyData) use ($orderId) { + return $historyData['order_id'] === $orderId + && $historyData['order_status_id'] === $this->app->getConfigValue('orders.order_default_status_id') + && $historyData['notify'] === 0 + && $historyData['comment'] === 'Заказ оформлен через Telegram Mini App.' + . "\n\nДополнительная информация по заказу:" + . "\nfield_1: кирилица" + . "\nfield_2: hello\n" + && $historyData['date_added'] instanceof Carbon; + }), )->andReturnTrue(); $cartService = m::mock(CartService::class); @@ -176,17 +176,19 @@ class OrderCreateServiceTest extends TestCase $telegramServiceMock = m::mock(TelegramService::class); $loggerMock = m::mock(LoggerInterface::class); - $telegramCustomerService = m::mock(TelegramCustomerService::class); - $telegramCustomerService->shouldReceive('assignOcCustomer')->once() - ->with($telegramUserId, $ocCustomerId) - ->andReturn($telecartCustomerId); + $telegramCustomerService = m::mock(TelecartCustomerService::class); + $telegramCustomerService->shouldReceive('saveOrUpdate')->once() + ->with($tgData['user']) + ->andReturn(['id' => $telecartCustomerId]); $telegramCustomerService->shouldReceive('increaseOrdersCount')->once() ->with($telecartCustomerId) ->andReturnNull(); $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->shouldReceive('insert')