Merge branch '4.80' of https://git.aycode.com/Adam/Mango.Nop.Plugins into 4.80
This commit is contained in:
commit
015f42bef9
File diff suppressed because one or more lines are too long
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -74,4 +74,5 @@
|
|||
@using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin
|
||||
|
||||
@* @using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Components *@
|
||||
@using DevExtreme.AspNet.Mvc
|
||||
@using DevExtreme.AspNet.Mvc
|
||||
@using Nop.Web.Areas.Admin.Models.Orders;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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)
|
||||
{
|
||||
|
||||
string modelName = GetModelName();
|
||||
public async Task<string?> GetSimpleResponseAsync(string systemMessage, string userMessage, string? assistantMessage = null)
|
||||
{
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -348,7 +347,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
|||
|
||||
public async Task<string?> AnalyzePdfAsync(Stream file, string fileName, string userPrompt)
|
||||
{
|
||||
|
||||
|
||||
await EnsureAssistantAndVectorStoreAsync();
|
||||
var fileId = await UploadFileAsync(file, fileName);
|
||||
var isImage = IsImageFile(fileName);
|
||||
|
|
@ -361,7 +360,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
|||
var threadId = await CreateThreadAsync();
|
||||
|
||||
if (isImage)
|
||||
{
|
||||
{
|
||||
await AddUserMessageWithImageAsync(threadId, userPrompt, fileId);
|
||||
}
|
||||
else
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue