diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/PreorderAdminController.Edit.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/PreorderAdminController.Edit.cs new file mode 100644 index 0000000..a13ac1c --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/PreorderAdminController.Edit.cs @@ -0,0 +1,75 @@ +using FruitBank.Common.Enums; +using Microsoft.AspNetCore.Mvc; +using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; +using Nop.Services.Security; +using Nop.Web.Framework.Mvc.Filters; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers; + +public partial class PreorderAdminController +{ + [HttpPost, Route("Admin/Preorders/UpdateHeader/{id:int}")] + public async Task UpdateHeader(int id, string deliveryDateTime, string? customerNote) + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) return Forbid(); + var p = await _preorderDbContext.Preorders.GetByIdAsync(id); + if (p == null || p.Status == PreorderStatus.Cancelled) return Json(new { success = false }); + if (!DateTime.TryParse(deliveryDateTime, out var d)) return Json(new { success = false, error = "Érvénytelen dátum" }); + p.DateOfReceipt = d; p.CustomerNote = customerNote?.Trim(); p.UpdatedOnUtc = DateTime.UtcNow; + await _preorderDbContext.Preorders.UpdateAsync(p); + return Json(new { success = true }); + } + + [HttpPost, Route("Admin/Preorders/UpdateItem/{id:int}")] + public async Task UpdateItem(int id, int quantity, decimal unitPrice) + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) return Forbid(); + var item = await _preorderDbContext.PreorderItems.GetByIdAsync(id); + if (item == null || item.FulfilledQuantity > 0) return Json(new { success = false, error = "Módosítás nem lehetséges" }); + item.RequestedQuantity = Math.Max(1, quantity); + item.UnitPriceInclTax = Math.Max(0, unitPrice); + await _preorderDbContext.PreorderItems.UpdateAsync(item); + return Json(new { success = true }); + } + + [HttpPost, Route("Admin/Preorders/ReplaceItemProduct/{id:int}")] + public async Task ReplaceItemProduct(int id, int newProductId) + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) return Forbid(); + var item = await _preorderDbContext.PreorderItems.GetByIdAsync(id); + if (item == null || item.FulfilledQuantity > 0) return Json(new { success = false, error = "Csere nem lehetséges" }); + var product = await _dbContext.Products.GetByIdAsync(newProductId); + if (product == null || product.Deleted) return Json(new { success = false, error = "Termék nem található" }); + + item.ProductId = newProductId; + + // Recalculate price for the preorder's customer using CustomPriceCalculationService + var preorder = await _preorderDbContext.Preorders.GetByIdAsync(item.PreorderId); + var customer = preorder != null ? await _customerService.GetCustomerByIdAsync(preorder.CustomerId) : null; + if (customer != null) + { + var store = await _storeContext.GetCurrentStoreAsync(); + var calc = await _priceCalculationService.GetFinalPriceAsync(product, customer, store, includeDiscounts: true); + item.UnitPriceInclTax = calc.finalPrice; + } + + await _preorderDbContext.PreorderItems.UpdateAsync(item); + return Json(new { success = true, productName = product.Name, newPrice = item.UnitPriceInclTax }); + } + + [HttpPost, Route("Admin/Preorders/ConvertNow/{id:int}")] + public async Task ConvertNow(int id) + { + if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) return Forbid(); + var preorder = await _preorderDbContext.Preorders.GetByIdAsync(id); + if (preorder == null || preorder.Status == PreorderStatus.Cancelled) + return Json(new { success = false, error = "Konvertálás nem lehetséges" }); + var pending = await _preorderDbContext.PreorderItems.GetAll() + .Where(i => i.PreorderId == id && i.FulfilledQuantity < i.RequestedQuantity).ToListAsync(); + if (!pending.Any()) return Json(new { success = false, error = "Nincs teljesítetlen tétel" }); + await _preorderConversionService.ConvertPreordersForProductsAsync( + pending.Select(i => i.ProductId).Distinct().ToList(), 0); + var refreshed = await _preorderDbContext.Preorders.GetByIdAsync(id); + return Json(new { success = true, orderId = refreshed?.OrderId }); + } +} diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/PreorderAdminController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/PreorderAdminController.cs index d400687..9fe273a 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/PreorderAdminController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/PreorderAdminController.cs @@ -1,3 +1,4 @@ +using Nop.Core; using FruitBank.Common.Entities; using FruitBank.Common.Enums; using Microsoft.AspNetCore.Mvc; @@ -5,6 +6,7 @@ using Nop.Core.Domain.Customers; using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; using Nop.Plugin.Misc.FruitBankPlugin.Services; +using Nop.Services.Catalog; using Nop.Services.Customers; using Nop.Services.Security; using Nop.Web.Framework; @@ -16,13 +18,15 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers; [AuthorizeAdmin] [Area(AreaNames.ADMIN)] [AutoValidateAntiforgeryToken] -public class PreorderAdminController : BasePluginController +public partial class PreorderAdminController : BasePluginController { private readonly IPermissionService _permissionService; private readonly PreorderDbContext _preorderDbContext; private readonly FruitBankDbContext _dbContext; private readonly ICustomerService _customerService; private readonly PreorderConversionService _preorderConversionService; + private readonly IPriceCalculationService _priceCalculationService; + private readonly IStoreContext _storeContext; private static readonly Dictionary StatusLabels = new() { @@ -45,13 +49,17 @@ public class PreorderAdminController : BasePluginController PreorderDbContext preorderDbContext, FruitBankDbContext dbContext, ICustomerService customerService, - PreorderConversionService preorderConversionService) + PreorderConversionService preorderConversionService, + IPriceCalculationService priceCalculationService, + IStoreContext storeContext) { _permissionService = permissionService; _preorderDbContext = preorderDbContext; _dbContext = dbContext; _customerService = customerService; _preorderConversionService = preorderConversionService; + _priceCalculationService = priceCalculationService; + _storeContext = storeContext; } // ── LIST PAGE ───────────────────────────────────────────────────────────── @@ -121,11 +129,12 @@ public class PreorderAdminController : BasePluginController // Derive a display status: use the DB enum if it looks valid (non-zero), // otherwise infer from quantities - var effectiveStatus = (int)p.Status != 0 - ? p.Status - : allFulfilled ? PreorderStatus.Confirmed - : anyFulfilled ? PreorderStatus.PartiallyFulfilled - : PreorderStatus.Pending; + var effectiveStatus = + p.Status == PreorderStatus.Cancelled ? PreorderStatus.Cancelled : + (int)p.Status != 0 ? p.Status : + allFulfilled ? PreorderStatus.Confirmed : + anyFulfilled || p.OrderId.HasValue ? PreorderStatus.PartiallyFulfilled : + PreorderStatus.Pending; return new PreorderListRow { @@ -222,13 +231,13 @@ public class PreorderAdminController : BasePluginController // Derive item status from quantities — enum reads unreliable in LinqToDB var derivedStatus = i.FulfilledQuantity == 0 - ? PreorderItemStatus.Pending - : i.FulfilledQuantity >= i.RequestedQuantity - ? PreorderItemStatus.Fulfilled - : PreorderItemStatus.PartiallyFulfilled; + ? PreorderItemStatus.Pending + : i.FulfilledQuantity >= i.RequestedQuantity + ? PreorderItemStatus.Fulfilled + : PreorderItemStatus.PartiallyFulfilled; - // If DB enum read as non-zero, prefer it; otherwise use derived - var effectiveItemStatus = (int)i.Status != 0 ? i.Status : derivedStatus; + // If DB enum read as non-zero, prefer it; otherwise use derived + var effectiveItemStatus = (int)i.Status != 0 ? i.Status : derivedStatus; return new PreorderDetailItemRow { @@ -357,10 +366,13 @@ public class PreorderAdminController : BasePluginController if (preorder == null) return Json(new { success = false, error = "Preorder not found" }); - if (preorder.Status != PreorderStatus.Pending) - return Json(new { success = false, error = "Only pending preorders can be cancelled" }); + if (preorder.Status == PreorderStatus.Cancelled) + return Json(new { success = false, error = "Már visszavont előrendelés" }); + + preorder.Status = PreorderStatus.Cancelled; + preorder.UpdatedOnUtc = DateTime.UtcNow; + await _preorderDbContext.Preorders.UpdateAsync(preorder); - await _preorderDbContext.CancelPreorderAsync(id); return Json(new { success = true }); } diff --git a/Nop.Plugin.Misc.AIPlugin/Controllers/OrderController.cs b/Nop.Plugin.Misc.AIPlugin/Controllers/OrderController.cs index eebcdeb..56e9e05 100644 --- a/Nop.Plugin.Misc.AIPlugin/Controllers/OrderController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Controllers/OrderController.cs @@ -356,6 +356,32 @@ public class OrderController : BasePluginController } } + // ── TRANSCRIBE ONLY (for preorder history page filter) ───────────────── + + [HttpPost] + public async Task TranscribeOnly(Microsoft.AspNetCore.Http.IFormFile audioFile) + { + var customer = await _workContext.GetCurrentCustomerAsync(); + if (await _customerService.IsGuestAsync(customer)) + return Json(new { success = false }); + + if (audioFile == null || audioFile.Length == 0) + return Json(new { success = false, message = "Nem érkezett hangfájl" }); + + try + { + var text = await TranscribeAudioFile(audioFile, "hu"); + if (string.IsNullOrWhiteSpace(text)) + return Json(new { success = false, message = "Nem sikerült a hangfelismerés" }); + + return Json(new { success = true, transcription = text }); + } + catch (Exception ex) + { + return Json(new { success = false, message = ex.Message }); + } + } + // ── ADD TO CART (Quick Order flow) ──────────────────────────────────────── [HttpPost] diff --git a/Nop.Plugin.Misc.AIPlugin/Services/PreorderConversionService.cs b/Nop.Plugin.Misc.AIPlugin/Services/PreorderConversionService.cs index ba01858..e4a0d0d 100644 --- a/Nop.Plugin.Misc.AIPlugin/Services/PreorderConversionService.cs +++ b/Nop.Plugin.Misc.AIPlugin/Services/PreorderConversionService.cs @@ -303,7 +303,7 @@ public partial class PreorderConversionService BillingAddressId = billingAddressId, OrderStatusId = (int)OrderStatus.Pending, PaymentStatusId = (int)PaymentStatus.Pending, - ShippingStatusId = (int)ShippingStatus.NotYetShipped, + ShippingStatusId = (int)ShippingStatus.ShippingNotRequired, PaymentMethodSystemName = "Payments.CheckMoneyOrder", CustomerLanguageId = 1, CustomerTaxDisplayTypeId = 0, @@ -433,6 +433,9 @@ public partial class PreorderConversionService private async Task InsertOrderItemsAsync(Order order, List items) { + var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId); + var store = await _storeContext.GetCurrentStoreAsync(); + foreach (var item in items) { var productDto = await _dbContext.ProductDtos.GetByIdAsync(item.ProductId, true); @@ -441,8 +444,20 @@ public partial class PreorderConversionService var product = await _productService.GetProductByIdAsync(item.ProductId); if (product == null) continue; - var unitPriceExclTax = Math.Round(item.UnitPriceInclTax / 1.27m, 4); - var priceInclTax = productDto.IsMeasurable ? 0m : item.UnitPriceInclTax * item.FulfilledQuantity; + // Recalculate price at conversion time — may have changed since preorder was placed + decimal unitPriceInclTax; + if (customer != null && _customPriceCalculationService != null) + { + var calc = await _customPriceCalculationService.GetFinalPriceAsync( + product, customer, store, includeDiscounts: true); + unitPriceInclTax = calc.finalPrice; + } + else + { + unitPriceInclTax = item.UnitPriceInclTax; + } + var unitPriceExclTax = Math.Round(unitPriceInclTax / 1.27m, 4); + var priceInclTax = productDto.IsMeasurable ? 0m : unitPriceInclTax * item.FulfilledQuantity; var priceExclTax = productDto.IsMeasurable ? 0m : unitPriceExclTax * item.FulfilledQuantity; var orderItem = new OrderItem @@ -451,7 +466,7 @@ public partial class PreorderConversionService OrderId = order.Id, ProductId = item.ProductId, Quantity = item.FulfilledQuantity, - UnitPriceInclTax = item.UnitPriceInclTax, + UnitPriceInclTax = unitPriceInclTax, UnitPriceExclTax = unitPriceExclTax, PriceInclTax = priceInclTax, PriceExclTax = priceExclTax, diff --git a/Nop.Plugin.Misc.AIPlugin/Views/CustomerPreorder/List.cshtml b/Nop.Plugin.Misc.AIPlugin/Views/CustomerPreorder/List.cshtml index 12809ba..b3e6c58 100644 --- a/Nop.Plugin.Misc.AIPlugin/Views/CustomerPreorder/List.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Views/CustomerPreorder/List.cshtml @@ -24,7 +24,39 @@ } else { - foreach (var preorder in Model) + +
+
+ + + + +
+ + +
+ + +
+ @foreach (var preorder in Model) { var statusClass = preorder.Status switch { @@ -40,8 +72,13 @@ PreorderStatus.Cancelled => "Törölve / Lejárt", _ => "Függőben" }; + // Build searchable text for client-side filtering + var searchText = string.Join(" ", + preorder.Items.Select(i => i.ProductName) + .Append(preorder.CustomerNote ?? "") + ).ToLowerInvariant(); -
+
#@preorder.PreorderId előrendelés @@ -126,13 +163,47 @@
} +
}
+@Html.AntiForgeryToken() + + +