diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs index 1291b4c..b696788 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs @@ -5,10 +5,14 @@ using FruitBank.Common.Server.Interfaces; using FruitBank.Common.SignalRs; using Microsoft.AspNetCore.Mvc; using Nop.Core.Domain.Orders; +using Nop.Core.Domain.Payments; +using Nop.Core.Domain.Shipping; +using Nop.Core.Domain.Tax; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; using Nop.Plugin.Misc.FruitBankPlugin.Factories; using Nop.Plugin.Misc.FruitBankPlugin.Models; using Nop.Services.Common; +using Nop.Services.Customers; using Nop.Services.Messages; using Nop.Services.Orders; using Nop.Services.Security; @@ -30,9 +34,10 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers private readonly IPermissionService _permissionService; private readonly IGenericAttributeService _genericAttributeService; private readonly INotificationService _notificationService; + private readonly ICustomerService _customerService; // ... other dependencies - public CustomOrderController(IOrderService orderService, IOrderModelFactory orderModelFactory, ICustomOrderSignalREndpointServer customOrderSignalREndpoint, IPermissionService permissionService, IGenericAttributeService genericAttributeService, INotificationService notificationService) + public CustomOrderController(IOrderService orderService, IOrderModelFactory orderModelFactory, ICustomOrderSignalREndpointServer customOrderSignalREndpoint, IPermissionService permissionService, IGenericAttributeService genericAttributeService, INotificationService notificationService, ICustomerService customerService) { _orderService = orderService; _orderModelFactory = orderModelFactory as CustomOrderModelFactory; @@ -40,6 +45,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers _permissionService = permissionService; _genericAttributeService = genericAttributeService; _notificationService = notificationService; + _customerService = customerService; // ... initialize other deps } @@ -131,6 +137,93 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers return RedirectToAction("Edit", "Order", new { id = model.OrderId }); } + [HttpPost] + public virtual async Task Create(int customerId) + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Orders.ORDERS_CREATE_EDIT_DELETE)) + return AccessDeniedView(); + + // Validate customer exists + var customer = await _customerService.GetCustomerByIdAsync(customerId); + if (customer == null) + return RedirectToAction("List"); + + // Create new empty order + var order = new Order + { + OrderGuid = Guid.NewGuid(), + CustomerId = customerId, + CustomerLanguageId = customer.LanguageId ?? 1, + CustomerTaxDisplayType = (TaxDisplayType)customer.TaxDisplayType, + CustomerIp = string.Empty, + OrderStatusId = (int)OrderStatus.Pending, + PaymentStatusId = (int)PaymentStatus.Pending, + ShippingStatusId = (int)ShippingStatus.ShippingNotRequired, + CreatedOnUtc = DateTime.UtcNow, + BillingAddressId = customer.BillingAddressId ?? 0, + ShippingAddressId = customer.ShippingAddressId + }; + + await _orderService.InsertOrderAsync(order); + + // Redirect to edit page + return RedirectToAction("Edit", new { id = order.Id }); + } + + + [HttpGet] // Change from [HttpPost] to [HttpGet] + [CheckPermission(StandardPermission.Customers.CUSTOMERS_VIEW)] + public virtual async Task CustomerSearchAutoComplete(string term) + { + if (string.IsNullOrWhiteSpace(term) || term.Length < 2) + return Json(new List()); + + const int maxResults = 15; + + // Search by email (contains) + var customersByEmail = await _customerService.GetAllCustomersAsync( + email: term, + pageIndex: 0, + pageSize: maxResults); + + // Search by first name (contains) + var customersByFirstName = await _customerService.GetAllCustomersAsync( + firstName: term, + pageIndex: 0, + pageSize: maxResults); + + // Search by last name (contains) + var customersByLastName = await _customerService.GetAllCustomersAsync( + lastName: term, + pageIndex: 0, + pageSize: maxResults); + + // Combine and deduplicate results + var allCustomers = customersByEmail + .Union(customersByFirstName) + .Union(customersByLastName) + .DistinctBy(c => c.Id) + .Take(maxResults) + .ToList(); + + var result = new List(); + foreach (var customer in allCustomers) + { + var fullName = await _customerService.GetCustomerFullNameAsync(customer); + var displayText = !string.IsNullOrEmpty(customer.Email) + ? $"{customer.Email} ({fullName})" + : fullName; + + result.Add(new + { + label = displayText, + value = customer.Id + }); + } + + return Json(result); + } + } } diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/ManagementPageController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/ManagementPageController.cs index dbfaf58..1acca9b 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/ManagementPageController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/ManagementPageController.cs @@ -186,6 +186,20 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers return Json(model); } + [HttpGet] + public async Task GetAllPartners() + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) + return AccessDeniedView(); + + // Mock data for now + var model = await _dbContext.Partners.GetAll().ToListAsync(); + var valami = model; + //model. = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId); + return Json(model); + } + + [HttpPost] [RequestSizeLimit(10485760)] // 10MB @@ -286,14 +300,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers { try { - // Open the PDF from the IFormFile's stream directly in memory + // Open the Image from the IFormFile's stream directly in memory using (var stream = file.OpenReadStream()) { try { // ✅ Use the service we implemented earlier - pdfText = await _openAIApiService.AnalyzePdfAsync(stream, file.FileName, "Please extract all readable text from this PDF."); + pdfText = await _openAIApiService.AnalyzePdfAsync(stream, file.FileName, "Please extract all readable text from this image."); } catch (Exception aiEx) { @@ -319,7 +333,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers } - string analysisPrompt = "Extract the document identification number from this document, determine the type of the " + "document IN ENGLISH from the available list, and return them as JSON: documentNumber, documentType. " + $"Available filetypes: {nameof(DocumentType.Invoice)}, {nameof(DocumentType.ShippingDocument)} , {nameof(DocumentType.OrderConfirmation)}, {nameof(DocumentType.Unknown)}" + @@ -342,10 +355,10 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers ShippingDocumentToFiles shippingDocumentToFiles = new ShippingDocumentToFiles { ShippingDocumentId = shippingDocumentId, - FilesId = dbFile.Id + FilesId = dbFile.Id, + DocumentType = extractedMetaData.DocumentType != null ? (DocumentType)Enum.Parse(typeof(DocumentType), extractedMetaData.DocumentType) : DocumentType.Unknown }; - await _dbContext.ShippingDocumentToFiles.InsertAsync(shippingDocumentToFiles); // - IF WE DON'T HAVE PARTNERID ALREADY: read partner information // (check if all 3 refers to the same partner) diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml index 0540362..cdf1a37 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml @@ -30,6 +30,10 @@ @T("Admin.Orders")
+
+
@@ -677,3 +682,68 @@
+@*create new order form*@ + + + + + \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/ShippingDocumentGridComponent.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/ShippingDocumentGridComponent.cshtml index f3a41a6..6b76abb 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/ShippingDocumentGridComponent.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/ShippingDocumentGridComponent.cshtml @@ -24,14 +24,22 @@ }) .Columns(c => { c.Add().DataField("Id").AllowEditing(false); - c.Add().DataField("Partner.Name").AllowEditing(false); + c.Add().DataField("PartnerId") + .AllowEditing(true) + .Lookup(lookup => lookup + .DataSource(d => d.Mvc().Controller("ManagementPage").LoadAction("GetAllPartners").Key("Id")) + .ValueExpr("Id") + .DisplayExpr("Name") + ) + .EditCellTemplate(new TemplateName("DropDownBoxTemplate")) + .Width(150); c.Add() .Caption("Items in order") .DataType(GridColumnDataType.Number) .CalculateCellValue("calculateItemsCount").AllowEditing(false); - c.Add().DataField("PartnerId"); + @* c.Add().DataField("PartnerId"); *@ c.Add().DataField("DocumentIdNumber"); - c.Add().DataField("IsAllMeasured"); + c.Add().DataField("IsAllMeasured").AllowEditing(false); c.Add() .Caption("Completed") .DataType(GridColumnDataType.Boolean) @@ -54,8 +62,10 @@ ); }); }) - .MasterDetail(md => md.Enabled(true).Template(new TemplateName("masterDetailTemplate"))) - ) + .MasterDetail(md => md.Enabled(true).Template(new TemplateName("masterDetailTemplate")) + ) +) + @using (Html.DevExtreme().NamedTemplate("masterDetailTemplate")) @@ -108,6 +118,40 @@ } } +@using(Html.DevExtreme().NamedTemplate("DropDownBoxTemplate")) { + @(Html.DevExtreme().DropDownBox() + .DataSource(d => d.Mvc().Controller("ManagementPage").LoadAction("GetAllPartners").Key("Id")) + .Value(new JS("value")) + .ValueExpr("Id") + .InputAttr("aria-label", "Partner") + .DisplayExpr("Name") + .DropDownOptions(options => options.Width(500)) + .Option("setValue", new JS("setValue")) + .ContentTemplate(new TemplateName("ContentTemplate")) + ) +} + +@using(Html.DevExtreme().NamedTemplate("ContentTemplate")) { + @(Html.DevExtreme().DataGrid() + .DataSource(d => d.Mvc().Controller("ManagementPage").LoadAction("GetAllPartners").Key("Id")) + .RemoteOperations(true) + .Height(250) + .Columns(c => { + c.Add().DataField("Name"); + c.Add().DataField("Country"); + c.Add().DataField("TaxId"); + }) + .Scrolling(s => s.Mode(GridScrollingMode.Virtual)) + .HoverStateEnabled(true) + .Selection(s => s.Mode(SelectionMode.Single)) + .SelectedRowKeys(new JS("component.option('value') !== undefined && component.option('value') !== null ? [component.option('value')] : []")) + .FocusedRowEnabled(true) + .FocusedRowKey(new JS("component.option('value')")) + .OnContextMenuPreparing("function(e) { e.items = [] }") + .OnSelectionChanged("function(e) { onPartnerSelectionChanged(e, component) }") + ) +} + \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs index c37686b..ebd5829 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs @@ -131,6 +131,11 @@ public class RouteProvider : IRouteProvider name: "Plugin.FruitBank.Admin.Orders.SaveOrderAttributes", pattern: "Admin/CustomOrder/SaveOrderAttributes", defaults: new { controller = "CustomOrder", action = "SaveOrderAttributes", area = AreaNames.ADMIN }); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Orders.CustomerSearchAutoComplete", + pattern: "Admin/CustomOrder/CustomerSearchAutoComplete", + defaults: new { controller = "CustomOrder", action = "CustomerSearchAutoComplete", area = AreaNames.ADMIN }); } /// diff --git a/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs b/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs index e2bf14a..0b11407 100644 --- a/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs +++ b/Nop.Plugin.Misc.AIPlugin/Services/EventConsumer.cs @@ -1,8 +1,9 @@ -using System.Linq; -using FruitBank.Common.Interfaces; +using FruitBank.Common.Interfaces; +using Microsoft.AspNetCore.Http; using Nop.Core; using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Orders; +using Nop.Core.Events; using Nop.Plugin.Misc.FruitBankPlugin.Models; using Nop.Services.Catalog; using Nop.Services.Common; @@ -13,10 +14,11 @@ using Nop.Services.Plugins; using Nop.Web.Framework.Events; using Nop.Web.Framework.Menu; using Nop.Web.Models.Sitemap; +using System.Linq; namespace Nop.Plugin.Misc.FruitBankPlugin.Services { - public class EventConsumer : BaseAdminMenuCreatedEventConsumer, IConsumer, IConsumer + public class EventConsumer : BaseAdminMenuCreatedEventConsumer, IConsumer, IConsumer>, IConsumer { private readonly IGenericAttributeService _genericAttributeService; private readonly IProductService _productService; @@ -27,6 +29,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services private readonly IStoreContext _storeContext; private readonly IAdminMenu _adminMenu; private readonly ILocalizationService _localizationService; + private readonly IHttpContextAccessor _httpContextAccessor; + private readonly FruitBankAttributeService _fruitBankAttributeService; public EventConsumer( IGenericAttributeService genericAttributeService, @@ -38,7 +42,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services IWorkContext workContext, IStoreContext storeContext, IAdminMenu adminMenu, - ILocalizationService localizationService) : base(pluginManager) + ILocalizationService localizationService, + IHttpContextAccessor httpContextAccessor, + FruitBankAttributeService fruitBankAttributeService) : base(pluginManager) { _genericAttributeService = genericAttributeService; _productService = productService; @@ -49,6 +55,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services _storeContext = storeContext; _adminMenu = adminMenu; _localizationService = localizationService; + _httpContextAccessor = httpContextAccessor; + _fruitBankAttributeService = fruitBankAttributeService; } protected override string PluginSystemName => "Misc.FruitBankPlugin"; @@ -120,6 +128,39 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services } } + public async Task HandleEventAsync(EntityUpdatedEvent eventMessage) + { + await SaveOrderCustomAttributesAsync(eventMessage.Entity); + } + + + private async Task SaveOrderCustomAttributesAsync(Order order) + { + if (order == null) return; + + var form = _httpContextAccessor.HttpContext?.Request?.Form; + if (form == null || form.Count == 0) return; + + if (form.ContainsKey(nameof(IMeasurable.IsMeasurable))) + { + var isMeasurable = form[nameof(IMeasurable.IsMeasurable)].ToString().Contains("true"); + //var isMeasurable = CommonHelper.To(form[nameof(IMeasurable.IsMeasurable)].ToString()); + + + await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(order.Id, nameof(IMeasurable.IsMeasurable), isMeasurable); + } + + if (form.ContainsKey(nameof(IOrderDto.DateOfReceipt))) + { + var dateOfReceipt = form[nameof(IOrderDto.DateOfReceipt)]; + + await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(order.Id, nameof(IOrderDto.DateOfReceipt), DateTime.Parse(dateOfReceipt)); + } + + } + + + public async Task HandleEventAsync(AdminMenuCreatedEvent eventMessage) { var rootNode = eventMessage.RootMenuItem; diff --git a/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml b/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml index 96e8e4e..b3dcd1d 100644 --- a/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml @@ -45,7 +45,7 @@ data: { orderId: "@Model.OrderId", isMeasurable: $("#@Html.IdFor(m => m.IsMeasurable)").is(":checked"), - pickupDateTimeUtc: $("#@Html.IdFor(m => m.DateOfReceipt)").val(), + dateOfReceipt: $("#@Html.IdFor(m => m.DateOfReceipt)").val(), __RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val() }, success: function () {