using AyCode.Core.Loggers; using AyCode.Services.SignalRs; using FruitBank.Common.Dtos; using FruitBank.Common.Entities; using FruitBank.Common.Interfaces; using FruitBank.Common.Server.Interfaces; using FruitBank.Common.SignalRs; using Mango.Nop.Core.Extensions; using Mango.Nop.Core.Loggers; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using Nop.Core; using Nop.Core.Domain.Customers; using Nop.Core.Domain.Orders; using Nop.Core.Domain.Payments; using Nop.Core.Domain.Shipping; using Nop.Core.Domain.Tax; using Nop.Core.Events; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; using Nop.Plugin.Misc.FruitBankPlugin.Factories; using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders; using Nop.Services.Catalog; using Nop.Services.Common; using Nop.Services.Customers; using Nop.Services.Localization; using Nop.Services.Logging; using Nop.Services.Messages; using Nop.Services.Orders; using Nop.Services.Payments; using Nop.Services.Plugins; using Nop.Services.Security; using Nop.Web.Areas.Admin.Controllers; using Nop.Web.Areas.Admin.Factories; using Nop.Web.Areas.Admin.Models.Orders; using Nop.Web.Framework; using Nop.Web.Framework.Controllers; using Nop.Web.Framework.Mvc.Filters; using System.Text.Json.Serialization; using System.Xml; using System.Xml.Serialization; namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers { [Area(AreaNames.ADMIN)] [AuthorizeAdmin] public class CustomOrderController : BaseAdminController, ICustomOrderSignalREndpointServer { private readonly FruitBankDbContext _dbContext; private readonly IOrderService _orderService; private readonly CustomOrderModelFactory _orderModelFactory; private readonly ICustomOrderSignalREndpointServer _customOrderSignalREndpoint; private readonly IPermissionService _permissionService; private readonly IGenericAttributeService _genericAttributeService; private readonly INotificationService _notificationService; private readonly ICustomerService _customerService; private readonly IProductService _productService; private readonly IStoreContext _storeContext; private readonly IWorkContext _workContext; private readonly IPriceCalculationService _priceCalculationService; protected readonly IEventPublisher _eventPublisher; protected readonly ILocalizationService _localizationService; protected readonly ICustomerActivityService _customerActivityService; // ... other dependencies private readonly Mango.Nop.Core.Loggers.ILogger _logger; public CustomOrderController(FruitBankDbContext fruitBankDbContext, IOrderService orderService, IPriceCalculationService priceCalculationService, IOrderModelFactory orderModelFactory, ICustomOrderSignalREndpointServer customOrderSignalREndpoint, IPermissionService permissionService, IGenericAttributeService genericAttributeService, INotificationService notificationService, ICustomerService customerService, IProductService productService, IEnumerable logWriters, IStoreContext storeContext, IWorkContext workContext, IEventPublisher eventPublisher, ILocalizationService localizationService, ICustomerActivityService customerActivityService) { _logger = new Logger(logWriters.ToArray()); _dbContext = fruitBankDbContext; _orderService = orderService; _orderModelFactory = orderModelFactory as CustomOrderModelFactory; _customOrderSignalREndpoint = customOrderSignalREndpoint; _permissionService = permissionService; _genericAttributeService = genericAttributeService; _notificationService = notificationService; _customerService = customerService; _productService = productService; _storeContext = storeContext; _workContext = workContext; _priceCalculationService = priceCalculationService; _eventPublisher = eventPublisher; _localizationService = localizationService; _customerActivityService = customerActivityService; // ... initialize other deps } #region CustomOrderSignalREndpoint [NonAction] public Task> GetAllOrderDtos() => _customOrderSignalREndpoint.GetAllOrderDtos(); [NonAction]public Task GetOrderDtoById(int orderId) => _customOrderSignalREndpoint.GetOrderDtoById(orderId); [NonAction]public Task> GetPendingOrderDtos() => _customOrderSignalREndpoint.GetPendingOrderDtos(); [NonAction]public Task> GetPendingOrderDtosForMeasuring() => _customOrderSignalREndpoint.GetPendingOrderDtosForMeasuring(); [NonAction] public Task StartMeasuring(int orderId, int userId) => _customOrderSignalREndpoint.StartMeasuring(orderId, userId); [NonAction]public Task SetOrderStatusToComplete(int orderId, int revisorId) => _customOrderSignalREndpoint.SetOrderStatusToComplete(orderId, revisorId); [NonAction] public Task> GetAllOrderDtoByIds(int[] orderIds) => _customOrderSignalREndpoint.GetAllOrderDtoByIds(orderIds); [NonAction] public Task AddOrUpdateMeasuredOrderItemPallet(OrderItemPallet orderItemPallet) => _customOrderSignalREndpoint.AddOrUpdateMeasuredOrderItemPallet(orderItemPallet); #endregion CustomOrderSignalREndpoint [CheckPermission(StandardPermission.Orders.ORDERS_VIEW)] public virtual async Task List(List orderStatuses = null, List paymentStatuses = null, List shippingStatuses = null) { //prepare model var model = await _orderModelFactory.PrepareOrderSearchModelAsync(new OrderSearchModel { OrderStatusIds = orderStatuses, PaymentStatusIds = paymentStatuses, ShippingStatusIds = shippingStatuses, }); return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/List.cshtml", model); } [HttpPost] [CheckPermission(StandardPermission.Orders.ORDERS_VIEW)] public async Task OrderList(OrderSearchModel searchModel) { //prepare model var orderListModel = await GetOrderListModelByFilter(searchModel); //var orderListModel = new OrderListModel(); var valami = Json(orderListModel); Console.WriteLine(valami); return valami; } [HttpPost, ActionName("List")] [FormValueRequired("go-to-order-by-number")] [CheckPermission(StandardPermission.Orders.ORDERS_VIEW)] public virtual async Task GoToOrderId(OrderSearchModel model) { var order = await _orderService.GetOrderByCustomOrderNumberAsync(model.GoDirectlyToCustomOrderNumber); if (order == null) return await List(); return RedirectToAction("Edit", new { id = order.Id }); } [HttpGet] //[Route("Edit/{id}")] [CheckPermission(StandardPermission.Orders.ORDERS_VIEW)] public virtual async Task Edit(int id) { //try to get an order with the specified id var order = await _orderService.GetOrderByIdAsync(id); if (order == null || order.Deleted) return RedirectToAction("List"); //a vendor does not have access to this functionality if (await _workContext.GetCurrentVendorAsync() != null) return RedirectToAction("List"); //prepare model var model = await _orderModelFactory.PrepareOrderModelAsync(null, order); return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/Edit.cshtml", model); } public async Task GetOrderListModelByFilter(OrderSearchModel searchModel) { //return _customOrderService. var orderListModel = await _orderModelFactory.PrepareOrderListModelExtendedAsync(searchModel); _logger.Detail($"Total: {orderListModel.RecordsTotal}, Data Count: {orderListModel.Data.Count()}"); foreach (var item in orderListModel.Data.Take(3)) { _logger.Detail($"Order: {item.Id}, {item.CustomOrderNumber}"); } return orderListModel; } public virtual IActionResult Test() { // Your custom logic here // This will use your custom List.cshtml view return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/Test.cshtml"); } //[HttpPost] //[CheckPermission(Nop.Services.Security.StandardPermission.Orders.ORDERS_VIEW)] //public virtual async Task OrderList(OrderSearchModel searchModel) //{ // //prepare model // var model = await _orderModelFactory.PrepareOrderListModelAsync(searchModel); // return Json(model); //} [HttpPost] [ValidateAntiForgeryToken] public async Task SaveOrderAttributes(OrderAttributesModel model) { if (!ModelState.IsValid) { // reload order page with errors return RedirectToAction("Edit", "Order", new { id = model.OrderId }); } var order = await _orderService.GetOrderByIdAsync(model.OrderId); if (order == null) return RedirectToAction("List", "Order"); // store attributes in GenericAttribute table //await _genericAttributeService.SaveAttributeAsync(order, nameof(IMeasurable.IsMeasurable), model.IsMeasurable, _storeContext.GetCurrentStore().Id); await _genericAttributeService.SaveAttributeAsync(order, nameof(IOrderDto.DateOfReceipt), model.DateOfReceipt, _storeContext.GetCurrentStore().Id); _notificationService.SuccessNotification("Custom attributes saved successfully."); return RedirectToAction("Edit", "Order", new { id = model.OrderId }); } [HttpPost] //[CheckPermission(StandardPermission.Orders.ORDERS_CREATE)] public virtual async Task Create(int customerId, string orderProductsJson) { if (!await _permissionService.AuthorizeAsync(StandardPermission.Orders.ORDERS_CREATE_EDIT_DELETE)) return AccessDeniedView(); // Validate customer var customer = await _customerService.GetCustomerByIdAsync(customerId); if (customer == null) return RedirectToAction("List"); var billingAddress = await _customerService.GetCustomerBillingAddressAsync(customer); if(billingAddress == null) { //let's see if he has any address at all var addresses = await _customerService.GetAddressesByCustomerIdAsync(customer.Id); if(addresses != null && addresses.Count > 0) { //set the first one as billing billingAddress = addresses[0]; customer.BillingAddressId = billingAddress.Id; await _customerService.UpdateCustomerAsync(customer); } else { //no address at all, cannot create order _logger.Error($"Cannot create order for customer {customer.Id}, no billing address found."); return RedirectToAction("List"); } } //var currency = await _workContext.GetWorkingCurrencyAsync(); //customer.CurrencyId = currency.Id; // Parse products var orderProducts = string.IsNullOrEmpty(orderProductsJson) ? [] : JsonConvert.DeserializeObject>(orderProductsJson); // Create order var order = new Order { OrderGuid = Guid.NewGuid(), CustomOrderNumber = "", CustomerId = customerId, CustomerLanguageId = customer.LanguageId ?? 1, CustomerTaxDisplayType = TaxDisplayType.IncludingTax, CustomerIp = string.Empty, OrderStatus = OrderStatus.Pending, PaymentStatus = PaymentStatus.Pending, ShippingStatus = ShippingStatus.ShippingNotRequired, CreatedOnUtc = DateTime.UtcNow, BillingAddressId = customer.BillingAddressId ?? 0, ShippingAddressId = customer.ShippingAddressId, PaymentMethodSystemName = "Payments.CheckMoneyOrder", // Default payment method CustomerCurrencyCode = "HUF", // TODO: GET Default currency - A. }; //var productDtosById = await _dbContext.ProductDtos.GetAllByIds(orderProducts.Select(op => op.Id)).ToDictionaryAsync(p => p.Id, prodDto => prodDto); var store = _storeContext.GetCurrentStore(); var productDtosByOrderItemId = (await _dbContext.ProductDtos.GetByIdsAsync(orderProducts.Select(x => x.Id).ToArray())).ToDictionary(k => k.Id, v => v); var transactionSuccess = await _dbContext.TransactionSafeAsync(async _ => { await _orderService.InsertOrderAsync(order); order.OrderTotal = 0; foreach (var item in orderProducts) { var product = await _productService.GetProductByIdAsync(item.Id); if (product == null || product.StockQuantity - item.Quantity < 0) { var errorText = $"product == null || product.StockQuantity - item.Quantity < 0; productId: {product?.Id}; product?.StockQuantity - item.Quantity: {product?.StockQuantity - item.Quantity}"; _logger.Error($"{errorText}"); throw new Exception($"{errorText}"); } var productDto = productDtosByOrderItemId[item.Id]; var isMeasurable = productDto.IsMeasurable; var orderItem = new OrderItem { OrderId = order.Id, ProductId = item.Id, Quantity = item.Quantity, UnitPriceInclTax = item.Price, UnitPriceExclTax = item.Price, PriceInclTax = isMeasurable ? 0 : item.Price * item.Quantity, PriceExclTax = isMeasurable ? 0 : item.Price * item.Quantity, OriginalProductCost = product.ProductCost, AttributeDescription = string.Empty, AttributesXml = string.Empty, DiscountAmountInclTax = 0, DiscountAmountExclTax = 0 }; order.OrderTotal += orderItem.PriceInclTax; await _orderService.InsertOrderItemAsync(orderItem); //await _productService.AddStockQuantityHistoryEntryAsync(product, -orderItem.Quantity, product.StockQuantity, 1); await _productService.AdjustInventoryAsync(product, -orderItem.Quantity, orderItem.AttributesXml, ""); //await _productService.BookReservedInventoryAsync(product, 1, item.Quantity, ""); } order.CustomOrderNumber = order.Id.ToString(); order.OrderSubtotalInclTax = order.OrderTotal; order.OrderSubtotalExclTax = order.OrderTotal; order.OrderSubTotalDiscountInclTax = order.OrderTotal; order.OrderSubTotalDiscountExclTax = order.OrderTotal; await _orderService.UpdateOrderAsync(order); return true; }); if (transactionSuccess) return RedirectToAction("Edit", "Order", new { id = order.Id }); _logger.Error($"(transactionSuccess == false)"); return RedirectToAction("Error", new { id = order.Id }); } public class OrderProductItem { public int Id { get; set; } public string Name { get; set; } public string Sku { get; set; } public int Quantity { get; set; } public decimal Price { get; set; } public override string ToString() { return $"{nameof(OrderProductItem)} [Id: {Id}; Name: {Name}; Sku: {Sku}; Quantity: {Quantity}; Price: {Price}]"; } } //private static OrderItem CreateOrderItem(ProductToAuctionMapping productToAuction, Order order, decimal orderTotal) //{ // return new OrderItem // { // ProductId = productToAuction.ProductId, // OrderId = order.Id, // OrderItemGuid = Guid.NewGuid(), // PriceExclTax = orderTotal, // PriceInclTax = orderTotal, // UnitPriceExclTax = orderTotal, // UnitPriceInclTax = orderTotal, // Quantity = productToAuction.ProductAmount, // }; //} //private static Order CreateOrder(ProductToAuctionMapping productToAuction, decimal orderTotal, Customer customer, Address billingAddress, int storeId, Dictionary customValues) //{ // return new Order // { // BillingAddressId = billingAddress.Id, // CreatedOnUtc = DateTime.UtcNow, // CurrencyRate = 1, // CustomOrderNumber = productToAuction.AuctionId + "/" + productToAuction.SortIndex, // CustomValuesXml = SerializeCustomValuesToXml(customValues), // CustomerCurrencyCode = "HUF", // CustomerId = productToAuction.WinnerCustomerId, // CustomerLanguageId = 2, // CustomerTaxDisplayType = TaxDisplayType.IncludingTax, // OrderGuid = Guid.NewGuid(), // OrderStatus = OrderStatus.Pending, // OrderTotal = orderTotal, // PaymentStatus = PaymentStatus.Pending, // PaymentMethodSystemName = "Payments.CheckMoneyOrder", // ShippingStatus = ShippingStatus.ShippingNotRequired, // StoreId = storeId, // VatNumber = customer.VatNumber, // CustomerIp = customer.LastIpAddress, // OrderSubtotalExclTax = orderTotal, // OrderSubtotalInclTax = orderTotal // }; //} private static OrderNote CreateOrderNote(Order order, string note) { return new OrderNote { CreatedOnUtc = order.CreatedOnUtc, DisplayToCustomer = true, OrderId = order.Id, Note = note }; } private static string SerializeCustomValuesToXml(Dictionary sourceDictionary) { ArgumentNullException.ThrowIfNull(sourceDictionary); if (!sourceDictionary.Any()) return null; var ds = new DictionarySerializer(sourceDictionary); var xs = new XmlSerializer(typeof(DictionarySerializer)); using var textWriter = new StringWriter(); using (var xmlWriter = XmlWriter.Create(textWriter)) { xs.Serialize(xmlWriter, ds); } var result = textWriter.ToString(); return result; } [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); var customersByCompanyName = await _customerService.GetAllCustomersAsync( company: term, pageIndex: 0, pageSize: maxResults); // Combine and deduplicate results var allCustomers = customersByEmail .Union(customersByFirstName) .Union(customersByLastName) .Union(customersByCompanyName) .DistinctBy(c => c.Id) .Take(maxResults) .ToList(); var result = new List(); foreach (var customer in allCustomers) { var fullName = await _customerService.GetCustomerFullNameAsync(customer); var company = customer.Company; if (string.IsNullOrEmpty(fullName)) fullName = "[No name]"; if(string.IsNullOrEmpty(company)) company = "[No company]"; string fullText = $"{company} ({fullName}), {customer.Email}"; //var displayText = !string.IsNullOrEmpty(customer.Email) // ? $"{customer.Email}, {customer.Company} ({fullName})" // : fullName; result.Add(new { label = fullText, value = customer.Id }); } return Json(result); } [HttpGet] [CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)] public virtual async Task ProductSearchAutoComplete(string term) { if (string.IsNullOrWhiteSpace(term) || term.Length < 2) return Json(new List()); const int maxResults = 15; // Search products by name or SKU var products = await _productService.SearchProductsAsync( keywords: term, pageIndex: 0, pageSize: maxResults); var result = new List(); foreach (var product in products) { result.Add(new { label = $"{product.Name} [KÉSZLET: {product.StockQuantity}] [ÁR: {product.Price}]", value = product.Id, sku = product.Sku, price = product.Price, stockQuantity = product.StockQuantity }); } return Json(result); } //[HttpPost] //public async Task CreateInvoice(int orderId) //{ // try // { // var order = await _orderService.GetOrderByIdAsync(orderId); // if (order == null) // return Json(new { success = false, message = "Order not found" }); // var billingAddress = await _customerService.GetCustomerBillingAddressAsync(order.Customer); // if (billingAddress == null) // return Json(new { success = false, message = "Billing address not found" }); // var country = await _countryService.GetCountryByAddressAsync(billingAddress); // var countryCode = country?.TwoLetterIsoCode ?? "HU"; // // Create invoice request // var invoiceRequest = new InvoiceCreateRequest // { // VevoNev = $"{billingAddress.FirstName} {billingAddress.LastName}", // VevoIrsz = billingAddress.ZipPostalCode ?? "", // VevoTelep = billingAddress.City ?? "", // VevoOrszag = countryCode, // VevoUtcaHsz = $"{billingAddress.Address1} {billingAddress.Address2}".Trim(), // SzamlatombID = 1, // Configure this based on your setup // SzamlaKelte = DateTime.Now, // TeljesitesKelte = DateTime.Now, // Hatarido = DateTime.Now.AddDays(15), // 15 days payment term // Devizanem = order.CustomerCurrencyCode, // FizetesiMod = order.PaymentMethodSystemName, // Felretett = false, // Proforma = false, // Email = billingAddress.Email, // Telefon = billingAddress.PhoneNumber // }; // // Add order items // var orderItems = await _orderService.GetOrderItemsAsync(order.Id); // foreach (var item in orderItems) // { // var product = await _productService.GetProductByIdAsync(item.ProductId); // invoiceRequest.AddItem(new InvoiceItem // { // TetelNev = product?.Name ?? "Product", // AfaSzoveg = "27%", // Configure VAT rate as needed // Brutto = true, // EgysegAr = item.UnitPriceInclTax, // Mennyiseg = item.Quantity, // MennyisegEgyseg = "db", // CikkSzam = product?.Sku // }); // } // // Create invoice via API // var response = await _innVoiceApiService.CreateInvoiceAsync(invoiceRequest); // if (response.IsSuccess) // { // // TODO: Save invoice details to your database for future reference // // You might want to create a custom table to store: // // - OrderId // // - InnVoice TableId // // - Invoice Number // // - PDF URL // // - Created Date // return Json(new // { // success = true, // message = "Invoice created successfully", // data = new // { // tableId = response.TableId, // invoiceNumber = response.Sorszam, // sorszam = response.Sorszam, // printUrl = response.PrintUrl // } // }); // } // else // { // return Json(new // { // success = false, // message = $"InnVoice API Error: {response.Message}" // }); // } // } // catch (Exception ex) // { // return Json(new // { // success = false, // message = $"Error: {ex.Message}" // }); // } //} //[HttpGet] //public async Task GetInvoiceStatus(int orderId) //{ // try // { // // TODO: Retrieve invoice details from your database // // This is a placeholder - you need to implement actual storage/retrieval // // Example: var invoiceData = await _yourInvoiceService.GetByOrderIdAsync(orderId); // // if (invoiceData != null) // // { // // return Json(new // // { // // success = true, // // data = new // // { // // tableId = invoiceData.TableId, // // invoiceNumber = invoiceData.InvoiceNumber, // // sorszam = invoiceData.InvoiceNumber, // // printUrl = invoiceData.PrintUrl // // } // // }); // // } // return Json(new // { // success = false, // message = "No invoice found for this order" // }); // } // catch (Exception ex) // { // return Json(new // { // success = false, // message = $"Error: {ex.Message}" // }); // } //} } }