Skip to content

Email-уведомления о заказе уходят на устаревший email из msCustomer вместо email из формы заказа #218

@biz87

Description

@biz87

Описание

Email-уведомления о заказе (customer-channel) уходят на msCustomer.email, а не на email, указанный пользователем в форме этого конкретного заказа. Это проблема для типового сценария «один браузер, несколько заказов от разных людей».

Воспроизведение

  1. Открыть сайт в приватной вкладке
  2. Оформить заказ с email = alice@example.com, дождаться email-уведомления — уходит корректно на alice@example.com
  3. Не закрывая вкладку (session token тот же) оформить второй заказ, в форме указать email = bob@example.com
  4. Ожидание: письмо о втором заказе уходит на bob@example.com
  5. Факт: письмо уходит на alice@example.com

Причина

Controllers/Customer/Customer.php::getOrCreate() находит существующего msCustomer по token (сессионный ID) — и не обновляет email/phone из новых данных формы. Это может быть осознанным решением (чтобы не затирать данные клиента), но оно приводит к багу в отправке уведомлений.

Services/Order/OrderStatusService.php::getCustomerRecipient() (строки 204-262) при сборе recipient для notification-канала смотрит в:

  1. msCustomer.email / .phone (строки 220-228)
  2. modUserProfile как fallback (строки 242-256)

msOrderAddress — самый свежий источник контактов, относящийся к этому заказу — не используется вообще. Email, который пользователь только что ввёл в форму, не попадает в recipient.

Предложение

В OrderStatusService::getCustomerRecipient() поставить msOrderAddress первым источником — как контакты, прикреплённые именно к этому заказу. msCustomer и modUserProfile становятся fallback'ом, когда в адресе пусто (backwards-совместимо для старых заказов, где адрес мог не сохраняться).

Псевдокод:

```php
protected function getCustomerRecipient(msOrder $msOrder): ?array
{
$recipient = ['type' => 'customer', 'email' => null, 'phone' => null, 'telegram_chat_id' => null];
$hasContact = false;

// 1) Order-level address — самые свежие контакты, относящиеся к этому заказу
/** @var msOrderAddress|null \$address */
\$address = \$msOrder->getOne('Address');
if (\$address) {
    if (\$email = \$address->get('email')) { \$recipient['email'] = \$email; \$hasContact = true; }
    if (\$phone = \$address->get('phone')) { \$recipient['phone'] = \$phone; \$hasContact = true; }
}

// 2) msCustomer — fallback, если в адресе пусто. Плюс всегда для telegram_chat_id/customer.*
\$customer = \$msOrder->getOne('Customer');
if (\$customer) {
    \$recipient['customer'] = \$customer->toArray();
    if (empty(\$recipient['email']) && \$customer->get('email'))  { \$recipient['email'] = \$customer->get('email'); \$hasContact = true; }
    if (empty(\$recipient['phone']) && \$customer->get('phone'))  { \$recipient['phone'] = \$customer->get('phone'); \$hasContact = true; }
    \$ext = \$customer->get('extended');
    if (is_array(\$ext) && !empty(\$ext['telegram_chat_id'])) {
        \$recipient['telegram_chat_id'] = \$ext['telegram_chat_id'];
        \$hasContact = true;
    }
}

// 3) modUserProfile — последний fallback для email/phone, без изменений
// ...

return \$hasContact ? \$recipient : null;

}
```

Почему именно такой порядок

  • Заказ — это source of truth. Что юзер ввёл на checkout'е — то и контакты уведомления о заказе. Менять email в msCustomer каждый раз опасно (сломает merge клиентов по email и CRM-интеграции).
  • Telegram/SMS логика не ломается. telegram_chat_id по-прежнему читается только из customer-extended (в адресе его нет — это отдельный канал доставки).
  • Manager-recipient не трогаем. getManagerRecipient() (строки 264+) никак не зависит от заказа, читает системные настройки — остаётся как есть.

Обратная совместимость

  • Старые заказы, где в msOrderAddress email пустой, продолжат работать через fallback на msCustomer — поведение не меняется.
  • Для заказов, где email в форме совпадает с email в customer'е — поведение не меняется.
  • Изменится только тот самый сценарий, который сейчас багован.

Альтернатива на одном плагине (временное решение, пока не смержено)

Перехват через событие msOnBeforeSendNotification с мутацией $recipient['email'] из $notification->getOrder()->getOne('Address')->get('email'). Работает точечно, не требует правки ядра, не меняет поведение если email в адресе отсутствует.

Затронутые файлы

  • core/components/minishop3/src/Services/Order/OrderStatusService.php:204-262 (getCustomerRecipient)

Дополнительно

Отдельным вопросом — стоит ли также обновлять msCustomer.email при новой submission (merge стратегия). Но это другой уровень решения и зависит от бизнес-правил (привязка клиента к единственному email vs. история контактов). Пока — ограничиваемся корректировкой resolver'а для уведомлений.

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: mediumСредний приоритет

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions