This commit is contained in:
Adam 2025-10-24 12:03:00 +02:00
commit 015f42bef9
16 changed files with 690 additions and 145 deletions

File diff suppressed because one or more lines are too long

View File

@ -35,6 +35,12 @@ public class CustomOrderSignalREndpoint(FruitBankDbContext ctx, IWorkContext wor
return await ctx.OrderDtos.GetAllByOrderStatus(OrderStatus.Pending).ToListAsync();
}
[SignalR(SignalRTags.GetPendingOrderDtosForMeasuring)]
public async Task<List<OrderDto>> GetPendingOrderDtosForMeasuring()
{
return await ctx.OrderDtos.GetAllForMeasuring().ToListAsync();
}
[SignalR(SignalRTags.GetAllOrderDtoByIds)]
public async Task<List<OrderDto>> GetAllOrderDtoByIds(int[] orderIds)
{

View File

@ -0,0 +1,82 @@
@model OrderModel
@{
//page title
ViewBag.PageTitle = T("Admin.Orders.EditOrderDetails").Text + "ANYÁD";
//active menu item (system name)
NopHtml.SetActiveMenuItemSystemName("Orders");
}
@{
const string hideInfoBlockAttributeName = "OrderPage.HideInfoBlock";
var customer = await workContext.GetCurrentCustomerAsync();
var hideInfoBlock = await genericAttributeService.GetAttributeAsync<bool>(customer, hideInfoBlockAttributeName);
const string hideBillingAndShippingBlockAttributeName = "OrderPage.HideBillingAndShippingBlock";
var hideBillingAndShippingBlock = await genericAttributeService.GetAttributeAsync<bool>(customer, hideBillingAndShippingBlockAttributeName);
const string hideProductsBlockAttributeName = "OrderPage.HideProductsBlock";
var hideProductsBlock = await genericAttributeService.GetAttributeAsync<bool>(customer, hideProductsBlockAttributeName);
const string hideNotesBlockAttributeName = "OrderPage.HideNotesBlock";
var hideNotesBlock = await genericAttributeService.GetAttributeAsync<bool>(customer, hideNotesBlockAttributeName);
}
<form asp-controller="Order" asp-action="Edit" method="post" id="order-form">
<div class="content-header clearfix">
<h1 class="float-left">
@T("Admin.Orders.EditOrderDetails") - @Model.CustomOrderNumber
<small>
<i class="fas fa-arrow-circle-left"></i>
<a asp-action="List">@T("Admin.Orders.BackToList")</a>
</small>
</h1>
<div class="float-right">
<a asp-action="PdfInvoice" asp-route-orderId="@Model.Id" class="btn btn-info">
<i class="far fa-file-pdf"></i>
@T("Admin.Orders.PdfInvoice")
</a>
@if (!Model.IsLoggedInAsVendor)
{
<span id="order-delete" class="btn btn-danger">
<i class="far fa-trash-can"></i>
@T("Admin.Common.Delete")
</span>
}
<button type="submit" id="btnRefreshPage" style="display: none"></button>
<script>
$(function() {
$('#btnRefreshPage').click(function () {
//refresh pageed
location.reload();
});
});
</script>
@await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.OrderDetailsButtons, additionalData = Model })
</div>
</div>
<div asp-validation-summary="All"></div>
<section class="content">
<div class="container-fluid">
<div class="form-horizontal">
<nop-cards id="order-cards">
@await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.OrderDetailsBlock, additionalData = Model })
<nop-card asp-name="order-info" asp-icon="fas fa-info" asp-title="@T("Admin.Orders.Info")" asp-hide-block-attribute-name="@hideInfoBlockAttributeName" asp-hide="@hideInfoBlock" asp-advanced="false">@await Html.PartialAsync("_OrderDetails.Info", Model)</nop-card>
<nop-card asp-name="order-billing-shipping" asp-icon="fas fa-truck" asp-title="@T("Admin.Orders.BillingShippingInfo")" asp-hide-block-attribute-name="@hideBillingAndShippingBlockAttributeName" asp-hide="@hideBillingAndShippingBlock" asp-advanced="false">@await Html.PartialAsync("_OrderDetails.BillingShipping", Model)</nop-card>
<nop-card asp-name="order-products" asp-icon="fas fa-table-list" asp-title="@T("Admin.Orders.Products")" asp-hide-block-attribute-name="@hideProductsBlockAttributeName" asp-hide="@hideProductsBlock" asp-advanced="true">@await Html.PartialAsync("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/_CustomOrderDetails.Products.cshtml", Model)</nop-card>
@if (!Model.IsLoggedInAsVendor)
{
<nop-card asp-name="order-notes" asp-icon="far fa-sticky-note" asp-title="@T("Admin.Orders.OrderNotes")" asp-hide-block-attribute-name="@hideNotesBlockAttributeName" asp-hide="@hideNotesBlock" asp-advanced="true">@await Html.PartialAsync("_OrderDetails.Notes", Model)</nop-card>
}
</nop-cards>
</div>
</div>
</section>
</form>
<nop-delete-confirmation asp-model-id="@Model.Id" asp-button-id="order-delete" />

View File

@ -752,8 +752,8 @@
<thead>
<tr>
<th>Product</th>
<th style="width: 100px;">Quantity</th>
<th style="width: 120px;">Price</th>
<th style="width: 100px;">Mennyisség</th>
<th style="width: 120px;">Egységár</th>
<th style="width: 50px;"></th>
</tr>
</thead>

View File

@ -0,0 +1,399 @@
@model OrderModel
@using Nop.Core.Domain.Tax;
@using Nop.Core.Domain.Catalog;
<div class="card-body">
<div class="form-group row">
<div class="col-md-12" style="overflow-x: auto;">ANYÁD!!!!!
@foreach (var item in Model.Items)
{
<script>
$(function() {
toggleOrderItemEdit@(item.Id)(false);
// Attach event listeners for automatic calculation
setupAutoCalculation@(item.Id)();
});
</script>
<script>
// Function to automatically calculate and update total price
function calculateTotal@(item.Id)() {
// Get values
var unitPriceInclTax = parseFloat($('#pvUnitPriceInclTax@(item.Id)').val()) || 0;
var unitPriceExclTax = parseFloat($('#pvUnitPriceExclTax@(item.Id)').val()) || 0;
var quantity = parseFloat($('#pvQuantity@(item.Id)').val()) || 0;
var discountInclTax = parseFloat($('#pvDiscountInclTax@(item.Id)').val()) || 0;
var discountExclTax = parseFloat($('#pvDiscountExclTax@(item.Id)').val()) || 0;
// Calculate totals
var totalInclTax = (unitPriceInclTax * quantity) - discountInclTax;
var totalExclTax = (unitPriceExclTax * quantity) - discountExclTax;
// Update total price fields
$('#pvPriceInclTax@(item.Id)').val(totalInclTax.toFixed(0));
$('#pvPriceExclTax@(item.Id)').val(totalExclTax.toFixed(0));
}
// Function to setup event listeners for automatic calculation
function setupAutoCalculation@(item.Id)() {
// Attach change and input events to all relevant fields
$('#pvUnitPriceInclTax@(item.Id), #pvUnitPriceExclTax@(item.Id), #pvQuantity@(item.Id), #pvDiscountInclTax@(item.Id), #pvDiscountExclTax@(item.Id)').on('input change', function() {
calculateTotal@(item.Id)();
});
}
function toggleOrderItemEdit@(item.Id)(editMode) {
if (editMode) {
$('#pnlEditPvUnitPrice@(item.Id)').showElement();
$('#pnlEditPvQuantity@(item.Id)').showElement();
$('#pnlEditPvDiscount@(item.Id)').showElement();
$('#pnlEditPvPrice@(item.Id)').showElement();
$('#btnSaveOrderItem@(item.Id)').showElement();
$('#btnCancelOrderItem@(item.Id)').showElement();
$('#pvUnitPriceInclTax@(item.Id)').prop("disabled", false);
$('#pvUnitPriceExclTax@(item.Id)').prop("disabled", false);
$('#pvQuantity@(item.Id)').prop("disabled", false);
$('#pvDiscountInclTax@(item.Id)').prop("disabled", false);
$('#pvDiscountExclTax@(item.Id)').prop("disabled", false);
$('#pvPriceInclTax@(item.Id)').prop("disabled", false);
$('#pvPriceExclTax@(item.Id)').prop("disabled", false);
$('#btnEditOrderItem@(item.Id)').hideElement();
$('#btnDeleteOrderItem@(item.Id)').hideElement();
} else {
$('#pnlEditPvUnitPrice@(item.Id)').hideElement();
$('#pnlEditPvQuantity@(item.Id)').hideElement();
$('#pnlEditPvDiscount@(item.Id)').hideElement();
$('#pnlEditPvPrice@(item.Id)').hideElement();
$('#btnSaveOrderItem@(item.Id)').hideElement();
$('#btnCancelOrderItem@(item.Id)').hideElement();
$('#pvUnitPriceInclTax@(item.Id)').prop("disabled", true);
$('#pvUnitPriceExclTax@(item.Id)').prop("disabled", true);
$('#pvQuantity@(item.Id)').prop("disabled", true);
$('#pvDiscountInclTax@(item.Id)').prop("disabled", true);
$('#pvDiscountExclTax@(item.Id)').prop("disabled", true);
$('#pvPriceInclTax@(item.Id)').prop("disabled", true);
$('#pvPriceExclTax@(item.Id)').prop("disabled", true);
$('#btnEditOrderItem@(item.Id)').showElement();
$('#btnDeleteOrderItem@(item.Id)').showElement();
}
}
</script>
}
<table class="table table-hover table-bordered">
<col />
<col />
@if (Model.HasDownloadableProducts)
{
<col />
}
<col />
<col />
<col />
<col />
@if (!Model.IsLoggedInAsVendor)
{
<col />
}
<thead>
<tr>
<th>
@T("Admin.Orders.Products.Picture")
</th>
<th>
@T("Admin.Orders.Products.ProductName")
</th>
@if (Model.HasDownloadableProducts)
{
<th>
@T("Admin.Orders.Products.Download")
</th>
}
<th>
@T("Admin.Orders.Products.Price")
</th>
<th>
@T("Admin.Orders.Products.Quantity")
</th>
<th>
@T("Admin.Orders.Products.Discount")
</th>
<th>
@T("Admin.Orders.Products.Total")
</th>
@if (!Model.IsLoggedInAsVendor)
{
<th>
@T("Admin.Common.Edit")
</th>
}
</tr>
</thead>
<tbody>
@foreach (var item in Model.Items)
{
<tr>
<td class="text-center preview">
<img src="@item.PictureThumbnailUrl" alt="" title="" />
</td>
<td style="width: 25%;" class="text-left">
<em><a asp-controller="Product" asp-action="Edit" asp-route-id="@item.ProductId">@item.ProductName</a></em>
@if (!string.IsNullOrEmpty(item.AttributeInfo))
{
<p>
@Html.Raw(item.AttributeInfo)
</p>
}
@if (!string.IsNullOrEmpty(item.RecurringInfo))
{
<p>
@Html.Raw(item.RecurringInfo)
</p>
}
@if (!string.IsNullOrEmpty(item.RentalInfo))
{
<p>
@Html.Raw(item.RentalInfo)
</p>
}
@if (!string.IsNullOrEmpty(item.Sku))
{
<p>
<strong>@T("Admin.Orders.Products.SKU")</strong><text>:</text>
@item.Sku
</p>
}
@if (!string.IsNullOrEmpty(item.VendorName))
{
<p>
<strong>@T("Admin.Orders.Products.Vendor")</strong><text>:</text>
@item.VendorName
</p>
}
@if (item.ReturnRequests.Count > 0)
{
<p>
@T("Admin.Orders.Products.ReturnRequests")<text>:</text>
@for (var i = 0; i < item.ReturnRequests.Count; i++)
{
var returnRequest = item.ReturnRequests[i];
<a asp-controller="ReturnRequest" asp-action="Edit" asp-route-id="@returnRequest.Id">@returnRequest.CustomNumber</a>
if (i != item.ReturnRequests.Count - 1)
{
<text>, </text>
}
}
</p>
}
</td>
@if (Model.HasDownloadableProducts)
{
<td style="width: 15%;" class="text-center">
@if (item.IsDownload)
{
<a asp-controller="Download" asp-action="DownloadFile" asp-route-orderItemId="@item.Id">
@T("Admin.Orders.Products.Download.Download")
</a>
}
else
{
@T("Admin.Orders.Products.Download.NotAvailable")
}
</td>
}
<td style="width: 15%;" class="text-center">
@if (Model.AllowCustomersToSelectTaxDisplayType)
{
<div>@Html.Raw(item.UnitPriceInclTax)</div>
<div>@Html.Raw(item.UnitPriceExclTax)</div>
}
else
{
switch (Model.TaxDisplayType)
{
case TaxDisplayType.ExcludingTax:
{
@Html.Raw(item.UnitPriceExclTax)
}
break;
case TaxDisplayType.IncludingTax:
{
@Html.Raw(item.UnitPriceInclTax)
}
break;
default:
break;
}
}
<div id="pnlEditPvUnitPrice@(item.Id)">
<div class="form-group row">
<div class="col-md-5">
@T("Admin.Orders.Products.Edit.InclTax")
</div>
<div class="col-md-7">
<input name="pvUnitPriceInclTax@(item.Id)" type="text" value="@item.UnitPriceInclTaxValue" id="pvUnitPriceInclTax@(item.Id)" class="form-control input-sm" />
</div>
</div>
<div class="form-group row">
<div class="col-md-5">
@T("Admin.Orders.Products.Edit.ExclTax")
</div>
<div class="col-md-7">
<input name="pvUnitPriceExclTax@(item.Id)" type="text" value="@item.UnitPriceExclTaxValue" id="pvUnitPriceExclTax@(item.Id)" class="form-control input-sm" />
</div>
</div>
</div>
</td>
<td style="width: 10%;" class="text-center">
<div>@item.Quantity</div>
<div id="pnlEditPvQuantity@(item.Id)">
<div class="form-group row">
<div class="col-md-8 offset-md-2">
<input name="pvQuantity@(item.Id)" type="text" value="@item.Quantity" id="pvQuantity@(item.Id)" class="form-control input-sm" />
</div>
</div>
</div>
</td>
<td style="width: 15%;" class="text-center">
@if (Model.AllowCustomersToSelectTaxDisplayType)
{
<div>@Html.Raw(item.DiscountInclTax)</div>
<div>@Html.Raw(item.DiscountExclTax)</div>
}
else
{
switch (Model.TaxDisplayType)
{
case TaxDisplayType.ExcludingTax:
{
@Html.Raw(item.DiscountExclTax)
}
break;
case TaxDisplayType.IncludingTax:
{
@Html.Raw(item.DiscountInclTax)
}
break;
default:
break;
}
}
<div id="pnlEditPvDiscount@(item.Id)">
<div class="form-group row">
<div class="col-md-5">
@T("Admin.Orders.Products.Edit.InclTax")
</div>
<div class="col-md-7">
<input name="pvDiscountInclTax@(item.Id)" type="text" value="@item.DiscountInclTaxValue" id="pvDiscountInclTax@(item.Id)" class="form-control input-sm" />
</div>
</div>
<div class="form-group row">
<div class="col-md-5">
@T("Admin.Orders.Products.Edit.ExclTax")
</div>
<div class="col-md-7">
<input name="pvDiscountExclTax@(item.Id)" type="text" value="@item.DiscountExclTaxValue" id="pvDiscountExclTax@(item.Id)" class="form-control input-sm" />
</div>
</div>
</div>
</td>
<td style="width: 15%;" class="text-center">
@if (Model.AllowCustomersToSelectTaxDisplayType)
{
<div>@Html.Raw(item.SubTotalInclTax)</div>
<div>@Html.Raw(item.SubTotalExclTax)</div>
}
else
{
switch (Model.TaxDisplayType)
{
case TaxDisplayType.ExcludingTax:
{
@Html.Raw(item.SubTotalExclTax)
}
break;
case TaxDisplayType.IncludingTax:
{
@Html.Raw(item.SubTotalInclTax)
}
break;
default:
break;
}
}
<div id="pnlEditPvPrice@(item.Id)">
<div class="form-group row">
<div class="col-md-5">
@T("Admin.Orders.Products.Edit.InclTax")
</div>
<div class="col-md-7">
<input name="pvPriceInclTax@(item.Id)" type="text" value="@item.SubTotalInclTaxValue" id="pvPriceInclTax@(item.Id)" class="form-control input-sm" />
</div>
</div>
<div class="form-group row">
<div class="col-md-5">
@T("Admin.Orders.Products.Edit.ExclTax")
</div>
<div class="col-md-7">
<input name="pvPriceExclTax@(item.Id)" type="text" value="@item.SubTotalExclTaxValue" id="pvPriceExclTax@(item.Id)" class="form-control input-sm" />
</div>
</div>
</div>
</td>
@if (!Model.IsLoggedInAsVendor)
{
<td style="width: 15%;" class="text-center">
<button type="submit" class="btn btn-default" name="btnEditOrderItem@(item.Id)" onclick="toggleOrderItemEdit@(item.Id)(true);return false;" id="btnEditOrderItem@(item.Id)">
<i class="fas fa-pencil"></i>
@T("Admin.Common.Edit")
</button>
<button type="submit" class="btn btn-default" name="btnDeleteOrderItem@(item.Id)" id="btnDeleteOrderItem@(item.Id)">
<i class="far fa-trash-can"></i>
@T("Admin.Common.Delete")
</button>
<nop-action-confirmation asp-button-id="@("btnDeleteOrderItem" + item.Id)" />
<button type="submit" class="btn btn-default" name="btnSaveOrderItem@(item.Id)" id="btnSaveOrderItem@(item.Id)">
<i class="far fa-floppy-disk"></i>
@T("Admin.Common.Save")
</button>
<nop-action-confirmation asp-button-id="@("btnSaveOrderItem" + item.Id)" />
<button type="submit" class="btn btn-default" name="btnCancelOrderItem@(item.Id)" onclick="toggleOrderItemEdit@(item.Id)(false);return false;" id="btnCancelOrderItem@(item.Id)">
<i class="fas fa-times"></i>
@T("Admin.Common.Cancel")
</button>
</td>
}
</tr>
}
</tbody>
</table>
</div>
</div>
@if (!string.IsNullOrEmpty(Model.CheckoutAttributeInfo) && !Model.IsLoggedInAsVendor)
{
<div class="form-group row">
<div class="col-md-12">
@Html.Raw(Model.CheckoutAttributeInfo)
</div>
</div>
}
@if (!Model.IsLoggedInAsVendor)
{
<div class="form-group row">
<div class="col-md-12">
<button type="submit" id="btnAddNewProduct" name="btnAddNewProduct" onclick="javascript:setLocation('@(Url.Action("AddProductToOrder", "Order", new { orderId = Model.Id }))'); return false;" class="btn btn-primary">
@T("Admin.Orders.Products.AddNew")
</button>
</div>
</div>
}
</div>

View File

@ -75,3 +75,4 @@
@* @using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Components *@
@using DevExtreme.AspNet.Mvc
@using Nop.Web.Areas.Admin.Models.Orders;

View File

@ -2,22 +2,24 @@
using AyCode.Core.Extensions;
using AyCode.Core.Loggers;
using AyCode.Utils.Extensions;
using FruitBank.Common.Dtos;
using FruitBank.Common.Entities;
using FruitBank.Common.Interfaces;
using FruitBank.Common.Models;
using Mango.Nop.Core.Extensions;
using Mango.Nop.Core.Loggers;
using Mango.Nop.Core.Repositories;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Orders;
using Nop.Core.Events;
using Nop.Data;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer.Interfaces;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Services.Catalog;
using FruitBank.Common.Dtos;
using Mango.Nop.Core.Extensions;
using Mango.Nop.Core.Loggers;
using Nop.Core.Domain.Orders;
using Nop.Services.Security;
using WebMarkupMin.Core.Loggers;
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
@ -38,6 +40,7 @@ public class FruitBankDbContext : MgDbContextBase,
private readonly IStoreContext _storeContext;
private readonly IProductService _productService;
private readonly IStaticCacheManager _staticCacheManager;
protected readonly IEventPublisher _eventPublisher;
public ProductDtoDbTable ProductDtos { get; set; }
@ -55,7 +58,6 @@ public class FruitBankDbContext : MgDbContextBase,
public FilesDbTable Files { get; set; }
public ShippingDocumentToFilesDbTable ShippingDocumentToFiles { get; set; }
public IRepository<Product> Products { get; set; }
public IRepository<Customer> Customers { get; set; }
public IRepository<CustomerRole> CustomerRoles { get; set; }
public IRepository<CustomerCustomerRoleMapping> CustomerRoleMappings { get; set; }
@ -65,12 +67,15 @@ public class FruitBankDbContext : MgDbContextBase,
ShippingItemPalletDbTable shippingItemPalletDbTable, FilesDbTable filesDbTable, ShippingDocumentToFilesDbTable shippingDocumentToFilesDbTable,
ProductDtoDbTable productDtoDbTable, OrderDtoDbTable orderDtoDbTable, OrderItemDtoDbTable orderItemDtoDbTable, OrderItemPalletDbTable orderItemPalletDbTable,
IProductService productService, IStaticCacheManager staticCacheManager,
IRepository<Order> orderRepository,
IRepository<OrderItem> orderItemRepository,
IRepository<Product> productRepository,
IRepository<Customer> customerRepository,
IRepository<CustomerCustomerRoleMapping> customerCustomerRoleMappingRepository,
IRepository<CustomerRole> customerRoleRepository,
IEnumerable<IAcLogWriterBase> logWriters) : base(dataProvider, lockService, new Logger<FruitBankDbContext>(logWriters.ToArray()))
IRepository<CustomerRole> customerRoleRepository,IEventPublisher eventPublisher,
IEnumerable<IAcLogWriterBase> logWriters) : base(productRepository, orderRepository, orderItemRepository, dataProvider, lockService, new Logger<FruitBankDbContext>(logWriters.ToArray()))
{
_eventPublisher = eventPublisher;
_storeContext = storeContext;
_productService = productService;
_staticCacheManager = staticCacheManager;
@ -411,6 +416,8 @@ public class FruitBankDbContext : MgDbContextBase,
if (orderDto == null) return null;
if (!orderDto.IsMeasuredAndValid() || orderDto.OrderStatus == OrderStatus.Complete) return null; //throw new Exception($"SetOrderDtoToComplete; orderDto.IsMeasured == false; {orderDto}");
var prevOrderStatus = orderDto.OrderStatus;
orderDto.OrderStatus = OrderStatus.Complete;
await OrderDtos.UpdateAsync(orderDto);
@ -435,6 +442,9 @@ public class FruitBankDbContext : MgDbContextBase,
(orderItemDto.ProductId, -(orderItemDto.NetWeight-gaNetWeight), orderItemDto.IsMeasurable, true);
}
var order = Orders.GetById(orderDto.Id);
await _eventPublisher.PublishAsync(new OrderStatusChangedEvent(order, prevOrderStatus));
return orderDto;
}

View File

@ -33,5 +33,8 @@ public class OrderDtoDbTable : MgDtoDbTableBase<OrderDto, Order>
public IQueryable<OrderDto> GetAllByOrderStatus(OrderStatus orderStatus, bool loadRelations = true)
=> GetAll(loadRelations).Where(o => o.OrderStatusId == (int)orderStatus);
public IQueryable<OrderDto> GetAllForMeasuring(bool loadRelations = true)
=> GetAllByOrderStatus(OrderStatus.Pending, loadRelations).Where(o => o.DateOfReceipt != null);
public IQueryable<OrderDto> GetAllByIds(IEnumerable<int> orderIds, bool loadRelations = true) => GetAll(loadRelations).Where(o => orderIds.Contains(o.Id));
}

View File

@ -10,11 +10,12 @@ using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Services.Events;
using Mango.Nop.Core.Extensions;
using Nop.Core.Domain.Orders;
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.EventConsumers;
public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBankDbContext ctx, FruitBankAttributeService fruitBankAttributeService, IEnumerable<IAcLogWriterBase> logWriters) :
MgEventConsumer(httpContextAcc, logWriters),
MgEventConsumerBase(ctx, httpContextAcc, logWriters),
IConsumer<EntityDeletedEvent<Shipping>>,
IConsumer<EntityInsertedEvent<ShippingItem>>,
IConsumer<EntityUpdatedEvent<ShippingItem>>,
@ -24,13 +25,16 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa
IConsumer<EntityDeletedEvent<ShippingDocument>>,
IConsumer<EntityInsertedEvent<ShippingItemPallet>>,
IConsumer<EntityUpdatedEvent<ShippingItemPallet>>,
IConsumer<EntityDeletedEvent<ShippingItemPallet>>
IConsumer<EntityDeletedEvent<ShippingItemPallet>>,
IConsumer<EntityInsertedEvent<OrderItem>>,
IConsumer<EntityUpdatedEvent<OrderItem>>
{
public override async Task HandleEventAsync(EntityUpdatedEvent<Product> eventMessage)
{
var product = eventMessage.Entity;
var product = await CheckAndUpdateProductManageInventoryMethodToManageStock(eventMessage.Entity);
var saveProductCustomAttributesResult = await SaveProductCustomAttributesAsync(eventMessage.Entity);
var saveProductCustomAttributesResult = await SaveProductCustomAttributesAsync(product);
//var isMeasurableProduct = await fruitBankAttributeService.IsMeasurableEntityAsync<Product>(product.Id);
@ -52,7 +56,9 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa
public override async Task HandleEventAsync(EntityInsertedEvent<Product> eventMessage)
{
await SaveProductCustomAttributesAsync(eventMessage.Entity); //TODO: ez ide miért kell? - J.
var product = await CheckAndUpdateProductManageInventoryMethodToManageStock(eventMessage.Entity);
await SaveProductCustomAttributesAsync(product); //TODO: ez ide miért kell? - J.
await base.HandleEventAsync(eventMessage);
}
@ -124,7 +130,7 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa
{
return;
Logger.Info($"HandleEventAsync EntityInsertedEvent<ShippingItemPallet>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityInsertedEvent<ShippingItemPallet>; id: {eventMessage.Entity.Id}");
await UpdateShippingItemMeasuringValuesAsync(eventMessage.Entity);
}
@ -132,13 +138,13 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa
{
return;
Logger.Info($"HandleEventAsync EntityUpdatedEvent<ShippingItemPallet>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityUpdatedEvent<ShippingItemPallet>; id: {eventMessage.Entity.Id}");
await UpdateShippingItemMeasuringValuesAsync(eventMessage.Entity);
}
public async Task HandleEventAsync(EntityDeletedEvent<ShippingItemPallet> eventMessage)
{
Logger.Info($"HandleEventAsync EntityDeletedEvent<ShippingItemPallet>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityDeletedEvent<ShippingItemPallet>; id: {eventMessage.Entity.Id}");
await UpdateShippingItemMeasuringValuesAsync(eventMessage.Entity);
}
@ -151,7 +157,7 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa
public async Task HandleEventAsync(EntityInsertedEvent<ShippingItem> eventMessage)
{
Logger.Info($"HandleEventAsync EntityInsertedEvent<ShippingItemPallet>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityInsertedEvent<ShippingItemPallet>; id: {eventMessage.Entity.Id}");
await UpdateShippingDocumentIsAllMeasuredAsync(eventMessage.Entity);
}
@ -160,7 +166,7 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa
public async Task HandleEventAsync(EntityUpdatedEvent<ShippingItem> eventMessage)
{
Logger.Info($"HandleEventAsync EntityUpdatedEvent<ShippingItem>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityUpdatedEvent<ShippingItem>; id: {eventMessage.Entity.Id}");
var shippingItem = eventMessage.Entity;
//var isMeasured = shippingItem.IsValidMeasuringValues();
@ -189,14 +195,14 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa
public async Task HandleEventAsync(EntityInsertedEvent<ShippingDocument> eventMessage)
{
Logger.Info($"HandleEventAsync EntityInsertedEvent<ShippingDocument>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityInsertedEvent<ShippingDocument>; id: {eventMessage.Entity.Id}");
await UpdateShippingIsAllMeasuredAsync(eventMessage.Entity);
}
public async Task HandleEventAsync(EntityUpdatedEvent<ShippingDocument> eventMessage)
{
Logger.Info($"HandleEventAsync EntityUpdatedEvent<ShippingDocument>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityUpdatedEvent<ShippingDocument>; id: {eventMessage.Entity.Id}");
await UpdateShippingIsAllMeasuredAsync(eventMessage.Entity);
}
@ -219,24 +225,48 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa
public async Task HandleEventAsync(EntityDeletedEvent<Shipping> eventMessage)
{
Logger.Info($"HandleEventAsync EntityDeletedEvent<Shipping>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityDeletedEvent<Shipping>; id: {eventMessage.Entity.Id}");
await ctx.ShippingDocuments.DeleteAsync(sd => sd.ShippingId == eventMessage.Entity.Id, true);
}
public async Task HandleEventAsync(EntityDeletedEvent<ShippingDocument> eventMessage)
{
Logger.Info($"HandleEventAsync EntityDeletedEvent<ShippingDocument>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityDeletedEvent<ShippingDocument>; id: {eventMessage.Entity.Id}");
await ctx.ShippingItems.DeleteAsync(si => si.ShippingDocumentId == eventMessage.Entity.Id, true);
}
public async Task HandleEventAsync(EntityDeletedEvent<ShippingItem> eventMessage)
{
Logger.Info($"HandleEventAsync EntityDeletedEvent<ShippingItem>; id: {eventMessage.Entity.Id}");
Logger.Info($"HandleEventAsync->EntityDeletedEvent<ShippingItem>; id: {eventMessage.Entity.Id}");
await ctx.ShippingItemPallets.DeleteAsync(sp => sp.ShippingItemId == eventMessage.Entity.Id, false);
}
public async Task HandleEventAsync(EntityUpdatedEvent<OrderItem> eventMessage) => await CheckAndUpdateOrderItemFinalPrices(eventMessage.Entity);
public async Task HandleEventAsync(EntityInsertedEvent<OrderItem> eventMessage) => await CheckAndUpdateOrderItemFinalPrices(eventMessage.Entity);
private async Task CheckAndUpdateOrderItemFinalPrices(OrderItem orderItem)
{
Logger.Info($"HandleEventAsync->CheckAndUpdateOrderItemFinalPrices; orderItem.Id: {orderItem.Id}");
var finalPriceInclTax = decimal.Round(orderItem.UnitPriceInclTax * orderItem.Quantity, 0);
var finalPriceExclTax = decimal.Round(orderItem.UnitPriceExclTax * orderItem.Quantity, 0);
if (orderItem.PriceInclTax != finalPriceInclTax || orderItem.PriceExclTax != finalPriceExclTax)
{
Logger.Error($"HandleEventAsync->CheckAndUpdateOrderItemFinalPrices; " +
$"orderItem.PriceInclTax({orderItem.PriceInclTax}) != finalPriceInclTax({finalPriceInclTax}) || " +
$"orderItem.PriceExclTax({orderItem.PriceExclTax}) != finalPriceExclTax({finalPriceExclTax})");
orderItem.PriceInclTax = finalPriceInclTax;
orderItem.PriceExclTax = finalPriceExclTax;
await ctx.OrderItems.UpdateAsync(orderItem, false);
}
}
#endregion Delete
}

View File

@ -176,7 +176,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
//orderModelExtended.IsMeasurable = await ShouldMarkAsNeedsMeasurementAsync(orderModel);
//orderModelExtended.DateOfReceipt = await GetPickupDateTimeAsync(orderModel);
Console.WriteLine(orderModelExtended.Id);
//Console.WriteLine(orderModelExtended.Id);
});
return orderListModelExtended;

View File

@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<None Remove="Areas\Admin\Views\Order\Edit.cshtml" />
<None Remove="logo.jpg" />
<None Remove="plugin.json" />
<None Remove="Views\_ViewImports.cshtml" />
@ -40,6 +41,16 @@
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Areas\Admin\Views\Order\Edit.cshtml">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Areas\Admin\Views\Order\_CustomOrderDetails.Products.cshtml">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="Areas\Admin\Views\Product\List.cshtml">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>

File diff suppressed because one or more lines are too long

View File

@ -1,14 +1,18 @@
using System.Text.Json;
using System.Text;
using Microsoft.Extensions.Configuration;
using AyCode.Core.Loggers;
using Mango.Nop.Core.Loggers;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Models;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Services.Configuration;
using System.Text;
using System.Text.Json;
#nullable enable
namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{
public class CerebrasAPIService : IAIAPIService
{
private readonly ILogger _logger;
private readonly ISettingService _settingService;
private readonly FruitBankSettings _fruitBankSettings;
private readonly HttpClient _httpClient;
@ -18,23 +22,20 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
private const string CerebrasEndpoint = "https://api.cerebras.ai/v1/chat/completions";
public CerebrasAPIService(ISettingService settingService, HttpClient httpClient)
public CerebrasAPIService(ISettingService settingService, HttpClient httpClient, IEnumerable<IAcLogWriterBase> logWriters)
{
_logger = new Logger<CerebrasAPIService>(logWriters.ToArray());
_settingService = settingService;
_fruitBankSettings = _settingService.LoadSetting<FruitBankSettings>();
_httpClient = httpClient;
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {GetApiKey()}");
}
public string GetApiKey() =>
_fruitBankSettings.ApiKey;
//_configuration?.GetSection("Cerebras")?.GetValue<string>("ApiKey") ?? string.Empty;
public string GetApiKey() => _fruitBankSettings.ApiKey; //_configuration?.GetSection("Cerebras")?.GetValue<string>("ApiKey") ?? string.Empty;
public string GetModelName() =>
_fruitBankSettings.ModelName;
//_configuration?.GetSection("Cerebras")?.GetValue<string>("Model") ?? string.Empty;
public string GetModelName() => _fruitBankSettings.ModelName; //_configuration?.GetSection("Cerebras")?.GetValue<string>("Model") ?? string.Empty;
public void RegisterCallback(Action<string, string> callback, Action<string> onCompleteCallback, Action<string, string> onErrorCallback)
{
@ -44,27 +45,26 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
}
public async Task<string> GetSimpleResponseAsync(string systemMessage, string userMessage, string? assistantMessage = null)
public async Task<string?> GetSimpleResponseAsync(string systemMessage, string userMessage, string? assistantMessage = null)
{
string modelName = GetModelName();
var modelName = GetModelName();
var requestBody = new CerebrasAIChatRequest
{
Model = modelName,
Temperature = 0.2,
Messages = assistantMessage == null || assistantMessage == string.Empty
? new[]
{
Messages = string.IsNullOrEmpty(assistantMessage)
?
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "user", Content = userMessage }
}
: new[]
{
]
:
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "assistant", Content = assistantMessage },
new AIChatMessage { Role = "user", Content = userMessage }
},
],
Stream = false
};
@ -78,12 +78,13 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
using var response = await _httpClient.PostAsync(CerebrasEndpoint, requestContent);
response.EnsureSuccessStatusCode();
using var responseStream = await response.Content.ReadAsStreamAsync();
await using var responseStream = await response.Content.ReadAsStreamAsync();
using var document = await JsonDocument.ParseAsync(responseStream);
var inputTokens = document.RootElement.GetProperty("usage").GetProperty("prompt_tokens").GetInt32();
var outputTokens = document.RootElement.GetProperty("usage").GetProperty("completion_tokens").GetInt32();
var sum = inputTokens + outputTokens;
Console.WriteLine($"USAGE STATS - Tokens: {inputTokens.ToString()} + {outputTokens.ToString()} = {sum.ToString()}");
_logger.Info($"USAGE STATS - Tokens: {inputTokens.ToString()} + {outputTokens.ToString()} = {sum.ToString()}");
return document.RootElement
.GetProperty("choices")[0]
@ -95,24 +96,24 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
public async Task<string> GetStreamedResponseAsync(string sessionId, string systemMessage, string userMessage, string? assistantMessage = null)
{
string modelName = GetModelName();
var modelName = GetModelName();
var requestBody = new CerebrasAIChatRequest
{
Model = modelName,
Temperature = 0.2,
Messages = assistantMessage == null
? new[]
{
?
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "user", Content = userMessage }
}
: new[]
{
]
:
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "assistant", Content = assistantMessage },
new AIChatMessage { Role = "user", Content = userMessage }
},
],
Stream = true
};
@ -122,16 +123,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
});
var requestContent = new StringContent(requestJson, Encoding.UTF8, "application/json");
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, CerebrasEndpoint)
{
Content = requestContent
};
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, CerebrasEndpoint);
httpRequest.Content = requestContent;
using var response = await _httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
var stringBuilder = new StringBuilder();
using var responseStream = await response.Content.ReadAsStreamAsync();
await using var responseStream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(responseStream);
try

View File

@ -53,8 +53,6 @@ public class CustomPriceCalculationService : PriceCalculationService
}
//decimal? overriddenProductPrice = null
public override async Task<(decimal priceWithoutDiscounts, decimal finalPrice, decimal appliedDiscountAmount, List<Discount> appliedDiscounts)> GetFinalPriceAsync(
Product product, Customer customer, Store store, decimal? overriddenProductPrice, decimal additionalCharge = 0, bool includeDiscounts = true,
int quantity = 1, DateTime? rentalStartDate = null, DateTime? rentalEndDate = null)

View File

@ -2,7 +2,7 @@
namespace Nop.Plugin.Misc.FruitBankPlugin.Services;
public class LockService : MgLockService, ILockService
public class LockService : MgLockServiceBase, ILockService
{
public LockService() : this(new SemaphoreSlim(1))
{}

View File

@ -1,12 +1,11 @@
using Microsoft.Extensions.Configuration;
using Nop.Plugin.Misc.FruitBankPlugin.Models;
using Nop.Plugin.Misc.FruitBankPlugin.Services;
using Nop.Plugin.Misc.FruitBankPlugin.Models;
using Nop.Services.Configuration;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using AyCode.Utils.Extensions;
#nullable enable
namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{
public class OpenAIApiService : IAIAPIService
@ -45,29 +44,30 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
}
#region === CHAT (TEXT INPUT) ===
public async Task<string?> GetSimpleResponseAsync(string systemMessage, string userMessage, string? assistantMessage = null)
{
string modelName = GetModelName();
StringContent requestContent = new("");
var modelName = GetModelName();
StringContent requestContent;
if (modelName == "gpt-4.1-mini" || modelName == "gpt-4o-mini" || modelName == "gpt-4.1-nano" || modelName == "gpt-5-nano")
if (modelName is "gpt-4.1-mini" or "gpt-4o-mini" or "gpt-4.1-nano" or "gpt-5-nano")
{
var requestBody = new OpenAIGpt4MiniAIChatRequest
{
Model = modelName,
Temperature = 0.2,
Messages = assistantMessage == null || assistantMessage == string.Empty
? new[]
{
Messages = string.IsNullOrEmpty(assistantMessage)
?
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "user", Content = userMessage }
}
: new[]
{
]
:
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "assistant", Content = assistantMessage },
new AIChatMessage { Role = "user", Content = userMessage }
},
],
Stream = false
};
@ -84,18 +84,18 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{
Model = modelName,
Temperature = 1,
Messages = assistantMessage == null || assistantMessage == string.Empty
? new[]
{
Messages = string.IsNullOrEmpty(assistantMessage)
?
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "user", Content = userMessage }
}
: new[]
{
]
:
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "assistant", Content = assistantMessage },
new AIChatMessage { Role = "user", Content = userMessage }
},
],
ReasoningEffort = "minimal",
Verbosity = "high",
Stream = false
@ -134,12 +134,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
return null;
}
}
#endregion
#region === CHAT (STREAMING) ===
public async Task<string> GetStreamedResponseAsync(string sessionId, string systemMessage, string userMessage, string? assistantMessage = null)
{
string modelName = GetModelName();
var modelName = GetModelName();
StringContent requestContent = new("");
if (modelName == "gpt-4.1-mini" || modelName == "gpt-4o-mini" || modelName == "gpt-4.1-nano" || modelName == "gpt-5-nano")
@ -148,18 +150,18 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{
Model = modelName,
Temperature = 0.2,
Messages = assistantMessage == null || assistantMessage == string.Empty
? new[]
{
Messages = string.IsNullOrEmpty(assistantMessage)
?
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "user", Content = userMessage }
}
: new[]
{
]
:
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "assistant", Content = assistantMessage },
new AIChatMessage { Role = "user", Content = userMessage }
},
],
Stream = true
};
@ -176,18 +178,18 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{
Model = modelName,
Temperature = 1,
Messages = assistantMessage == null || assistantMessage == string.Empty
? new[]
{
Messages = string.IsNullOrEmpty(assistantMessage)
?
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "user", Content = userMessage }
}
: new[]
{
]
:
[
new AIChatMessage { Role = "system", Content = systemMessage },
new AIChatMessage { Role = "assistant", Content = assistantMessage },
new AIChatMessage { Role = "user", Content = userMessage }
},
],
Stream = true
};
@ -200,7 +202,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
}
using var httpRequest = new HttpRequestMessage(HttpMethod.Post, OpenAiEndpoint);
httpRequest.Content = requestContent;
httpRequest.Content = new StringContent("");
using var response = await _httpClient.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
@ -216,7 +218,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
var line = await reader.ReadLineAsync();
if (string.IsNullOrWhiteSpace(line) || !line.StartsWith("data: ")) continue;
var jsonResponse = line.Substring(6);
var jsonResponse = line[6..];
if (jsonResponse == "[DONE]")
{
@ -259,9 +261,11 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
return stringBuilder.ToString();
}
#endregion
#region === IMAGE GENERATION ===
public async Task<string?> GenerateImageAsync(string prompt)
{
var request = new HttpRequestMessage(HttpMethod.Post, OpenAiImageEndpoint);
@ -294,6 +298,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
return $"data:image/png;base64,{base64Image}";
}
#endregion
#region === PDF ANALYSIS (NEW) ===
@ -301,16 +306,10 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
private async Task EnsureAssistantAndVectorStoreAsync()
{
// Find or create vector store
if (_vectorStoreId == null)
{
_vectorStoreId = await FindOrCreateVectorStoreAsync("pdf-analysis-store");
}
_vectorStoreId ??= await FindOrCreateVectorStoreAsync("pdf-analysis-store");
// Find or create assistant
if (_assistantId == null)
{
_assistantId = await FindOrCreateAssistantAsync("PDF and Image Analyzer Assistant");
}
_assistantId ??= await FindOrCreateAssistantAsync("PDF and Image Analyzer Assistant");
}
//TEMPORARY: Cleanup all assistants (for testing purposes) - A.
@ -375,7 +374,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
return await GetAssistantResponseAsync(threadId);
}
private bool IsImageFile(string fileName)
private static bool IsImageFile(string fileName)
{
var extension = Path.GetExtension(fileName).ToLowerInvariant();
return extension == ".jpg" || extension == ".jpeg" || extension == ".png" || extension == ".gif" || extension == ".webp";
@ -461,8 +460,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
role = "user",
content = new object[]
{
new { type = "text", text = userPrompt },
new { type = "image_file", image_file = new { file_id = fileId } }
new { type = "text", text = userPrompt },
new { type = "image_file", image_file = new { file_id = fileId } }
}
};
@ -507,7 +506,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{
const int pollIntervalMs = 1000;
const int maxAttempts = 60; // 1 minute timeout
int attempts = 0;
var attempts = 0;
while (attempts < maxAttempts)
{
@ -560,7 +559,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
return firstMessage ?? "No response";
}
private HttpRequestMessage CreateAssistantRequest(HttpMethod method, string url, object? body = null)
private static HttpRequestMessage CreateAssistantRequest(HttpMethod method, string url, object? body = null)
{
var request = new HttpRequestMessage(method, url);
request.Headers.Add("OpenAI-Beta", "assistants=v2");
@ -577,7 +576,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
return request;
}
private async Task EnsureSuccessAsync(HttpResponseMessage response, string operation)
private static async Task EnsureSuccessAsync(HttpResponseMessage response, string operation)
{
if (!response.IsSuccessStatusCode)
{
@ -588,8 +587,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
}
}
private async Task<string> FindOrCreateVectorStoreAsync(string name)
{
// List existing vector stores
@ -602,12 +599,12 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
using var json = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync());
var stores = json.RootElement.GetProperty("data");
foreach (var store in stores.EnumerateArray())
//var getString = stores.EnumerateArray().FirstOrDefault(store => store.GetProperty("name").GetString() == name)?.GetProperty("id").GetString();
//if (!getString.IsNullOrWhiteSpace()) return getString;
foreach (var store in stores.EnumerateArray().Where(store => store.GetProperty("name").GetString() == name))
{
if (store.GetProperty("name").GetString() == name)
{
return store.GetProperty("id").GetString()!;
}
return store.GetProperty("id").GetString()!;
}
}
@ -633,12 +630,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
using var json = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync());
var assistants = json.RootElement.GetProperty("data");
foreach (var assistant in assistants.EnumerateArray())
foreach (var assistant in assistants.EnumerateArray().Where(assistant => assistant.GetProperty("name").GetString() == name))
{
if (assistant.GetProperty("name").GetString() == name)
{
return assistant.GetProperty("id").GetString()!;
}
return assistant.GetProperty("id").GetString()!;
}
}
@ -659,9 +653,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
return createJson.RootElement.GetProperty("id").GetString()!;
}
#endregion
#endregion
}
}