diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs index 429ad2a..8a44025 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs @@ -48,6 +48,7 @@ using System.Xml.Serialization; using AyCode.Services.Server.SignalRs; using AyCode.Core.Extensions; using MessagePack.Resolvers; +using Nop.Services.Tax; namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers { @@ -76,6 +77,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers protected readonly IGiftCardService _giftCardService; protected readonly IImportManager _importManager; protected readonly IDateTimeHelper _dateTimeHelper; + protected readonly ITaxService _taxService; private static readonly char[] _separator = [',']; // ... other dependencies @@ -121,7 +123,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers IExportManager exportManager, IGiftCardService giftCardService, IImportManager importManager, - IDateTimeHelper dateTimeHelper) + IDateTimeHelper dateTimeHelper, + ITaxService taxService) { _logger = new Logger(logWriters.ToArray()); @@ -147,7 +150,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers _giftCardService = giftCardService; _importManager = importManager; _dateTimeHelper = dateTimeHelper; - + _taxService = taxService; // ... initialize other deps } @@ -340,6 +343,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers }; orderListModel.Data = sortedData.ToList(); + orderListModel.RecordsTotal = orderListModel.Data.Count(); + //orderListModel.Draw = searchModel.Draw; + + Console.WriteLine($"Sorted Data Count: {orderListModel.Data.Count()}"); + Console.WriteLine($"Total Records: {orderListModel.RecordsTotal}"); + Console.WriteLine($"Filtered Records: {orderListModel.RecordsFiltered}"); + Console.WriteLine($"Draw: {orderListModel.Draw}"); + _logger.Detail($"Sorted by {searchModel.SortColumn} {searchModel.SortColumnDirection}"); } @@ -1117,6 +1128,184 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers return Json(new { success = false, message = $"Hiba történt: {ex.Message}" }); } } + + + [HttpPost] + //[IgnoreAntiforgeryToken] + [ValidateAntiForgeryToken] + public async Task FruitBankAddProductToOrder(int orderId, string productsJson) + { + try + { + _logger.Info($"AddProductToOrder - OrderId: {orderId}, ProductsJson: {productsJson}"); + + if (!await _permissionService.AuthorizeAsync(StandardPermission.Orders.ORDERS_CREATE_EDIT_DELETE)) + { + return Json(new { success = false, message = "Access denied" }); + } + + if (string.IsNullOrEmpty(productsJson)) + { + return Json(new { success = false, message = "No products data received" }); + } + + var order = await _orderService.GetOrderByIdAsync(orderId); + if (order == null || order.Deleted) + { + return Json(new { success = false, message = "Order not found" }); + } + + // Deserialize products + var products = JsonConvert.DeserializeObject>(productsJson); + + if (products == null || !products.Any()) + { + return Json(new { success = false, message = "No products to add" }); + } + + var productDtosByOrderItemId = await _dbContext.ProductDtos.GetAllByIds(products.Select(x => x.Id).ToArray()).ToDictionaryAsync(k => k.Id, v => v); + var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId); + var store = await _storeContext.GetCurrentStoreAsync(); + var admin = await _workContext.GetCurrentCustomerAsync(); + string errorMessage = ""; + + var transactionSuccess = await _dbContext.TransactionSafeAsync(async _ => + { + + + // Add each product to the order + foreach (var productModel in products) + { + var product = await _productService.GetProductByIdAsync(productModel.Id); + if (product == null) + { + _logger.Warning($"Product with ID {productModel.Id} not found"); + continue; + } + + // Validate stock + var stockQuantity = await _productService.GetTotalStockQuantityAsync(product); + var productDto = productDtosByOrderItemId[productModel.Id]; + var isMeasurable = productDto.IsMeasurable; + + //if (stockQuantity < productModel.Quantity) + //{ + // return Json(new + // { + // success = false, + // message = $"Product '{product.Name}' has insufficient stock. Available: {stockQuantity}, Requested: {productModel.Quantity}" + // }); + //} + + if ((product.StockQuantity + productDto.IncomingQuantity) - productModel.Quantity < 0) + { + errorMessage = $"Nem elérhető készleten!"; + var errorText = $"((product.StockQuantity + productDto.IncomingQuantity) - item.Quantity < 0); productId: {product.Id}; (product.StockQuantity + productDto.IncomingQuantity) - item.Quantity: {(product.StockQuantity + productDto.IncomingQuantity) - productModel.Quantity}"; + _logger.Error($"{errorText}"); + throw new Exception($"{errorText}"); + + } + + // Get or calculate price + var unitPrice = productModel.Price > 0 + ? productModel.Price + : (await _priceCalculationService.GetFinalPriceAsync(product, customer, store)).finalPrice; + + // Calculate tax + var (unitPriceInclTaxValue, _) = await _taxService.GetProductPriceAsync(product, unitPrice, true, customer); + var (unitPriceExclTaxValue, _) = await _taxService.GetProductPriceAsync(product, unitPrice, false, customer); + + // Create order item + var orderItem = new OrderItem + { + OrderItemGuid = Guid.NewGuid(), + OrderId = order.Id, + ProductId = product.Id, + UnitPriceInclTax = unitPriceInclTaxValue, + UnitPriceExclTax = unitPriceExclTaxValue, + PriceInclTax = unitPriceInclTaxValue * productModel.Quantity, + PriceExclTax = unitPriceExclTaxValue * productModel.Quantity, + OriginalProductCost = await _priceCalculationService.GetProductCostAsync(product, null), + Quantity = productModel.Quantity, + DiscountAmountInclTax = decimal.Zero, + DiscountAmountExclTax = decimal.Zero, + DownloadCount = 0, + IsDownloadActivated = false, + LicenseDownloadId = 0, + ItemWeight = product.Weight * productModel.Quantity, + RentalStartDateUtc = null, + RentalEndDateUtc = null + }; + + await _orderService.InsertOrderItemAsync(orderItem); + + // Adjust inventory + await _productService.AdjustInventoryAsync( + product, + -productModel.Quantity, + orderItem.AttributesXml, + string.Format(await _localizationService.GetResourceAsync("Admin.StockQuantityHistory.Messages.PlaceOrder"), order.Id)); + } + + // Update order totals + var orderSubtotalInclTax = decimal.Zero; + var orderSubtotalExclTax = decimal.Zero; + + var orderItems = await _orderService.GetOrderItemsAsync(order.Id); + foreach (var item in orderItems) + { + orderSubtotalInclTax += item.PriceInclTax; + orderSubtotalExclTax += item.PriceExclTax; + } + + order.OrderSubtotalInclTax = orderSubtotalInclTax; + order.OrderSubtotalExclTax = orderSubtotalExclTax; + order.OrderTotal = orderSubtotalInclTax + order.OrderShippingInclTax + order.PaymentMethodAdditionalFeeInclTax - order.OrderDiscount; + + await _orderService.UpdateOrderAsync(order); + + // Add order note + await _orderService.InsertOrderNoteAsync(new OrderNote + { + OrderId = order.Id, + Note = $"Products added to order by {admin.FirstName} {admin.LastName}, (Id: {admin.Id})", + DisplayToCustomer = false, + CreatedOnUtc = DateTime.UtcNow + }); + + return true; + }); + + if(transactionSuccess) + { + _logger.Info($"Successfully added {products.Count} products to order {orderId}"); + + return Json(new { success = true, message = "Products added successfully" }); + } + else { + return Json(new { success = false, message = errorMessage }); + } + } + catch (Exception ex) + { + _logger.Error($"Error adding products to order {orderId}, {ex.Message}"); + return Json(new { success = false, message = $"Error: {ex.Message}" }); + } + } + + // Helper model for deserialization + public class AddProductModel + { + 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 int StockQuantity { get; set; } + public int IncomingQuantity { get; set; } + } + + } } diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InnVoiceOrderController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InnVoiceOrderController.cs index 3cde4d9..0fb48c7 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InnVoiceOrderController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InnVoiceOrderController.cs @@ -29,6 +29,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers private readonly ICountryService _countryService; private readonly IProductService _productService; private readonly InnVoiceOrderService _innVoiceOrderService; + private readonly FruitBankAttributeService _fruitBankAttributeService; private readonly IGenericAttributeService _genericAttributeService; private readonly FruitBankDbContext _dbContext; @@ -40,6 +41,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers ICountryService countryService, IProductService productService, InnVoiceOrderService innVoiceOrderService, + FruitBankAttributeService fruitBankAttributeService, IGenericAttributeService genericAttributeService, FruitBankDbContext dbContext) { @@ -50,6 +52,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers _countryService = countryService; _productService = productService; _innVoiceOrderService = innVoiceOrderService; + _fruitBankAttributeService = fruitBankAttributeService; _genericAttributeService = genericAttributeService; _dbContext = dbContext; } @@ -175,6 +178,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers var storeId = currentStore?.Id ?? 0; // Save InnVoice order details as attributes + //await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync( + // order.Id, + // "InnVoiceOrderTechId", + // response.TechId, + // storeId + //); await _genericAttributeService.SaveAttributeAsync( order, "InnVoiceOrderTechId", 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 5b565d8..0083277 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/List.cshtml @@ -397,6 +397,13 @@ Width = "150" //Render = new RenderCustom("renderColumnCustomer") }); + gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModelExtended.InnvoiceTechId)) + { + Title = "Innvoiceba beküldve", + Width = "150", + Render = new RenderCustom("renderColumnInnvoiceTechId"), + ClassName = NopColumnClassDefaults.CenterAll + }); gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModelExtended.IsMeasurable)) { Title = T($"FruitBank.{nameof(OrderModelExtended.IsMeasurable)}").Text, @@ -531,11 +538,18 @@ return `${textRenderer(row.Company)}
${data} `; } + function renderColumnInnvoiceTechId(data, type, row, meta) { + if(data != null) { + return 'Igen'; + } + return 'Nem'; + } + function renderColumnIsMeasurable(data, type, row, meta) { if(data === true) { - return 'Yes'; + return ''; } - return 'No'; + return ''; } function renderColumnPickupDateAndTime(data, type, row, meta) { diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/_CustomOrderDetails.Products.cshtml b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/_CustomOrderDetails.Products.cshtml index 3d3f640..a41d112 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/_CustomOrderDetails.Products.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Views/Order/_CustomOrderDetails.Products.cshtml @@ -3,6 +3,8 @@ @using Nop.Core.Domain.Tax; @using Nop.Core.Domain.Catalog; +@Html.AntiForgeryToken() + diff --git a/Nop.Plugin.Misc.AIPlugin/Factories/CustomOrderModelFactory.cs b/Nop.Plugin.Misc.AIPlugin/Factories/CustomOrderModelFactory.cs index 3062375..2045dd1 100644 --- a/Nop.Plugin.Misc.AIPlugin/Factories/CustomOrderModelFactory.cs +++ b/Nop.Plugin.Misc.AIPlugin/Factories/CustomOrderModelFactory.cs @@ -40,6 +40,7 @@ using Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase; using FruitBank.Common.Dtos; using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders; using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.Order; +using Mango.Nop.Core.Extensions; namespace Nop.Plugin.Misc.FruitBankPlugin.Factories { @@ -184,6 +185,15 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories //var fullName = $"{orderDto.Customer.FirstName}_{orderDto.Customer.LastName}".Trim(); orderModelExtended.CustomerCompany = $"{orderDto.Customer.Company} {orderDto.Customer.FirstName}_{orderDto.Customer.LastName}"; + var genericAttributes = orderDto.GenericAttributes; + if(genericAttributes != null) + { + var innvoiceTechIdAttribute = genericAttributes.FirstOrDefault(ga => ga.Key == "InnVoiceOrderTechId"); + if (innvoiceTechIdAttribute != null) + { + orderModelExtended.InnvoiceTechId = innvoiceTechIdAttribute.Value; + } + } } public override async Task PrepareOrderModelAsync(OrderModel model, Order order, bool excludeProperties = false) diff --git a/Nop.Plugin.Misc.AIPlugin/Factories/MgBase/MgOrderModelFactory.cs b/Nop.Plugin.Misc.AIPlugin/Factories/MgBase/MgOrderModelFactory.cs index 76ab063..eceb169 100644 --- a/Nop.Plugin.Misc.AIPlugin/Factories/MgBase/MgOrderModelFactory.cs +++ b/Nop.Plugin.Misc.AIPlugin/Factories/MgBase/MgOrderModelFactory.cs @@ -36,6 +36,7 @@ using Nop.Web.Areas.Admin.Factories; using Nop.Web.Areas.Admin.Models.Common; using Nop.Web.Areas.Admin.Models.Orders; using Nop.Web.Framework.Extensions; +using Nop.Web.Framework.Models.Extensions; namespace Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase; @@ -250,10 +251,100 @@ public class MgOrderModelFactory : OrderMode var preFiltered = await base.PrepareOrderListModelAsync(searchModel); return preFiltered; } + + public async Task PrepareOrderListModelAsync(OrderSearchModel searchModel, int customerId) + { + ArgumentNullException.ThrowIfNull(searchModel); + + //get parameters to filter orders + var orderStatusIds = (searchModel.OrderStatusIds?.Contains(0) ?? true) ? null : searchModel.OrderStatusIds.ToList(); + var paymentStatusIds = (searchModel.PaymentStatusIds?.Contains(0) ?? true) ? null : searchModel.PaymentStatusIds.ToList(); + var shippingStatusIds = (searchModel.ShippingStatusIds?.Contains(0) ?? true) ? null : searchModel.ShippingStatusIds.ToList(); + var currentVendor = await _workContext.GetCurrentVendorAsync(); + if (currentVendor != null) + searchModel.VendorId = currentVendor.Id; + var startDateValue = !searchModel.StartDate.HasValue ? null + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(searchModel.StartDate.Value, await _dateTimeHelper.GetCurrentTimeZoneAsync()); + var endDateValue = !searchModel.EndDate.HasValue ? null + : (DateTime?)_dateTimeHelper.ConvertToUtcTime(searchModel.EndDate.Value, await _dateTimeHelper.GetCurrentTimeZoneAsync()).AddDays(1); + var product = await _productService.GetProductByIdAsync(searchModel.ProductId); + var filterByProductId = product != null && (currentVendor == null || product.VendorId == currentVendor.Id) + ? searchModel.ProductId : 0; + + //get orders + var orders = await _orderService.SearchOrdersAsync(storeId: searchModel.StoreId, + vendorId: searchModel.VendorId, + customerId: customerId, + productId: filterByProductId, + warehouseId: searchModel.WarehouseId, + paymentMethodSystemName: searchModel.PaymentMethodSystemName, + createdFromUtc: startDateValue, + createdToUtc: endDateValue, + osIds: orderStatusIds, + psIds: paymentStatusIds, + ssIds: shippingStatusIds, + billingPhone: searchModel.BillingPhone, + billingEmail: searchModel.BillingEmail, + billingLastName: searchModel.BillingLastName, + billingCountryId: searchModel.BillingCountryId, + orderNotes: searchModel.OrderNotes, + pageIndex: searchModel.Page - 1, pageSize: searchModel.PageSize); + + //prepare list model + var model = await new OrderListModel().PrepareToGridAsync(searchModel, orders, () => + { + //fill in model values from the entity + return orders.SelectAwait(async order => + { + var billingAddress = await _addressService.GetAddressByIdAsync(order.BillingAddressId); + + //fill in model values from the entity + var orderModel = new OrderModel + { + Id = order.Id, + OrderStatusId = order.OrderStatusId, + PaymentStatusId = order.PaymentStatusId, + ShippingStatusId = order.ShippingStatusId, + CustomerEmail = billingAddress.Email, + CustomerFullName = $"{billingAddress.FirstName} {billingAddress.LastName}", + CustomerId = order.CustomerId, + CustomOrderNumber = order.CustomOrderNumber + }; + + //convert dates to the user time + orderModel.CreatedOn = await _dateTimeHelper.ConvertToUserTimeAsync(order.CreatedOnUtc, DateTimeKind.Utc); + + //fill in additional values (not existing in the entity) + orderModel.StoreName = (await _storeService.GetStoreByIdAsync(order.StoreId))?.Name ?? "Deleted"; + orderModel.OrderStatus = await _localizationService.GetLocalizedEnumAsync(order.OrderStatus); + orderModel.PaymentStatus = await _localizationService.GetLocalizedEnumAsync(order.PaymentStatus); + orderModel.ShippingStatus = await _localizationService.GetLocalizedEnumAsync(order.ShippingStatus); + orderModel.OrderTotal = await _priceFormatter.FormatPriceAsync(order.OrderTotal, true, false); + + return orderModel; + }); + }); + + return model; + } public virtual async Task PrepareOrderListModelExtendedAsync(OrderSearchModelExtended searchModel, Func dataItemCopiedCallback) { - var prefiltered = await PrepareOrderListModelAsync(searchModel); - + var customerCompany = searchModel.BillingCompany; + var customer = await _customerService.GetCustomerByIdAsync(Convert.ToInt32(customerCompany)); + //var customer = customers.FirstOrDefault(c => c.Company != null && c.Company.Equals(customerCompany, StringComparison.InvariantCultureIgnoreCase)); + //var customer = customers.FirstOrDefault(c => c.Company != null && c.Company.Equals(customerCompany, StringComparison.InvariantCultureIgnoreCase)); + OrderListModel prefiltered; + if (customer != null) + { + + prefiltered = await PrepareOrderListModelAsync(searchModel, customer.Id); + } + else + { + prefiltered = await PrepareOrderListModelAsync(searchModel); + + } + var extendedRows = new List(prefiltered.RecordsFiltered); foreach (var orderModel in prefiltered.Data.ToList()) @@ -271,19 +362,24 @@ public class MgOrderModelFactory : OrderMode var totalRecords = prefiltered.RecordsTotal; var filteredRecords = prefiltered.RecordsFiltered; + Console.WriteLine($"Total Records before filtering: {totalRecords}"); + Console.WriteLine($"Filtered Records before filtering: {filteredRecords}"); //var orderListModelExtended = orderListModel.ToJson().JsonTo(); - if (searchModel.BillingCompany != null) - { - //extendedRows = extendedRows.Where(x => x.CustomerCompany != null && x.CustomerCompany.IndexOf(searchModel.BillingCompany, StringComparison.InvariantCultureIgnoreCase) >= 0).ToList(); - extendedRows = extendedRows.Where(x => x.CustomerId == Convert.ToInt32(searchModel.BillingCompany)).ToList(); - } + //if (searchModel.BillingCompany != null) + //{ + // //extendedRows = extendedRows.Where(x => x.CustomerCompany != null && x.CustomerCompany.IndexOf(searchModel.BillingCompany, StringComparison.InvariantCultureIgnoreCase) >= 0).ToList(); + // extendedRows = extendedRows.Where(x => x.CustomerId == Convert.ToInt32(searchModel.BillingCompany)).ToList(); + //} var orderListModelExtended = prefiltered.CloneTo(); orderListModelExtended.Data = extendedRows; + orderListModelExtended.RecordsTotal = extendedRows.Count; + //orderListModelExtended.RecordsFiltered = extendedRows.Count; - orderListModelExtended.RecordsTotal = totalRecords; + + //orderListModelExtended.RecordsTotal = totalRecords; orderListModelExtended.RecordsFiltered = filteredRecords; return orderListModelExtended; } diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs index aa7f93c..362a928 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs @@ -30,6 +30,7 @@ using Nop.Services.Events; using Nop.Web.Areas.Admin.Factories; using Nop.Web.Areas.Admin.Models.Catalog; using Nop.Web.Areas.Admin.Models.Orders; +using System.Net.Http.Headers; namespace Nop.Plugin.Misc.FruitBankPlugin.Infrastructure; @@ -87,6 +88,13 @@ public class PluginNopStartup : INopStartup services.AddScoped(); services.AddScoped(); + services.AddScoped(); + + services.AddHttpClient(client => + { + client.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", "r8_MUApXYIE5mRjxqy20tsGLehWBJkCzNj0Cwvrh"); + }); //services.AddScoped(); //services.AddScoped(); diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs index 1314147..88132a5 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs @@ -147,6 +147,11 @@ public class RouteProvider : IRouteProvider pattern: "Admin/Order/Edit/{id}", defaults: new { controller = "CustomOrder", action = "Edit", area = AreaNames.ADMIN }); + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Order.AddProduct", + pattern: "Admin/CustomOrder/FruitBankAddProductToOrder", + defaults: new { controller = "CustomOrder", action = "FruitBankAddProductToOrder", area = AreaNames.ADMIN }); + endpointRouteBuilder.MapControllerRoute( name: "Plugin.FruitBank.Admin.ManagementPage.ProcessShippingDocument", pattern: "Admin/ManagamentPage/ProcessShippingDocument/{id}", diff --git a/Nop.Plugin.Misc.AIPlugin/Models/Orders/OrderModelExtended.cs b/Nop.Plugin.Misc.AIPlugin/Models/Orders/OrderModelExtended.cs index 910be24..fb45d48 100644 --- a/Nop.Plugin.Misc.AIPlugin/Models/Orders/OrderModelExtended.cs +++ b/Nop.Plugin.Misc.AIPlugin/Models/Orders/OrderModelExtended.cs @@ -26,6 +26,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Orders public string CustomerCompany { get; set; } + public string InnvoiceTechId { get; set; } + public IList ItemExtendeds { get; set; } } } diff --git a/Nop.Plugin.Misc.AIPlugin/Services/CustomPriceCalculationService.cs b/Nop.Plugin.Misc.AIPlugin/Services/CustomPriceCalculationService.cs index 16e165e..dc435be 100644 --- a/Nop.Plugin.Misc.AIPlugin/Services/CustomPriceCalculationService.cs +++ b/Nop.Plugin.Misc.AIPlugin/Services/CustomPriceCalculationService.cs @@ -154,7 +154,7 @@ public class CustomPriceCalculationService : PriceCalculationService if (productDto.IsMeasurable) { - return (0, 0, 0m, []); + return (0, product.Price, 1000m, []); //return (overriddenProductPrice.GetValueOrDefault(0), overriddenProductPrice.GetValueOrDefault(0), 0m, []); } //var productAttributeMappings = await _specificationAttributeService.GetProductSpecificationAttributesAsync(product.Id); diff --git a/Nop.Plugin.Misc.AIPlugin/Services/ReplicateService.cs b/Nop.Plugin.Misc.AIPlugin/Services/ReplicateService.cs new file mode 100644 index 0000000..bfca4fd --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Services/ReplicateService.cs @@ -0,0 +1,173 @@ +using System.Net.Http.Json; +using System.Text.Json; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Services +{ + public class ReplicateService + { + private readonly HttpClient _http; + + public ReplicateService(HttpClient http) + { + _http = http; + } + public async Task GenerateImageAsync(string prompt, bool removeBackground) + { + return await GenerateImageAsync("https://api.replicate.com/v1/models/black-forest-labs/flux-schnell/predictions", prompt, removeBackground); + } + public async Task GenerateLogoAsync(string prompt, bool removeBackground) + { + return await GenerateImageAsync("https://api.replicate.com/v1/models/google/imagen-4-fast/predictions", prompt, removeBackground); + } + + public async Task GenerateImageAsync(string apiUrl, string prompt, bool removeBackground) + { + var request = new + { + input = new { prompt = prompt, aspect_ratio = "1:1", output_format = "jpg" } + + }; + + var createResponse = await _http.PostAsJsonAsync(apiUrl, request); + createResponse.EnsureSuccessStatusCode(); + + var createJson = await createResponse.Content.ReadFromJsonAsync(); + if (!createJson.TryGetProperty("id", out var idProp)) + throw new Exception("Replicate response missing prediction ID."); + + string predictionId = idProp.GetString(); + string status = ""; + JsonElement finalJson; + + for (int attempt = 0; attempt < 30; attempt++) + { + var getResponse = await _http.GetAsync($"https://api.replicate.com/v1/predictions/{predictionId}"); + getResponse.EnsureSuccessStatusCode(); + + finalJson = await getResponse.Content.ReadFromJsonAsync(); + status = finalJson.GetProperty("status").GetString(); + + if (status == "succeeded") + { + var output = finalJson.GetProperty("output"); + + if (output.ValueKind == JsonValueKind.String) + { + var imageUrl = output.GetString(); + if (removeBackground) + { + return await RemoveBackgroundAsync(imageUrl); + + } + else + { + return imageUrl; + } + } + else if (output.ValueKind == JsonValueKind.Array) + { + var length = output.GetArrayLength(); + var imageUrl = output[0].ToString(); + if (removeBackground) + { + return await RemoveBackgroundAsync(imageUrl); + + } + else + { + return imageUrl; + } + } + + + return "Replicate response succeeded but no output image URL found."; + } + else if (status == "failed") + { + return "Replicate prediction failed."; + } + + await Task.Delay(2500); + } + + return "Timeout waiting for Replicate prediction to complete."; + } + + public async Task RemoveBackgroundAsync(string imageUrl) + { + var request = new + { + version = "a029dff38972b5fda4ec5d75d7d1cd25aeff621d2cf4946a41055d7db66b80bc", + input = new { image = imageUrl } + }; + + var createResponse = await _http.PostAsJsonAsync("https://api.replicate.com/v1/predictions", request); + createResponse.EnsureSuccessStatusCode(); + + var createJson = await createResponse.Content.ReadFromJsonAsync(); + if (!createJson.TryGetProperty("id", out var idProp)) + throw new Exception("Replicate response missing prediction ID."); + + string predictionId = idProp.GetString(); + string status = ""; + JsonElement finalJson; + + for (int attempt = 0; attempt < 20; attempt++) + { + var getResponse = await _http.GetAsync($"https://api.replicate.com/v1/predictions/{predictionId}"); + getResponse.EnsureSuccessStatusCode(); + + finalJson = await getResponse.Content.ReadFromJsonAsync(); + status = finalJson.GetProperty("status").GetString(); + + if (status == "succeeded") + { + var output = finalJson.GetProperty("output"); + + if (output.ValueKind == JsonValueKind.String) + return output.GetString(); + + return "Replicate response succeeded but no output image URL found."; + } + else if (status == "failed") + { + return "Replicate prediction failed."; + } + + await Task.Delay(1500); + } + + return "Timeout waiting for Replicate prediction to complete."; + } + + public async Task AnalyzeImageAsync(string imageUrl) + { + var body = new + { + version = "a524caeaa23495bc9edc805ab08ab5fe943afd3febed884a4f3747aa32e9cd61", + input = new + { + image = imageUrl + } + }; + + var request = new HttpRequestMessage( + HttpMethod.Post, + "https://api.replicate.com/v1/predictions") + { + Content = JsonContent.Create(body) + }; + + // This makes Replicate wait until the prediction is done + request.Headers.Add("Prefer", "wait"); + + var response = await _http.SendAsync(request); + response.EnsureSuccessStatusCode(); + + var json = await response.Content.ReadFromJsonAsync(); + return json.ValueKind == JsonValueKind.Undefined + ? throw new Exception("Empty response from Replicate.") + : json; + } + } +} diff --git a/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml b/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml index 35080be..e271ef6 100644 --- a/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml @@ -2,42 +2,14 @@ @model OrderAttributesModel - +@*
-
-
-
-

Üzenet küldése

-
-
-
-
- - -
-
-
-
- -
-
-
-
- -
-
-
-
-
-
+ + *@
-
+
@@ -63,22 +35,50 @@
- -
+
+
+
+

Üzenet küldése

+
+
+
+
+ + +
+
+
+
+ +
+
+
+
+ +
+
+
+
+
+
InnVoice Management
-
+
Megrendelés beküldése Innvoice-ba
-
+