Keresés partnerre a rendelések között, Felhasználó létrehozásakor automatikus számlázási cím hozzáadás, adószám szinkronizálás, egyéb kisebb fixek

This commit is contained in:
Adam 2025-10-28 03:26:19 +01:00
parent 9a94bc6c06
commit 30c8cd3e00
10 changed files with 390 additions and 93 deletions

View File

@ -17,6 +17,7 @@ using Nop.Core.Domain.Payments;
using Nop.Core.Domain.Shipping; using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Tax; using Nop.Core.Domain.Tax;
using Nop.Core.Events; using Nop.Core.Events;
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.Order;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Factories; using Nop.Plugin.Misc.FruitBankPlugin.Factories;
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders; using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
@ -157,7 +158,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
public virtual async Task<IActionResult> List(List<int> orderStatuses = null, List<int> paymentStatuses = null, List<int> shippingStatuses = null) public virtual async Task<IActionResult> List(List<int> orderStatuses = null, List<int> paymentStatuses = null, List<int> shippingStatuses = null)
{ {
//prepare model //prepare model
var model = await _orderModelFactory.PrepareOrderSearchModelAsync(new OrderSearchModel var model = await _orderModelFactory.PrepareOrderSearchModelAsync(new OrderSearchModelExtended
{ {
OrderStatusIds = orderStatuses, OrderStatusIds = orderStatuses,
PaymentStatusIds = paymentStatuses, PaymentStatusIds = paymentStatuses,
@ -169,7 +170,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
[HttpPost] [HttpPost]
[CheckPermission(StandardPermission.Orders.ORDERS_VIEW)] [CheckPermission(StandardPermission.Orders.ORDERS_VIEW)]
public async Task<IActionResult> OrderList(OrderSearchModel searchModel) public async Task<IActionResult> OrderList(OrderSearchModelExtended searchModel)
{ {
//prepare model //prepare model
var orderListModel = await GetOrderListModelByFilter(searchModel); var orderListModel = await GetOrderListModelByFilter(searchModel);
@ -213,7 +214,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/Edit.cshtml", model); return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Order/Edit.cshtml", model);
} }
public async Task<OrderListModelExtended> GetOrderListModelByFilter(OrderSearchModel searchModel) public async Task<OrderListModelExtended> GetOrderListModelByFilter(OrderSearchModelExtended searchModel)
{ {
//return _customOrderService. //return _customOrderService.
var orderListModel = await _orderModelFactory.PrepareOrderListModelExtendedAsync(searchModel); var orderListModel = await _orderModelFactory.PrepareOrderListModelExtendedAsync(searchModel);
@ -736,7 +737,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
[HttpPost, ActionName("ExportXml")] [HttpPost, ActionName("ExportXml")]
[FormValueRequired("exportxml-all")] [FormValueRequired("exportxml-all")]
[CheckPermission(StandardPermission.Orders.ORDERS_IMPORT_EXPORT)] [CheckPermission(StandardPermission.Orders.ORDERS_IMPORT_EXPORT)]
public virtual async Task<IActionResult> ExportXmlAll(OrderSearchModel model) public virtual async Task<IActionResult> ExportXmlAll(OrderSearchModelExtended model)
{ {
var startDateValue = model.StartDate == null ? null var startDateValue = model.StartDate == null ? null
: (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, await _dateTimeHelper.GetCurrentTimeZoneAsync()); : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, await _dateTimeHelper.GetCurrentTimeZoneAsync());
@ -832,7 +833,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
[HttpPost, ActionName("ExportExcel")] [HttpPost, ActionName("ExportExcel")]
[FormValueRequired("exportexcel-all")] [FormValueRequired("exportexcel-all")]
[CheckPermission(StandardPermission.Orders.ORDERS_IMPORT_EXPORT)] [CheckPermission(StandardPermission.Orders.ORDERS_IMPORT_EXPORT)]
public virtual async Task<IActionResult> ExportExcelAll(OrderSearchModel model) public virtual async Task<IActionResult> ExportExcelAll(OrderSearchModelExtended model)
{ {
var startDateValue = model.StartDate == null ? null var startDateValue = model.StartDate == null ? null
: (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, await _dateTimeHelper.GetCurrentTimeZoneAsync()); : (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, await _dateTimeHelper.GetCurrentTimeZoneAsync());

View File

@ -63,125 +63,137 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
{ {
try try
{ {
// Validate and get order
var order = await _orderService.GetOrderByIdAsync(orderId); var order = await _orderService.GetOrderByIdAsync(orderId);
if (order == null) if (order == null)
{
return Json(new { success = false, message = "Order not found" }); return Json(new { success = false, message = "Order not found" });
}
// Validate and get customer
var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId); var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId);
if (customer == null)
{
return Json(new { success = false, message = "Customer not found" });
}
// Get billing address // Get and validate billing address
var billingAddress = await _customerService.GetCustomerBillingAddressAsync(customer); var billingAddress = await _customerService.GetCustomerBillingAddressAsync(customer);
if (billingAddress == null) if (billingAddress == null)
{
return Json(new { success = false, message = "Billing address not found" }); return Json(new { success = false, message = "Billing address not found" });
}
var billingCountry = await _countryService.GetCountryByAddressAsync(billingAddress); var billingCountry = await _countryService.GetCountryByAddressAsync(billingAddress);
var billingCountryCode = billingCountry?.TwoLetterIsoCode ?? "HU"; var billingCountryCode = billingCountry?.TwoLetterIsoCode ?? "HU";
// Get shipping address // Get shipping address (fallback to billing if not found)
var shippingAddress = await _customerService.GetCustomerShippingAddressAsync(customer); var shippingAddress = await _customerService.GetCustomerShippingAddressAsync(customer) ?? billingAddress;
var shippingCountry = await _countryService.GetCountryByAddressAsync(shippingAddress);
if (shippingAddress == null)
{
shippingAddress = billingAddress;
}
var shippingCountry = shippingAddress != null
? await _countryService.GetCountryByAddressAsync(shippingAddress)
: null;
var shippingCountryCode = shippingCountry?.TwoLetterIsoCode ?? billingCountryCode; var shippingCountryCode = shippingCountry?.TwoLetterIsoCode ?? billingCountryCode;
// Create order request // Create order request
var orderRequest = new OrderCreateRequest var orderRequest = new OrderCreateRequest
{ {
VevoNev = $"{billingAddress.FirstName} {billingAddress.LastName}", VevoNev = customer.Company ?? string.Empty,
VevoIrsz = billingAddress.ZipPostalCode ?? "", VevoIrsz = billingAddress.ZipPostalCode ?? string.Empty,
VevoTelep = billingAddress.City ?? "", VevoTelep = billingAddress.City ?? string.Empty,
VevoUtcaHsz = $"{billingAddress.Address1} {billingAddress.Address2}".Trim(), VevoUtcaHsz = $"{billingAddress.Address1} {billingAddress.Address2}".Trim(),
VevoOrszag = billingCountryCode, VevoOrszag = billingCountryCode,
VevoAdoszam = billingAddress.Company, // Or a custom field for tax number VevoAdoszam = customer.VatNumber,
MegrendelestombID = 1, // Configure this based on your setup MegrendelestombID = 1,
MegrendelesKelte = order.CreatedOnUtc.ToLocalTime(), MegrendelesKelte = order.CreatedOnUtc.ToLocalTime(),
Hatarido = DateTime.Now.AddDays(7), // 7 days delivery time Hatarido = DateTime.Now.AddDays(7),
Devizanem = "Ft", //TODO get real deault - A. Devizanem = "Ft", // TODO: get real default - A.
FizetesiMod = order.PaymentMethodSystemName ?? "átutalás", FizetesiMod = order.PaymentMethodSystemName ?? "átutalás",
Email = billingAddress.Email, Email = customer.Email ?? string.Empty,
Telefon = billingAddress.PhoneNumber, Telefon = billingAddress.PhoneNumber ?? string.Empty,
MegrendelesSzamStr = order.Id.ToString() MegrendelesSzamStr = order.Id.ToString()
}; };
// Add shipping address if different // Add shipping address details
if (shippingAddress != null) orderRequest.SzallNev = $"{shippingAddress.FirstName} {shippingAddress.LastName}".Trim();
{ orderRequest.SzallIrsz = shippingAddress.ZipPostalCode ?? string.Empty;
orderRequest.SzallNev = $"{shippingAddress.FirstName} {shippingAddress.LastName}"; orderRequest.SzallTelep = shippingAddress.City ?? string.Empty;
orderRequest.SzallIrsz = shippingAddress.ZipPostalCode ?? "";
orderRequest.SzallTelep = shippingAddress.City ?? "";
orderRequest.SzallUtcaHsz = $"{shippingAddress.Address1} {shippingAddress.Address2}".Trim(); orderRequest.SzallUtcaHsz = $"{shippingAddress.Address1} {shippingAddress.Address2}".Trim();
orderRequest.SzallOrszag = shippingCountryCode; orderRequest.SzallOrszag = shippingCountryCode;
// Get and validate order items
var orderItems = await _dbContext.OrderItemDtos.GetAllByOrderId(order.Id, true).ToListAsync();
if (orderItems == null || orderItems.Count == 0)
{
return Json(new { success = false, message = "Order has no items" });
} }
// Add order items // Add order items
//var orderItems = await _orderService.GetOrderItemsAsync(order.Id);
var orderItems = await _dbContext.OrderItemDtos.GetAllByOrderId(order.Id, true).ToListAsync();
Console.WriteLine($"Order Items Count: {orderItems.Count}");
foreach (var item in orderItems) foreach (var item in orderItems)
{ {
//var productDTO = await _productService.GetProductByIdAsync(item.ProductId);
var product = _dbContext.ProductDtos.GetById(item.ProductId); var product = _dbContext.ProductDtos.GetById(item.ProductId);
//string unit = product != null && product.IsMeasurable ? "kg" : "kt"; var productName = product?.Name ?? "Product";
if(item.IsMeasurable) { // in case of measurable products, quantity is in fact weight stored in item.EnteredQuantity
if (item.IsMeasurable)
{
// For measurable products, quantity is weight stored in NetWeight
orderRequest.AddItem(new InnVoiceOrderItem orderRequest.AddItem(new InnVoiceOrderItem
{ {
TetelNev = product?.Name ?? "Product", TetelNev = productName,
AfaSzoveg = "27%", // Configure VAT rate as needed AfaSzoveg = "27%", // TODO: Configure VAT rate dynamically
Brutto = true, Brutto = true,
EgysegAr = item.UnitPriceInclTax, EgysegAr = item.UnitPriceInclTax,
Mennyiseg = Convert.ToDecimal(item.NetWeight), Mennyiseg = item.NetWeight > 0 ? Convert.ToDecimal(item.NetWeight) : 0,
MennyisegEgyseg = "kg", MennyisegEgyseg = "kg",
CikkSzam = "" CikkSzam = string.Empty
}); });
} }
else else
{ {
orderRequest.AddItem(new InnVoiceOrderItem orderRequest.AddItem(new InnVoiceOrderItem
{ {
TetelNev = product?.Name ?? "Product", TetelNev = productName,
AfaSzoveg = "27%", // Configure VAT rate as needed AfaSzoveg = "27%", // TODO: Configure VAT rate dynamically
Brutto = true, Brutto = true,
EgysegAr = item.UnitPriceInclTax, EgysegAr = item.UnitPriceInclTax,
Mennyiseg = item.Quantity, Mennyiseg = item.Quantity,
MennyisegEgyseg = "kt", MennyisegEgyseg = "kt",
CikkSzam = "" CikkSzam = string.Empty
}); });
} }
} }
// Create order via API // Create order via API
var response = await _innVoiceOrderService.CreateOrderAsync(orderRequest); var response = await _innVoiceOrderService.CreateOrderAsync(orderRequest);
if (response == null)
{
return Json(new { success = false, message = "No response from InnVoice API" });
}
if (response.IsSuccess) if (response.IsSuccess)
{ {
// Save the TechId, TableId, and PrintUrl to the order for future reference var currentStore = await _storeContext.GetCurrentStoreAsync();
var storeId = currentStore?.Id ?? 0;
// Save InnVoice order details as attributes
await _genericAttributeService.SaveAttributeAsync( await _genericAttributeService.SaveAttributeAsync(
order, order,
"InnVoiceOrderTechId", "InnVoiceOrderTechId",
response.TechId, response.TechId,
(await _storeContext.GetCurrentStoreAsync()).Id storeId
); );
await _genericAttributeService.SaveAttributeAsync( await _genericAttributeService.SaveAttributeAsync(
order, order,
"InnVoiceOrderTableId", "InnVoiceOrderTableId",
response.TableId?.ToString(), response.TableId?.ToString() ?? string.Empty,
(await _storeContext.GetCurrentStoreAsync()).Id storeId
); );
await _genericAttributeService.SaveAttributeAsync( await _genericAttributeService.SaveAttributeAsync(
order, order,
"InnVoiceOrderPrintLink", "InnVoiceOrderPrintLink",
response.PrintUrl?.ToString(), response.PrintUrl?.ToString() ?? string.Empty,
(await _storeContext.GetCurrentStoreAsync()).Id storeId
); );
return Json(new return Json(new
@ -196,17 +208,18 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
} }
}); });
} }
else
{
return Json(new return Json(new
{ {
success = false, success = false,
message = $"InnVoice API Error: {response.Message}" message = $"InnVoice API Error: {response.Message ?? "Unknown error"}"
}); });
} }
}
catch (Exception ex) catch (Exception ex)
{ {
// Log the exception here
// _logger.LogError(ex, "Error creating order {OrderId} in InnVoice", orderId);
return Json(new return Json(new
{ {
success = false, success = false,

View File

@ -0,0 +1,21 @@
using Microsoft.AspNetCore.Mvc.Rendering;
using Nop.Web.Areas.Admin.Models.Orders;
using Nop.Web.Framework.Mvc.ModelBinding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.Order
{
public partial record OrderSearchModelExtended : OrderSearchModel
{
[NopResourceDisplayName("Admin.Orders.List.BillingCompany")]
public string BillingCompany { get; set; }
//public IList<SelectListItem> AvailableCompanies { get; set; }
}
}

View File

@ -1,7 +1,9 @@
@model OrderSearchModel @model OrderSearchModelExtended
@inject IStoreService storeService @inject IStoreService storeService
@using FruitBank.Common.Interfaces @using FruitBank.Common.Interfaces
@using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.Order
@using Nop.Plugin.Misc.FruitBankPlugin.Models @using Nop.Plugin.Misc.FruitBankPlugin.Models
@using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders @using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
@using Nop.Services.Stores @using Nop.Services.Stores
@ -96,7 +98,7 @@
</li> </li>
</ul> </ul>
</div> </div>
@await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.OrderListButtons, additionalData = Model }) @await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.OrderListButtons, additionalData = (OrderSearchModel)Model })
</div> </div>
</div> </div>
@ -174,6 +176,45 @@
</script> </script>
</div> </div>
</div> </div>
<div class="form-group row">
<div class="col-md-4">
<nop-label asp-for="BillingCompany" />
</div>
<div class="col-md-8">
<input type="text" id="search-company-name" autocomplete="off" class="form-control" />
<span id="search-company-friendly-name"></span>
<button type="button" id="search-company-clear" class="btn bg-gray" style="display: none; margin-top: 5px;">@T("Admin.Common.Clear")</button>
<input asp-for="BillingCompany" autocomplete="off" style="display: none;" />
<script>
$(function() {
$('#search-company-name').autocomplete({
delay: 500,
minLength: 3,
source: '@Url.Action("CustomerSearchAutoComplete", "CustomOrder")',
select: function(event, ui) {
$('#@Html.IdFor(model => model.BillingCompany)').val(ui.item.value);
$('#search-company-friendly-name').text(ui.item.label);
$('#search-company-clear').show();
return false;
}
});
//remove button
$('#search-company-clear').click(function() {
$('#@Html.IdFor(model => model.BillingCompany)').val('');
$('#search-company-friendly-name').text('');
$('#search-company-clear').hide();
return false;
});
});
</script>
</div>
</div>
<div class="form-group row" @(Model.IsLoggedInAsVendor? Html.Raw("style='display: none;'") : null)> <div class="form-group row" @(Model.IsLoggedInAsVendor? Html.Raw("style='display: none;'") : null)>
<div class="col-md-4"> <div class="col-md-4">
<nop-label asp-for="OrderStatusIds" /> <nop-label asp-for="OrderStatusIds" />
@ -326,6 +367,7 @@
new FilterParameter(nameof(Model.BillingEmail)), new FilterParameter(nameof(Model.BillingEmail)),
new FilterParameter(nameof(Model.BillingPhone)), new FilterParameter(nameof(Model.BillingPhone)),
new FilterParameter(nameof(Model.BillingLastName)), new FilterParameter(nameof(Model.BillingLastName)),
new FilterParameter(nameof(Model.BillingCompany)),
new FilterParameter(nameof(Model.BillingCountryId)), new FilterParameter(nameof(Model.BillingCountryId)),
new FilterParameter(nameof(Model.PaymentMethodSystemName)), new FilterParameter(nameof(Model.PaymentMethodSystemName)),
new FilterParameter(nameof(Model.ProductId)), new FilterParameter(nameof(Model.ProductId)),

View File

@ -61,6 +61,7 @@ public class FruitBankDbContext : MgDbContextBase,
public IRepository<Customer> Customers { get; set; } public IRepository<Customer> Customers { get; set; }
public IRepository<CustomerRole> CustomerRoles { get; set; } public IRepository<CustomerRole> CustomerRoles { get; set; }
public IRepository<CustomerCustomerRoleMapping> CustomerRoleMappings { get; set; } public IRepository<CustomerCustomerRoleMapping> CustomerRoleMappings { get; set; }
public IRepository<CustomerAddressMapping> CustomerAddressMappings { get; set; }
public FruitBankDbContext(INopDataProvider dataProvider, ILockService lockService, FruitBankAttributeService fruitBankAttributeService, IStoreContext storeContext, public FruitBankDbContext(INopDataProvider dataProvider, ILockService lockService, FruitBankAttributeService fruitBankAttributeService, IStoreContext storeContext,
PartnerDbTable partnerDbTable, ShippingDbTable shippingDbTable, ShippingDocumentDbTable shippingDocumentDbTable, ShippingItemDbTable shippingItemDbTable, PartnerDbTable partnerDbTable, ShippingDbTable shippingDbTable, ShippingDocumentDbTable shippingDocumentDbTable, ShippingItemDbTable shippingItemDbTable,
@ -72,6 +73,7 @@ public class FruitBankDbContext : MgDbContextBase,
IRepository<Product> productRepository, IRepository<Product> productRepository,
IRepository<Customer> customerRepository, IRepository<Customer> customerRepository,
IRepository<CustomerCustomerRoleMapping> customerCustomerRoleMappingRepository, IRepository<CustomerCustomerRoleMapping> customerCustomerRoleMappingRepository,
IRepository<CustomerAddressMapping> customerAddressMappingRepository,
IRepository<CustomerRole> customerRoleRepository,IEventPublisher eventPublisher, IRepository<CustomerRole> customerRoleRepository,IEventPublisher eventPublisher,
IEnumerable<IAcLogWriterBase> logWriters) : base(productRepository, orderRepository, orderItemRepository, dataProvider, lockService, new Logger<FruitBankDbContext>(logWriters.ToArray())) IEnumerable<IAcLogWriterBase> logWriters) : base(productRepository, orderRepository, orderItemRepository, dataProvider, lockService, new Logger<FruitBankDbContext>(logWriters.ToArray()))
{ {
@ -100,6 +102,7 @@ public class FruitBankDbContext : MgDbContextBase,
Customers = customerRepository; Customers = customerRepository;
CustomerRoles = customerRoleRepository; CustomerRoles = customerRoleRepository;
CustomerRoleMappings = customerCustomerRoleMappingRepository; CustomerRoleMappings = customerCustomerRoleMappingRepository;
CustomerAddressMappings = customerAddressMappingRepository;
} }
public IQueryable<Customer> GetCustomersBySystemRoleName(string systemRoleName) public IQueryable<Customer> GetCustomersBySystemRoleName(string systemRoleName)
@ -528,4 +531,15 @@ public class FruitBankDbContext : MgDbContextBase,
var list = await ShippingDocuments.GetAll(true).Where(sd => sd.ShippingId == shippingId).ToListAsync(); var list = await ShippingDocuments.GetAll(true).Where(sd => sd.ShippingId == shippingId).ToListAsync();
return list; return list;
} }
public async Task<CustomerAddressMapping?> AddCustomerAddressMappingAsync(int customerId, int addressId)
{
var customerAddressMapping = new CustomerAddressMapping
{
CustomerId = customerId,
AddressId = addressId
};
await CustomerAddressMappings.InsertAsync(customerAddressMapping);
return customerAddressMapping;
}
} }

View File

@ -39,6 +39,7 @@ using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase; using Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase;
using FruitBank.Common.Dtos; using FruitBank.Common.Dtos;
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders; using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.Order;
namespace Nop.Plugin.Misc.FruitBankPlugin.Factories namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
{ {
@ -149,7 +150,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
_orderMeasurementService = orderMeasurementService; _orderMeasurementService = orderMeasurementService;
} }
#endregion Ctor #endregion Ctor
public override async Task<OrderSearchModel> PrepareOrderSearchModelAsync(OrderSearchModel searchModel) public override async Task<OrderSearchModelExtended> PrepareOrderSearchModelAsync(OrderSearchModelExtended searchModel)
{ {
var baseModel = await base.PrepareOrderSearchModelAsync(searchModel); var baseModel = await base.PrepareOrderSearchModelAsync(searchModel);
return baseModel; return baseModel;
@ -158,7 +159,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
public override async Task<OrderListModel> PrepareOrderListModelAsync(OrderSearchModel searchModel) public override async Task<OrderListModel> PrepareOrderListModelAsync(OrderSearchModel searchModel)
=> await base.PrepareOrderListModelAsync(searchModel); => await base.PrepareOrderListModelAsync(searchModel);
public async Task<OrderListModelExtended> PrepareOrderListModelExtendedAsync(OrderSearchModel searchModel) public async Task<OrderListModelExtended> PrepareOrderListModelExtendedAsync(OrderSearchModelExtended searchModel)
{ {
Dictionary<int, OrderDto> orderDtosById = null; Dictionary<int, OrderDto> orderDtosById = null;

View File

@ -1,6 +1,8 @@
using AyCode.Core.Extensions; using AyCode.Core.Extensions;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.Routing;
using Newtonsoft.Json;
using Nop.Core; using Nop.Core;
using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common; using Nop.Core.Domain.Common;
@ -8,6 +10,7 @@ using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Orders; using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Shipping; using Nop.Core.Domain.Shipping;
using Nop.Core.Domain.Tax; using Nop.Core.Domain.Tax;
using Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Models.Order;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer; using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders; using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
using Nop.Plugin.Misc.FruitBankPlugin.Services; using Nop.Plugin.Misc.FruitBankPlugin.Services;
@ -30,7 +33,9 @@ using Nop.Services.Stores;
using Nop.Services.Tax; using Nop.Services.Tax;
using Nop.Services.Vendors; using Nop.Services.Vendors;
using Nop.Web.Areas.Admin.Factories; using Nop.Web.Areas.Admin.Factories;
using Nop.Web.Areas.Admin.Models.Common;
using Nop.Web.Areas.Admin.Models.Orders; using Nop.Web.Areas.Admin.Models.Orders;
using Nop.Web.Framework.Extensions;
namespace Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase; namespace Nop.Plugin.Misc.FruitBankPlugin.Factories.MgBase;
@ -138,18 +143,120 @@ public class MgOrderModelFactory<TOrderListModelExt, TOrderModelExt> : OrderMode
} }
#endregion Cotr #endregion Cotr
public override Task<OrderSearchModel> PrepareOrderSearchModelAsync(OrderSearchModel searchModel) public virtual async Task<OrderSearchModelExtended> PrepareOrderSearchModelAsync(OrderSearchModelExtended 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); ArgumentNullException.ThrowIfNull(searchModel);
var extendedRows = new List<TOrderModelExt>(orderListModel.RecordsFiltered);
foreach (var orderModel in orderListModel.Data.ToList()) searchModel.IsLoggedInAsVendor = await _workContext.GetCurrentVendorAsync() != null;
searchModel.BillingPhoneEnabled = _addressSettings.PhoneEnabled;
var licenseCheckModel = new LicenseCheckModel();
try
{
var result = await _nopHttpClient.GetLicenseCheckDetailsAsync();
if (!string.IsNullOrEmpty(result))
{
licenseCheckModel = JsonConvert.DeserializeObject<LicenseCheckModel>(result);
if (licenseCheckModel.DisplayWarning == false && licenseCheckModel.BlockPages == false)
await _settingService.SetSettingAsync($"{nameof(AdminAreaSettings)}.{nameof(AdminAreaSettings.CheckLicense)}", false);
}
}
catch { }
searchModel.LicenseCheckModel = licenseCheckModel;
//prepare available order, payment and shipping statuses
await _baseAdminModelFactory.PrepareOrderStatusesAsync(searchModel.AvailableOrderStatuses);
if (searchModel.AvailableOrderStatuses.Any())
{
if (searchModel.OrderStatusIds?.Any() ?? false)
{
var ids = searchModel.OrderStatusIds.Select(id => id.ToString());
var statusItems = searchModel.AvailableOrderStatuses.Where(statusItem => ids.Contains(statusItem.Value)).ToList();
foreach (var statusItem in statusItems)
{
statusItem.Selected = true;
}
}
else
searchModel.AvailableOrderStatuses.FirstOrDefault().Selected = true;
}
await _baseAdminModelFactory.PreparePaymentStatusesAsync(searchModel.AvailablePaymentStatuses);
if (searchModel.AvailablePaymentStatuses.Any())
{
if (searchModel.PaymentStatusIds?.Any() ?? false)
{
var ids = searchModel.PaymentStatusIds.Select(id => id.ToString());
var statusItems = searchModel.AvailablePaymentStatuses.Where(statusItem => ids.Contains(statusItem.Value)).ToList();
foreach (var statusItem in statusItems)
{
statusItem.Selected = true;
}
}
else
searchModel.AvailablePaymentStatuses.FirstOrDefault().Selected = true;
}
await _baseAdminModelFactory.PrepareShippingStatusesAsync(searchModel.AvailableShippingStatuses);
if (searchModel.AvailableShippingStatuses.Any())
{
if (searchModel.ShippingStatusIds?.Any() ?? false)
{
var ids = searchModel.ShippingStatusIds.Select(id => id.ToString());
var statusItems = searchModel.AvailableShippingStatuses.Where(statusItem => ids.Contains(statusItem.Value)).ToList();
foreach (var statusItem in statusItems)
{
statusItem.Selected = true;
}
}
else
searchModel.AvailableShippingStatuses.FirstOrDefault().Selected = true;
}
//prepare available stores
await _baseAdminModelFactory.PrepareStoresAsync(searchModel.AvailableStores);
//prepare available vendors
await _baseAdminModelFactory.PrepareVendorsAsync(searchModel.AvailableVendors);
//prepare available warehouses
await _baseAdminModelFactory.PrepareWarehousesAsync(searchModel.AvailableWarehouses);
//prepare available payment methods
searchModel.AvailablePaymentMethods = (await _paymentPluginManager.LoadAllPluginsAsync()).Select(method =>
new SelectListItem { Text = method.PluginDescriptor.FriendlyName, Value = method.PluginDescriptor.SystemName }).ToList();
searchModel.AvailablePaymentMethods.Insert(0, new SelectListItem { Text = await _localizationService.GetResourceAsync("Admin.Common.All"), Value = string.Empty });
//prepare available billing countries
searchModel.AvailableCountries = (await _countryService.GetAllCountriesForBillingAsync(showHidden: true))
.Select(country => new SelectListItem { Text = country.Name, Value = country.Id.ToString() }).ToList();
searchModel.AvailableCountries.Insert(0, new SelectListItem { Text = await _localizationService.GetResourceAsync("Admin.Common.All"), Value = "0" });
//searchModel.AvailableCompanies = (await _customerService.GetAllCustomersAsync())
// .Select(customer => new SelectListItem { Text = customer.Company, Value = customer.Id.ToString() }).ToList();
//searchModel.AvailableCompanies.Insert(0, new SelectListItem { Text = await _localizationService.GetResourceAsync("Admin.Common.All"), Value = "0" });
//prepare grid
searchModel.SetGridPageSize();
searchModel.HideStoresList = _catalogSettings.IgnoreStoreLimitations || searchModel.AvailableStores.SelectionIsNotPossible();
return searchModel;
}
public override async Task<OrderListModel> PrepareOrderListModelAsync(OrderSearchModel searchModel)
{
var preFiltered = await base.PrepareOrderListModelAsync(searchModel);
return preFiltered;
}
public virtual async Task<TOrderListModelExt> PrepareOrderListModelExtendedAsync(OrderSearchModelExtended searchModel, Func<OrderListModel, TOrderModelExt, Task> dataItemCopiedCallback)
{
var prefiltered = await PrepareOrderListModelAsync(searchModel);
var extendedRows = new List<TOrderModelExt>(prefiltered.RecordsFiltered);
foreach (var orderModel in prefiltered.Data.ToList())
{ {
var orderModelExtended = Activator.CreateInstance<TOrderModelExt>(); var orderModelExtended = Activator.CreateInstance<TOrderModelExt>();
@ -157,15 +264,22 @@ public class MgOrderModelFactory<TOrderListModelExt, TOrderModelExt> : OrderMode
extendedRows.Add(orderModelExtended); extendedRows.Add(orderModelExtended);
if (dataItemCopiedCallback == null) continue; if (dataItemCopiedCallback == null) continue;
await dataItemCopiedCallback.Invoke(orderListModel, orderModelExtended); await dataItemCopiedCallback.Invoke(prefiltered, orderModelExtended);
} }
orderListModel.Data = null; prefiltered.Data = null;
//var orderListModelExtended = orderListModel.ToJson().JsonTo<TOrderListModelExt>(); //var orderListModelExtended = orderListModel.ToJson().JsonTo<TOrderListModelExt>();
var orderListModelExtended = orderListModel.CloneTo<TOrderListModelExt>(); if(searchModel.BillingCompany != null)
orderListModelExtended.Data = extendedRows; {
//extendedRows = extendedRows.Where(x => x.CustomerCompany != null && x.CustomerCompany.IndexOf(searchModel.BillingCompany, StringComparison.InvariantCultureIgnoreCase) >= 0).ToList();
extendedRows = extendedRows.Where(x => x.CustomerId == Convert.ToInt32(searchModel.BillingCompany)).ToList();
}
var orderListModelExtended = prefiltered.CloneTo<TOrderListModelExt>();
orderListModelExtended.Data = extendedRows;
orderListModelExtended.RecordsFiltered = extendedRows.Count;
return orderListModelExtended; return orderListModelExtended;
} }
} }

View File

@ -1,9 +1,17 @@
using FruitBank.Common.Interfaces; using FruitBank.Common.Interfaces;
using FruitBank.Common.Server;
using Mango.Nop.Core.Dtos;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using Nop.Core; using Nop.Core;
using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Common;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Orders; using Nop.Core.Domain.Orders;
using Nop.Core.Domain.Tax;
using Nop.Core.Events; using Nop.Core.Events;
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Models; using Nop.Plugin.Misc.FruitBankPlugin.Models;
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders; using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
using Nop.Services.Catalog; using Nop.Services.Catalog;
@ -16,11 +24,10 @@ using Nop.Web.Framework.Events;
using Nop.Web.Framework.Menu; using Nop.Web.Framework.Menu;
using Nop.Web.Models.Sitemap; using Nop.Web.Models.Sitemap;
using System.Linq; using System.Linq;
using Microsoft.IdentityModel.Tokens;
namespace Nop.Plugin.Misc.FruitBankPlugin.Services namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{ {
public class EventConsumer : BaseAdminMenuCreatedEventConsumer, IConsumer<OrderPlacedEvent>, IConsumer<EntityUpdatedEvent<Order>>, IConsumer<AdminMenuCreatedEvent> public class EventConsumer : BaseAdminMenuCreatedEventConsumer, IConsumer<EntityUpdatedEvent<Customer>>, IConsumer<EntityInsertedEvent<Customer>>, IConsumer<OrderPlacedEvent>, IConsumer<EntityUpdatedEvent<Order>>, IConsumer<AdminMenuCreatedEvent>
{ {
private readonly IGenericAttributeService _genericAttributeService; private readonly IGenericAttributeService _genericAttributeService;
private readonly IProductService _productService; private readonly IProductService _productService;
@ -32,7 +39,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
private readonly IAdminMenu _adminMenu; private readonly IAdminMenu _adminMenu;
private readonly ILocalizationService _localizationService; private readonly ILocalizationService _localizationService;
private readonly IHttpContextAccessor _httpContextAccessor; private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IAddressService _addressService;
private readonly FruitBankAttributeService _fruitBankAttributeService; private readonly FruitBankAttributeService _fruitBankAttributeService;
private readonly FruitBankDbContext _dbContext;
public EventConsumer( public EventConsumer(
IGenericAttributeService genericAttributeService, IGenericAttributeService genericAttributeService,
@ -46,7 +55,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
IAdminMenu adminMenu, IAdminMenu adminMenu,
ILocalizationService localizationService, ILocalizationService localizationService,
IHttpContextAccessor httpContextAccessor, IHttpContextAccessor httpContextAccessor,
FruitBankAttributeService fruitBankAttributeService) : base(pluginManager) IAddressService addressService,
FruitBankAttributeService fruitBankAttributeService,
FruitBankDbContext dbContext) : base(pluginManager)
{ {
_genericAttributeService = genericAttributeService; _genericAttributeService = genericAttributeService;
_productService = productService; _productService = productService;
@ -58,7 +69,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
_adminMenu = adminMenu; _adminMenu = adminMenu;
_localizationService = localizationService; _localizationService = localizationService;
_httpContextAccessor = httpContextAccessor; _httpContextAccessor = httpContextAccessor;
_addressService = addressService;
_fruitBankAttributeService = fruitBankAttributeService; _fruitBankAttributeService = fruitBankAttributeService;
_dbContext = dbContext;
} }
protected override string PluginSystemName => "Misc.FruitBankPlugin"; protected override string PluginSystemName => "Misc.FruitBankPlugin";
@ -210,5 +223,82 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
}); });
} }
public async Task HandleEventAsync(EntityInsertedEvent<Customer> eventMessage)
{
await ProcessCustomerEventAsync(eventMessage?.Entity);
}
public async Task HandleEventAsync(EntityUpdatedEvent<Customer> eventMessage)
{
await ProcessCustomerEventAsync(eventMessage?.Entity);
}
private async Task ProcessCustomerEventAsync(Customer customer)
{
if (customer == null)
{
return;
}
if (!IsRegisteredCustomer(customer))
{
return;
}
await EnsureBillingAddressAsync(customer);
await SynchronizeTaxInformationAsync(customer);
}
private bool IsRegisteredCustomer(Customer customer)
{
var customerRoles = _dbContext.GetCustomerRolesByCustomerId(customer.Id);
return customerRoles?.Any(role => role.SystemName == "Registered") ?? false;
}
private async Task EnsureBillingAddressAsync(Customer customer)
{
if (customer.BillingAddressId != null)
{
return;
}
var address = new Address
{
ZipPostalCode = customer.ZipPostalCode,
City = customer.City,
Address1 = customer.StreetAddress,
Address2 = customer.StreetAddress2,
CountryId = customer.CountryId > 0 ? customer.CountryId : null,
StateProvinceId = customer.StateProvinceId > 0 ? customer.StateProvinceId : null,
PhoneNumber = customer.Phone,
Company = customer.Company,
FirstName = customer.FirstName,
LastName = customer.LastName,
Email = customer.Email,
County = customer.County
};
await _addressService.InsertAddressAsync(address);
await _dbContext.AddCustomerAddressMappingAsync(customer.Id, address.Id);
customer.BillingAddressId = address.Id;
}
private async Task SynchronizeTaxInformationAsync(Customer customer)
{
var taxId = await _fruitBankAttributeService.GetGenericAttributeValueAsync<Customer, string>(customer.Id, "TaxId");
if (!string.IsNullOrWhiteSpace(taxId) && string.IsNullOrWhiteSpace(customer.VatNumber))
{
customer.VatNumber = taxId;
}
else if (string.IsNullOrWhiteSpace(taxId) && !string.IsNullOrWhiteSpace(customer.VatNumber))
{
await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync<Customer, string>(
customer.Id,
"TaxId",
customer.VatNumber);
}
}
} }
} }

View File

@ -18,7 +18,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
private readonly string _password; private readonly string _password;
private readonly string _baseUrl; private readonly string _baseUrl;
public InnVoiceOrderService(string companyName = "fruitbank", string username = "fruitbank", string password = "YkKzM1dwJax0HTIPWIMABSqdSA", string baseUrl = "https://api.innvoice.hu") public InnVoiceOrderService(string companyName = "fruitbank", string username = "fruitbank", string password = "YkKzM1dwJax0HTlPWlMABSqdSA", string baseUrl = "https://api.innvoice.hu")
{ {
_companyName = companyName ?? throw new ArgumentNullException(nameof(companyName)); _companyName = companyName ?? throw new ArgumentNullException(nameof(companyName));
_username = username ?? throw new ArgumentNullException(nameof(username)); _username = username ?? throw new ArgumentNullException(nameof(username));

View File

@ -10,8 +10,9 @@
<div class="card-body"> <div class="card-body">
<!-- Order Subsection --> <!-- Order Subsection -->
<div class="form-group row"> <div class="form-group row">
<h3>Innvoice</h3>
<div class="col-12 col-md-3"> <div class="col-12 col-md-3">
<h5><i class="fas fa-shopping-cart"></i> Order</h5> <h5><i class="fas fa-shopping-cart"></i> Megrendelés beküldése Innvoice-ba</h5>
<div id="orderStatus" class="alert alert-info" style="display: none;"> <div id="orderStatus" class="alert alert-info" style="display: none;">
<i class="fas fa-info-circle"></i> <span id="orderStatusMessage"></span> <i class="fas fa-info-circle"></i> <span id="orderStatusMessage"></span>
</div> </div>
@ -20,22 +21,22 @@
<p><strong>Order Tech ID:</strong> <span id="orderTechId"></span></p> <p><strong>Order Tech ID:</strong> <span id="orderTechId"></span></p>
<p> <p>
<a id="orderPdfLink" href="#" target="_blank" class="btn btn-sm btn-info"> <a id="orderPdfLink" href="#" target="_blank" class="btn btn-sm btn-info">
<i class="fas fa-file-pdf"></i> View Order PDF <i class="fas fa-file-pdf"></i> PDF megtekintése
</a> </a>
</p> </p>
</div> </div>
</div> </div>
<div class="col-12 col-md-3 text-right"> <div class="col-12 col-md-3 text-right float-end">
<button type="button" id="createOrderBtn" class="btn btn-success"> <button type="button" id="createOrderBtn" class="btn btn-success">
<i class="fas fa-shopping-cart"></i> Create Order in InnVoice <i class="fas fa-shopping-cart"></i> Létrehozás
</button> </button>
<button type="button" id="checkOrderBtn" class="btn btn-secondary" style="display: none;"> <button type="button" id="checkOrderBtn" class="btn btn-secondary" style="display: none;">
<i class="fas fa-sync"></i> Refresh Order <i class="fas fa-sync"></i> Invoice adat ellenőrzése
</button> </button>
</div> </div>
<div class="col-12 col-md-3"> @* <div class="col-12 col-md-3">
<h5><i class="fas fa-file-invoice-dollar"></i> Invoice</h5> <h5><i class="fas fa-file-invoice-dollar"></i> Invoice</h5>
<div id="invoiceStatus" class="alert alert-info" style="display: none;"> <div id="invoiceStatus" class="alert alert-info" style="display: none;">
<i class="fas fa-info-circle"></i> <span id="invoiceStatusMessage"></span> <i class="fas fa-info-circle"></i> <span id="invoiceStatusMessage"></span>
@ -49,16 +50,16 @@
</a> </a>
</p> </p>
</div> </div>
</div> </div> *@
<div class="col-12 col-md-3 text-right"> @* <div class="col-12 col-md-3 text-right">
<button type="button" id="createInvoiceBtn" class="btn btn-success"> <button type="button" id="createInvoiceBtn" class="btn btn-success">
<i class="fas fa-file-invoice-dollar"></i> Create Invoice <i class="fas fa-file-invoice-dollar"></i> Create Invoice
</button> </button>
<button type="button" id="checkInvoiceBtn" class="btn btn-secondary" style="display: none;"> <button type="button" id="checkInvoiceBtn" class="btn btn-secondary" style="display: none;">
<i class="fas fa-sync"></i> Refresh Invoice <i class="fas fa-sync"></i> Refresh Invoice
</button> </button>
</div> </div> *@
</div> </div>
<!-- Attributes Section (NO FORM TAG - just a div) --> <!-- Attributes Section (NO FORM TAG - just a div) -->