From c8be4b4f8eb9a56f276281e4830d8125c80ee364 Mon Sep 17 00:00:00 2001 From: Dmitriy Benyuk Date: Wed, 14 May 2025 16:15:21 +0300 Subject: [PATCH 1/2] Send order to ERP on order and download order XML edit --- ...Ecommerce.DynamicwebLiveIntegration.csproj | 4 +- .../OrderHandler.cs | 1 + .../UI/Commands/DownloadOrderXmlCommand.cs | 96 +++++++++++++++++ .../UI/Commands/TransferOrderToErpCommand.cs | 42 ++++++++ .../UI/Commands/TransferOrdersToErpCommand.cs | 102 ++++++++++++++++++ .../UI/Injectors/OrderEditScreenInjector.cs | 94 ++++++++++++++++ .../UI/Injectors/OrderListScreenInjector.cs | 46 ++++++++ .../Injectors/OrderOverviewScreenInjector.cs | 21 ++++ .../XmlGenerators/OrderXmlGenerator.cs | 2 +- 9 files changed, 406 insertions(+), 2 deletions(-) create mode 100644 src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/DownloadOrderXmlCommand.cs create mode 100644 src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/TransferOrderToErpCommand.cs create mode 100644 src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/TransferOrdersToErpCommand.cs create mode 100644 src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderEditScreenInjector.cs create mode 100644 src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderListScreenInjector.cs create mode 100644 src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderOverviewScreenInjector.cs diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj index 1d23077..c08430a 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Dynamicweb.Ecommerce.DynamicwebLiveIntegration.csproj @@ -1,6 +1,6 @@  - 10.4.22 + 10.4.23 1.0.0.0 Live Integration Live Integration @@ -24,8 +24,10 @@ + + diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/OrderHandler.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/OrderHandler.cs index b7dd3c3..1eae2c6 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/OrderHandler.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/OrderHandler.cs @@ -120,6 +120,7 @@ private static ResponseCacheLevel GetOrderCacheLevel(Settings settings) string lastHash = GetLastOrderHash(settings); if (liveIntegrationSubmitType != SubmitType.ScheduledTask && liveIntegrationSubmitType != SubmitType.CaptureTask && + liveIntegrationSubmitType != SubmitType.ManualSubmit && !string.IsNullOrEmpty(lastHash) && lastHash == currentHash) { // no changes to order diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/DownloadOrderXmlCommand.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/DownloadOrderXmlCommand.cs new file mode 100644 index 0000000..2eeacec --- /dev/null +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/DownloadOrderXmlCommand.cs @@ -0,0 +1,96 @@ +using Dynamicweb.CoreUI.Data; +using Dynamicweb.CoreUI.Data.Validation; +using Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Configuration; +using Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Logging; +using Dynamicweb.Ecommerce.DynamicwebLiveIntegration.XmlGenerators; +using Dynamicweb.Ecommerce.Orders; +using System.IO; + +namespace Dynamicweb.Ecommerce.DynamicwebLiveIntegration.UI.Commands; + +public sealed class DownloadOrderXmlCommand : CommandBase +{ + [Required] + public string OrderId { get; set; } + + [Required] + public bool GetOriginalXml { get; set; } + + public override CommandResult Handle() + { + var order = Services.Orders.GetById(OrderId); + if (order is null) + { + return new CommandResult + { + Message = $"The order with id: '{OrderId}' was not found", + Status = CommandResult.ResultType.NotFound + }; + } + + Settings settings = SettingsManager.GetSettingsByShop(order.ShopId); + if (settings is null && !GetOriginalXml) + { + return new CommandResult + { + Message = $"No active Dynamicweb Live integration instance found for Order id: {order.Id} and Shop Id: {order.ShopId}.", + Status = CommandResult.ResultType.NotFound + }; + } + + var fileName = $"Order_{order.Id}.xml"; + var xml = GetOriginalXml ? GetOrderOriginalXml(order) : GetOrderCurrentXml(settings, order); + + var stream = new MemoryStream(); + using (var writeFile = new StreamWriter(stream, leaveOpen: true)) + { + writeFile.Write(xml); + } + stream.Position = 0; + + return new CommandResult + { + Status = CommandResult.ResultType.Ok, + Model = new FileResult + { + FileStream = stream, + ContentType = "application/octet-stream", + FileDownloadName = fileName + } + }; + } + + /// + /// Occurs when the button was clicked from edit order page. Gets the XML for the order and returns it to the browser. + /// + private static string GetOrderCurrentXml(Settings settings, Order order) + { + var logger = new Logger(settings); + var xmlGeneratorSettings = new OrderXmlGeneratorSettings + { + AddOrderLineFieldsToRequest = settings.AddOrderLineFieldsToRequest, + AddOrderFieldsToRequest = settings.AddOrderFieldsToRequest, + CreateOrder = true, + Beautify = true, + LiveIntegrationSubmitType = SubmitType.DownloadedFromBackEnd, + ReferenceName = "OrdersPut", + ErpControlsDiscount = settings.ErpControlsDiscount, + ErpControlsShipping = settings.ErpControlsShipping, + ErpShippingItemKey = settings.ErpShippingItemKey, + ErpShippingItemType = settings.ErpShippingItemType, + CalculateOrderUsingProductNumber = settings.CalculateOrderUsingProductNumber + }; + return new OrderXmlGenerator().GenerateOrderXml(settings, order, xmlGeneratorSettings, logger); + } + + /// + /// Gets the original XML by reading the original file from disk. + /// + private static string GetOrderOriginalXml(Order order) => File.ReadAllText(BuildXmlFileName(order)); + + internal static string BuildXmlFileName(Order order) + { + return OrderHandler.BuildXmlCopyPath(order.Id, OrderHandler.GetLogFolderForXmlCopies(order.CompletedDate)); + } +} + diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/TransferOrderToErpCommand.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/TransferOrderToErpCommand.cs new file mode 100644 index 0000000..4e6bd40 --- /dev/null +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/TransferOrderToErpCommand.cs @@ -0,0 +1,42 @@ +using Dynamicweb.CoreUI.Data; +using Dynamicweb.CoreUI.Data.Validation; +using Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Configuration; + +namespace Dynamicweb.Ecommerce.DynamicwebLiveIntegration.UI.Commands; + +public sealed class TransferOrderToErpCommand : CommandBase +{ + [Required] + public string OrderId { get; set; } + + public override CommandResult Handle() + { + var order = Services.Orders.GetById(OrderId); + if (order is null) + { + return new CommandResult + { + Message = $"The order with id: '{OrderId}' was not found", + Status = CommandResult.ResultType.NotFound + }; + } + + Settings settings = SettingsManager.GetSettingsByShop(order.ShopId); + if (settings is null) + { + return new CommandResult + { + Message = $"No active Dynamicweb Live integration instance found for Order id: {order.Id} and Shop Id: {order.ShopId}.", + Status = CommandResult.ResultType.NotFound + }; + } + + bool result = OrderHandler.UpdateOrder(settings, order, SubmitType.ManualSubmit) ?? false; + return new CommandResult + { + Message = result ? "Order successfully transferred to ERP" : "Error creating order in ERP. Check the LiveIntegration log for details", + Status = result ? CommandResult.ResultType.Ok : CommandResult.ResultType.Error + }; + } +} + diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/TransferOrdersToErpCommand.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/TransferOrdersToErpCommand.cs new file mode 100644 index 0000000..cc06e03 --- /dev/null +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Commands/TransferOrdersToErpCommand.cs @@ -0,0 +1,102 @@ +using Dynamicweb.CoreUI.Data; +using Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Configuration; +using Dynamicweb.Ecommerce.Orders; +using Dynamicweb.Ecommerce.UI.Commands; +using System.Collections.Generic; +using System.Linq; + +namespace Dynamicweb.Ecommerce.DynamicwebLiveIntegration.UI.Commands; + +public sealed class TransferOrdersToErpCommand : OrderBulkActionCommand +{ + public override CommandResult Handle() + { + var orders = GetSelectedOrders().ToList(); + if (orders.Count == 0) + { + return new() + { + Status = CommandResult.ResultType.NotFound, + Message = "The selected Orders could not be found" + }; + } + + List exportedOrders = new List(); + List alreadyExportedOrders = new List(); + + foreach (var order in orders) + { + if (string.IsNullOrEmpty(order.IntegrationOrderId)) + { + var settings = SettingsManager.GetSettingsByShop(order.ShopId); + if (settings is null) + continue; + + bool exported = OrderHandler.UpdateOrder(settings, order, SubmitType.ManualSubmit) ?? false; + if (exported) + { + exportedOrders.Add(order.Id); + } + } + else + { + alreadyExportedOrders.Add(order.Id); + } + } + + var message = GetExportedOrdersMessage(orders, exportedOrders, alreadyExportedOrders, out var success); + + return new() + { + Status = success ? CommandResult.ResultType.Ok : CommandResult.ResultType.Error, + Message = message + }; + } + + /// + /// Gets the exported orders message. + /// + /// System.String. + private static string GetExportedOrdersMessage(List orders, List exportedOrders, List alreadyExportedOrders, out bool success) + { + string output = string.Empty; + success = false; + + if (alreadyExportedOrders.Count > 0 && alreadyExportedOrders.Count == orders.Count()) + { + output = "All selected orders are already transferred to ERP."; + success = true; + } + else if (exportedOrders.Count > 0 || alreadyExportedOrders.Count > 0) + { + if ((exportedOrders.Count + alreadyExportedOrders.Count) == orders.Count()) + { + output = "All selected orders were successfully transferred to ERP."; + success = true; + } + else + { + if (alreadyExportedOrders.Count > 0) + { + output += $"Orders with IDs [{string.Join(",", alreadyExportedOrders)}] were already transferred to ERP. "; + } + + if (exportedOrders.Count > 0) + { + output += $"Orders with IDs [{string.Join(",", exportedOrders)}] were successfully transferred to ERP. "; + } + + output += $"Orders with IDs [{string.Join(",", orders + .Where(o => !(exportedOrders.Contains(o.Id) || + alreadyExportedOrders.Contains(o.Id))) + .Select(o => o.Id).Distinct().ToArray())}] were not transferred to ERP. Check the LiveIntegration log for details"; + } + } + else + { + output = "None of the selected orders were transferred to ERP. Check the LiveIntegration log for details."; + } + + return output; + } +} diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderEditScreenInjector.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderEditScreenInjector.cs new file mode 100644 index 0000000..9e7088d --- /dev/null +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderEditScreenInjector.cs @@ -0,0 +1,94 @@ +using Dynamicweb.CoreUI.Actions; +using Dynamicweb.CoreUI.Actions.Implementations; +using Dynamicweb.CoreUI.Icons; +using Dynamicweb.CoreUI.Screens; +using Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Configuration; +using Dynamicweb.Ecommerce.DynamicwebLiveIntegration.UI.Commands; +using Dynamicweb.Ecommerce.Orders; +using Dynamicweb.Ecommerce.UI.Models; +using Dynamicweb.Ecommerce.UI.Screens; +using System.Collections.Generic; + +namespace Dynamicweb.Ecommerce.DynamicwebLiveIntegration.UI.Injectors +{ + public sealed class OrderEditScreenInjector : EditScreenInjector + { + internal static readonly string LiveIntegrationTab = "Live Integration"; + + public override IEnumerable GetScreenActions() + { + return GetOrderScreenActions(Screen?.Model?.Id, Screen?.Model?.IntegrationOrderId); + } + + internal static List GetOrderScreenActions(string orderId, string integrationOrderId) + { + var order = Services.Orders.GetById(orderId); + if (order is null) + return []; + + var settings = SettingsManager.GetSettingsByShop(order.ShopId); + bool isLiveIntegrationFound = settings is not null; + + if (isLiveIntegrationFound) + { + bool exported = !string.IsNullOrEmpty(integrationOrderId); + var actionNodes = new List() + { + new() + { + Name = "Transfer via Live Integration", + Title = exported ? "Order already transferred" : "Transfer to ERP via Live Integration", + Icon = Icon.SignOutAlt, + NodeAction = ConfirmAction.For(RunCommandAction.For(new TransferOrderToErpCommand { OrderId = orderId }).WithReloadOnSuccess(), + "Transfer to ERP via Live Integration?", + exported ? $"Order {orderId} is already in the ERP, update again?" : $"Transfer order {orderId} to ERP via Live Integration?") + } + }; + actionNodes.AddRange(GetOrderExportToXmlActions(settings, order)); + + return new List() + { + new() + { + Name = LiveIntegrationTab, + Title = LiveIntegrationTab, + Nodes = actionNodes + } + }; + } + + return []; + } + + private static IEnumerable GetOrderExportToXmlActions(Settings settings, Order order) + { + bool saveOrderXml = settings.SaveCopyOfOrderXml; + + bool enableButton = saveOrderXml && System.IO.File.Exists(DownloadOrderXmlCommand.BuildXmlFileName(order)); + + return [ + new ActionNode() + { + Icon = Icon.FileDownload, + Name = "Original XML", + Title = enableButton ? + "Downloads the original XML for an order as sent to the ERP" : + !saveOrderXml ? + "This option is not available because saving XML files is not enabled in the Live Integration setup." : + "This option is not available because the XML file does not exist.", + Disabled = !enableButton, + NodeAction = enableButton ? DownloadFileAction.Using(new DownloadOrderXmlCommand { OrderId = order.Id, GetOriginalXml = true }) : null + }, + + new ActionNode() + { + Icon = Icon.FileDownload, + Name = "Current XML", + Title = "Exports the order to an XML document", + NodeAction = DownloadFileAction.Using(new DownloadOrderXmlCommand { OrderId = order.Id, GetOriginalXml = false }) + } + ]; + } + } +} + diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderListScreenInjector.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderListScreenInjector.cs new file mode 100644 index 0000000..ab688fc --- /dev/null +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderListScreenInjector.cs @@ -0,0 +1,46 @@ +using Dynamicweb.CoreUI.Actions; +using Dynamicweb.CoreUI.Actions.Implementations; +using Dynamicweb.CoreUI.Icons; +using Dynamicweb.CoreUI.Screens; +using Dynamicweb.Ecommerce.DynamicwebLiveIntegration.Configuration; +using Dynamicweb.Ecommerce.DynamicwebLiveIntegration.UI.Commands; +using Dynamicweb.Ecommerce.UI.Models; +using Dynamicweb.Ecommerce.UI.Screens; +using System.Collections.Generic; +using System.Linq; + +namespace Dynamicweb.Ecommerce.DynamicwebLiveIntegration.UI.Injectors; + +public sealed class OrderListScreenInjector : ListScreenInjector +{ + public override IEnumerable GetScreenActions() + { + if (SettingsManager.ActiveSettingsByShopId.Count == 0 || + Screen?.Model?.Data is null || + Screen?.Query is null || + !Screen.Model.Data.Any(o => SettingsManager.GetSettingsByShop(o.ShopId) is not null)) + return []; + + return new List() + { + new() + { + Name = OrderEditScreenInjector.LiveIntegrationTab, + Title = OrderEditScreenInjector.LiveIntegrationTab, + Nodes = new List() + { + new ActionNode() + { + Name = "Transfer via Live Integration", + Title = "Transfer to ERP via Live Integration", + Icon = Icon.SignOutAlt, + NodeAction = ConfirmAction.For(RunCommandAction.For().With(Screen.Query).WithReloadOnSuccess(), + "Transfer to ERP via Live Integration?", + "Transfer selected orders to ERP via Live Integration?") + } + } + } + }; + + } +} diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderOverviewScreenInjector.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderOverviewScreenInjector.cs new file mode 100644 index 0000000..2ac6f34 --- /dev/null +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/UI/Injectors/OrderOverviewScreenInjector.cs @@ -0,0 +1,21 @@ +using Dynamicweb.CoreUI; +using Dynamicweb.CoreUI.Layout; +using Dynamicweb.CoreUI.Screens; +using Dynamicweb.Ecommerce.UI.Screens; + +namespace Dynamicweb.Ecommerce.DynamicwebLiveIntegration.UI.Injectors; + +public sealed class OrderOverviewScreenInjector : ScreenInjector +{ + public override void OnAfter(OrderOverviewScreen screen, UiComponentBase content) + { + if (!content.TryGet(out var layout)) + return; + + var actions = OrderEditScreenInjector.GetOrderScreenActions(Screen?.Model?.Id, Screen?.Model?.IntegrationOrderId); + if (actions.Count > 0) + { + layout.ContextActionGroups.AddRange(actions); + } + } +} diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/XmlGenerators/OrderXmlGenerator.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/XmlGenerators/OrderXmlGenerator.cs index 6e2eee3..6af86f5 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/XmlGenerators/OrderXmlGenerator.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/XmlGenerators/OrderXmlGenerator.cs @@ -89,7 +89,7 @@ private void AddOrderDeliveryInformation(OrderXmlGeneratorSettings settings, Xml deliveryAddress = UserManagementServices.UserAddresses.GetAddressById(order.DeliveryAddressId); } string deliveryName = (deliveryAddress is object && !string.IsNullOrEmpty(order.DeliveryName)) ? order.DeliveryName : - !string.IsNullOrWhiteSpace(order.CustomerName) ? order.CustomerName : user.Name; + !string.IsNullOrWhiteSpace(order.CustomerName) ? order.CustomerName : user?.Name ?? ""; AddChildXmlNode(orderNode, "OrderDeliveryName", deliveryName); AddChildXmlNode(orderNode, "OrderDeliveryAddress", order.DeliveryAddress); From 826be55c31551a8ad2009b75c404c42b5fcd4f83 Mon Sep 17 00:00:00 2001 From: Dmitriy Benyuk Date: Wed, 21 May 2025 10:44:06 +0300 Subject: [PATCH 2/2] Do not log license check when invalid response --- .../Licensing/LicenseService.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Licensing/LicenseService.cs b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Licensing/LicenseService.cs index 51d530b..42eb3b7 100644 --- a/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Licensing/LicenseService.cs +++ b/src/Dynamicweb.Ecommerce.DynamicwebLiveIntegration/Licensing/LicenseService.cs @@ -47,11 +47,7 @@ internal static void ValidateLicense(string endpoint, string response, Logger lo var status = GetStatus(endpoint); if (status == null || status.Expired) { - if (!Helpers.ParseResponseToXml(response, logger, out XmlDocument doc)) - { - logger.Log(ErrorLevel.DebugInfo, $"License Response is not valid XML"); - } - else + if (Helpers.ParseResponseToXml(response, logger, out XmlDocument doc)) { ValidateLicense(doc, endpoint, status, logger); }