using Nop.Core; using Nop.Core.Domain.Common; using Nop.Core.Domain.Messages; using Nop.Core.Domain.Orders; using Nop.Core.Events; using Nop.Services.Affiliates; using Nop.Services.Catalog; using Nop.Services.Common; using Nop.Services.Customers; using Nop.Services.Localization; using Nop.Services.Messages; using Nop.Services.Orders; using Nop.Services.Stores; namespace Nop.Plugin.Misc.FruitBankPlugin.Services; public class FruitBankNotificationService( CommonSettings commonSettings, IMessageTemplateService messageTemplateService, IEmailAccountService emailAccountService, EmailAccountSettings emailAccountSettings, IMessageTokenProvider messageTokenProvider, IWorkflowMessageService workflowMessageService, ICustomerService customerService, IStoreContext storeContext, IAddressService addressService, IAffiliateService affiliateService, IEventPublisher eventPublisher, ILanguageService languageService, ILocalizationService localizationService, IOrderService orderService, IProductService productService, IQueuedEmailService queuedEmailService, IStoreService storeService, ITokenizer tokenizer, MessagesSettings messagesSettings) : WorkflowMessageService(commonSettings, emailAccountSettings, addressService, affiliateService, customerService, emailAccountService, eventPublisher, languageService, localizationService, messageTemplateService, messageTokenProvider, orderService, productService, queuedEmailService, storeContext, storeService, tokenizer, messagesSettings) { public const string ORDER_AUDITED_TEMPLATE_NAME = "FruitBank.OrderAudited.CustomerNotification"; public const string ORDER_STARTED_TEMPLATE_NAME = "FruitBank.OrderStarted.CustomerNotification"; public override async Task> SendOrderPlacedCustomerNotificationAsync(Order order, int languageId, string attachmentFilePath = null, string attachmentFileName = null) { ArgumentNullException.ThrowIfNull(order); var store = await _storeService.GetStoreByIdAsync(order.StoreId) ?? await _storeContext.GetCurrentStoreAsync(); languageId = await EnsureLanguageIsActiveAsync(languageId, store.Id); var messageTemplates = await GetActiveMessageTemplatesAsync(MessageTemplateSystemNames.ORDER_PLACED_CUSTOMER_NOTIFICATION, store.Id); if (!messageTemplates.Any()) return new List(); //tokens var commonTokens = new List(); await _messageTokenProvider.AddOrderTokensAsync(commonTokens, order, languageId); await _messageTokenProvider.AddCustomerTokensAsync(commonTokens, order.CustomerId); var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId); return await messageTemplates.SelectAwait(async messageTemplate => { //email account var emailAccount = await GetEmailAccountOfMessageTemplateAsync(messageTemplate, languageId); var tokens = new List(commonTokens); await _messageTokenProvider.AddStoreTokensAsync(tokens, store, emailAccount, languageId); //event notification await _eventPublisher.MessageTokensAddedAsync(messageTemplate, tokens); var billingAddress = await _addressService.GetAddressByIdAsync(order.BillingAddressId); string toEmail; //we surely have shipping address for orders with shipping method, but let's be safe if (billingAddress.Email != null) { if (!billingAddress.Email.EndsWith("inval.id")) { toEmail = billingAddress.Email; } else { Console.WriteLine($"Customer {customer.Id} has BillinggAddressId but emailaddress is invalid: {billingAddress.Email}"); return -1; } } else { Console.WriteLine($"Customer {customer.Id} has BillinggAddressId but emailaddress not found."); return -1; } var toName = $"{billingAddress.FirstName} {billingAddress.LastName}"; return await SendNotificationAsync(messageTemplate, emailAccount, languageId, tokens, toEmail, toName, attachmentFilePath, attachmentFileName); }).ToListAsync(); } /// /// Re-sends the order info email to the customer on demand from the admin. /// Reuses the OrderPlaced template which already contains the full order table. /// public Task> SendOrderInfoEmailAsync(Order order) => SendOrderPlacedCustomerNotificationAsync(order, order.CustomerLanguageId); /// /// Sends the "order started" (being prepared) customer notification. /// For measurable orders, informs the customer that final prices will be /// confirmed after weighing. Fires once when MeasuringStatus transitions to Started. /// /// public async Task SendOrderStartedCustomerNotificationAsync(Order order, bool isMeasurable) { var measurableNote = isMeasurable ? "

Rendelésed mérhető tételeket tartalmaz. A végleges ár a mérés után kerül megerősítésre.

" : string.Empty; return await SendNotificationAsync(ORDER_STARTED_TEMPLATE_NAME, order, measurableNote); } /// /// Sends the "order audited" customer notification. /// For measurable orders, confirms that weights have been recorded and /// the final price is as shown on the order. /// Fires once when MeasuringStatus transitions to Audited. /// public async Task SendOrderAuditedCustomerNotificationAsync(Order order, bool isMeasurable) { var measurableNote = isMeasurable ? "

A mért tételek súlyait rögzítettük, a végleges ár a rendelésen feltüntetett összeg.

" : string.Empty; return await SendNotificationAsync(ORDER_AUDITED_TEMPLATE_NAME, order, measurableNote); } // ── shared core ───────────────────────────────────────────────────────── private async Task SendNotificationAsync(string templateName, Order order, string measurableNote) { var store = await storeContext.GetCurrentStoreAsync(); var templates = await messageTemplateService.GetMessageTemplatesByNameAsync(templateName, store.Id); var messageTemplate = templates?.FirstOrDefault(); if (messageTemplate is null || !messageTemplate.IsActive) return 0; var emailAccount = await emailAccountService.GetEmailAccountByIdAsync(messageTemplate.EmailAccountId) ?? await emailAccountService.GetEmailAccountByIdAsync(emailAccountSettings.DefaultEmailAccountId); var tokens = new List(); await messageTokenProvider.AddStoreTokensAsync(tokens, store, emailAccount, order.CustomerLanguageId); await messageTokenProvider.AddOrderTokensAsync(tokens, order, order.CustomerLanguageId); var customer = await customerService.GetCustomerByIdAsync(order.CustomerId); await messageTokenProvider.AddCustomerTokensAsync(tokens, customer); tokens.Add(new Token("Order.MeasurableNote", measurableNote, true)); int addressId = 0; string customerEmail = customer.Email; //bool customerHasShippingAddress = customer.ShippingAddressId.HasValue; if (customer.ShippingAddressId.HasValue) { addressId = (int)customer.ShippingAddressId; customerEmail = (await addressService.GetAddressByIdAsync(addressId)).Email ?? customer.Email; } Console.WriteLine($"Customer email determined as: {customerEmail} (addressId: {addressId})"); var toName = $"{customer.FirstName} {customer.LastName}".Trim(); if (string.IsNullOrWhiteSpace(toName)) toName = customer.Email; return await workflowMessageService.SendNotificationAsync( messageTemplate, emailAccount, order.CustomerLanguageId, tokens, customerEmail, toName); } }