Files
kupshop/bundles/KupShop/KupShopBundle/Email/OrderEmail.php
2025-08-02 16:30:27 +02:00

493 lines
17 KiB
PHP

<?php
namespace KupShop\KupShopBundle\Email;
use KupShop\KupShopBundle\Context\ContextManager;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Query\JsonOperator;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Mail\Microdata;
use KupShop\OrderingBundle\Attachment\InvoicePDFAttachment;
use KupShop\OrderingBundle\Util\Attachment\AttachmentLocator;
use KupShop\OrderingBundle\Util\Order\OrderInfo;
use KupShop\SellerBundle\Utils\SellerOrderUtil;
use Query\Operator;
use Symfony\Component\Routing\Router;
use Symfony\Contracts\Service\Attribute\Required;
class OrderEmail extends BaseEmail
{
protected static $name = 'Komentář k objednávce';
protected static $type = 'ORDER_COMMENT';
protected static $group = EmailGroupTypeEnum::ORDER;
protected static $priority = 8;
protected $subject = 'Důležitá informace k objednávce {KOD}';
protected $template = 'email/email_order_comment.tpl';
protected $baseTemplate = 'email/order.tpl';
protected $logSentEmail = true;
protected bool $isAdminEmail = false;
protected ?bool $sendToInvoiceCopyEmail = null; // null: default, true: always, false: never
protected $order_message;
/** @var AttachmentLocator */
private $attachmentLocator;
/** @var ContextManager */
protected $contextManager;
public function __construct(
protected OrderInfo $orderInfo,
protected Microdata $microdataUtil,
LanguageContext $languageContext,
) {
parent::__construct($languageContext);
}
public static string $type_test_email = 'orders';
public static function attachmentsAllowed()
{
return true;
}
/**
* @required
*/
public function setAttachmentLocator(AttachmentLocator $attachmentLocator)
{
$this->attachmentLocator = $attachmentLocator;
}
/** @required */
public function setContextManager(ContextManager $contextManager)
{
$this->contextManager = $contextManager;
}
/** @var \Order */
protected $order;
public function setOrder(\Order $order)
{
$this->order = $order;
return $this;
}
public function getOrder()
{
return $this->order;
}
protected $comment;
public function setComment($comment)
{
$this->comment = $comment;
return $this;
}
public function setIsAdminEmail(bool $isAdminEmail): self
{
$this->isAdminEmail = $isAdminEmail;
return $this;
}
public function setSendToInvoiceCopyEmail(?bool $sendToInvoiceCopyEmail): self
{
$this->sendToInvoiceCopyEmail = $sendToInvoiceCopyEmail;
return $this;
}
public function getEmailTemplate($languageID = null): array
{
if (!$languageID && $this->order) {
$languageID = $this->order->id_language;
}
return parent::getEmailTemplate($languageID);
}
/**
* @return array
*/
public function getBasicTemplate($languageID = null)
{
if (!$languageID && $this->order) {
$languageID = $this->order->id_language;
}
return parent::getBasicTemplate($languageID);
}
public function renderEmail($template, $data = [], $base_template = 'order.tpl'): array
{
if ($this->comment) {
$template['body'] = str_replace('{KOMENTAR}', $this->comment, $template['body']);
}
if ($this->order) {
$data = array_merge($data, ['order' => $this->order]);
}
$message = parent::renderEmail($template, $data, $base_template);
if ($this->order) {
$message['to'] = $this->order->getUserEmail();
if ($this->shouldSendCopyEmail($template['attachments'])) {
if ($copyEmail = $this->order->getCopyEmail()) {
$message['cc'] = $copyEmail;
}
}
}
$message['template'] = $template;
return $message;
}
public function replacePlaceholdersItem($placeholder)
{
switch ($placeholder) {
case 'DATUM_DORUCENI':
$delivery_date = $this->orderInfo->getDeliveryDate($this->order);
if (!$delivery_date) {
// produkt neni skladem a nevime kdy bude => nemuzeme napsat datum doruceni
return translate_shop('unknownDeliveryDate', 'order');
}
return $delivery_date->format(\Settings::getDateFormat());
case 'POLOZKY_EXCEL':
return path('kupshop_orderingbundle_xlsinvoice', ['id_order' => $this->order->id, 'cf' => $this->order->getSecurityCode()], Router::ABSOLUTE_URL);
case 'ODKAZ_FAKTURA':
// spousta shopu ma tenhle placeholder pridany custom na shopu v class.Order.php, takze nejdriv to zkusim vytahnout
// pres order a pokud to existuje, tak vratim to kvuli backward compatibility
if ($result = $this->order->replacePlaceholdersItem($placeholder)) {
return $result;
}
return path(
'kupshop_orderingbundle_pdfinvoice',
[
'id_order' => $this->order->id,
'cf' => $this->order->getSecurityCode(),
],
Router::ABSOLUTE_URL
);
case 'BALIKY':
// could be more than one package
$packages = [];
foreach ($this->orderInfo->getPackages($this->order) as $package_id => $package) {
if ($link = $package['track_url'] ?? null) {
$packages[] = "<a href=\"{$link}\">{$package_id}</a>";
} else {
$packages[] = $package_id;
}
}
return implode(', ', $packages);
case 'TELEFON_FAKTURACNI':
return $this->order->invoice_phone;
case 'TELEFON_DODACI':
return $this->order->delivery_phone;
case 'MISTO_ODBERU':
$value = '';
$deliveryType = $this->order->getDeliveryType();
// vracim udaje jen kdy je typ doruceni POINT
if ($deliveryType->delivery_class->getType() == \DeliveryBase::TYPE_POINT) {
$value = $deliveryType->getDelivery()->name.': '.$deliveryType->getDelivery()->printDeliveryInfo();
}
return $value;
case 'URL_PRODEJCE':
$value = '';
$deliveryType = $this->order->getDeliveryType();
// vracim udaje jen kdyz je typ doruceni IN_PERSON
if ($deliveryType->delivery_class->getType() == \DeliveryBase::TYPE_IN_PERSON) {
if ($sellerId = SellerOrderUtil::getOrderSellerId($this->order->id)) {
$value = path('shop_seller_seller_detail', ['id_seller' => $sellerId]);
}
}
return $value;
case 'JMENO':
return $this->order->invoice_name;
case 'PRIJMENI':
return $this->order->invoice_surname;
}
$parent = parent::replacePlaceholdersItem($placeholder);
if ($parent === null) {
return $this->order->replacePlaceholdersItem($placeholder);
}
return $parent;
}
public function getAttachments($template): array
{
if (empty($template['attachments']) || !$this->order) {
return [];
}
return $this->renderAttachments(function () use ($template): iterable {
$types = json_decode($template['attachments'], true);
foreach ($types as $type) {
$attachment = $this->attachmentLocator->getServiceByType($type, $this->order);
yield $attachment;
}
});
}
public function testEmail(): array
{
if (!isset($this->order)) {
$this->setTestEntity();
}
$this->order->setPlaceholder('KOMENTAR', '<p>Testovací komentář od uživatele.</p>');
try {
return $this->contextManager->activateOrder(
$this->order,
function () {
return parent::testEmail();
}
);
} catch (\Throwable $e) {
throw new \InvalidArgumentException('E-mail nelze otestovat, protože neexistuje žádná objednávka', previous: $e);
}
}
public function setTestEntity(?int $id = null): void
{
$order = new \Order();
$order->createFromDB($id ?: sqlQueryBuilder()->select('MAX(id)')->from('orders')->execute()->fetchOne());
$this->setOrder($order);
}
public function sendEmail($message, $logComment = null)
{
// pokud je email vypnutej, tak nedava smysl logovat odeslani mailu
if ($this->isEnabled()) {
$this->logHistory($message, $logComment);
}
if (($bcc = (\Settings::getDefault()->archive_email ?? null)) && $bcc !== ($message['to'] ?? null)) {
$message['bcc'] = [$bcc];
}
$messageId = parent::sendEmail($message);
if ($messageId && !empty($message['id_history'])) {
// update orders_history with email ID
sqlQueryBuilder()->update('orders_history')
->set('custom_data', JsonOperator::set('custom_data', 'email_id', $messageId))
->where(Operator::equals(['id' => $message['id_history']]))
->execute();
}
return $messageId;
}
protected function logHistory(&$message, $comment = null)
{
$historyId = null;
$notifiedStatus = $this->isAdminEmail ? 0 : 4;
if ($this->logSentEmail && $this->order) {
$template = $message['template'];
$customData['email_type'] = self::getType();
if (self::getType() == 'ORDER_MESSAGE') {
$customData['email_type'] = 'Zprávy uživatelům: '.$this->order_message;
}
$customData['subject'] = $message['subject'];
$customData['isAdminEmail'] = $this->isAdminEmail;
if ($template['attachments']) {
// get attachments types because of saving them into history
$types = json_decode($template['attachments'], true);
$customData['attachments'] = empty($types) ? null : $types;
}
// Renders only body from email to $comment
if (is_null($comment)) {
$comment = $this->renderOnlyBody($message, ['order' => $this->order]);
}
// Write history
$comment = $this->replacePlaceholders($comment);
$historyId = $this->order->logHistory($comment, $notifiedStatus, $customData);
}
$message['isAdminEmail'] = $this->isAdminEmail;
$message['id_history'] = $historyId;
}
public static function getPlaceholders()
{
$placeholders = [
'KOD' => [
'text' => translate('OrderCode', 'emails'),
],
'STAV' => [
'text' => translate('NewOrderStatus', 'emails'),
],
'CENA' => [
'text' => translate('OrderPrice', 'emails'),
],
'ZAPLATIT' => [
'text' => translate('RemainsPaid', 'emails'),
],
'ODKAZ' => [
'text' => translate('ReferenceDetail', 'emails'),
],
'ODKAZ_PLATBA' => [
'text' => translate('ReferencePayment', 'emails'),
],
'DATUM_OBJEDNAVKY' => [
'text' => translate('OrderDate', 'emails'),
],
'CAS_OBJEDNAVKY' => [
'text' => translate('OrderTime', 'emails'),
],
'JMENO' => [
'text' => translate('UserName', 'emails'),
],
'PRIJMENI' => [
'text' => translate('UserSurname', 'emails'),
],
'OSLOVENI_JMENO' => [
'text' => translate('GreetingUserName', 'emails'),
],
'OSLOVENI_PRIJMENI' => [
'text' => translate('GreetingUserSurname', 'emails'),
],
'EMAIL_UZIVATELE' => [
'text' => translate('UsersEmail', 'emails'),
],
'KOMENTAR' => [
'text' => translate('CommentEntered', 'emails'),
],
'POLOZKY_OBJEDNAVKY' => [
'text' => translate('ListOfItems', 'emails'),
],
'QR_PLATBA' => [
'text' => translate('qrcodePayment', 'emails'),
],
'DATUM_SPLATNOSTI' => [
'text' => translate('dueDate', 'emails'),
],
'FAKTURACNI_ADRESA' => [
'text' => translate('invoiceAddress', 'emails'),
],
'DODACI_ADRESA' => [
'text' => translate('deliveryAddress', 'emails'),
],
'POZNAMKA_UZIVATELE' => [
'text' => translate('userNote', 'emails'),
],
'POLOZKY_EXCEL' => [
'text' => translate('invoice_xls', 'emails'),
],
'ODKAZ_FAKTURA' => [
'text' => translate('invoice_pdf', 'emails'),
],
'BALIKY' => [
'text' => translate('LinkToWebpage', 'emails'),
],
'CISLO_FAKTURY' => [
'text' => translate('invoice_no', 'emails'),
],
];
if (findModule(\Modules::DELIVERY_TYPES)) {
$placeholders['POPIS_ZPUSOBU_DORUCENI'] = [
'text' => translate('DescriptionOfSelected', 'emails').'
<a href="launch.php?s=main.php&l=s:menu.php|type:delivery&r=s:list.php|type:delivery"
target="_blank">'.translate('DescriptionOfSelectedLink', 'emails').'</a>',
];
$placeholders['POPIS_PLATBY'] = [
'text' => translate('DescriptionOfSelectedPay', 'emails').'
<a href="launch.php?s=main.php&l=s:menu.php|type:deliveryPayment&r=s:list.php|type:deliveryPayment"
target="_blank">'.translate('DescriptionOfSelectedPayLink', 'emails').'</a>',
];
$placeholders['POPIS_DOPRAVY'] = [
'text' => translate('DescriptionOfSelectedPay', 'emails').'
<a href="launch.php?s=main.php&l=s:menu.php|type:deliveryDelivery&r=s:list.php|type:deliveryDelivery"
target="_blank">'.translate('DescriptionOfSelectedTransport', 'emails').'</a>',
];
}
$placeholders['DATUM_DORUCENI'] = ['text' => translate('deliveryDate', 'emails')];
if (findModule(\Modules::BONUS_PROGRAM)) {
$placeholders['BONUS_PROGRAM_BODY'] = [
'text' => translate('bonusProgramPoints', 'emails'),
];
$placeholders['ZBYVAJICI_BONUS_PROGRAM_BODY'] = [
'text' => translate('bonusProgramPointsRemaining', 'emails'),
];
}
$placeholders['TELEFON_FAKTURACNI'] = ['text' => translate('invoicePhone', 'emails')];
$placeholders['TELEFON_DODACI'] = ['text' => translate('deliveryPhone', 'emails')];
$placeholders['MISTO_ODBERU'] = ['text' => translate('deliveryLocation', 'emails')];
$placeholders['URL_PRODEJCE'] = ['text' => translate('sellerUrl', 'emails')];
return [self::$type => $placeholders] + (parent::getPlaceholders() ?? []);
}
public function getMicrodata(): array
{
if ($order = $this->getOrder()) {
try {
return $this->microdataUtil->getOrderMicrodata($order);
} catch (\Throwable $exception) {
\Sentry\captureException($exception);
}
}
return [];
}
public function __wakeup()
{
parent::__wakeup();
$this->attachmentLocator = ServiceContainer::getService(AttachmentLocator::class);
$this->contextManager = ServiceContainer::getService(ContextManager::class);
$this->orderInfo = ServiceContainer::getService(OrderInfo::class);
$this->microdataUtil = ServiceContainer::getService(Microdata::class);
}
public function __sleep()
{
return array_diff(parent::__sleep(), ['attachmentLocator', 'contextManager', 'orderInfo', 'microdataUtil']);
}
public function getTestType(): ?string
{
return self::$type_test_email;
}
public function shouldSendCopyEmail($attachments): bool
{
return $this->sendToInvoiceCopyEmail ?? in_array(InvoicePDFAttachment::getType(), json_decode($attachments ?? '', true) ?? []);
}
}