improvements, fixes, etc...
This commit is contained in:
parent
fbf83e5736
commit
ba52be2847
|
|
@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Factories;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Services.Messages;
|
||||
using Nop.Services.Orders;
|
||||
|
|
@ -126,7 +126,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
return RedirectToAction("List", "Order");
|
||||
|
||||
// store attributes in GenericAttribute table
|
||||
await _genericAttributeService.SaveAttributeAsync(order, nameof(OrderModelExtended.IsMeasurable), model.IsMeasurable);
|
||||
await _genericAttributeService.SaveAttributeAsync(order, nameof(IMeasurable.IsMeasurable), model.IsMeasurable);
|
||||
await _genericAttributeService.SaveAttributeAsync(order, nameof(IOrderDto.DateOfReceipt), model.DateOfReceipt);
|
||||
|
||||
_notificationService.SuccessNotification("Custom attributes saved successfully.");
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using Nop.Core.Domain.Tax;
|
|||
using Nop.Core.Domain.Vendors;
|
||||
using Nop.Core.Http;
|
||||
using Nop.Core.Infrastructure;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Factories;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Services.Configuration;
|
||||
|
|
@ -44,6 +45,7 @@ public partial class CustomProductController : BaseAdminController
|
|||
{
|
||||
#region Fields
|
||||
|
||||
private readonly CustomProductModelFactory _productModelFactory;
|
||||
protected readonly AdminAreaSettings _adminAreaSettings;
|
||||
protected readonly IAclService _aclService;
|
||||
protected readonly IBackInStockSubscriptionService _backInStockSubscriptionService;
|
||||
|
|
@ -70,7 +72,6 @@ public partial class CustomProductController : BaseAdminController
|
|||
protected readonly IProductAttributeFormatter _productAttributeFormatter;
|
||||
protected readonly IProductAttributeParser _productAttributeParser;
|
||||
protected readonly IProductAttributeService _productAttributeService;
|
||||
protected readonly IProductModelFactory _productModelFactory;
|
||||
protected readonly IProductService _productService;
|
||||
protected readonly IProductTagService _productTagService;
|
||||
protected readonly ISettingService _settingService;
|
||||
|
|
@ -159,7 +160,7 @@ public partial class CustomProductController : BaseAdminController
|
|||
_productAttributeFormatter = productAttributeFormatter;
|
||||
_productAttributeParser = productAttributeParser;
|
||||
_productAttributeService = productAttributeService;
|
||||
_productModelFactory = productModelFactory;
|
||||
_productModelFactory = productModelFactory as CustomProductModelFactory;
|
||||
_productService = productService;
|
||||
_productTagService = productTagService;
|
||||
_settingService = settingService;
|
||||
|
|
@ -878,7 +879,6 @@ public partial class CustomProductController : BaseAdminController
|
|||
[CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
|
||||
public virtual async Task<IActionResult> List()
|
||||
{
|
||||
//prepare model
|
||||
var model = await _productModelFactory.PrepareProductSearchModelAsync(new ProductSearchModel());
|
||||
|
||||
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Product/List.cshtml", model);
|
||||
|
|
@ -918,7 +918,7 @@ public partial class CustomProductController : BaseAdminController
|
|||
public virtual async Task<IActionResult> ProductList(ProductSearchModel searchModel)
|
||||
{
|
||||
//prepare model
|
||||
var model = await _productModelFactory.PrepareProductListModelAsync(searchModel);
|
||||
var model = await _productModelFactory.PrepareProductListModelExtendedAsync(searchModel);
|
||||
|
||||
return Json(model);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,466 +1,488 @@
|
|||
@model ProductSearchModel
|
||||
|
||||
@using Nop.Core.Domain.Catalog;
|
||||
@using Nop.Plugin.Misc.FruitBankPlugin.Models.Products
|
||||
|
||||
@{
|
||||
//page title
|
||||
ViewBag.PageTitle = T("Admin.Catalog.Products").Text;
|
||||
//active menu item (system name)
|
||||
NopHtml.SetActiveMenuItemSystemName("Products");
|
||||
//page title
|
||||
ViewBag.PageTitle = T("Admin.Catalog.Products").Text;
|
||||
//active menu item (system name)
|
||||
NopHtml.SetActiveMenuItemSystemName("Products");
|
||||
}
|
||||
|
||||
@{
|
||||
const string hideSearchBlockAttributeName = "ProductListPage.HideSearchBlock";
|
||||
var hideSearchBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideSearchBlockAttributeName);
|
||||
const string hideSearchBlockAttributeName = "ProductListPage.HideSearchBlock";
|
||||
var hideSearchBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideSearchBlockAttributeName);
|
||||
}
|
||||
|
||||
@if (Model.LicenseCheckModel.BlockPages != true)
|
||||
{
|
||||
<form asp-controller="Product" asp-action="List" method="post">
|
||||
<div class="content-header clearfix">
|
||||
<h1 class="float-left">
|
||||
Fruitbank @T("Admin.Catalog.Products")
|
||||
</h1>
|
||||
<div class="float-right">
|
||||
<a asp-action="Create" class="btn btn-primary">
|
||||
<i class="fas fa-square-plus"></i>
|
||||
@T("Admin.Common.AddNew")
|
||||
</a>
|
||||
<a asp-action="BulkEdit" class="btn btn-info">
|
||||
<i class="fas fa-pen"></i>
|
||||
@T("Admin.Catalog.Products.BulkEdit")
|
||||
</a>
|
||||
<button asp-action="DownloadCatalogPDF" type="submit" name="download-catalog-pdf" class="btn bg-purple">
|
||||
<i class="far fa-file-pdf"></i>
|
||||
@T("Admin.Catalog.Products.List.DownloadPDF")
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-success">
|
||||
<i class="fas fa-download"></i>
|
||||
@T("Admin.Common.Export")
|
||||
</button>
|
||||
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only"> </span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li class="dropdown-item">
|
||||
<form asp-controller="Product" asp-action="List" method="post">
|
||||
<div class="content-header clearfix">
|
||||
<h1 class="float-left">
|
||||
Fruitbank @T("Admin.Catalog.Products")
|
||||
</h1>
|
||||
<div class="float-right">
|
||||
<a asp-action="Create" class="btn btn-primary">
|
||||
<i class="fas fa-square-plus"></i>
|
||||
@T("Admin.Common.AddNew")
|
||||
</a>
|
||||
<a asp-action="BulkEdit" class="btn btn-info">
|
||||
<i class="fas fa-pen"></i>
|
||||
@T("Admin.Catalog.Products.BulkEdit")
|
||||
</a>
|
||||
<button asp-action="DownloadCatalogPDF" type="submit" name="download-catalog-pdf" class="btn bg-purple">
|
||||
<i class="far fa-file-pdf"></i>
|
||||
@T("Admin.Catalog.Products.List.DownloadPDF")
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-success">
|
||||
<i class="fas fa-download"></i>
|
||||
@T("Admin.Common.Export")
|
||||
</button>
|
||||
<button type="button" class="btn btn-success dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
|
||||
<span class="caret"></span>
|
||||
<span class="sr-only"> </span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li class="dropdown-item">
|
||||
|
||||
<button asp-action="ExportToXml" type="submit" name="exportxml-all">
|
||||
<i class="far fa-file-code"></i>
|
||||
@T("Admin.Common.ExportToXml.All")
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<button asp-action="ExportToXml" type="submit" name="exportxml-all">
|
||||
<i class="far fa-file-code"></i>
|
||||
@T("Admin.Common.ExportToXml.All")
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
|
||||
<button type="button" id="exportxml-selected">
|
||||
<i class="far fa-file-code"></i>
|
||||
@T("Admin.Common.ExportToXml.Selected")
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown-divider"></li>
|
||||
<li class="dropdown-item">
|
||||
<button asp-action="ExportToExcel" type="submit" name="exportexcel-all">
|
||||
<i class="far fa-file-excel"></i>
|
||||
@T("Admin.Common.ExportToExcel.All")
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<button type="button" id="exportexcel-selected">
|
||||
<i class="far fa-file-excel"></i>
|
||||
@T("Admin.Common.ExportToExcel.Selected")
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@if (!Model.IsLoggedInAsVendor || Model.AllowVendorsToImportProducts)
|
||||
{
|
||||
//a vendor cannot import products
|
||||
<button type="button" name="importexcel" class="btn bg-olive" data-toggle="modal" data-target="#importexcel-window">
|
||||
<i class="fas fa-upload"></i>
|
||||
@T("Admin.Common.Import")
|
||||
</button>
|
||||
}
|
||||
<button type="button" id="delete-selected" class="btn btn-danger">
|
||||
<i class="far fa-trash-can"></i>
|
||||
@T("Admin.Common.Delete.Selected")
|
||||
</button>
|
||||
<nop-action-confirmation asp-button-id="delete-selected" />
|
||||
@await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.ProductListButtons, additionalData = Model })
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" id="exportxml-selected">
|
||||
<i class="far fa-file-code"></i>
|
||||
@T("Admin.Common.ExportToXml.Selected")
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown-divider"></li>
|
||||
<li class="dropdown-item">
|
||||
<button asp-action="ExportToExcel" type="submit" name="exportexcel-all">
|
||||
<i class="far fa-file-excel"></i>
|
||||
@T("Admin.Common.ExportToExcel.All")
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<button type="button" id="exportexcel-selected">
|
||||
<i class="far fa-file-excel"></i>
|
||||
@T("Admin.Common.ExportToExcel.Selected")
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@if (!Model.IsLoggedInAsVendor || Model.AllowVendorsToImportProducts)
|
||||
{
|
||||
//a vendor cannot import products
|
||||
<button type="button" name="importexcel" class="btn bg-olive" data-toggle="modal" data-target="#importexcel-window">
|
||||
<i class="fas fa-upload"></i>
|
||||
@T("Admin.Common.Import")
|
||||
</button>
|
||||
}
|
||||
<button type="button" id="delete-selected" class="btn btn-danger">
|
||||
<i class="far fa-trash-can"></i>
|
||||
@T("Admin.Common.Delete.Selected")
|
||||
</button>
|
||||
<nop-action-confirmation asp-button-id="delete-selected" />
|
||||
@await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.ProductListButtons, additionalData = Model })
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="form-horizontal">
|
||||
<div class="cards-group">
|
||||
<div class="card card-default card-search">
|
||||
<div class="card-body">
|
||||
<div class="row search-row @(!hideSearchBlock ? "opened" : "")" data-hideAttribute="@hideSearchBlockAttributeName">
|
||||
<div class="search-text">@T("Admin.Common.Search")</div>
|
||||
<div class="icon-search"><i class="fas fa-magnifying-glass" aria-hidden="true"></i></div>
|
||||
<div class="icon-collapse"><i class="far fa-angle-@(!hideSearchBlock ? "up" : "down")" aria-hidden="true"></i></div>
|
||||
</div>
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
<div class="form-horizontal">
|
||||
<div class="cards-group">
|
||||
<div class="card card-default card-search">
|
||||
<div class="card-body">
|
||||
<div class="row search-row @(!hideSearchBlock ? "opened" : "")" data-hideAttribute="@hideSearchBlockAttributeName">
|
||||
<div class="search-text">@T("Admin.Common.Search")</div>
|
||||
<div class="icon-search"><i class="fas fa-magnifying-glass" aria-hidden="true"></i></div>
|
||||
<div class="icon-collapse"><i class="far fa-angle-@(!hideSearchBlock ? "up" : "down")" aria-hidden="true"></i></div>
|
||||
</div>
|
||||
|
||||
<div class="search-body @(hideSearchBlock ? "closed" : "")">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchProductName" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="SearchProductName" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableCategories.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchCategoryId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchCategoryId" asp-items="Model.AvailableCategories" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableCategories.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchIncludeSubCategories" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="SearchIncludeSubCategories" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableManufacturers.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchManufacturerId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchManufacturerId" asp-items="Model.AvailableManufacturers" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableVendors.SelectionIsNotPossible() || Model.IsLoggedInAsVendor ? Html.Raw("style='display: none;'") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchVendorId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchVendorId" asp-items="Model.AvailableVendors" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="form-group row" @(Model.HideStoresList ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchStoreId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchStoreId" asp-items="Model.AvailableStores" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableWarehouses.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchWarehouseId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchWarehouseId" asp-items="Model.AvailableWarehouses" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchProductTypeId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchProductTypeId" asp-items="Model.AvailableProductTypes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchPublishedId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchPublishedId" asp-items="Model.AvailablePublishedOptions" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="GoDirectlyToSku" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="input-group input-group-short">
|
||||
<nop-editor asp-for="GoDirectlyToSku" />
|
||||
<span class="input-group-append">
|
||||
<button type="submit" id="go-to-product-by-sku" name="go-to-product-by-sku" class="btn btn-info btn-flat">
|
||||
@T("Admin.Common.Go")
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="text-center col-12">
|
||||
<button type="button" id="search-products" class="btn btn-primary btn-search">
|
||||
<i class="fas fa-magnifying-glass"></i>
|
||||
@T("Admin.Common.Search")
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="search-body @(hideSearchBlock ? "closed" : "")">
|
||||
<div class="row">
|
||||
<div class="col-md-5">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchProductName" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="SearchProductName" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableCategories.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchCategoryId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchCategoryId" asp-items="Model.AvailableCategories" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableCategories.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchIncludeSubCategories" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="SearchIncludeSubCategories" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableManufacturers.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchManufacturerId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchManufacturerId" asp-items="Model.AvailableManufacturers" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableVendors.SelectionIsNotPossible() || Model.IsLoggedInAsVendor ? Html.Raw("style='display: none;'") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchVendorId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchVendorId" asp-items="Model.AvailableVendors" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-7">
|
||||
<div class="form-group row" @(Model.HideStoresList? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchStoreId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchStoreId" asp-items="Model.AvailableStores" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableWarehouses.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchWarehouseId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchWarehouseId" asp-items="Model.AvailableWarehouses" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchProductTypeId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchProductTypeId" asp-items="Model.AvailableProductTypes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="SearchPublishedId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="SearchPublishedId" asp-items="Model.AvailablePublishedOptions" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="GoDirectlyToSku" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="input-group input-group-short">
|
||||
<nop-editor asp-for="GoDirectlyToSku" />
|
||||
<span class="input-group-append">
|
||||
<button type="submit" id="go-to-product-by-sku" name="go-to-product-by-sku" class="btn btn-info btn-flat">
|
||||
@T("Admin.Common.Go")
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="text-center col-12">
|
||||
<button type="button" id="search-products" class="btn btn-primary btn-search">
|
||||
<i class="fas fa-magnifying-glass"></i>
|
||||
@T("Admin.Common.Search")
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-default">
|
||||
<div class="card-body">
|
||||
<nop-doc-reference asp-string-resource="@T("Admin.Documentation.Reference.Products", Docs.Products + Utm.OnAdmin)" />
|
||||
<div class="card card-default">
|
||||
<div class="card-body">
|
||||
<nop-doc-reference asp-string-resource="@T("Admin.Documentation.Reference.Products", Docs.Products + Utm.OnAdmin)" />
|
||||
|
||||
@await Html.PartialAsync("Table", new DataTablesModel
|
||||
{
|
||||
Name = "products-grid",
|
||||
UrlRead = new DataUrl("ProductList", "CustomProduct", null),
|
||||
SearchButtonId = "search-products",
|
||||
Length = Model.PageSize,
|
||||
LengthMenu = Model.AvailablePageSizes,
|
||||
Filters = new List<FilterParameter>
|
||||
{
|
||||
new FilterParameter(nameof(Model.SearchProductName)),
|
||||
new FilterParameter(nameof(Model.SearchCategoryId)),
|
||||
new FilterParameter(nameof(Model.SearchIncludeSubCategories), typeof(bool)),
|
||||
new FilterParameter(nameof(Model.SearchManufacturerId)),
|
||||
new FilterParameter(nameof(Model.SearchStoreId)),
|
||||
new FilterParameter(nameof(Model.SearchWarehouseId)),
|
||||
new FilterParameter(nameof(Model.SearchVendorId)),
|
||||
new FilterParameter(nameof(Model.SearchProductTypeId)),
|
||||
new FilterParameter(nameof(Model.SearchPublishedId))
|
||||
},
|
||||
ColumnCollection = new List<ColumnProperty>
|
||||
{
|
||||
new ColumnProperty(nameof(ProductModel.Id))
|
||||
{
|
||||
IsMasterCheckBox = true,
|
||||
Render = new RenderCheckBox("checkbox_products"),
|
||||
ClassName = NopColumnClassDefaults.CenterAll,
|
||||
Width = "50"
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.PictureThumbnailUrl))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.PictureThumbnailUrl").Text,
|
||||
Width = "100",
|
||||
Render = new RenderPicture(width: 100)
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.Name))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.Name").Text
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.Sku))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.Sku").Text,
|
||||
Width = "100"
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.FormattedPrice))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.Price").Text
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.StockQuantityStr))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.StockQuantity").Text
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.Published))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.Published").Text,
|
||||
Width = "80",
|
||||
ClassName = NopColumnClassDefaults.CenterAll,
|
||||
Render = new RenderBoolean()
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.Id))
|
||||
{
|
||||
Title = T("Admin.Common.Edit").Text,
|
||||
Width = "80",
|
||||
ClassName = NopColumnClassDefaults.Button,
|
||||
Render = new RenderButtonEdit(new DataUrl("~/Admin/Product/Edit"))
|
||||
}
|
||||
}
|
||||
})
|
||||
@await Html.PartialAsync("Table", new DataTablesModel
|
||||
{
|
||||
Name = "products-grid",
|
||||
UrlRead = new DataUrl("ProductList", "CustomProduct", null),
|
||||
SearchButtonId = "search-products",
|
||||
Length = Model.PageSize,
|
||||
LengthMenu = Model.AvailablePageSizes,
|
||||
Filters = new List<FilterParameter>
|
||||
{
|
||||
new FilterParameter(nameof(Model.SearchProductName)),
|
||||
new FilterParameter(nameof(Model.SearchCategoryId)),
|
||||
new FilterParameter(nameof(Model.SearchIncludeSubCategories), typeof(bool)),
|
||||
new FilterParameter(nameof(Model.SearchManufacturerId)),
|
||||
new FilterParameter(nameof(Model.SearchStoreId)),
|
||||
new FilterParameter(nameof(Model.SearchWarehouseId)),
|
||||
new FilterParameter(nameof(Model.SearchVendorId)),
|
||||
new FilterParameter(nameof(Model.SearchProductTypeId)),
|
||||
new FilterParameter(nameof(Model.SearchPublishedId))
|
||||
},
|
||||
ColumnCollection = new List<ColumnProperty>
|
||||
{
|
||||
new ColumnProperty(nameof(ProductModel.Id))
|
||||
{
|
||||
IsMasterCheckBox = true,
|
||||
Render = new RenderCheckBox("checkbox_products"),
|
||||
ClassName = NopColumnClassDefaults.CenterAll,
|
||||
Width = "40"
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.PictureThumbnailUrl))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.PictureThumbnailUrl").Text,
|
||||
Width = "100",
|
||||
Render = new RenderPicture(width: 100),
|
||||
Visible = false
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.Name))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.Name").Text
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.Sku))
|
||||
{
|
||||
Title = "SKU", //T("Admin.Catalog.Products.Fields.Sku").Text,
|
||||
Width = "100"
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.FormattedPrice))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.Price").Text
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.StockQuantityStr))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.StockQuantity").Text
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModelExtended.NetWeight))
|
||||
{
|
||||
Title = "Súly(kg)"//T("Admin.Catalog.Products.Fields.NetWeight").Text
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModelExtended.Tare))
|
||||
{
|
||||
Title = "Tára(kg)"//T("Admin.Catalog.Products.Fields.Tare").Text
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModelExtended.IncomingQuantity))
|
||||
{
|
||||
Title = "Bejövő"//T("Admin.Catalog.Products.Fields.Tare").Text
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModelExtended.IsMeasurable))
|
||||
{
|
||||
Title = "Mérendő?",//T("Admin.Catalog.Products.Fields.Tare").Text
|
||||
Width = "75",
|
||||
ClassName = NopColumnClassDefaults.CenterAll,
|
||||
Render = new RenderBoolean()
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.Published))
|
||||
{
|
||||
Title = T("Admin.Catalog.Products.Fields.Published").Text,
|
||||
Width = "75",
|
||||
ClassName = NopColumnClassDefaults.CenterAll,
|
||||
Render = new RenderBoolean()
|
||||
},
|
||||
new ColumnProperty(nameof(ProductModel.Id))
|
||||
{
|
||||
Title = T("Admin.Common.Edit").Text,
|
||||
Width = "80",
|
||||
ClassName = NopColumnClassDefaults.Button,
|
||||
Render = new RenderButtonEdit(new DataUrl("~/Admin/Product/Edit"))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
<script>
|
||||
<script>
|
||||
|
||||
$(function() {
|
||||
$('#delete-selected-action-confirmation-submit-button').bind('click', function () {
|
||||
var postData = {
|
||||
selectedIds: selectedIds
|
||||
};
|
||||
addAntiForgeryToken(postData);
|
||||
$.ajax({
|
||||
cache: false,
|
||||
type: "POST",
|
||||
url: "@(Url.Action("DeleteSelected", "Product"))",
|
||||
data: postData,
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
showAlert('deleteSelectedFailed', errorThrown);
|
||||
},
|
||||
complete: function (jqXHR, textStatus) {
|
||||
if (jqXHR.status === 204)
|
||||
{
|
||||
showAlert('nothingSelectedAlert', '@T("Admin.Common.Alert.NothingSelected")');
|
||||
return;
|
||||
}
|
||||
updateTable('#products-grid');
|
||||
}
|
||||
});
|
||||
$('#delete-selected-action-confirmation').modal('toggle');
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<nop-alert asp-alert-id="deleteSelectedFailed" />
|
||||
<nop-alert asp-alert-id="nothingSelectedAlert" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
$(function() {
|
||||
$("#@Html.IdFor(model => model.GoDirectlyToSku)").keydown(function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
$("#go-to-product-by-sku").trigger("click");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
$(function() {
|
||||
$('#delete-selected-action-confirmation-submit-button').bind('click', function () {
|
||||
var postData = {
|
||||
selectedIds: selectedIds
|
||||
};
|
||||
addAntiForgeryToken(postData);
|
||||
$.ajax({
|
||||
cache: false,
|
||||
type: "POST",
|
||||
url: "@(Url.Action("DeleteSelected", "Product"))",
|
||||
data: postData,
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
showAlert('deleteSelectedFailed', errorThrown);
|
||||
},
|
||||
complete: function (jqXHR, textStatus) {
|
||||
if (jqXHR.status === 204)
|
||||
{
|
||||
showAlert('nothingSelectedAlert', '@T("Admin.Common.Alert.NothingSelected")');
|
||||
return;
|
||||
}
|
||||
updateTable('#products-grid');
|
||||
}
|
||||
});
|
||||
$('#delete-selected-action-confirmation').modal('toggle');
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
<nop-alert asp-alert-id="deleteSelectedFailed" />
|
||||
<nop-alert asp-alert-id="nothingSelectedAlert" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<script>
|
||||
$(function() {
|
||||
$("#@Html.IdFor(model => model.GoDirectlyToSku)").keydown(function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
$("#go-to-product-by-sku").trigger("click");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</form>
|
||||
}
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
var displayModal = @((Model.LicenseCheckModel?.DisplayWarning == true || Model.LicenseCheckModel?.BlockPages == true).ToString().ToLower());
|
||||
if (displayModal) {
|
||||
$('#license-window').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
$(function() {
|
||||
var displayModal = @((Model.LicenseCheckModel?.DisplayWarning == true || Model.LicenseCheckModel?.BlockPages == true).ToString().ToLower());
|
||||
if (displayModal) {
|
||||
$('#license-window').modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false
|
||||
});
|
||||
|
||||
$('#license-window').on('shown.bs.modal', function (event) {
|
||||
var modalCloseEl = $(this).find('button.close');
|
||||
var closeTextEl = $('span', modalCloseEl);
|
||||
$('#license-window').on('shown.bs.modal', function (event) {
|
||||
var modalCloseEl = $(this).find('button.close');
|
||||
var closeTextEl = $('span', modalCloseEl);
|
||||
|
||||
var startFrom = 5;
|
||||
closeTextEl.text(startFrom);
|
||||
var startFrom = 5;
|
||||
closeTextEl.text(startFrom);
|
||||
|
||||
const timer = setInterval(function() {
|
||||
if (startFrom-- > 0)
|
||||
closeTextEl.text(startFrom);
|
||||
}, 1000);
|
||||
const timer = setInterval(function() {
|
||||
if (startFrom-- > 0)
|
||||
closeTextEl.text(startFrom);
|
||||
}, 1000);
|
||||
|
||||
setTimeout(function() {
|
||||
closeTextEl.html('×');
|
||||
modalCloseEl.on('click', function() {
|
||||
$('#license-window').modal('hide')
|
||||
});
|
||||
clearInterval(timer);
|
||||
}, startFrom*1000);
|
||||
});
|
||||
}
|
||||
});
|
||||
setTimeout(function() {
|
||||
closeTextEl.html('×');
|
||||
modalCloseEl.on('click', function() {
|
||||
$('#license-window').modal('hide')
|
||||
});
|
||||
clearInterval(timer);
|
||||
}, startFrom*1000);
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<div id="license-window" class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
@Html.Raw(Model.LicenseCheckModel?.WarningText)
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
@Html.Raw(Model.LicenseCheckModel?.WarningText)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@*import products form*@
|
||||
<div id="importexcel-window" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="importexcel-window-title">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="importexcel-window-title">@T("Admin.Common.ImportFromExcel")</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<form asp-controller="Product" asp-action="ImportExcel" method="post" enctype="multipart/form-data">
|
||||
<div class="form-horizontal">
|
||||
<div class="modal-body">
|
||||
<ul class="common-list">
|
||||
<li>
|
||||
<em>@T("Admin.Catalog.Products.List.ImportFromExcelTip")</em>
|
||||
</li>
|
||||
<li>
|
||||
<em>@T("Admin.Common.ImportFromExcel.ManyRecordsWarning")</em>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-2">
|
||||
<div class="label-wrapper">
|
||||
<label class="col-form-label">
|
||||
@T("Admin.Common.ExcelFile")
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<input type="file" id="importexcelfile" name="importexcelfile" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
@T("Admin.Common.ImportFromExcel")
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@*import products form*@
|
||||
<div id="importexcel-window" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="importexcel-window-title">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="importexcel-window-title">@T("Admin.Common.ImportFromExcel")</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<form asp-controller="Product" asp-action="ImportExcel" method="post" enctype="multipart/form-data">
|
||||
<div class="form-horizontal">
|
||||
<div class="modal-body">
|
||||
<ul class="common-list">
|
||||
<li>
|
||||
<em>@T("Admin.Catalog.Products.List.ImportFromExcelTip")</em>
|
||||
</li>
|
||||
<li>
|
||||
<em>@T("Admin.Common.ImportFromExcel.ManyRecordsWarning")</em>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-2">
|
||||
<div class="label-wrapper">
|
||||
<label class="col-form-label">
|
||||
@T("Admin.Common.ExcelFile")
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<input type="file" id="importexcelfile" name="importexcelfile" class="form-control" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
@T("Admin.Common.ImportFromExcel")
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@*export selected (XML). We don't use GET approach because it's limited to 2K-4K chars and won't work for large number of entities*@
|
||||
<form asp-controller="Product" asp-action="ExportXmlSelected" method="post" id="export-xml-selected-form">
|
||||
<input type="hidden" id="selectedIds" name="selectedIds" value="" />
|
||||
</form>
|
||||
@*export selected (XML). We don't use GET approach because it's limited to 2K-4K chars and won't work for large number of entities*@
|
||||
<form asp-controller="Product" asp-action="ExportXmlSelected" method="post" id="export-xml-selected-form">
|
||||
<input type="hidden" id="selectedIds" name="selectedIds" value="" />
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$('#exportxml-selected').click(function (e) {
|
||||
e.preventDefault();
|
||||
var ids = selectedIds.join(",");
|
||||
if (!ids) {
|
||||
$('#exportXmlSelected-info').text("@T("Admin.Products.NoProducts")");
|
||||
$("#exportXmlSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#export-xml-selected-form #selectedIds').val(ids);
|
||||
$('#export-xml-selected-form').submit();
|
||||
updateTable('#products-grid');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
$(function() {
|
||||
$('#exportxml-selected').click(function (e) {
|
||||
e.preventDefault();
|
||||
var ids = selectedIds.join(",");
|
||||
if (!ids) {
|
||||
$('#exportXmlSelected-info').text("@T("Admin.Products.NoProducts")");
|
||||
$("#exportXmlSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#export-xml-selected-form #selectedIds').val(ids);
|
||||
$('#export-xml-selected-form').submit();
|
||||
updateTable('#products-grid');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<nop-alert asp-alert-id="exportXmlSelected" />
|
||||
|
||||
@*export selected (Excel). We don't use GET approach because it's limited to 2K-4K chars and won't work for large number of entities*@
|
||||
<form asp-controller="Product" asp-action="ExportExcelSelected" method="post" id="export-excel-selected-form">
|
||||
<input type="hidden" id="selectedIds" name="selectedIds" value="" />
|
||||
</form>
|
||||
@*export selected (Excel). We don't use GET approach because it's limited to 2K-4K chars and won't work for large number of entities*@
|
||||
<form asp-controller="Product" asp-action="ExportExcelSelected" method="post" id="export-excel-selected-form">
|
||||
<input type="hidden" id="selectedIds" name="selectedIds" value="" />
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$('#exportexcel-selected').click(function (e) {
|
||||
e.preventDefault();
|
||||
var ids = selectedIds.join(",");
|
||||
if (!ids) {
|
||||
$('#exportExcelSelected-info').text("@T("Admin.Products.NoProducts")");
|
||||
$("#exportExcelSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#export-excel-selected-form #selectedIds').val(ids);
|
||||
$('#export-excel-selected-form').submit();
|
||||
updateTable('#products-grid');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
$(function() {
|
||||
$('#exportexcel-selected').click(function (e) {
|
||||
e.preventDefault();
|
||||
var ids = selectedIds.join(",");
|
||||
if (!ids) {
|
||||
$('#exportExcelSelected-info').text("@T("Admin.Products.NoProducts")");
|
||||
$("#exportExcelSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#export-excel-selected-form #selectedIds').val(ids);
|
||||
$('#export-excel-selected-form').submit();
|
||||
updateTable('#products-grid');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<nop-alert asp-alert-id="exportExcelSelected" />
|
||||
|
|
@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Mvc;
|
|||
using Nop.Core;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Web.Areas.Admin.Models.Catalog;
|
||||
|
|
@ -37,7 +37,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Components
|
|||
if (model.OrderId > 0)
|
||||
{
|
||||
var orderPickupAttributeValue = await _fruitBankAttributeService.GetGenericAttributeValueAsync<Order, DateTime?>(model.OrderId, nameof(IOrderDto.DateOfReceipt));
|
||||
var orderMeasurableAttributeValue = await _fruitBankAttributeService.GetGenericAttributeValueAsync<Order, bool>(model.OrderId, nameof(OrderModelExtended.IsMeasurable));
|
||||
var orderMeasurableAttributeValue = await _fruitBankAttributeService.GetGenericAttributeValueAsync<Order, bool>(model.OrderId, nameof(IMeasurable.IsMeasurable));
|
||||
|
||||
model.IsMeasurable = orderMeasurableAttributeValue;
|
||||
if(orderPickupAttributeValue.HasValue && orderPickupAttributeValue.Value != DateTime.MinValue)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Components
|
|||
}
|
||||
|
||||
model.Tare = await _fruitBankAttributeService.GetGenericAttributeValueAsync<Product, double>(model.ProductId, nameof(ITare.Tare));
|
||||
model.IncomingQuantity = await _fruitBankAttributeService.GetGenericAttributeValueAsync<Product, int>(model.ProductId, "IncomingQuantity");
|
||||
model.IncomingQuantity = await _fruitBankAttributeService.GetGenericAttributeValueAsync<Product, int>(model.ProductId, nameof(IIncomingQuantity.IncomingQuantity));
|
||||
}
|
||||
|
||||
return View("~/Plugins/Misc.FruitBankPlugin/Views/ProductAttributes.cshtml", model);
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ public class FruitBankDbContext : MgDbContextBase,
|
|||
private readonly IProductService _productService;
|
||||
private readonly IStaticCacheManager _staticCacheManager;
|
||||
|
||||
public ProductDtoDbTable ProductDtos { get; set; }
|
||||
|
||||
public OrderDtoDbTable OrderDtos { get; set; }
|
||||
public OrderItemDtoDbTable OrderItemDtos { get; set; }
|
||||
|
||||
|
|
@ -67,7 +69,7 @@ public class FruitBankDbContext : MgDbContextBase,
|
|||
public FruitBankDbContext(INopDataProvider dataProvider, ILockService lockService, FruitBankAttributeService fruitBankAttributeService, IStoreContext storeContext,
|
||||
PartnerDbTable partnerDbTable, ShippingDbTable shippingDbTable, ShippingDocumentDbTable shippingDocumentDbTable, ShippingItemDbTable shippingItemDbTable,
|
||||
ShippingItemPalletDbTable shippingItemPalletDbTable, FilesDbTable filesDbTable, ShippingDocumentToFilesDbTable shippingDocumentToFilesDbTable,
|
||||
OrderDtoDbTable orderDtoDbTable, OrderItemDtoDbTable orderItemDtoDbTable, OrderItemPalletDbTable orderItemPalletDbTable,
|
||||
ProductDtoDbTable productDtoDbTable, OrderDtoDbTable orderDtoDbTable, OrderItemDtoDbTable orderItemDtoDbTable, OrderItemPalletDbTable orderItemPalletDbTable,
|
||||
IProductService productService, IStaticCacheManager staticCacheManager,
|
||||
IRepository<Product> productRepository,
|
||||
IRepository<Customer> customerRepository,
|
||||
|
|
@ -84,6 +86,8 @@ public class FruitBankDbContext : MgDbContextBase,
|
|||
Partners = partnerDbTable;
|
||||
Products = productRepository;
|
||||
|
||||
ProductDtos = productDtoDbTable;
|
||||
|
||||
OrderDtos = orderDtoDbTable;
|
||||
OrderItemDtos = orderItemDtoDbTable;
|
||||
OrderItemPallets = orderItemPalletDbTable;
|
||||
|
|
|
|||
|
|
@ -11,27 +11,6 @@ using Nop.Services.Logging;
|
|||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||
|
||||
public class OrderItemDtoDbTable : MgDbTableBase<OrderItemDto>
|
||||
{
|
||||
public OrderItemDtoDbTable(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings, ILogger logger)
|
||||
: base(eventPublisher, dataProvider, shortTermCacheManager, staticCacheManager, appSettings, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public IQueryable<OrderItemDto> GetAll(bool loadRelations)
|
||||
{
|
||||
return GetAll()
|
||||
.LoadWith(oi => oi.OrderDto)
|
||||
.LoadWith(oi => oi.OrderItemPallets)
|
||||
.LoadWith(oi => oi.GenericAttributes)
|
||||
.LoadWith(oi => oi.ProductDto).ThenLoad(prod => prod.GenericAttributes);
|
||||
}
|
||||
|
||||
public Task<OrderItemDto> GetByIdAsync(int orderItemId) => GetAll(true).Where(x => x.Id == orderItemId).FirstOrDefaultAsync(null);
|
||||
|
||||
public IQueryable<OrderItemDto> GetAllByOrderId(int orderId)=> GetAll(true).Where(o => o.OrderId == orderId);
|
||||
}
|
||||
|
||||
public class OrderDtoDbTable : MgDbTableBase<OrderDto>
|
||||
{
|
||||
public OrderDtoDbTable(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings, ILogger logger)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
using FruitBank.Common.Dtos;
|
||||
using LinqToDB;
|
||||
using Mango.Nop.Core.Repositories;
|
||||
using Nop.Core.Caching;
|
||||
using Nop.Core.Configuration;
|
||||
using Nop.Core.Events;
|
||||
using Nop.Data;
|
||||
using Nop.Services.Logging;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||
|
||||
public class OrderItemDtoDbTable : MgDbTableBase<OrderItemDto>
|
||||
{
|
||||
public OrderItemDtoDbTable(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings, ILogger logger)
|
||||
: base(eventPublisher, dataProvider, shortTermCacheManager, staticCacheManager, appSettings, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public IQueryable<OrderItemDto> GetAll(bool loadRelations)
|
||||
{
|
||||
return GetAll()
|
||||
.LoadWith(oi => oi.OrderDto)
|
||||
.LoadWith(oi => oi.OrderItemPallets)
|
||||
.LoadWith(oi => oi.GenericAttributes)
|
||||
.LoadWith(oi => oi.ProductDto).ThenLoad(prod => prod.GenericAttributes);
|
||||
}
|
||||
|
||||
public Task<OrderItemDto> GetByIdAsync(int orderItemId) => GetAll(true).Where(x => x.Id == orderItemId).FirstOrDefaultAsync(null);
|
||||
|
||||
public IQueryable<OrderItemDto> GetAllByOrderId(int orderId)=> GetAll(true).Where(o => o.OrderId == orderId);
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
using FruitBank.Common.Dtos;
|
||||
using LinqToDB;
|
||||
using Mango.Nop.Core.Repositories;
|
||||
using Nop.Core.Caching;
|
||||
using Nop.Core.Configuration;
|
||||
using Nop.Core.Events;
|
||||
using Nop.Data;
|
||||
using Nop.Services.Logging;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||
|
||||
public class ProductDtoDbTable : MgDbTableBase<ProductDto>
|
||||
{
|
||||
public ProductDtoDbTable(IEventPublisher eventPublisher, INopDataProvider dataProvider, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager, AppSettings appSettings, ILogger logger)
|
||||
: base(eventPublisher, dataProvider, shortTermCacheManager, staticCacheManager, appSettings, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public IQueryable<ProductDto> GetAll(bool loadRelations)
|
||||
{
|
||||
return GetAll().LoadWith(p => p.GenericAttributes);
|
||||
}
|
||||
|
||||
public Task<ProductDto> GetByIdAsync(int productId) => GetAll(true).Where(x => x.Id == productId).FirstOrDefaultAsync(null);
|
||||
|
||||
public IQueryable<ProductDto> GetAllByIds(IEnumerable<int> productIds) => GetAll(true).Where(p => productIds.Contains(p.Id));
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ public class ShippingDbTable : MgDbTableBase<Shipping>
|
|||
? GetAll()
|
||||
.LoadWith(s => s.ShippingDocuments).ThenLoad(sd => sd.ShippingItems).ThenLoad(si => si.ShippingItemPallets)
|
||||
.LoadWith(s => s.ShippingDocuments).ThenLoad(sd => sd.ShippingItems).ThenLoad(si => si.Pallet)
|
||||
.LoadWith(s => s.ShippingDocuments).ThenLoad(sd => sd.ShippingItems).ThenLoad(si => si.Product)
|
||||
.LoadWith(s => s.ShippingDocuments).ThenLoad(sd => sd.ShippingItems).ThenLoad(si => si.ProductDto).ThenLoad(prod => prod.GenericAttributes)
|
||||
.LoadWith(s => s.ShippingDocuments).ThenLoad(sd => sd.ShippingDocumentToFiles).ThenLoad(sdtof => sdtof.ShippingDocumentFile)
|
||||
.LoadWith(s => s.ShippingDocuments).ThenLoad(sd => sd.Partner)
|
||||
: GetAll();
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public class ShippingDocumentDbTable : MgDbTableBase<ShippingDocument>
|
|||
? GetAll()
|
||||
.LoadWith(sd => sd.Shipping)
|
||||
.LoadWith(sd => sd.ShippingItems).ThenLoad(si => si.Pallet)
|
||||
.LoadWith(sd => sd.ShippingItems).ThenLoad(si => si.Product)
|
||||
.LoadWith(sd => sd.ShippingItems).ThenLoad(si => si.ProductDto).ThenLoad(prod => prod.GenericAttributes)
|
||||
.LoadWith(sd => sd.ShippingItems).ThenLoad(si => si.ShippingItemPallets)
|
||||
.LoadWith(sd => sd.ShippingDocumentToFiles).ThenLoad(sdtof => sdtof.ShippingDocumentFile)
|
||||
.LoadWith(sd => sd.Partner)
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ public class ShippingItemDbTable : MgDbTableBase<ShippingItem>
|
|||
.LoadWith(si => si.ShippingDocument).ThenLoad(p => p.Partner)
|
||||
.LoadWith(si => si.ShippingDocument).ThenLoad(sd => sd.ShippingDocumentToFiles).ThenLoad(sdtof => sdtof.ShippingDocumentFile)
|
||||
.LoadWith(si => si.ShippingItemPallets)
|
||||
.LoadWith(si => si.Product)
|
||||
.LoadWith(si => si.ProductDto).ThenLoad(prod => prod.GenericAttributes)
|
||||
.LoadWith(si => si.Pallet)
|
||||
: GetAll().LoadWith(si => si.ShippingItemPallets).LoadWith(si => si.Product).LoadWith(si => si.Pallet);
|
||||
: GetAll().LoadWith(si => si.ShippingItemPallets).LoadWith(si => si.ProductDto).ThenLoad(prod => prod.GenericAttributes).LoadWith(si => si.Pallet);
|
||||
}
|
||||
|
||||
public IQueryable<ShippingItem> GetAllNotMeasured(bool loadRelations)
|
||||
|
|
|
|||
|
|
@ -81,12 +81,12 @@ public class FruitBankEventConsumer(IHttpContextAccessor httpContextAcc, FruitBa
|
|||
await fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync<Product, double>(product.Id, nameof(ITare.Tare), tare);
|
||||
}
|
||||
|
||||
if (form.ContainsKey("IncomingQuantity"))
|
||||
if (form.ContainsKey(nameof(IIncomingQuantity.IncomingQuantity)))
|
||||
{
|
||||
var incomingQuantity = CommonHelper.To<int>(form["IncomingQuantity"].ToString());
|
||||
var incomingQuantity = CommonHelper.To<int>(form[nameof(IIncomingQuantity.IncomingQuantity)].ToString());
|
||||
if (incomingQuantity < 0) throw new Exception($"FruitBankEventConsumer->SaveProductCustomAttributesAsync(); (incomingQuantity < 0); productId: {product.Id}; incomingQuantity: {incomingQuantity}");
|
||||
|
||||
await fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync<Product, int>(product.Id, "IncomingQuantity", incomingQuantity);
|
||||
await fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync<Product, int>(product.Id, nameof(IIncomingQuantity.IncomingQuantity), incomingQuantity);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ using Nop.Core.Domain.Orders;
|
|||
using Nop.Core.Domain.Shipping;
|
||||
using Nop.Core.Domain.Tax;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Helpers;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||
using Nop.Services.Affiliates;
|
||||
using Nop.Services.Catalog;
|
||||
|
|
@ -37,15 +36,18 @@ using System.Collections;
|
|||
using System.Reflection;
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase;
|
||||
using FruitBank.Common.Dtos;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
|
||||
{
|
||||
public class CustomOrderModelFactory : OrderModelFactory
|
||||
public class CustomOrderModelFactory : MgOrderModelFactory<OrderListModelExtended, OrderModelExtended>
|
||||
{
|
||||
private FruitBankDbContext _ctx;
|
||||
private readonly IOrderMeasurementService _orderMeasurementService;
|
||||
private readonly IGenericAttributeService _genericAttributeService;
|
||||
|
||||
#region Ctor
|
||||
public CustomOrderModelFactory(
|
||||
FruitBankDbContext ctx,
|
||||
IOrderMeasurementService orderMeasurementService,
|
||||
|
|
@ -139,40 +141,17 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
|
|||
orderSettings,
|
||||
shippingSettings,
|
||||
urlRecordService,
|
||||
taxSettings
|
||||
taxSettings,
|
||||
genericAttributeService
|
||||
)
|
||||
{
|
||||
_ctx = ctx;
|
||||
_orderMeasurementService = orderMeasurementService;
|
||||
_genericAttributeService = genericAttributeService;
|
||||
}
|
||||
|
||||
#endregion Ctor
|
||||
public override async Task<OrderSearchModel> PrepareOrderSearchModelAsync(OrderSearchModel searchModel)
|
||||
{
|
||||
// let base prepare default model first
|
||||
var baseModel = await base.PrepareOrderSearchModelAsync(searchModel);
|
||||
|
||||
//foreach (var order in baseModel.order)
|
||||
|
||||
|
||||
// create derived/extended instance
|
||||
//var extended = new OrderSearchModelExtended();
|
||||
|
||||
// copy all public instance properties from baseModel to extended
|
||||
//CopyModelHelper.CopyPublicProperties(baseModel, extended);
|
||||
|
||||
// try to obtain NeedsMeasurement from the incoming searchModel (if it's extended)
|
||||
//bool? needsMeasurement = null;
|
||||
//var prop = searchModel?.GetType().GetProperty("NeedsMeasurement", BindingFlags.Public | BindingFlags.Instance);
|
||||
//if (prop != null && prop.PropertyType == typeof(bool?))
|
||||
//{
|
||||
// needsMeasurement = (bool?)prop.GetValue(searchModel);
|
||||
//}
|
||||
|
||||
//extended.NeedsMeasurement = needsMeasurement;
|
||||
|
||||
// return the extended object (it's assignable to OrderSearchModel)
|
||||
//return extended;
|
||||
return baseModel;
|
||||
}
|
||||
|
||||
|
|
@ -181,18 +160,14 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
|
|||
|
||||
public async Task<OrderListModelExtended> PrepareOrderListModelExtendedAsync(OrderSearchModel searchModel)
|
||||
{
|
||||
var orderListModel = await PrepareOrderListModelAsync(searchModel);
|
||||
var extendedRows = new List<OrderModelExtended>(orderListModel.RecordsFiltered);
|
||||
Dictionary<int, OrderDto> orderDtosById = null;
|
||||
|
||||
var orderDtosById = await _ctx.OrderDtos.GetAllByIds(orderListModel.Data.Select(x => x.Id)).ToDictionaryAsync(k => k.Id, v => v);
|
||||
|
||||
foreach (var orderModel in orderListModel.Data)
|
||||
var orderListModelExtended = await base.PrepareOrderListModelExtendedAsync(searchModel, async (orderListModel, orderModelExtended) =>
|
||||
{
|
||||
var orderDto = orderDtosById[orderModel.Id];
|
||||
|
||||
var orderModelExtended = new OrderModelExtended();
|
||||
PropertyHelper.CopyPublicValueTypeProperties(orderModel, orderModelExtended);
|
||||
orderDtosById ??= await _ctx.OrderDtos.GetAllByIds(orderListModel.Data.Select(x => x.Id)).ToDictionaryAsync(k => k.Id, v => v);
|
||||
var orderDto = orderDtosById[orderModelExtended.Id];
|
||||
|
||||
orderModelExtended.IsMeasured = orderDto.IsMeasured;
|
||||
orderModelExtended.IsMeasurable = orderDto.IsMeasurable;
|
||||
orderModelExtended.DateOfReceipt = orderDto.DateOfReceipt;
|
||||
|
||||
|
|
@ -200,13 +175,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
|
|||
//orderModelExtended.DateOfReceipt = await GetPickupDateTimeAsync(orderModel);
|
||||
|
||||
Console.WriteLine(orderModelExtended.Id);
|
||||
extendedRows.Add(orderModelExtended);
|
||||
}
|
||||
|
||||
orderListModel.Data = null;
|
||||
|
||||
var orderListModelExtended = orderListModel.ToJson().JsonTo<OrderListModelExtended>();
|
||||
orderListModelExtended.Data = extendedRows;
|
||||
});
|
||||
|
||||
return orderListModelExtended;
|
||||
}
|
||||
|
|
@ -232,7 +201,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
|
|||
var fullOrder = await _orderService.GetOrderByIdAsync(order.Id);
|
||||
if (fullOrder != null)
|
||||
{
|
||||
dateTime = await _genericAttributeService.GetAttributeAsync<DateTime>(fullOrder, nameof(IOrderDto.DateOfReceipt));
|
||||
dateTime = await GenericAttributeService.GetAttributeAsync<DateTime>(fullOrder, nameof(IOrderDto.DateOfReceipt));
|
||||
if(dateTime == DateTime.MinValue || !dateTime.HasValue)
|
||||
{
|
||||
dateTime = null;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,170 @@
|
|||
using AyCode.Core.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Directory;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Core.Domain.Shipping;
|
||||
using Nop.Core.Domain.Tax;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||
using Nop.Services.Affiliates;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Services.Configuration;
|
||||
using Nop.Services.Customers;
|
||||
using Nop.Services.Directory;
|
||||
using Nop.Services.Discounts;
|
||||
using Nop.Services.Helpers;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Services.Media;
|
||||
using Nop.Services.Orders;
|
||||
using Nop.Services.Payments;
|
||||
using Nop.Services.Security;
|
||||
using Nop.Services.Seo;
|
||||
using Nop.Services.Shipping;
|
||||
using Nop.Services.Stores;
|
||||
using Nop.Services.Tax;
|
||||
using Nop.Services.Vendors;
|
||||
using Nop.Web.Areas.Admin.Factories;
|
||||
using Nop.Web.Areas.Admin.Models.Orders;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase;
|
||||
|
||||
public class MgOrderModelFactory<TOrderListModelExt, TOrderModelExt> : OrderModelFactory
|
||||
where TOrderListModelExt:OrderListModelExtended where TOrderModelExt: OrderModelExtended
|
||||
{
|
||||
protected IGenericAttributeService GenericAttributeService;
|
||||
|
||||
#region Ctor
|
||||
public MgOrderModelFactory(
|
||||
AddressSettings addressSettings,
|
||||
CatalogSettings catalogSettings,
|
||||
CurrencySettings currencySettings,
|
||||
IActionContextAccessor actionContextAccessor,
|
||||
IAddressModelFactory addressModelFactory,
|
||||
IAddressService addressService,
|
||||
IAffiliateService affiliateService,
|
||||
IBaseAdminModelFactory baseAdminModelFactory,
|
||||
ICountryService countryService,
|
||||
ICurrencyService currencyService,
|
||||
ICustomerService customerService,
|
||||
IDateTimeHelper dateTimeHelper,
|
||||
IDiscountService discountService,
|
||||
IDownloadService downloadService,
|
||||
IEncryptionService encryptionService,
|
||||
IGiftCardService giftCardService,
|
||||
ILocalizationService localizationService,
|
||||
IMeasureService measureService,
|
||||
IOrderProcessingService orderProcessingService,
|
||||
IOrderReportService orderReportService,
|
||||
IOrderService orderService,
|
||||
IPaymentPluginManager paymentPluginManager,
|
||||
IPaymentService paymentService,
|
||||
IPictureService pictureService,
|
||||
IPriceCalculationService priceCalculationService,
|
||||
IPriceFormatter priceFormatter,
|
||||
IProductAttributeService productAttributeService,
|
||||
IProductService productService,
|
||||
IReturnRequestService returnRequestService,
|
||||
IRewardPointService rewardPointService,
|
||||
ISettingService settingService,
|
||||
IShipmentService shipmentService,
|
||||
IShippingService shippingService,
|
||||
IStateProvinceService stateProvinceService,
|
||||
IStoreService storeService,
|
||||
ITaxService taxService,
|
||||
IUrlHelperFactory urlHelperFactory,
|
||||
IVendorService vendorService,
|
||||
IWorkContext workContext,
|
||||
MeasureSettings measureSettings,
|
||||
NopHttpClient nopHttpClient,
|
||||
OrderSettings orderSettings,
|
||||
ShippingSettings shippingSettings,
|
||||
IUrlRecordService urlRecordService,
|
||||
TaxSettings taxSettings,
|
||||
IGenericAttributeService genericAttributeService)
|
||||
: base(addressSettings,
|
||||
catalogSettings,
|
||||
currencySettings,
|
||||
actionContextAccessor,
|
||||
addressModelFactory,
|
||||
addressService,
|
||||
affiliateService,
|
||||
baseAdminModelFactory,
|
||||
countryService,
|
||||
currencyService,
|
||||
customerService,
|
||||
dateTimeHelper,
|
||||
discountService,
|
||||
downloadService,
|
||||
encryptionService,
|
||||
giftCardService,
|
||||
localizationService,
|
||||
measureService,
|
||||
orderProcessingService,
|
||||
orderReportService,
|
||||
orderService,
|
||||
paymentPluginManager,
|
||||
paymentService,
|
||||
pictureService,
|
||||
priceCalculationService,
|
||||
priceFormatter,
|
||||
productAttributeService,
|
||||
productService,
|
||||
returnRequestService,
|
||||
rewardPointService,
|
||||
settingService,
|
||||
shipmentService,
|
||||
shippingService,
|
||||
stateProvinceService,
|
||||
storeService,
|
||||
taxService,
|
||||
urlHelperFactory,
|
||||
vendorService,
|
||||
workContext,
|
||||
measureSettings,
|
||||
nopHttpClient,
|
||||
orderSettings,
|
||||
shippingSettings,
|
||||
urlRecordService,
|
||||
taxSettings
|
||||
)
|
||||
{
|
||||
GenericAttributeService = genericAttributeService;
|
||||
}
|
||||
#endregion Cotr
|
||||
|
||||
public override Task<OrderSearchModel> PrepareOrderSearchModelAsync(OrderSearchModel searchModel)
|
||||
=> base.PrepareOrderSearchModelAsync(searchModel);
|
||||
|
||||
public override Task<OrderListModel> PrepareOrderListModelAsync(OrderSearchModel searchModel)
|
||||
=> base.PrepareOrderListModelAsync(searchModel);
|
||||
|
||||
public virtual async Task<TOrderListModelExt> PrepareOrderListModelExtendedAsync(OrderSearchModel searchModel, Func<OrderListModel, TOrderModelExt, Task> dataItemCopiedCallback)
|
||||
{
|
||||
var orderListModel = await PrepareOrderListModelAsync(searchModel);
|
||||
var extendedRows = new List<TOrderModelExt>(orderListModel.RecordsFiltered);
|
||||
|
||||
foreach (var orderModel in orderListModel.Data.ToList())
|
||||
{
|
||||
var orderModelExtended = Activator.CreateInstance<TOrderModelExt>();
|
||||
|
||||
PropertyHelper.CopyPublicValueTypeProperties(orderModel, orderModelExtended);
|
||||
extendedRows.Add(orderModelExtended);
|
||||
|
||||
if (dataItemCopiedCallback == null) continue;
|
||||
await dataItemCopiedCallback.Invoke(orderListModel, orderModelExtended);
|
||||
}
|
||||
|
||||
orderListModel.Data = null;
|
||||
|
||||
var orderListModelExtended = orderListModel.ToJson().JsonTo<TOrderListModelExt>();
|
||||
orderListModelExtended.Data = extendedRows;
|
||||
|
||||
return orderListModelExtended;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
using AyCode.Core.Extensions;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Common;
|
||||
using Nop.Core.Domain.Directory;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Core.Domain.Shipping;
|
||||
using Nop.Core.Domain.Tax;
|
||||
using Nop.Core.Domain.Vendors;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Products;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Services;
|
||||
using Nop.Services.Affiliates;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Services.Configuration;
|
||||
using Nop.Services.Customers;
|
||||
using Nop.Services.Directory;
|
||||
using Nop.Services.Discounts;
|
||||
using Nop.Services.Helpers;
|
||||
using Nop.Services.Localization;
|
||||
using Nop.Services.Media;
|
||||
using Nop.Services.Orders;
|
||||
using Nop.Services.Payments;
|
||||
using Nop.Services.Security;
|
||||
using Nop.Services.Seo;
|
||||
using Nop.Services.Shipping;
|
||||
using Nop.Services.Stores;
|
||||
using Nop.Services.Tax;
|
||||
using Nop.Services.Vendors;
|
||||
using Nop.Web.Areas.Admin.Factories;
|
||||
using Nop.Web.Areas.Admin.Models.Catalog;
|
||||
using Nop.Web.Areas.Admin.Models.Orders;
|
||||
using Nop.Web.Framework.Factories;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase;
|
||||
|
||||
public class MgProductModelFactory<TProductListModelExt, TProductModelExt> : ProductModelFactory
|
||||
where TProductListModelExt : ProductListModelExtended where TProductModelExt : ProductModelExtended
|
||||
{
|
||||
protected IGenericAttributeService GenericAttributeService;
|
||||
|
||||
#region Ctor
|
||||
|
||||
public MgProductModelFactory(
|
||||
CatalogSettings catalogSettings,
|
||||
CurrencySettings currencySettings,
|
||||
IAddressService addressService,
|
||||
IBaseAdminModelFactory baseAdminModelFactory,
|
||||
ICategoryService categoryService,
|
||||
ICurrencyService currencyService,
|
||||
ICustomerService customerService,
|
||||
IDateTimeHelper dateTimeHelper,
|
||||
IDiscountService discountService,
|
||||
IDiscountSupportedModelFactory discountSupportedModelFactory,
|
||||
ILocalizationService localizationService,
|
||||
ILocalizedModelFactory localizedModelFactory,
|
||||
IManufacturerService manufacturerService,
|
||||
IMeasureService measureService,
|
||||
IOrderService orderService,
|
||||
IPictureService pictureService,
|
||||
IPriceFormatter priceFormatter,
|
||||
IProductAttributeFormatter productAttributeFormatter,
|
||||
IProductAttributeParser productAttributeParser,
|
||||
IProductAttributeService productAttributeService,
|
||||
IProductService productService,
|
||||
IProductTagService productTagService,
|
||||
IProductTemplateService productTemplateService,
|
||||
ISettingModelFactory settingModelFactory,
|
||||
ISettingService settingService,
|
||||
IShipmentService shipmentService,
|
||||
IShippingService shippingService,
|
||||
IShoppingCartService shoppingCartService,
|
||||
ISpecificationAttributeService specificationAttributeService,
|
||||
IStoreMappingSupportedModelFactory storeMappingSupportedModelFactory,
|
||||
IStoreContext storeContext,
|
||||
IStoreService storeService,
|
||||
IUrlRecordService urlRecordService,
|
||||
IVideoService videoService,
|
||||
IWorkContext workContext,
|
||||
MeasureSettings measureSettings,
|
||||
NopHttpClient nopHttpClient,
|
||||
TaxSettings taxSettings,
|
||||
VendorSettings vendorSettings,
|
||||
IGenericAttributeService genericAttributeService)
|
||||
: base(
|
||||
catalogSettings,
|
||||
currencySettings,
|
||||
addressService,
|
||||
baseAdminModelFactory,
|
||||
categoryService,
|
||||
currencyService,
|
||||
customerService,
|
||||
dateTimeHelper,
|
||||
discountService,
|
||||
discountSupportedModelFactory,
|
||||
localizationService,
|
||||
localizedModelFactory,
|
||||
manufacturerService,
|
||||
measureService,
|
||||
orderService,
|
||||
pictureService,
|
||||
priceFormatter,
|
||||
productAttributeFormatter,
|
||||
productAttributeParser,
|
||||
productAttributeService,
|
||||
productService,
|
||||
productTagService,
|
||||
productTemplateService,
|
||||
settingModelFactory,
|
||||
settingService,
|
||||
shipmentService,
|
||||
shippingService,
|
||||
shoppingCartService,
|
||||
specificationAttributeService,
|
||||
storeMappingSupportedModelFactory,
|
||||
storeContext,
|
||||
storeService,
|
||||
urlRecordService,
|
||||
videoService,
|
||||
workContext,
|
||||
measureSettings,
|
||||
nopHttpClient,
|
||||
taxSettings,
|
||||
vendorSettings)
|
||||
{
|
||||
GenericAttributeService = genericAttributeService;
|
||||
}
|
||||
|
||||
#endregion Cotr
|
||||
|
||||
public override Task<ProductSearchModel> PrepareProductSearchModelAsync(ProductSearchModel searchModel)
|
||||
=> base.PrepareProductSearchModelAsync(searchModel);
|
||||
|
||||
public override Task<ProductListModel> PrepareProductListModelAsync(ProductSearchModel searchModel)
|
||||
=> base.PrepareProductListModelAsync(searchModel);
|
||||
|
||||
public virtual async Task<TProductListModelExt> PrepareProductListModelExtendedAsync(ProductSearchModel searchModel, Func<ProductListModel, TProductModelExt, Task> dataItemCopiedCallback)
|
||||
{
|
||||
var productListModel = await PrepareProductListModelAsync(searchModel);
|
||||
var extendedRows = new List<TProductModelExt>(productListModel.RecordsFiltered);
|
||||
|
||||
foreach (var productModel in productListModel.Data.ToList())
|
||||
{
|
||||
var productModelExtended = Activator.CreateInstance<TProductModelExt>();
|
||||
|
||||
PropertyHelper.CopyPublicValueTypeProperties(productModel, productModelExtended);
|
||||
extendedRows.Add(productModelExtended);
|
||||
|
||||
if (dataItemCopiedCallback == null) continue;
|
||||
await dataItemCopiedCallback.Invoke(productListModel, productModelExtended);
|
||||
}
|
||||
|
||||
productListModel.Data = null;
|
||||
|
||||
var productListModelExtended = productListModel.ToJson().JsonTo<TProductListModelExt>();
|
||||
productListModelExtended.Data = extendedRows;
|
||||
|
||||
return productListModelExtended;
|
||||
}
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@ public class PluginNopStartup : INopStartup
|
|||
|
||||
services.AddScoped<FruitBankAttributeService>();
|
||||
|
||||
services.AddScoped<ProductDtoDbTable>();
|
||||
services.AddScoped<OrderDtoDbTable>();
|
||||
services.AddScoped<OrderItemDtoDbTable>();
|
||||
services.AddScoped<OrderItemPalletDbTable>();
|
||||
|
|
@ -86,6 +87,7 @@ public class PluginNopStartup : INopStartup
|
|||
//services.AddScoped<OrderSearchModel, OrderSearchModelExtended>();
|
||||
|
||||
services.AddScoped<IOrderModelFactory, CustomOrderModelFactory>();
|
||||
services.AddScoped<IProductModelFactory, CustomProductModelFactory>();
|
||||
services.AddScoped<IGenericAttributeService, GenericAttributeService>();
|
||||
services.AddScoped<CerebrasAPIService>();
|
||||
services.AddScoped<OpenAIApiService>();
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models;
|
||||
|
||||
public interface IOrderListModelExtended<T>: IPagedModel<T> where T : BaseNopModel
|
||||
{
|
||||
public bool? NeedsMeasurement { get; set; }
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase;
|
||||
|
||||
public interface IMgListModelExtended<T>: IPagedModel<T> where T : BaseNopModel, IMgModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase;
|
||||
|
||||
public interface IMgModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
|
||||
public interface IMgOrderListModelExtended<T>: IMgListModelExtended<T> where T : MgOrderModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
|
||||
public interface IMgOrderModelExtended : IMgModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
|
||||
public partial record MgOrderListModelExtended<TOrderModelExt> : BasePagedListModel<TOrderModelExt>, IMgOrderListModelExtended<TOrderModelExt>
|
||||
where TOrderModelExt: MgOrderModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Nop.Web.Areas.Admin.Models.Orders;
|
||||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
|
||||
public partial record MgOrderModelExtended : OrderModel, IMgOrderModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.ProductModels;
|
||||
|
||||
public interface IMgProductListModelExtended<T>: IMgListModelExtended<T> where T : MgProductModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.ProductModels;
|
||||
|
||||
public interface IMgProductModelExtended : IMgModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.ProductModels;
|
||||
|
||||
public partial record MgProductListModelExtended<TProductModelExt> : BasePagedListModel<TProductModelExt>, IMgProductListModelExtended<TProductModelExt>
|
||||
where TProductModelExt: MgProductModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Nop.Web.Areas.Admin.Models.Catalog;
|
||||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.ProductModels;
|
||||
|
||||
public partial record MgProductModelExtended : ProductModel, IMgProductModelExtended
|
||||
{
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
using Nop.Web.Areas.Admin.Models.Orders;
|
||||
using Nop.Web.Framework.Models;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models
|
||||
{
|
||||
public partial record OrderListModelExtended : BasePagedListModel<OrderModelExtended>, IOrderListModelExtended<OrderModelExtended>
|
||||
{
|
||||
public bool? NeedsMeasurement { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
using Nop.Web.Areas.Admin.Models.Orders;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models
|
||||
{
|
||||
public partial record OrderModelExtended : OrderModel
|
||||
{
|
||||
public bool IsMeasurable { get; set; }
|
||||
public DateTime? DateOfReceipt { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
using Nop.Web.Areas.Admin.Models.Orders;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models
|
||||
{
|
||||
public partial record OrderSearchModelExtended : OrderSearchModel
|
||||
{
|
||||
public bool? NeedsMeasurement { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
|
||||
public interface IOrderListModelExtended: IMgOrderListModelExtended<OrderModelExtended>
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using FruitBank.Common.Interfaces;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
|
||||
public interface IOrderModelExtended : IMgOrderModelExtended, IMeasurable, IMeasured
|
||||
{
|
||||
public DateTime? DateOfReceipt { get; set; }
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
using Nop.Web.Framework.Models;
|
||||
using Nop.Web.Framework.Mvc.ModelBinding;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
|
||||
{
|
||||
public record OrderAttributesModel : BaseNopModel, IMeasurable
|
||||
{
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
using Nop.Web.Areas.Admin.Models.Orders;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
|
||||
{
|
||||
public partial record OrderListModelExtended : MgOrderListModelExtended<OrderModelExtended>, IOrderListModelExtended
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
|
||||
{
|
||||
public partial record OrderModelExtended : MgOrderModelExtended, IOrderModelExtended
|
||||
{
|
||||
public bool IsMeasured { get; set; }
|
||||
public bool IsMeasurable { get; set; }
|
||||
public DateTime? DateOfReceipt { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4,7 +4,7 @@ using Nop.Web.Framework.Mvc.ModelBinding;
|
|||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models
|
||||
{
|
||||
public record ProductAttributesModel : BaseNopModel, IMeasurable, IMeasuringWeights, ITare
|
||||
public record ProductAttributesModel : BaseNopModel, IMeasurable, IMeasuringWeights, ITare, IIncomingQuantity
|
||||
{
|
||||
public int ProductId { get; set; }
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Models
|
|||
public double GrossWeight { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Plugins.YourCompany.ProductAttributes.Fields.IncomingQuantity")]
|
||||
public int? IncomingQuantity { get; set; }
|
||||
public int IncomingQuantity { get; set; }
|
||||
|
||||
[NopResourceDisplayName("Plugins.YourCompany.ProductAttributes.Fields.Tare")]
|
||||
public double Tare { get; set; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.ProductModels;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Products;
|
||||
|
||||
public interface IProductListModelExtended: IMgProductListModelExtended<ProductModelExtended>
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using FruitBank.Common.Interfaces;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.ProductModels;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Products;
|
||||
|
||||
public interface IProductModelExtended : IMgProductModelExtended, IMeasurable, ITare, IMeasuringNetWeight, IIncomingQuantity
|
||||
{
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.ProductModels;
|
||||
using Nop.Web.Areas.Admin.Models.Orders;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Products
|
||||
{
|
||||
public partial record ProductListModelExtended : MgProductListModelExtended<ProductModelExtended>, IProductListModelExtended
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.OrderModels;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.MgBase.ProductModels;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Products
|
||||
{
|
||||
public partial record ProductModelExtended : MgProductModelExtended, IProductModelExtended
|
||||
{
|
||||
public bool IsMeasurable { get; set; }
|
||||
|
||||
public double Tare { get; set; }
|
||||
public double NetWeight { get; set; }
|
||||
public int IncomingQuantity { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3,7 +3,7 @@ using FruitBank.Common.Interfaces;
|
|||
using Nop.Core;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Services.Events;
|
||||
|
|
@ -114,7 +114,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
|||
var store = await _storeContext.GetCurrentStoreAsync();
|
||||
// itt adjuk hozzá a GenericAttribute flag-et az orderhez
|
||||
await _genericAttributeService.SaveAttributeAsync(order,
|
||||
nameof(OrderModelExtended.IsMeasurable), true, store.Id);
|
||||
nameof(IMeasurable.IsMeasurable), true, store.Id);
|
||||
// status pending
|
||||
// paymentstatus pending
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models;
|
||||
using FruitBank.Common.Interfaces;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
using Nop.Services.Common;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||
|
|
@ -24,7 +25,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
|||
return false;
|
||||
|
||||
return await _genericAttributeService.GetAttributeAsync<bool>(
|
||||
order, nameof(OrderModelExtended.IsMeasurable), order.StoreId);
|
||||
order, nameof(IMeasurable.IsMeasurable), order.StoreId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
@model Nop.Plugin.Misc.FruitBankPlugin.Models.OrderAttributesModel
|
||||
@using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
|
||||
@model OrderAttributesModel
|
||||
|
||||
<div class="card card-default">
|
||||
<div class="card-header">
|
||||
|
|
|
|||
Loading…
Reference in New Issue