Nop4.40.3
Installare Nop 4.40.3
In questa guida saranno evidenziati in rosso tutti i file del core di Nop che verranno toccati.
Operazioni sul DB
- Creare un dump del database di produzione (Nop 4.3)
- Ripristinarlo
- Per ripristinare l'utente wmuser è necessario disassociarlo dal catalogo fulltext
- Da SQL Management Aprire
Storage -> Fulltext Catalogs -> nopCommerceFullTextCatalog, nella tab General impostare come Owner => dbo - Eliminare l'utente wmuser
- Ri-mappare l'utente wmuser sul database nuovo
- Aprire di nuovo
Storage -> Fulltext Catalogs -> nopCommerceFullTextCatalog, nella tab General inserire come Owner => wmuser - Aprire di nuovo
Storage -> Fulltext Catalogs -> nopCommerceFullTextCatalog, nella tab Tables/Views, pannello Eligible columns impostare italian per tutti i campi
Configurazione della Solution
- Aggiornare i file
Presentation\Nop.Web\App_Data\dataSettings.jsonePresentation\Nop.Web\App_Data\appsettings.json - Copiare i plugin di terze parti nella cartella
Plugins\Nop.Plugin.Compiled - Aggiungere il seguente codice nei build events => post build event progetto Nop.Web per copiare i plugin compilati (7spikes & co) nel corretto folder durante il build.
xcopy /y /E $(SolutionDir)Plugins\Nop.Plugin.Compiled $(SolutionDir)Presentation\Nop.Web\Plugins
- Creare dal Configuration Manager una nuova Solution Configuration chiamata ReleaseTest
- Tasto destro sul web.config e cliccare su Add Configs Transform per creare i web.Release.config e web.ReleaseTest.config
- Nel web.config aggiungere la variabile d'ambiente ASPNETCORE_ENVIRONMENT con valore Development sotto la sezione aspNetCore
<environmentVariables> <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" /> </environmentVariables>
- Nel web.ReleaseTest.config mettere il valore Testing
<environmentVariables> <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Testing" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" /> </environmentVariables>
- nel web.Release.config mettere il valore Production
<environmentVariables> <environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Production" xdt:Transform="SetAttributes" xdt:Locator="Match(name)" /> </environmentVariables>
- Nel web.config aggiungere la variabile d'ambiente ASPNETCORE_ENVIRONMENT con valore Development sotto la sezione aspNetCore
- Dalle proprietà del progetto Nop.Web andare sulla tab Build, selezionare la configurazione Release e aggiungere il simbolo condizionale OFFICIAL
- Configuriamo l'applicazione in modo che cambi file di configurazione a seconda della Solution Configuration selezionata
- In
Libraries\Nop.Data\NopDataSettingsDefaults.cscambiare l'assegnamento di FilePath con quanto seguepublic static string FilePath => #if DEBUG "~/App_Data/dataSettings.development.json"; #elif !OFFICIAL "~/App_Data/dataSettings.testing.json"; #else "~/App_Data/dataSettings.json"; #endif
- In
Libraries\Nop.Services\Configuration\NopConfigurationDefaults.cscambiare l'assegnamento di AppSettingsFilePath con quanto seguepublic static string AppSettingsFilePath => #if DEBUG "App_Data/appsettings.development.json"; #elif !OFFICIAL "App_Data/appsettings.testing.json"; #else "App_Data/appsettings.json"; #endif
- In
- Addare alla Solution (cartella Libraries) i progetti GenericUtilites, WM4Search, WM_Core e USO
- Runnando la Solution dovrebbe partire con tutti i plugin disabilitati. Una volta entrati in admin è possibile vederli dall'elenco e installarli.
- Non ci saranno tutti i plugin, in tal caso cliccare su ricaricare la lista di plugin, la soluzione si chiuderà da sola, ma al successivo login la lista dovrebbe essere aggiornata.
- Aggiornare tutti i pacchetti nuGet della Solution che abbiano un update minor (dalla seconda cifra in poi)
- Creazione di IWMCoreModelFactory per le chiamate alla WMCore.
- INTERFACCIA: creare
Presentation\Nop.Web\Factories\IWMCoreModelFactory.cscosìusing GPC.WM_Core; namespace Nop.Web.Factories { public interface IWMCoreModelFactory { WMRetriever WMCore { get; } } }
- IMPLEMENTAZIONE: creare
Presentation\Nop.Web\Factories\IWMCoreModelFactory.cscosìusing GPC.WM_Core; using Microsoft.Extensions.Configuration; namespace Nop.Web.Factories { public class WMCoreModelFactory : IWMCoreModelFactory { public WMRetriever WMCore { get; } = null; public WMCoreModelFactory(IConfiguration configuration) { WMCore = new WMRetriever( configuration.GetSection("WMConnectionStrings")["Designbest"], configuration.GetSection("WMConnectionStrings")["Designbest"], configuration.GetSection("WMConnectionStrings")["Trovaprodotti"], configuration.GetSection("WMApplicationSettings")["ResourcesPath"], "Test_WM47"); } } }
- BINDING: comunicare la sua esistenza al framework modificando
Presentation\Nop.Web\Startup.csaggiungendo una riga a ConfigureServices() cosìpublic void ConfigureServices(IServiceCollection services) { // aggiunta di IWMCoreModelFactory services.AddSingleton<IWMCoreModelFactory, WMCoreModelFactory>(); services.ConfigureApplicationServices(_configuration, _webHostEnvironment); }
- INTERFACCIA: creare
- Inserimento di UsoUtilitiesController uguale a quello precedente con la differenza che le chiamate a services sono diventate tutte ASYNC e perciò il trucco è aggiungere al fondo
.Resultper le variabili o.Wait()per i metodi come ad esempioCustomer customer = _customerService.GetCustomerByGuidAsync(customerGuid).Result; _authenticationService.SignInAsync(customer, true).Wait();
Prodotto
- Aggiunta metodi frmt() di utilità nelle viste per restituire decimali in
Nop.Web\Extensions\HtmlExtensions.cs:
/// Restituisce (come stringa) il numero decimale specificato inserendo il punto
/// ogni tre cifre. I numeri decimali sono limitati a 2
/// </summary>
/// <param name="val">Un decimal</param>
/// <returns>Il numero nella forma mmm.kkk.uuu,dd</returns>
public static string frmt(this decimal val) {
var ci = new CultureInfo("it-IT");
ci.NumberFormat.NumberDecimalDigits = 2;
return val.ToString("N", ci);
}
/// Restituisce (come stringa) il numero intero specificato inserendo il punto
/// ogni tre cifre.
/// </summary>
/// <param name="val">Un decimal</param>
/// <returns>Il numero nella forma mmm.kkk.uuu,dd</returns>
public static string frmt(this int val) {
var ci = new CultureInfo("it-IT");
ci.NumberFormat.NumberDecimalDigits = 2;
return val.ToString("N", ci);
}
- Nel modello del prodotto in
Nop.Web\Models\Catalog\ProductDetailsModel.csaggiungere:
#region WEBMOBILI
/// <summary>
/// Nella nostra struttura c'è sempre un solo manufacturer per prodotto. Questa property lo restituisce.
/// In caso di manufacturer non settato (errore di dati) restituisce un manu vuoto per evitare una NullPointerException
/// </summary>
public ManufacturerBriefInfoModel Manufacturer {
get {
if (ProductManufacturers.Count > 0) {
return ProductManufacturers[0];
}
return new ManufacturerBriefInfoModel { Name = "", SeName = "", IsActive = false };
}
}
public string ShopCity { get; set; }
public decimal AdditionalShippingCharge { get; set; }
#endregion
- ProductController: al momento dichiarata l'istanza dell'interfaccia IWMCoreModelFactory, ma non utilizzata, i campi necessari sono stati aggiunti direttamente nel modello del prodotto, aggiungiamo quindi solo questa istruzione nel metodo ProductDetails(int productId, int updatecartitemid = 0):
#region WEBMOBILI
model.AdditionalShippingCharge = product.AdditionalShippingCharge; // spese di spedizione da stampare in frontend
#endregion
- Nella vista del prodotto, copiare la vista del tema base
Nop.Web\Views\Product\_DeliveryInfo.csinNop.Web\Themes\Brooklyn\Views\Product\_DeliveryInfo.cse modificarla così:
@model ProductDetailsModel
@if (Model.FreeShippingNotificationEnabled && Model.IsFreeShipping@*|| !string.IsNullOrWhiteSpace(Model.DeliveryDate)*@) {
<div class="delivery">
<script asp-location="Footer">
$(document).on("product_attributes_changed", function (data) {
if (data.changedData.isFreeShipping) {
$("#free-shipping-" + data.changedData.productId).removeClass("invisible");
} else {
$("#free-shipping-" + data.changedData.productId).addClass("invisible");
}
});
</script>
@if (Model.FreeShippingNotificationEnabled && Model.IsFreeShipping) {
<div id="free-shipping-@Model.Id" class="free-shipping">@T("Products.FreeShipping")</div>
}
@if (!string.IsNullOrWhiteSpace(Model.DeliveryDate)) {
<div class="delivery-date">
<span class="label">@T("Products.DeliveryDate"):</span>
<span class="value">@Model.DeliveryDate</span>
</div>
}
</div>
}
else {
<div class="delivery">
@if (Model.IsShipEnabled) {
<div class="free-shipping">
<span class="label">SPEDIZIONE STANDARD ITALIA € @Model.AdditionalShippingCharge.frmt() <span class="testo-minuscolo">(cad.)</span></span>
</div>
@if (!string.IsNullOrWhiteSpace(Model.DeliveryDate)) {
<div class="delivery-date">
<span class="label">@T("Products.DeliveryDate"):</span>
<span class="value">@Model.DeliveryDate</span>
</div>
}
}
else {
<div class="free-shipping">
<span class="label">SPEDIZIONE PERSONALIZZATA A PREVENTIVO</span>
</div>
}
</div>
}
LATO ADMIN
Home - Vendor
- Nascondere il pulsante Visualizza nei best seller(un Vendor non deve entrare nella pagina di modifica del prodotto lato NOP), bisogna commentare l'ultima
ColumnPropertyin queste 2 viste:
Nop.Web\Areas\Admin\Views\Home\_BestsellersBriefReportByAmount.cshtml
Nop.Web\Areas\Admin\Views\Home\_BestsellersBriefReportByQuantity.cshtml
Vendite - Ordini del Cliente
- Nascondere il filtro Magazzino (aggiungere la classe d-none), altrimenti un vendor potrebbe vedere le vendite degli altri vendor, vista da modificare
Nop.Web\Areas\Admin\Views\Order\List.cshtml.
<div class="form-group row d-none" @(Model.AvailableWarehouses.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
<div class="col-md-4">
<nop-label asp-for="WarehouseId" />
</div>
<div class="col-md-8">
<nop-select asp-for="WarehouseId" asp-items="Model.AvailableWarehouses" />
</div>
</div>
Ordine - dettaglio
- Modificare il modello
Nop.Web\Areas\Admin\Models\Orders\OrderItemModel.csaggiungendopublic string ProductSeoName { get; set; }
- Factory
Nop.Web\Areas\Admin\Factories\OrderModelFactory.csvalorizzare il campo ProductSeoName usando la WMCore, nel metodoPrepareOrderItemModelsAsync
ProductSeoName = _wmcore.WMCore.GetNopProductSeoName(orderItem.ProductId),
- Nel box dove sono elencati i prodotti, il link del prodotti rimanderà alla scheda del prodotto nel frontend, mentre solo se siamo Admin entreremo nell'edit del prodotto di NOP
File:Esempio.jpg
modificare in Nop.Web\Areas\Admin\Views\Order\_OrderDetails.Products.cshtml
- Dobbiamo recuperare il VendorID del dropship di Webmobili:
@inject Nop.Services.Catalog.IProductService prodService;
@inject Microsoft.Extensions.Configuration.IConfiguration configuration;
@{
int vendorWMDropship = 0;
int.TryParse(configuration.GetSection("WMApplicationSettings")["DropshipVendorID"], out vendorWMDropship);
}
- Controllo se l'utente è un Vendor o Admin
<em>
@if (!Model.IsLoggedInAsVendor) {
<a asp-controller="Product" asp-action="Edit" asp-route-id="@item.ProductId">@item.ProductName</a>
}else {
<a href="@Url.RouteUrl("Product", new { SeName = item.ProductSeoName })" target="_blank">@item.ProductName</a>
}</em>
- Se il vendorID è quello di Webmobili-dropship mostro il pulsante CLONA e ASSEGNA a RIVENDITORE.
Attenzione il metodo è cambiato da prodService.GetProductById(item.ProductId).VendorId a GetProductByIdAsync:
@{
var productById = await prodService.GetProductByIdAsync(item.ProductId);
}
@if (productById != null) {
if (productById.VendorId == vendorWMDropship) {
<button type="button" id="setProductToVendor" name="setProductToVendor" class="btn bg-olive" onclick="apriModaleAssegnazione(@item.ProductId,@item.Id,'@item.ProductName','@item.PictureThumbnailUrl');" data-toggle="modal" data-target="#setProductToVendor-window"><i class="fa fa-clone"></i>
CLONA e ASSEGNA a RIVENDITORE</button>
}
}
- Il pulsante CLONA e ASSEGNA a RIVENDITORE apre un modale in cui selezionare il rivenditore, innanzitutto dobbiamo creare la vista/componente del modale ProductToVendorModal.
Creiamo il componente in Nop.Web\Areas\Admin\Components\ProductToVendorModal.cs così:
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Nop.Web.Framework.Components;
using Microsoft.Extensions.Configuration;
using Nop.Services.Vendors;
namespace Nop.Web.Areas.Admin.Components {
public class ProductToVendorModal : NopViewComponent {
private readonly IVendorService _vendorService;
private readonly IConfiguration _configuration;
public ProductToVendorModal(IVendorService vendorService, IConfiguration configuration) {
_vendorService = vendorService;
_configuration = configuration;
}
public IViewComponentResult Invoke() {
int dropshippingVendorId = 0;
int.TryParse(_configuration.GetSection("WMApplicationSettings")["DropshipVendorID"], out dropshippingVendorId);
var vendorList = _vendorService.GetAllVendorsAsync().Result
.Where(c => c.Id != dropshippingVendorId)
.OrderBy(o => o.Name).ToList();
return View(vendorList);
}
}
}
Creiamo la cartella e la vista del componente in Nop.Web\Areas\Admin\Views\Shared\Components\ProductToVendorModal\Default.cshtml così:
@model List<Nop.Core.Domain.Vendors.Vendor>
<div id="setProductToVendor-window" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="setProductToVendor-window-title">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="copyproduct-window-title">Seleziona il rivenditore a cui assegnare il prodotto venduto</h4>
</div>
<form asp-controller="Order" asp-action="CopyAndSetProductWMtoVendor" method="post">
<div class="form-horizontal">
<div class="modal-body">
<input type="hidden" id="hdnProductId" name="productId" value="" />
<input type="hidden" id="hdnOrderItemId" name="orderItemId" value="" />
<div class="media">
<a class="pull-left" href="#">
<img id="modalProductToAssignThumb" class="media-object" src="">
</a>
<div class="media-body">
<h4 id="modalProductToAssign" class="media-heading">...</h4>
<p></p>
</div>
</div>
<hr />
<div class="form-group">
<div class="col-md-8 mx-auto">
<label>Seleziona il rivenditore:</label>
<select name="vendorId" class="form-control">
@foreach (var vendor in Model) {
<option value="@vendor.Id">@vendor.Name</option>
}
</select>
</div>
</div>
<hr />
<div>
<span>Il prodotto verrà:</span>
<ul>
<li>clonato;</li>
<li>assegnato al rivenditore selezionato;</li>
<li>segnato come non pubblicato, ma sarà cmq visibile dal pannello di gestione del Rivenditore;</li>
<li>segnato con quantita' di pezzi a 0 (al prodotto clonato, non all'originale).</li>
</ul>
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">
CLONA E ASSEGNA IL PRODOTTO
</button>
</div>
</div>
</form>
</div>
</div>
</div>
Torniamo nella pagina contenitore di dettaglio dell'ordine in Nop.Web\Areas\Admin\Views\Order\Edit.cshtml e inseriamo al fondo la chiamata al nostro component modale appena creato con questa riga di codice:
@*Modal product to Vendor*@
@await Component.InvokeAsync("ProductToVendorModal")