Merge branch '4.80' of https://git.aycode.com/Adam/Mango.Nop.Plugins into 4.80
This commit is contained in:
commit
cc4f5a3f8c
|
|
@ -5,19 +5,30 @@ using FruitBank.Common.Interfaces;
|
|||
using FruitBank.Common.Server.Interfaces;
|
||||
using FruitBank.Common.SignalRs;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Newtonsoft.Json;
|
||||
using Nop.Core.Domain.Customers;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Core.Domain.Payments;
|
||||
using Nop.Core.Domain.Shipping;
|
||||
using Nop.Core.Domain.Tax;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Domains.DataLayer;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Factories;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Common;
|
||||
using Nop.Services.Customers;
|
||||
using Nop.Services.Messages;
|
||||
using Nop.Services.Orders;
|
||||
using Nop.Services.Payments;
|
||||
using Nop.Services.Security;
|
||||
using Nop.Web.Areas.Admin.Controllers;
|
||||
using Nop.Web.Areas.Admin.Factories;
|
||||
using Nop.Web.Areas.Admin.Models.Orders;
|
||||
using Nop.Web.Framework;
|
||||
using Nop.Web.Framework.Mvc.Filters;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
||||
{
|
||||
|
|
@ -31,9 +42,11 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
private readonly IPermissionService _permissionService;
|
||||
private readonly IGenericAttributeService _genericAttributeService;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly ICustomerService _customerService;
|
||||
private readonly IProductService _productService;
|
||||
// ... other dependencies
|
||||
|
||||
public CustomOrderController(IOrderService orderService, IOrderModelFactory orderModelFactory, ICustomOrderSignalREndpointServer customOrderSignalREndpoint, IPermissionService permissionService, IGenericAttributeService genericAttributeService, INotificationService notificationService)
|
||||
public CustomOrderController(IOrderService orderService, IOrderModelFactory orderModelFactory, ICustomOrderSignalREndpointServer customOrderSignalREndpoint, IPermissionService permissionService, IGenericAttributeService genericAttributeService, INotificationService notificationService, ICustomerService customerService, IProductService productService)
|
||||
{
|
||||
_orderService = orderService;
|
||||
_orderModelFactory = orderModelFactory as CustomOrderModelFactory;
|
||||
|
|
@ -41,6 +54,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
_permissionService = permissionService;
|
||||
_genericAttributeService = genericAttributeService;
|
||||
_notificationService = notificationService;
|
||||
_customerService = customerService;
|
||||
_productService = productService;
|
||||
// ... initialize other deps
|
||||
}
|
||||
|
||||
|
|
@ -134,6 +149,377 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
return RedirectToAction("Edit", "Order", new { id = model.OrderId });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
//[CheckPermission(StandardPermission.Orders.ORDERS_CREATE)]
|
||||
public virtual async Task<IActionResult> Create(int customerId, string orderProductsJson)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Orders.ORDERS_CREATE_EDIT_DELETE))
|
||||
return AccessDeniedView();
|
||||
|
||||
// Validate customer
|
||||
var customer = await _customerService.GetCustomerByIdAsync(customerId);
|
||||
if (customer == null)
|
||||
return RedirectToAction("List");
|
||||
|
||||
// Parse products
|
||||
var orderProducts = string.IsNullOrEmpty(orderProductsJson)
|
||||
? new List<OrderProductItem>()
|
||||
: JsonConvert.DeserializeObject<List<OrderProductItem>>(orderProductsJson);
|
||||
|
||||
// Create order
|
||||
var order = new Order
|
||||
{
|
||||
OrderGuid = Guid.NewGuid(),
|
||||
CustomOrderNumber = "",
|
||||
CustomerId = customerId,
|
||||
CustomerLanguageId = customer.LanguageId ?? 1,
|
||||
CustomerTaxDisplayType = TaxDisplayType.IncludingTax,
|
||||
CustomerIp = string.Empty,
|
||||
OrderStatusId = (int)OrderStatus.Pending,
|
||||
PaymentStatusId = (int)PaymentStatus.Pending,
|
||||
ShippingStatusId = (int)ShippingStatus.ShippingNotRequired,
|
||||
CreatedOnUtc = DateTime.UtcNow,
|
||||
BillingAddressId = customer.BillingAddressId ?? 0,
|
||||
ShippingAddressId = customer.ShippingAddressId
|
||||
};
|
||||
|
||||
await _orderService.InsertOrderAsync(order);
|
||||
|
||||
// Add order items
|
||||
foreach (var item in orderProducts)
|
||||
{
|
||||
var product = await _productService.GetProductByIdAsync(item.Id);
|
||||
if (product != null)
|
||||
{
|
||||
var orderItem = new OrderItem
|
||||
{
|
||||
OrderId = order.Id,
|
||||
ProductId = item.Id,
|
||||
Quantity = item.Quantity,
|
||||
UnitPriceInclTax = item.Price,
|
||||
UnitPriceExclTax = item.Price,
|
||||
PriceInclTax = item.Price * item.Quantity,
|
||||
PriceExclTax = item.Price * item.Quantity,
|
||||
OriginalProductCost = product.ProductCost,
|
||||
AttributeDescription = string.Empty,
|
||||
AttributesXml = string.Empty,
|
||||
DiscountAmountInclTax = 0,
|
||||
DiscountAmountExclTax = 0
|
||||
};
|
||||
|
||||
await _orderService.InsertOrderItemAsync(orderItem);
|
||||
}
|
||||
}
|
||||
|
||||
return RedirectToAction("Edit", new { id = order.Id });
|
||||
}
|
||||
|
||||
public class OrderProductItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Sku { get; set; }
|
||||
public int Quantity { get; set; }
|
||||
public decimal Price { get; set; }
|
||||
}
|
||||
|
||||
//private static OrderItem CreateOrderItem(ProductToAuctionMapping productToAuction, Order order, decimal orderTotal)
|
||||
//{
|
||||
// return new OrderItem
|
||||
// {
|
||||
// ProductId = productToAuction.ProductId,
|
||||
// OrderId = order.Id,
|
||||
// OrderItemGuid = Guid.NewGuid(),
|
||||
// PriceExclTax = orderTotal,
|
||||
// PriceInclTax = orderTotal,
|
||||
// UnitPriceExclTax = orderTotal,
|
||||
// UnitPriceInclTax = orderTotal,
|
||||
// Quantity = productToAuction.ProductAmount,
|
||||
// };
|
||||
//}
|
||||
|
||||
//private static Order CreateOrder(ProductToAuctionMapping productToAuction, decimal orderTotal, Customer customer, Address billingAddress, int storeId, Dictionary<string, object> customValues)
|
||||
//{
|
||||
// return new Order
|
||||
// {
|
||||
// BillingAddressId = billingAddress.Id,
|
||||
// CreatedOnUtc = DateTime.UtcNow,
|
||||
// CurrencyRate = 1,
|
||||
// CustomOrderNumber = productToAuction.AuctionId + "/" + productToAuction.SortIndex,
|
||||
// CustomValuesXml = SerializeCustomValuesToXml(customValues),
|
||||
// CustomerCurrencyCode = "HUF",
|
||||
// CustomerId = productToAuction.WinnerCustomerId,
|
||||
// CustomerLanguageId = 2,
|
||||
// CustomerTaxDisplayType = TaxDisplayType.IncludingTax,
|
||||
// OrderGuid = Guid.NewGuid(),
|
||||
// OrderStatus = OrderStatus.Pending,
|
||||
// OrderTotal = orderTotal,
|
||||
// PaymentStatus = PaymentStatus.Pending,
|
||||
// PaymentMethodSystemName = "Payments.CheckMoneyOrder",
|
||||
// ShippingStatus = ShippingStatus.ShippingNotRequired,
|
||||
// StoreId = storeId,
|
||||
// VatNumber = customer.VatNumber,
|
||||
// CustomerIp = customer.LastIpAddress,
|
||||
// OrderSubtotalExclTax = orderTotal,
|
||||
// OrderSubtotalInclTax = orderTotal
|
||||
// };
|
||||
//}
|
||||
|
||||
private static OrderNote CreateOrderNote(Order order, string note)
|
||||
{
|
||||
return new OrderNote
|
||||
{
|
||||
CreatedOnUtc = order.CreatedOnUtc,
|
||||
DisplayToCustomer = true,
|
||||
OrderId = order.Id,
|
||||
Note = note
|
||||
};
|
||||
}
|
||||
|
||||
private static string SerializeCustomValuesToXml(Dictionary<string, object> sourceDictionary)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(sourceDictionary);
|
||||
|
||||
if (!sourceDictionary.Any())
|
||||
return null;
|
||||
|
||||
var ds = new DictionarySerializer(sourceDictionary);
|
||||
var xs = new XmlSerializer(typeof(DictionarySerializer));
|
||||
|
||||
using var textWriter = new StringWriter();
|
||||
using (var xmlWriter = XmlWriter.Create(textWriter))
|
||||
{
|
||||
xs.Serialize(xmlWriter, ds);
|
||||
}
|
||||
|
||||
var result = textWriter.ToString();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
[HttpGet] // Change from [HttpPost] to [HttpGet]
|
||||
[CheckPermission(StandardPermission.Customers.CUSTOMERS_VIEW)]
|
||||
public virtual async Task<IActionResult> CustomerSearchAutoComplete(string term)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(term) || term.Length < 2)
|
||||
return Json(new List<object>());
|
||||
|
||||
const int maxResults = 15;
|
||||
|
||||
// Search by email (contains)
|
||||
var customersByEmail = await _customerService.GetAllCustomersAsync(
|
||||
email: term,
|
||||
pageIndex: 0,
|
||||
pageSize: maxResults);
|
||||
|
||||
// Search by first name (contains)
|
||||
var customersByFirstName = await _customerService.GetAllCustomersAsync(
|
||||
firstName: term,
|
||||
pageIndex: 0,
|
||||
pageSize: maxResults);
|
||||
|
||||
// Search by last name (contains)
|
||||
var customersByLastName = await _customerService.GetAllCustomersAsync(
|
||||
lastName: term,
|
||||
pageIndex: 0,
|
||||
pageSize: maxResults);
|
||||
|
||||
// Combine and deduplicate results
|
||||
var allCustomers = customersByEmail
|
||||
.Union(customersByFirstName)
|
||||
.Union(customersByLastName)
|
||||
.DistinctBy(c => c.Id)
|
||||
.Take(maxResults)
|
||||
.ToList();
|
||||
|
||||
var result = new List<object>();
|
||||
foreach (var customer in allCustomers)
|
||||
{
|
||||
var fullName = await _customerService.GetCustomerFullNameAsync(customer);
|
||||
var displayText = !string.IsNullOrEmpty(customer.Email)
|
||||
? $"{customer.Email} ({fullName})"
|
||||
: fullName;
|
||||
|
||||
result.Add(new
|
||||
{
|
||||
label = displayText,
|
||||
value = customer.Id
|
||||
});
|
||||
}
|
||||
|
||||
return Json(result);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[CheckPermission(StandardPermission.Catalog.PRODUCTS_VIEW)]
|
||||
public virtual async Task<IActionResult> ProductSearchAutoComplete(string term)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(term) || term.Length < 2)
|
||||
return Json(new List<object>());
|
||||
|
||||
const int maxResults = 15;
|
||||
|
||||
// Search products by name or SKU
|
||||
var products = await _productService.SearchProductsAsync(
|
||||
keywords: term,
|
||||
pageIndex: 0,
|
||||
pageSize: maxResults);
|
||||
|
||||
var result = new List<object>();
|
||||
foreach (var product in products)
|
||||
{
|
||||
result.Add(new
|
||||
{
|
||||
label = product.Name,
|
||||
value = product.Id,
|
||||
sku = product.Sku,
|
||||
price = product.Price
|
||||
});
|
||||
}
|
||||
|
||||
return Json(result);
|
||||
}
|
||||
|
||||
|
||||
//[HttpPost]
|
||||
//public async Task<IActionResult> CreateInvoice(int orderId)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// var order = await _orderService.GetOrderByIdAsync(orderId);
|
||||
// if (order == null)
|
||||
// return Json(new { success = false, message = "Order not found" });
|
||||
|
||||
// var billingAddress = await _customerService.GetCustomerBillingAddressAsync(order.Customer);
|
||||
// if (billingAddress == null)
|
||||
// return Json(new { success = false, message = "Billing address not found" });
|
||||
|
||||
// var country = await _countryService.GetCountryByAddressAsync(billingAddress);
|
||||
// var countryCode = country?.TwoLetterIsoCode ?? "HU";
|
||||
|
||||
// // Create invoice request
|
||||
// var invoiceRequest = new InvoiceCreateRequest
|
||||
// {
|
||||
// VevoNev = $"{billingAddress.FirstName} {billingAddress.LastName}",
|
||||
// VevoIrsz = billingAddress.ZipPostalCode ?? "",
|
||||
// VevoTelep = billingAddress.City ?? "",
|
||||
// VevoOrszag = countryCode,
|
||||
// VevoUtcaHsz = $"{billingAddress.Address1} {billingAddress.Address2}".Trim(),
|
||||
// SzamlatombID = 1, // Configure this based on your setup
|
||||
// SzamlaKelte = DateTime.Now,
|
||||
// TeljesitesKelte = DateTime.Now,
|
||||
// Hatarido = DateTime.Now.AddDays(15), // 15 days payment term
|
||||
// Devizanem = order.CustomerCurrencyCode,
|
||||
// FizetesiMod = order.PaymentMethodSystemName,
|
||||
// Felretett = false,
|
||||
// Proforma = false,
|
||||
// Email = billingAddress.Email,
|
||||
// Telefon = billingAddress.PhoneNumber
|
||||
// };
|
||||
|
||||
// // Add order items
|
||||
// var orderItems = await _orderService.GetOrderItemsAsync(order.Id);
|
||||
// foreach (var item in orderItems)
|
||||
// {
|
||||
// var product = await _productService.GetProductByIdAsync(item.ProductId);
|
||||
|
||||
// invoiceRequest.AddItem(new InvoiceItem
|
||||
// {
|
||||
// TetelNev = product?.Name ?? "Product",
|
||||
// AfaSzoveg = "27%", // Configure VAT rate as needed
|
||||
// Brutto = true,
|
||||
// EgysegAr = item.UnitPriceInclTax,
|
||||
// Mennyiseg = item.Quantity,
|
||||
// MennyisegEgyseg = "db",
|
||||
// CikkSzam = product?.Sku
|
||||
// });
|
||||
// }
|
||||
|
||||
// // Create invoice via API
|
||||
// var response = await _innVoiceApiService.CreateInvoiceAsync(invoiceRequest);
|
||||
|
||||
// if (response.IsSuccess)
|
||||
// {
|
||||
// // TODO: Save invoice details to your database for future reference
|
||||
// // You might want to create a custom table to store:
|
||||
// // - OrderId
|
||||
// // - InnVoice TableId
|
||||
// // - Invoice Number
|
||||
// // - PDF URL
|
||||
// // - Created Date
|
||||
|
||||
// return Json(new
|
||||
// {
|
||||
// success = true,
|
||||
// message = "Invoice created successfully",
|
||||
// data = new
|
||||
// {
|
||||
// tableId = response.TableId,
|
||||
// invoiceNumber = response.Sorszam,
|
||||
// sorszam = response.Sorszam,
|
||||
// printUrl = response.PrintUrl
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return Json(new
|
||||
// {
|
||||
// success = false,
|
||||
// message = $"InnVoice API Error: {response.Message}"
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// return Json(new
|
||||
// {
|
||||
// success = false,
|
||||
// message = $"Error: {ex.Message}"
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
|
||||
//[HttpGet]
|
||||
//public async Task<IActionResult> GetInvoiceStatus(int orderId)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// // TODO: Retrieve invoice details from your database
|
||||
// // This is a placeholder - you need to implement actual storage/retrieval
|
||||
|
||||
// // Example: var invoiceData = await _yourInvoiceService.GetByOrderIdAsync(orderId);
|
||||
// // if (invoiceData != null)
|
||||
// // {
|
||||
// // return Json(new
|
||||
// // {
|
||||
// // success = true,
|
||||
// // data = new
|
||||
// // {
|
||||
// // tableId = invoiceData.TableId,
|
||||
// // invoiceNumber = invoiceData.InvoiceNumber,
|
||||
// // sorszam = invoiceData.InvoiceNumber,
|
||||
// // printUrl = invoiceData.PrintUrl
|
||||
// // }
|
||||
// // });
|
||||
// // }
|
||||
|
||||
// return Json(new
|
||||
// {
|
||||
// success = false,
|
||||
// message = "No invoice found for this order"
|
||||
// });
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// return Json(new
|
||||
// {
|
||||
// success = false,
|
||||
// message = $"Error: {ex.Message}"
|
||||
// });
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -186,6 +186,20 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
return Json(model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetAllPartners()
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL))
|
||||
return AccessDeniedView();
|
||||
|
||||
// Mock data for now
|
||||
var model = await _dbContext.Partners.GetAll().ToListAsync();
|
||||
var valami = model;
|
||||
//model. = await _dbContext.GetShippingDocumentsByShippingIdAsync(shippingId);
|
||||
return Json(model);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpPost]
|
||||
[RequestSizeLimit(10485760)] // 10MB
|
||||
|
|
@ -240,7 +254,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
//if (file.Length > 0 && file.ContentType == "application/pdf")
|
||||
if (file.Length > 0)
|
||||
{
|
||||
if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)){
|
||||
if (file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)){
|
||||
try
|
||||
{
|
||||
// Open the PDF from the IFormFile's stream directly in memory
|
||||
|
|
@ -282,18 +296,18 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
return StatusCode(500, $"Error processing PDF file: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
else //read from image
|
||||
{
|
||||
try
|
||||
{
|
||||
// Open the PDF from the IFormFile's stream directly in memory
|
||||
// Open the Image from the IFormFile's stream directly in memory
|
||||
using (var stream = file.OpenReadStream())
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
// ✅ Use the service we implemented earlier
|
||||
pdfText = await _openAIApiService.AnalyzePdfAsync(stream, file.FileName, "Please extract all readable text from this PDF.");
|
||||
pdfText = await _openAIApiService.AnalyzePdfAsync(stream, file.FileName, "Please extract all readable text from this image.");
|
||||
}
|
||||
catch (Exception aiEx)
|
||||
{
|
||||
|
|
@ -319,7 +333,6 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
}
|
||||
|
||||
|
||||
|
||||
string analysisPrompt = "Extract the document identification number from this document, determine the type of the " +
|
||||
"document IN ENGLISH from the available list, and return them as JSON: documentNumber, documentType. " +
|
||||
$"Available filetypes: {nameof(DocumentType.Invoice)}, {nameof(DocumentType.ShippingDocument)} , {nameof(DocumentType.OrderConfirmation)}, {nameof(DocumentType.Unknown)}" +
|
||||
|
|
@ -342,9 +355,11 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
ShippingDocumentToFiles shippingDocumentToFiles = new ShippingDocumentToFiles
|
||||
{
|
||||
ShippingDocumentId = shippingDocumentId,
|
||||
FilesId = dbFile.Id
|
||||
FilesId = dbFile.Id,
|
||||
DocumentType = extractedMetaData.DocumentType != null ? (DocumentType)Enum.Parse(typeof(DocumentType), extractedMetaData.DocumentType) : DocumentType.Unknown
|
||||
};
|
||||
|
||||
Console.WriteLine(shippingDocumentToFiles.DocumentType);
|
||||
|
||||
await _dbContext.ShippingDocumentToFiles.InsertAsync(shippingDocumentToFiles);
|
||||
// - IF WE DON'T HAVE PARTNERID ALREADY: read partner information
|
||||
|
|
@ -425,7 +440,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
// Analyze PDF with AI to extract structured data
|
||||
var aiAnalysis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
|
||||
pdfText.ToString(),
|
||||
"Extract the following information from this shipping document and return as JSON: documentDate, recipientName, senderName, invoiceNumber, totalAmount, itemCount, notes. If a field is not found, return null for that field."
|
||||
"You work for FruitBank. Extract the following information from this shipping document and return as JSON: documentDate, recipientName, senderName, invoiceNumber, totalAmount, itemCount, notes. If a field is not found, return null for that field."
|
||||
);
|
||||
|
||||
// Parse AI response (assuming it returns JSON)
|
||||
|
|
|
|||
|
|
@ -12,680 +12,950 @@
|
|||
@using static Nop.Services.Common.NopLinksDefaults
|
||||
|
||||
@{
|
||||
//page title
|
||||
ViewBag.PageTitle = T("Admin.Orders").Text;
|
||||
//active menu item (system name)
|
||||
NopHtml.SetActiveMenuItemSystemName("Orders");
|
||||
//page title
|
||||
ViewBag.PageTitle = T("Admin.Orders").Text;
|
||||
//active menu item (system name)
|
||||
NopHtml.SetActiveMenuItemSystemName("Orders");
|
||||
}
|
||||
|
||||
@{
|
||||
const string hideSearchBlockAttributeName = "OrdersPage.HideSearchBlock";
|
||||
var hideSearchBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideSearchBlockAttributeName);
|
||||
const string hideSearchBlockAttributeName = "OrdersPage.HideSearchBlock";
|
||||
var hideSearchBlock = await genericAttributeService.GetAttributeAsync<bool>(await workContext.GetCurrentCustomerAsync(), hideSearchBlockAttributeName);
|
||||
}
|
||||
|
||||
@if (Model.LicenseCheckModel.BlockPages != true)
|
||||
{
|
||||
<form asp-controller="Order" asp-action="List" method="post">
|
||||
<div class="content-header clearfix">
|
||||
<h1 class="float-left">
|
||||
@T("Admin.Orders")
|
||||
</h1>
|
||||
<div class="float-right">
|
||||
<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 dropdown-icon" data-toggle="dropdown" aria-expanded="false">
|
||||
<span class="sr-only"> </span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li class="dropdown-item">
|
||||
<button asp-action="ExportXml" 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="ExportExcel" 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>
|
||||
<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>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-info">
|
||||
<i class="far fa-file-pdf"></i>
|
||||
@T("Admin.Orders.PdfInvoices")
|
||||
</button>
|
||||
<button type="button" class="btn btn-info dropdown-toggle dropdown-icon" data-toggle="dropdown" aria-expanded="false">
|
||||
<span class="sr-only"> </span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li class="dropdown-item">
|
||||
<button asp-action="PdfInvoice" type="submit" name="pdf-invoice-all">
|
||||
@T("Admin.Orders.PdfInvoices.All")
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<button type="button" id="pdf-invoice-selected">
|
||||
@T("Admin.Orders.PdfInvoices.Selected")
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.OrderListButtons, 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>
|
||||
<form asp-controller="Order" asp-action="List" method="post">
|
||||
<div class="content-header clearfix">
|
||||
<h1 class="float-left">
|
||||
@T("Admin.Orders")
|
||||
</h1>
|
||||
<div class="float-right">
|
||||
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#create-order-window">
|
||||
<i class="fas fa-plus"></i>
|
||||
@T("Admin.Common.AddNew")
|
||||
</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 dropdown-icon" data-toggle="dropdown" aria-expanded="false">
|
||||
<span class="sr-only"> </span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li class="dropdown-item">
|
||||
<button asp-action="ExportXml" 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="ExportExcel" 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>
|
||||
<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>
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-info">
|
||||
<i class="far fa-file-pdf"></i>
|
||||
@T("Admin.Orders.PdfInvoices")
|
||||
</button>
|
||||
<button type="button" class="btn btn-info dropdown-toggle dropdown-icon" data-toggle="dropdown" aria-expanded="false">
|
||||
<span class="sr-only"> </span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li class="dropdown-item">
|
||||
<button asp-action="PdfInvoice" type="submit" name="pdf-invoice-all">
|
||||
@T("Admin.Orders.PdfInvoices.All")
|
||||
</button>
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<button type="button" id="pdf-invoice-selected">
|
||||
@T("Admin.Orders.PdfInvoices.Selected")
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@await Component.InvokeAsync(typeof(AdminWidgetViewComponent), new { widgetZone = AdminWidgetZones.OrderListButtons, 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>
|
||||
|
||||
<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="StartDate" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="StartDate" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="EndDate" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="EndDate" />
|
||||
</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="WarehouseId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="WarehouseId" asp-items="Model.AvailableWarehouses" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="ProductId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input type="text" id="search-product-name" autocomplete="off" class="form-control" />
|
||||
<span id="search-product-friendly-name"></span>
|
||||
<button type="button" id="search-product-clear" class="btn bg-gray" style="display: none; margin-top: 5px;">@T("Admin.Common.Clear")</button>
|
||||
<input asp-for="ProductId" autocomplete="off" style="display: none;" />
|
||||
<script>
|
||||
$(function() {
|
||||
$('#search-product-name').autocomplete({
|
||||
delay: 500,
|
||||
minLength: 3,
|
||||
source: '@Url.Action("SearchAutoComplete", "SearchComplete")',
|
||||
select: function(event, ui) {
|
||||
$('#@Html.IdFor(model => model.ProductId)').val(ui.item.productid);
|
||||
$('#search-product-friendly-name').text(ui.item.label);
|
||||
<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="StartDate" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="StartDate" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="EndDate" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="EndDate" />
|
||||
</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="WarehouseId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="WarehouseId" asp-items="Model.AvailableWarehouses" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="ProductId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input type="text" id="search-product-name" autocomplete="off" class="form-control" />
|
||||
<span id="search-product-friendly-name"></span>
|
||||
<button type="button" id="search-product-clear" class="btn bg-gray" style="display: none; margin-top: 5px;">@T("Admin.Common.Clear")</button>
|
||||
<input asp-for="ProductId" autocomplete="off" style="display: none;" />
|
||||
<script>
|
||||
$(function() {
|
||||
$('#search-product-name').autocomplete({
|
||||
delay: 500,
|
||||
minLength: 3,
|
||||
source: '@Url.Action("SearchAutoComplete", "SearchComplete")',
|
||||
select: function(event, ui) {
|
||||
$('#@Html.IdFor(model => model.ProductId)').val(ui.item.productid);
|
||||
$('#search-product-friendly-name').text(ui.item.label);
|
||||
|
||||
$('#search-product-clear').show();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$('#search-product-clear').show();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
//remove button
|
||||
$('#search-product-clear').click(function() {
|
||||
$('#@Html.IdFor(model => model.ProductId)').val('0');
|
||||
$('#search-product-friendly-name').text('');
|
||||
$('#search-product-clear').hide();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.IsLoggedInAsVendor? Html.Raw("style='display: none;'") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="OrderStatusIds" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="OrderStatusIds" asp-items="Model.AvailableOrderStatuses" asp-multiple="true" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="PaymentStatusIds" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="PaymentStatusIds" asp-items="Model.AvailablePaymentStatuses" asp-multiple="true" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.IsLoggedInAsVendor? Html.Raw("style='display: none;'") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="ShippingStatusIds" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="ShippingStatusIds" asp-items="Model.AvailableShippingStatuses" asp-multiple="true" />
|
||||
</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="StoreId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="StoreId" asp-items="Model.AvailableStores" />
|
||||
</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="VendorId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="VendorId" asp-items="Model.AvailableVendors" />
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.BillingPhoneEnabled)
|
||||
{
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="BillingPhone" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="BillingPhone" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="BillingEmail" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="BillingEmail" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="BillingLastName" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="BillingLastName" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableCountries.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="BillingCountryId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="BillingCountryId" asp-items="Model.AvailableCountries" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailablePaymentMethods.SelectionIsNotPossible() || Model.IsLoggedInAsVendor ? Html.Raw("style='display: none;'") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="PaymentMethodSystemName" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="PaymentMethodSystemName" asp-items="Model.AvailablePaymentMethods" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="OrderNotes" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="OrderNotes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="GoDirectlyToCustomOrderNumber" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="input-group input-group-short">
|
||||
<nop-editor asp-for="GoDirectlyToCustomOrderNumber" />
|
||||
<span class="input-group-append">
|
||||
<button type="submit" id="go-to-order-by-number" name="go-to-order-by-number" 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-orders" class="btn btn-primary btn-search">
|
||||
<i class="fas fa-magnifying-glass"></i>
|
||||
@T("Admin.Common.Search")
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
//remove button
|
||||
$('#search-product-clear').click(function() {
|
||||
$('#@Html.IdFor(model => model.ProductId)').val('0');
|
||||
$('#search-product-friendly-name').text('');
|
||||
$('#search-product-clear').hide();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.IsLoggedInAsVendor ? Html.Raw("style='display: none;'") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="OrderStatusIds" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="OrderStatusIds" asp-items="Model.AvailableOrderStatuses" asp-multiple="true" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="PaymentStatusIds" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="PaymentStatusIds" asp-items="Model.AvailablePaymentStatuses" asp-multiple="true" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.IsLoggedInAsVendor ? Html.Raw("style='display: none;'") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="ShippingStatusIds" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="ShippingStatusIds" asp-items="Model.AvailableShippingStatuses" asp-multiple="true" />
|
||||
</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="StoreId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="StoreId" asp-items="Model.AvailableStores" />
|
||||
</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="VendorId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="VendorId" asp-items="Model.AvailableVendors" />
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.BillingPhoneEnabled)
|
||||
{
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="BillingPhone" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="BillingPhone" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="BillingEmail" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="BillingEmail" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="BillingLastName" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="BillingLastName" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailableCountries.SelectionIsNotPossible() ? Html.Raw("style=\"display:none\"") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="BillingCountryId" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="BillingCountryId" asp-items="Model.AvailableCountries" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" @(Model.AvailablePaymentMethods.SelectionIsNotPossible() || Model.IsLoggedInAsVendor ? Html.Raw("style='display: none;'") : null)>
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="PaymentMethodSystemName" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-select asp-for="PaymentMethodSystemName" asp-items="Model.AvailablePaymentMethods" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="OrderNotes" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<nop-editor asp-for="OrderNotes" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-4">
|
||||
<nop-label asp-for="GoDirectlyToCustomOrderNumber" />
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="input-group input-group-short">
|
||||
<nop-editor asp-for="GoDirectlyToCustomOrderNumber" />
|
||||
<span class="input-group-append">
|
||||
<button type="submit" id="go-to-order-by-number" name="go-to-order-by-number" 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-orders" 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 card-default">
|
||||
|
||||
<div class="card-body">
|
||||
<nop-doc-reference asp-string-resource="@T("Admin.Documentation.Reference.Orders", Docs.Orders + Utm.OnAdmin)" />
|
||||
|
||||
<div class="card-body">
|
||||
<nop-doc-reference asp-string-resource="@T("Admin.Documentation.Reference.Orders", Docs.Orders + Utm.OnAdmin)" />
|
||||
@{
|
||||
var gridModel = new DataTablesModel
|
||||
{
|
||||
Name = "orders-grid",
|
||||
UrlRead = new DataUrl("OrderList", "CustomOrder", null),
|
||||
SearchButtonId = "search-orders",
|
||||
Length = Model.PageSize,
|
||||
LengthMenu = Model.AvailablePageSizes,
|
||||
FooterCallback = !Model.IsLoggedInAsVendor ? "ordersfootercallback" : null,
|
||||
FooterColumns = !Model.IsLoggedInAsVendor ? 10 : 0,
|
||||
Filters = new List<FilterParameter>
|
||||
{
|
||||
new FilterParameter(nameof(Model.StartDate), typeof(DateTime?)),
|
||||
new FilterParameter(nameof(Model.EndDate), typeof(DateTime?)),
|
||||
new FilterParameter(nameof(Model.OrderStatusIds)),
|
||||
new FilterParameter(nameof(Model.PaymentStatusIds)),
|
||||
new FilterParameter(nameof(Model.ShippingStatusIds)),
|
||||
new FilterParameter(nameof(Model.StoreId)),
|
||||
new FilterParameter(nameof(Model.VendorId)),
|
||||
new FilterParameter(nameof(Model.WarehouseId)),
|
||||
new FilterParameter(nameof(Model.BillingEmail)),
|
||||
new FilterParameter(nameof(Model.BillingPhone)),
|
||||
new FilterParameter(nameof(Model.BillingLastName)),
|
||||
new FilterParameter(nameof(Model.BillingCountryId)),
|
||||
new FilterParameter(nameof(Model.PaymentMethodSystemName)),
|
||||
new FilterParameter(nameof(Model.ProductId)),
|
||||
new FilterParameter(nameof(Model.OrderNotes))
|
||||
}
|
||||
};
|
||||
gridModel.ColumnCollection = new List<ColumnProperty>
|
||||
{
|
||||
new ColumnProperty(nameof(OrderModel.Id))
|
||||
{
|
||||
IsMasterCheckBox = true,
|
||||
Render = new RenderCheckBox("checkbox_orders"),
|
||||
ClassName = NopColumnClassDefaults.CenterAll,
|
||||
Width = "50"
|
||||
},
|
||||
new ColumnProperty(nameof(OrderModel.CustomOrderNumber))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.CustomOrderNumber").Text,
|
||||
Width = "80"
|
||||
}
|
||||
};
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModelExtended.IsMeasured))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.IsMeasured").Text,
|
||||
Width = "100",
|
||||
Render = new RenderCustom("renderColumnIsMeasurable"),
|
||||
ClassName = NopColumnClassDefaults.CenterAll
|
||||
});
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(IOrderDto.DateOfReceipt))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.PickupDate").Text,
|
||||
Width = "100",
|
||||
Render = new RenderCustom("renderColumnPickupDateAndTime"),
|
||||
ClassName = NopColumnClassDefaults.CenterAll
|
||||
});
|
||||
|
||||
@{
|
||||
var gridModel = new DataTablesModel
|
||||
{
|
||||
Name = "orders-grid",
|
||||
UrlRead = new DataUrl("OrderList", "CustomOrder", null),
|
||||
SearchButtonId = "search-orders",
|
||||
Length = Model.PageSize,
|
||||
LengthMenu = Model.AvailablePageSizes,
|
||||
FooterCallback = !Model.IsLoggedInAsVendor ? "ordersfootercallback" : null,
|
||||
FooterColumns = !Model.IsLoggedInAsVendor ? 10 : 0,
|
||||
Filters = new List<FilterParameter>
|
||||
{
|
||||
new FilterParameter(nameof(Model.StartDate), typeof(DateTime?)),
|
||||
new FilterParameter(nameof(Model.EndDate), typeof(DateTime?)),
|
||||
new FilterParameter(nameof(Model.OrderStatusIds)),
|
||||
new FilterParameter(nameof(Model.PaymentStatusIds)),
|
||||
new FilterParameter(nameof(Model.ShippingStatusIds)),
|
||||
new FilterParameter(nameof(Model.StoreId)),
|
||||
new FilterParameter(nameof(Model.VendorId)),
|
||||
new FilterParameter(nameof(Model.WarehouseId)),
|
||||
new FilterParameter(nameof(Model.BillingEmail)),
|
||||
new FilterParameter(nameof(Model.BillingPhone)),
|
||||
new FilterParameter(nameof(Model.BillingLastName)),
|
||||
new FilterParameter(nameof(Model.BillingCountryId)),
|
||||
new FilterParameter(nameof(Model.PaymentMethodSystemName)),
|
||||
new FilterParameter(nameof(Model.ProductId)),
|
||||
new FilterParameter(nameof(Model.OrderNotes))
|
||||
}
|
||||
};
|
||||
gridModel.ColumnCollection = new List<ColumnProperty>
|
||||
{
|
||||
new ColumnProperty(nameof(OrderModel.Id))
|
||||
{
|
||||
IsMasterCheckBox = true,
|
||||
Render = new RenderCheckBox("checkbox_orders"),
|
||||
ClassName = NopColumnClassDefaults.CenterAll,
|
||||
Width = "50"
|
||||
},
|
||||
new ColumnProperty(nameof(OrderModel.CustomOrderNumber))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.CustomOrderNumber").Text,
|
||||
Width = "80"
|
||||
}
|
||||
};
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModelExtended.IsMeasurable))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.ToBeMeasured").Text,
|
||||
Width = "100",
|
||||
Render = new RenderCustom("renderColumnIsMeasurable"),
|
||||
ClassName = NopColumnClassDefaults.CenterAll
|
||||
});
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModelExtended.IsMeasured))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.IsMeasured").Text,
|
||||
Width = "100",
|
||||
Render = new RenderCustom("renderColumnIsMeasurable"),
|
||||
ClassName = NopColumnClassDefaults.CenterAll
|
||||
});
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(IOrderDto.DateOfReceipt))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.PickupDate").Text,
|
||||
Width = "100",
|
||||
//Render = new RenderCustom("renderColumnPickupDateAndTime"),
|
||||
Render = new RenderDate(),
|
||||
ClassName = NopColumnClassDefaults.CenterAll
|
||||
});
|
||||
//a vendor does not have access to this functionality
|
||||
if (!Model.IsLoggedInAsVendor)
|
||||
{
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.OrderStatus))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.OrderStatus").Text,
|
||||
Width = "100",
|
||||
Render = new RenderCustom("renderColumnOrderStatus")
|
||||
});
|
||||
}
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.PaymentStatus))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.PaymentStatus").Text,
|
||||
Width = "150"
|
||||
});
|
||||
//a vendor does not have access to this functionality
|
||||
if (!Model.IsLoggedInAsVendor)
|
||||
{
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.ShippingStatus))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.ShippingStatus").Text,
|
||||
Width = "150"
|
||||
});
|
||||
}
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.CustomerEmail))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.Customer").Text,
|
||||
Render = new RenderCustom("renderColumnCustomer")
|
||||
});
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.StoreName))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.Store").Text,
|
||||
Width = "100",
|
||||
Visible = (await storeService.GetAllStoresAsync()).Count > 1
|
||||
});
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.CreatedOn))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.CreatedOn").Text,
|
||||
Width = "120",
|
||||
Render = new RenderDate()
|
||||
});
|
||||
//a vendor does not have access to this functionality
|
||||
if (!Model.IsLoggedInAsVendor)
|
||||
{
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.OrderTotal))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.OrderTotal").Text,
|
||||
Width = "100",
|
||||
});
|
||||
}
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.Id))
|
||||
{
|
||||
Title = T("Admin.Common.View").Text,
|
||||
Width = "50",
|
||||
ClassName = NopColumnClassDefaults.Button,
|
||||
Render = new RenderButtonView(new DataUrl("~/Admin/Order/Edit"))
|
||||
});
|
||||
var orderSummaryColumnNumber = 8;
|
||||
}
|
||||
@await Html.PartialAsync("Table", gridModel)
|
||||
|
||||
//a vendor does not have access to this functionality
|
||||
if (!Model.IsLoggedInAsVendor)
|
||||
{
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.OrderStatus))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.OrderStatus").Text,
|
||||
Width = "100",
|
||||
Render = new RenderCustom("renderColumnOrderStatus")
|
||||
});
|
||||
}
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.PaymentStatus))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.PaymentStatus").Text,
|
||||
Width = "150"
|
||||
});
|
||||
//a vendor does not have access to this functionality
|
||||
if (!Model.IsLoggedInAsVendor)
|
||||
{
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.ShippingStatus))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.ShippingStatus").Text,
|
||||
Width = "150"
|
||||
});
|
||||
}
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.CustomerEmail))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.Customer").Text,
|
||||
Render = new RenderCustom("renderColumnCustomer")
|
||||
});
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.StoreName))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.Store").Text,
|
||||
Width = "100",
|
||||
Visible = (await storeService.GetAllStoresAsync()).Count > 1
|
||||
});
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.CreatedOn))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.CreatedOn").Text,
|
||||
Width = "120",
|
||||
Render = new RenderDate()
|
||||
});
|
||||
//a vendor does not have access to this functionality
|
||||
if (!Model.IsLoggedInAsVendor)
|
||||
{
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.OrderTotal))
|
||||
{
|
||||
Title = T("Admin.Orders.Fields.OrderTotal").Text,
|
||||
Width = "100",
|
||||
});
|
||||
}
|
||||
gridModel.ColumnCollection.Add(new ColumnProperty(nameof(OrderModel.Id))
|
||||
{
|
||||
Title = T("Admin.Common.View").Text,
|
||||
Width = "50",
|
||||
ClassName = NopColumnClassDefaults.Button,
|
||||
Render = new RenderButtonView(new DataUrl("~/Admin/Order/Edit"))
|
||||
});
|
||||
var orderSummaryColumnNumber = 8;
|
||||
}
|
||||
@await Html.PartialAsync("Table", gridModel)
|
||||
<script>
|
||||
function renderColumnOrderStatus(data, type, row, meta) {
|
||||
try {
|
||||
console.log("OrderStatus render - data:", data, "row:", row);
|
||||
|
||||
<script>
|
||||
function renderColumnOrderStatus(data, type, row, meta) {
|
||||
try {
|
||||
console.log("OrderStatus render - data:", data, "row:", row);
|
||||
if (!row.OrderStatusId) {
|
||||
console.error("OrderStatusId is missing from row data");
|
||||
return data || '';
|
||||
}
|
||||
|
||||
if (!row.OrderStatusId) {
|
||||
console.error("OrderStatusId is missing from row data");
|
||||
return data || '';
|
||||
}
|
||||
var color;
|
||||
switch (row.OrderStatusId) {
|
||||
case 10: color = 'yellow'; break;
|
||||
case 20: color = 'blue'; break;
|
||||
case 30: color = 'green'; break;
|
||||
case 40: color = 'red'; break;
|
||||
default: color = 'gray';
|
||||
}
|
||||
return '<span class="grid-report-item ' + color + '">' + data + '</span>';
|
||||
} catch (e) {
|
||||
console.error("Error in renderColumnOrderStatus:", e);
|
||||
return data || '';
|
||||
}
|
||||
}
|
||||
|
||||
var color;
|
||||
switch (row.OrderStatusId) {
|
||||
case 10: color = 'yellow'; break;
|
||||
case 20: color = 'blue'; break;
|
||||
case 30: color = 'green'; break;
|
||||
case 40: color = 'red'; break;
|
||||
default: color = 'gray';
|
||||
}
|
||||
return '<span class="grid-report-item ' + color + '">' + data + '</span>';
|
||||
} catch (e) {
|
||||
console.error("Error in renderColumnOrderStatus:", e);
|
||||
return data || '';
|
||||
}
|
||||
}
|
||||
function renderColumnCustomer(data, type, row, meta) {
|
||||
console.log("Hello World 2");
|
||||
var link = '@Url.Content("~/Admin/Customer/Edit/")' + row.CustomerId;
|
||||
var textRenderer = $.fn.dataTable.render.text().display;
|
||||
return `${textRenderer(row.CustomerFullName)} <br /><a href="${link}">${data}</a > `;
|
||||
}
|
||||
|
||||
function renderColumnCustomer(data, type, row, meta) {
|
||||
console.log("Hello World 2");
|
||||
var link = '@Url.Content("~/Admin/Customer/Edit/")' + row.CustomerId;
|
||||
var textRenderer = $.fn.dataTable.render.text().display;
|
||||
return `${textRenderer(row.CustomerFullName)} <br /><a href="${link}">${data}</a > `;
|
||||
}
|
||||
function renderColumnIsMeasurable(data, type, row, meta) {
|
||||
if(data === true) {
|
||||
return '<span class="badge badge-warning" disabled>Yes</span>';
|
||||
}
|
||||
return '<span class="badge badge-secondary" disabled>No</span>';
|
||||
}
|
||||
|
||||
function renderColumnIsMeasurable(data, type, row, meta) {
|
||||
if(data === true) {
|
||||
return '<span class="badge badge-warning" disabled>Yes</span>';
|
||||
}
|
||||
return '<span class="badge badge-secondary" disabled>No</span>';
|
||||
}
|
||||
function renderColumnPickupDateAndTime(data, type, row, meta) {
|
||||
if (!data) return '<span>-</span>';
|
||||
|
||||
function renderColumnPickupDateAndTime(data, type, row, meta) {
|
||||
const date = new Date(data);
|
||||
|
||||
var formattedDate = "";
|
||||
if(data != null) formattedDate = data;//.toString();//('YYYY-MM-DD HH:mm');
|
||||
// Format: "Oct 18, 2025, Friday 14:30"
|
||||
const options = {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
weekday: 'long',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false // Use true for 12-hour format with AM/PM
|
||||
};
|
||||
|
||||
return `<span>${formattedDate}</span>`;
|
||||
}
|
||||
const formatted = date.toLocaleString('en-US', options);
|
||||
|
||||
$(function() {
|
||||
$("#@Html.IdFor(model => model.GoDirectlyToCustomOrderNumber)").keydown(
|
||||
function(event) {
|
||||
if (event.keyCode === 13) {
|
||||
$("#go-to-order-by-number").trigger("click");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
function ordersfootercallback(tfoot, data, start, end, display) {
|
||||
//update order totals summary
|
||||
var postData = {
|
||||
StartDate: $('#@Html.IdFor(model => model.StartDate)').val(),
|
||||
EndDate: $('#@Html.IdFor(model => model.EndDate)').val(),
|
||||
OrderStatusIds: $('#@Html.IdFor(model => model.OrderStatusIds)').val(),
|
||||
PaymentStatusIds: $('#@Html.IdFor(model => model.PaymentStatusIds)').val(),
|
||||
ShippingStatusIds: $('#@Html.IdFor(model => model.ShippingStatusIds)').val(),
|
||||
StoreId: $('#@Html.IdFor(model => model.StoreId)').val(),
|
||||
VendorId: $('#@Html.IdFor(model => model.VendorId)').val(),
|
||||
WarehouseId: $('#@Html.IdFor(model => model.WarehouseId)').val(),
|
||||
BillingEmail: $('#@Html.IdFor(model => model.BillingEmail)').val(),
|
||||
BillingPhone: $('#@Html.IdFor(model => model.BillingPhone)').val(),
|
||||
BillingLastName: $('#@Html.IdFor(model => model.BillingLastName)').val(),
|
||||
BillingCountryId: $('#@Html.IdFor(model => model.BillingCountryId)').val(),
|
||||
PaymentMethodSystemName: $('#@Html.IdFor(model => model.PaymentMethodSystemName)').val(),
|
||||
ProductId: $('#@Html.IdFor(model => model.ProductId)').val(),
|
||||
OrderNotes: $('#@Html.IdFor(model => model.OrderNotes)').val()
|
||||
};
|
||||
addAntiForgeryToken(postData);
|
||||
return `<span>${formatted}</span>`;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
cache: false,
|
||||
type: "POST",
|
||||
url: "@(Url.Action("ReportAggregates", "Order"))",
|
||||
data: postData,
|
||||
success: function (data, textStatus, jqXHR) {
|
||||
if (data) {
|
||||
for (var key in data) {
|
||||
var reportSummary = '<div><strong>@T("Admin.Orders.Report.Summary").Text</strong></div>' +
|
||||
'<div>@T("Admin.Orders.Report.Profit").Text <span>' + data['AggregatorProfit'] +'</span></div>' +
|
||||
'<div>@T("Admin.Orders.Report.Shipping").Text <span>' + data['AggregatorShipping'] + '</span></div>' +
|
||||
'<div>@T("Admin.Orders.Report.Tax").Text <span>' + data['AggregatorTax'] + '</span></div>' +
|
||||
'<div>@T("Admin.Orders.Report.Total").Text <span>' + data['AggregatorTotal'] + '</span></div>'
|
||||
var orderTotalsColumn = $('#orders-grid').DataTable().column(@(orderSummaryColumnNumber));
|
||||
$(orderTotalsColumn.footer()).html(reportSummary);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</form>
|
||||
$(function() {
|
||||
$("#@Html.IdFor(model => model.GoDirectlyToCustomOrderNumber)").keydown(
|
||||
function(event) {
|
||||
if (event.keyCode === 13) {
|
||||
$("#go-to-order-by-number").trigger("click");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
function ordersfootercallback(tfoot, data, start, end, display) {
|
||||
//update order totals summary
|
||||
var postData = {
|
||||
StartDate: $('#@Html.IdFor(model => model.StartDate)').val(),
|
||||
EndDate: $('#@Html.IdFor(model => model.EndDate)').val(),
|
||||
OrderStatusIds: $('#@Html.IdFor(model => model.OrderStatusIds)').val(),
|
||||
PaymentStatusIds: $('#@Html.IdFor(model => model.PaymentStatusIds)').val(),
|
||||
ShippingStatusIds: $('#@Html.IdFor(model => model.ShippingStatusIds)').val(),
|
||||
StoreId: $('#@Html.IdFor(model => model.StoreId)').val(),
|
||||
VendorId: $('#@Html.IdFor(model => model.VendorId)').val(),
|
||||
WarehouseId: $('#@Html.IdFor(model => model.WarehouseId)').val(),
|
||||
BillingEmail: $('#@Html.IdFor(model => model.BillingEmail)').val(),
|
||||
BillingPhone: $('#@Html.IdFor(model => model.BillingPhone)').val(),
|
||||
BillingLastName: $('#@Html.IdFor(model => model.BillingLastName)').val(),
|
||||
BillingCountryId: $('#@Html.IdFor(model => model.BillingCountryId)').val(),
|
||||
PaymentMethodSystemName: $('#@Html.IdFor(model => model.PaymentMethodSystemName)').val(),
|
||||
ProductId: $('#@Html.IdFor(model => model.ProductId)').val(),
|
||||
OrderNotes: $('#@Html.IdFor(model => model.OrderNotes)').val()
|
||||
};
|
||||
addAntiForgeryToken(postData);
|
||||
|
||||
$.ajax({
|
||||
cache: false,
|
||||
type: "POST",
|
||||
url: "@(Url.Action("ReportAggregates", "Order"))",
|
||||
data: postData,
|
||||
success: function (data, textStatus, jqXHR) {
|
||||
if (data) {
|
||||
for (var key in data) {
|
||||
var reportSummary = '<div><strong>@T("Admin.Orders.Report.Summary").Text</strong></div>' +
|
||||
'<div>@T("Admin.Orders.Report.Profit").Text <span>' + data['AggregatorProfit'] +'</span></div>' +
|
||||
'<div>@T("Admin.Orders.Report.Shipping").Text <span>' + data['AggregatorShipping'] + '</span></div>' +
|
||||
'<div>@T("Admin.Orders.Report.Tax").Text <span>' + data['AggregatorTax'] + '</span></div>' +
|
||||
'<div>@T("Admin.Orders.Report.Total").Text <span>' + data['AggregatorTotal'] + '</span></div>'
|
||||
var orderTotalsColumn = $('#orders-grid').DataTable().column(@(orderSummaryColumnNumber));
|
||||
$(orderTotalsColumn.footer()).html(reportSummary);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</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>
|
||||
|
||||
@*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="Order" asp-action="ExportXmlSelected" method="post" id="export-xml-selected-form">
|
||||
<input type="hidden" id="selectedIds" name="selectedIds" value="" />
|
||||
<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.Orders.NoOrders")");
|
||||
$("#exportXmlSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#export-xml-selected-form #selectedIds').val(ids);
|
||||
$('#export-xml-selected-form').submit();
|
||||
updateTable('#orders-grid');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
$(function() {
|
||||
$('#exportxml-selected').click(function (e) {
|
||||
e.preventDefault();
|
||||
var ids = selectedIds.join(",");
|
||||
if (!ids) {
|
||||
$('#exportXmlSelected-info').text("@T("Admin.Orders.NoOrders")");
|
||||
$("#exportXmlSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#export-xml-selected-form #selectedIds').val(ids);
|
||||
$('#export-xml-selected-form').submit();
|
||||
updateTable('#orders-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="Order" asp-action="ExportExcelSelected" method="post" id="export-excel-selected-form">
|
||||
<input type="hidden" id="selectedIds" name="selectedIds" value="" />
|
||||
<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.Orders.NoOrders")");
|
||||
$("#exportExcelSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#export-excel-selected-form #selectedIds').val(ids);
|
||||
$('#export-excel-selected-form').submit();
|
||||
updateTable('#orders-grid');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
$(function() {
|
||||
$('#exportexcel-selected').click(function (e) {
|
||||
e.preventDefault();
|
||||
var ids = selectedIds.join(",");
|
||||
if (!ids) {
|
||||
$('#exportExcelSelected-info').text("@T("Admin.Orders.NoOrders")");
|
||||
$("#exportExcelSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#export-excel-selected-form #selectedIds').val(ids);
|
||||
$('#export-excel-selected-form').submit();
|
||||
updateTable('#orders-grid');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<nop-alert asp-alert-id="exportExcelSelected" />
|
||||
|
||||
@*Print packaging slips 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="Order" asp-action="PdfInvoiceSelected" method="post" id="pdf-invoice-selected-form">
|
||||
<input type="hidden" id="selectedIds" name="selectedIds" value="" />
|
||||
<input type="hidden" id="selectedIds" name="selectedIds" value="" />
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
$('#pdf-invoice-selected').click(function (e) {
|
||||
e.preventDefault();
|
||||
var ids = selectedIds.join(",");
|
||||
if (!ids) {
|
||||
$('#pdfInvoiceSelected-info').text("@T("Admin.Orders.NoOrders")");
|
||||
$("#pdfInvoiceSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#pdf-invoice-selected-form #selectedIds').val(ids);
|
||||
$('#pdf-invoice-selected-form').submit();
|
||||
updateTable('#orders-grid');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
$(function() {
|
||||
$('#pdf-invoice-selected').click(function (e) {
|
||||
e.preventDefault();
|
||||
var ids = selectedIds.join(",");
|
||||
if (!ids) {
|
||||
$('#pdfInvoiceSelected-info').text("@T("Admin.Orders.NoOrders")");
|
||||
$("#pdfInvoiceSelected").trigger("click");
|
||||
}
|
||||
else {
|
||||
$('#pdf-invoice-selected-form #selectedIds').val(ids);
|
||||
$('#pdf-invoice-selected-form').submit();
|
||||
updateTable('#orders-grid');
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<nop-alert asp-alert-id="pdfInvoiceSelected" />
|
||||
|
||||
@*import orders 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="Order" asp-action="ImportFromXlsx" method="post" enctype="multipart/form-data">
|
||||
<div class="form-horizontal">
|
||||
<div class="modal-body">
|
||||
<ul class="common-list">
|
||||
<li>
|
||||
<em>@T("Admin.Orders.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 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="Order" asp-action="ImportFromXlsx" method="post" enctype="multipart/form-data">
|
||||
<div class="form-horizontal">
|
||||
<div class="modal-body">
|
||||
<ul class="common-list">
|
||||
<li>
|
||||
<em>@T("Admin.Orders.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>
|
||||
|
||||
@*create new order form*@
|
||||
<div id="create-order-window" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="create-order-window-title">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title" id="create-order-window-title">@T("Admin.Orders.AddNew")</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
</div>
|
||||
<form asp-controller="CustomOrder" asp-action="Create" method="post" id="create-order-form">
|
||||
<div class="form-horizontal">
|
||||
<div class="modal-body">
|
||||
<!-- Customer Selection -->
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<div class="label-wrapper">
|
||||
<label class="col-form-label">
|
||||
@T("Admin.Orders.Fields.Customer")
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="create-order-customer-search" autocomplete="off" class="form-control" placeholder="Type customer name or email..." />
|
||||
<span id="create-order-customer-name" class="mt-2 d-inline-block"></span>
|
||||
<input type="hidden" id="create-order-customer-id" name="customerId" value="" />
|
||||
<span class="field-validation-error" id="create-order-customer-error" style="display:none;">Please select a customer</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Product Selection -->
|
||||
<div class="form-group row" id="product-search-section" style="display:none;">
|
||||
<div class="col-md-3">
|
||||
<div class="label-wrapper">
|
||||
<label class="col-form-label">
|
||||
@T("Admin.Orders.Fields.Product")
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<input type="text" id="create-order-product-search" autocomplete="off" class="form-control" placeholder="Type product name or SKU..." />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Selected Products List -->
|
||||
<div id="selected-products-section" style="display:none;">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12">
|
||||
<label class="col-form-label"><strong>Selected Products:</strong></label>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-bordered" id="selected-products-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th style="width: 100px;">Quantity</th>
|
||||
<th style="width: 120px;">Price</th>
|
||||
<th style="width: 50px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="selected-products-body">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Hidden input for products JSON -->
|
||||
<input type="hidden" id="order-products-json" name="orderProductsJson" value="" />
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
||||
@T("Admin.Common.Cancel")
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary" id="create-order-submit">
|
||||
@T("Admin.Common.Create")
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
/* Fix z-index for autocomplete dropdown in modal */
|
||||
.ui-autocomplete {
|
||||
z-index: 1060 !important;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#selected-products-table input[type="number"] {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
#selected-products-table input[type="text"] {
|
||||
width: 100px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
var selectedProducts = [];
|
||||
|
||||
// Customer autocomplete
|
||||
$('#create-order-customer-search').autocomplete({
|
||||
delay: 500,
|
||||
minLength: 2,
|
||||
source: '@Url.Action("CustomerSearchAutoComplete", "CustomOrder")',
|
||||
select: function(event, ui) {
|
||||
$('#create-order-customer-id').val(ui.item.value);
|
||||
$('#create-order-customer-name').html('<strong>' + ui.item.label + '</strong>');
|
||||
$('#create-order-customer-search').val('');
|
||||
$('#create-order-customer-error').hide();
|
||||
|
||||
// Show product search section after customer is selected
|
||||
$('#product-search-section').slideDown();
|
||||
$('#create-order-product-search').focus();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Product autocomplete
|
||||
$('#create-order-product-search').autocomplete({
|
||||
delay: 500,
|
||||
minLength: 2,
|
||||
source: '@Url.Action("ProductSearchAutoComplete", "CustomOrder")',
|
||||
select: function(event, ui) {
|
||||
addProduct(ui.item);
|
||||
$('#create-order-product-search').val('');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Add product to selected list
|
||||
function addProduct(product) {
|
||||
var existingProduct = selectedProducts.find(p => p.id === product.value);
|
||||
if (existingProduct) {
|
||||
alert('This product is already added to the order.');
|
||||
return;
|
||||
}
|
||||
|
||||
var productItem = {
|
||||
id: product.value,
|
||||
name: product.label,
|
||||
sku: product.sku || '',
|
||||
quantity: 1,
|
||||
price: product.price || 0
|
||||
};
|
||||
|
||||
selectedProducts.push(productItem);
|
||||
renderProductsList();
|
||||
updateProductsJson();
|
||||
}
|
||||
|
||||
// Render products list
|
||||
function renderProductsList() {
|
||||
var tbody = $('#selected-products-body');
|
||||
tbody.empty();
|
||||
|
||||
if (selectedProducts.length === 0) {
|
||||
$('#selected-products-section').hide();
|
||||
return;
|
||||
}
|
||||
|
||||
$('#selected-products-section').show();
|
||||
|
||||
selectedProducts.forEach(function(product, index) {
|
||||
var row = $('<tr>');
|
||||
|
||||
var nameCell = $('<td>').html(
|
||||
'<strong>' + product.name + '</strong>' +
|
||||
(product.sku ? '<br><small>SKU: ' + product.sku + '</small>' : '')
|
||||
);
|
||||
|
||||
var quantityCell = $('<td>').html(
|
||||
'<input type="number" class="form-control form-control-sm" min="1" value="' + product.quantity + '" data-index="' + index + '" />'
|
||||
);
|
||||
|
||||
var priceCell = $('<td>').html(
|
||||
'<input type="text" class="form-control form-control-sm" value="' + product.price + '" data-index="' + index + '" />'
|
||||
);
|
||||
|
||||
var removeCell = $('<td class="text-center">').html(
|
||||
'<button type="button" class="btn btn-sm btn-danger" data-index="' + index + '"><i class="fas fa-trash"></i></button>'
|
||||
);
|
||||
|
||||
row.append(nameCell).append(quantityCell).append(priceCell).append(removeCell);
|
||||
tbody.append(row);
|
||||
});
|
||||
}
|
||||
|
||||
// Update quantity
|
||||
$(document).on('change', '#selected-products-body input[type="number"]', function() {
|
||||
var index = $(this).data('index');
|
||||
var newQuantity = parseInt($(this).val());
|
||||
if (newQuantity > 0) {
|
||||
selectedProducts[index].quantity = newQuantity;
|
||||
updateProductsJson();
|
||||
} else {
|
||||
$(this).val(selectedProducts[index].quantity);
|
||||
}
|
||||
});
|
||||
|
||||
// Update price
|
||||
$(document).on('change', '#selected-products-body input[type="text"]', function() {
|
||||
var index = $(this).data('index');
|
||||
var newPrice = parseFloat($(this).val());
|
||||
if (!isNaN(newPrice) && newPrice >= 0) {
|
||||
selectedProducts[index].price = newPrice;
|
||||
updateProductsJson();
|
||||
} else {
|
||||
$(this).val(selectedProducts[index].price);
|
||||
}
|
||||
});
|
||||
|
||||
// Remove product
|
||||
$(document).on('click', '#selected-products-body button[data-index]', function() {
|
||||
var index = $(this).data('index');
|
||||
selectedProducts.splice(index, 1);
|
||||
renderProductsList();
|
||||
updateProductsJson();
|
||||
});
|
||||
|
||||
// Update hidden JSON field
|
||||
function updateProductsJson() {
|
||||
$('#order-products-json').val(JSON.stringify(selectedProducts));
|
||||
}
|
||||
|
||||
// Validate form submission
|
||||
$('#create-order-form').on('submit', function(e) {
|
||||
var customerId = $('#create-order-customer-id').val();
|
||||
if (!customerId || customerId === '0' || customerId === '') {
|
||||
e.preventDefault();
|
||||
$('#create-order-customer-error').show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Clear error when typing
|
||||
$('#create-order-customer-search').on('input', function() {
|
||||
$('#create-order-customer-error').hide();
|
||||
});
|
||||
|
||||
// Reset form when modal is closed
|
||||
$('#create-order-window').on('hidden.bs.modal', function () {
|
||||
$('#create-order-customer-search').val('');
|
||||
$('#create-order-customer-id').val('');
|
||||
$('#create-order-customer-name').html('');
|
||||
$('#create-order-customer-error').hide();
|
||||
$('#create-order-product-search').val('');
|
||||
$('#product-search-section').hide();
|
||||
selectedProducts = [];
|
||||
renderProductsList();
|
||||
updateProductsJson();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
@ -24,14 +24,22 @@
|
|||
})
|
||||
.Columns(c => {
|
||||
c.Add().DataField("Id").AllowEditing(false);
|
||||
c.Add().DataField("Partner.Name").AllowEditing(false);
|
||||
c.Add().DataField("PartnerId")
|
||||
.AllowEditing(true)
|
||||
.Lookup(lookup => lookup
|
||||
.DataSource(d => d.Mvc().Controller("ManagementPage").LoadAction("GetAllPartners").Key("Id"))
|
||||
.ValueExpr("Id")
|
||||
.DisplayExpr("Name")
|
||||
)
|
||||
.EditCellTemplate(new TemplateName("DropDownBoxTemplate"))
|
||||
.Width(150);
|
||||
c.Add()
|
||||
.Caption("Items in order")
|
||||
.DataType(GridColumnDataType.Number)
|
||||
.CalculateCellValue("calculateItemsCount").AllowEditing(false);
|
||||
c.Add().DataField("PartnerId");
|
||||
@* c.Add().DataField("PartnerId"); *@
|
||||
c.Add().DataField("DocumentIdNumber");
|
||||
c.Add().DataField("IsAllMeasured");
|
||||
c.Add().DataField("IsAllMeasured").AllowEditing(false);
|
||||
c.Add()
|
||||
.Caption("Completed")
|
||||
.DataType(GridColumnDataType.Boolean)
|
||||
|
|
@ -54,8 +62,10 @@
|
|||
);
|
||||
});
|
||||
})
|
||||
.MasterDetail(md => md.Enabled(true).Template(new TemplateName("masterDetailTemplate")))
|
||||
)
|
||||
.MasterDetail(md => md.Enabled(true).Template(new TemplateName("masterDetailTemplate"))
|
||||
)
|
||||
)
|
||||
|
||||
</div>
|
||||
|
||||
@using (Html.DevExtreme().NamedTemplate("masterDetailTemplate"))
|
||||
|
|
@ -108,6 +118,40 @@
|
|||
}
|
||||
}
|
||||
|
||||
@using(Html.DevExtreme().NamedTemplate("DropDownBoxTemplate")) {
|
||||
@(Html.DevExtreme().DropDownBox()
|
||||
.DataSource(d => d.Mvc().Controller("ManagementPage").LoadAction("GetAllPartners").Key("Id"))
|
||||
.Value(new JS("value"))
|
||||
.ValueExpr("Id")
|
||||
.InputAttr("aria-label", "Partner")
|
||||
.DisplayExpr("Name")
|
||||
.DropDownOptions(options => options.Width(500))
|
||||
.Option("setValue", new JS("setValue"))
|
||||
.ContentTemplate(new TemplateName("ContentTemplate"))
|
||||
)
|
||||
}
|
||||
|
||||
@using(Html.DevExtreme().NamedTemplate("ContentTemplate")) {
|
||||
@(Html.DevExtreme().DataGrid()
|
||||
.DataSource(d => d.Mvc().Controller("ManagementPage").LoadAction("GetAllPartners").Key("Id"))
|
||||
.RemoteOperations(true)
|
||||
.Height(250)
|
||||
.Columns(c => {
|
||||
c.Add().DataField("Name");
|
||||
c.Add().DataField("Country");
|
||||
c.Add().DataField("TaxId");
|
||||
})
|
||||
.Scrolling(s => s.Mode(GridScrollingMode.Virtual))
|
||||
.HoverStateEnabled(true)
|
||||
.Selection(s => s.Mode(SelectionMode.Single))
|
||||
.SelectedRowKeys(new JS("component.option('value') !== undefined && component.option('value') !== null ? [component.option('value')] : []"))
|
||||
.FocusedRowEnabled(true)
|
||||
.FocusedRowKey(new JS("component.option('value')"))
|
||||
.OnContextMenuPreparing("function(e) { e.items = [] }")
|
||||
.OnSelectionChanged("function(e) { onPartnerSelectionChanged(e, component) }")
|
||||
)
|
||||
}
|
||||
|
||||
<script>
|
||||
// Store the parent grid model as JSON
|
||||
var parentGridModel = @Html.Raw(Json.Serialize(Model));
|
||||
|
|
@ -180,10 +224,6 @@
|
|||
});
|
||||
}
|
||||
|
||||
function onSelectionChanged(data) {
|
||||
let dataGrid = $("#orderDataGridContainer").dxDataGrid("instance");
|
||||
dataGrid.option("toolbar.items[1].options.disabled", !data.selectedRowsData.length);
|
||||
}
|
||||
|
||||
function onRowExpanded(e) {
|
||||
// Trigger loading of first tab when row expands
|
||||
|
|
@ -196,4 +236,20 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
function onInitNewRow(e) {
|
||||
// Replace this with actual default values and db insert
|
||||
|
||||
}
|
||||
|
||||
function onPartnerSelectionChanged(e, dropDownBoxComponent) {
|
||||
var selectedRowKey = e.selectedRowKeys[0];
|
||||
|
||||
if (e.selectedRowKeys.length > 0) {
|
||||
dropDownBoxComponent.option('value', selectedRowKey);
|
||||
dropDownBoxComponent.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
|
@ -131,6 +131,11 @@ public class RouteProvider : IRouteProvider
|
|||
name: "Plugin.FruitBank.Admin.Orders.SaveOrderAttributes",
|
||||
pattern: "Admin/CustomOrder/SaveOrderAttributes",
|
||||
defaults: new { controller = "CustomOrder", action = "SaveOrderAttributes", area = AreaNames.ADMIN });
|
||||
|
||||
endpointRouteBuilder.MapControllerRoute(
|
||||
name: "Plugin.FruitBank.Admin.Orders.CustomerSearchAutoComplete",
|
||||
pattern: "Admin/CustomOrder/CustomerSearchAutoComplete",
|
||||
defaults: new { controller = "CustomOrder", action = "CustomerSearchAutoComplete", area = AreaNames.ADMIN });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
using System.Linq;
|
||||
using FruitBank.Common.Interfaces;
|
||||
using FruitBank.Common.Interfaces;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Nop.Core;
|
||||
using Nop.Core.Domain.Catalog;
|
||||
using Nop.Core.Domain.Orders;
|
||||
using Nop.Core.Events;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models;
|
||||
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
|
||||
using Nop.Services.Catalog;
|
||||
using Nop.Services.Common;
|
||||
|
|
@ -13,10 +15,11 @@ using Nop.Services.Plugins;
|
|||
using Nop.Web.Framework.Events;
|
||||
using Nop.Web.Framework.Menu;
|
||||
using Nop.Web.Models.Sitemap;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
||||
{
|
||||
public class EventConsumer : BaseAdminMenuCreatedEventConsumer, IConsumer<OrderPlacedEvent>, IConsumer<AdminMenuCreatedEvent>
|
||||
public class EventConsumer : BaseAdminMenuCreatedEventConsumer, IConsumer<OrderPlacedEvent>, IConsumer<EntityUpdatedEvent<Order>>, IConsumer<AdminMenuCreatedEvent>
|
||||
{
|
||||
private readonly IGenericAttributeService _genericAttributeService;
|
||||
private readonly IProductService _productService;
|
||||
|
|
@ -27,6 +30,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
|||
private readonly IStoreContext _storeContext;
|
||||
private readonly IAdminMenu _adminMenu;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly FruitBankAttributeService _fruitBankAttributeService;
|
||||
|
||||
public EventConsumer(
|
||||
IGenericAttributeService genericAttributeService,
|
||||
|
|
@ -38,7 +43,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
|||
IWorkContext workContext,
|
||||
IStoreContext storeContext,
|
||||
IAdminMenu adminMenu,
|
||||
ILocalizationService localizationService) : base(pluginManager)
|
||||
ILocalizationService localizationService,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
FruitBankAttributeService fruitBankAttributeService) : base(pluginManager)
|
||||
{
|
||||
_genericAttributeService = genericAttributeService;
|
||||
_productService = productService;
|
||||
|
|
@ -49,6 +56,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
|||
_storeContext = storeContext;
|
||||
_adminMenu = adminMenu;
|
||||
_localizationService = localizationService;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_fruitBankAttributeService = fruitBankAttributeService;
|
||||
}
|
||||
|
||||
protected override string PluginSystemName => "Misc.FruitBankPlugin";
|
||||
|
|
@ -120,6 +129,39 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
|
|||
}
|
||||
}
|
||||
|
||||
public async Task HandleEventAsync(EntityUpdatedEvent<Order> eventMessage)
|
||||
{
|
||||
await SaveOrderCustomAttributesAsync(eventMessage.Entity);
|
||||
}
|
||||
|
||||
|
||||
private async Task SaveOrderCustomAttributesAsync(Order order)
|
||||
{
|
||||
if (order == null) return;
|
||||
|
||||
var form = _httpContextAccessor.HttpContext?.Request?.Form;
|
||||
if (form == null || form.Count == 0) return;
|
||||
|
||||
if (form.ContainsKey(nameof(IMeasurable.IsMeasurable)))
|
||||
{
|
||||
var isMeasurable = form[nameof(IMeasurable.IsMeasurable)].ToString().Contains("true");
|
||||
//var isMeasurable = CommonHelper.To<bool>(form[nameof(IMeasurable.IsMeasurable)].ToString());
|
||||
|
||||
|
||||
await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync<Order, bool>(order.Id, nameof(IMeasurable.IsMeasurable), isMeasurable);
|
||||
}
|
||||
|
||||
if (form.ContainsKey(nameof(IOrderDto.DateOfReceipt)))
|
||||
{
|
||||
var dateOfReceipt = form[nameof(IOrderDto.DateOfReceipt)];
|
||||
|
||||
await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync<Order, DateTime>(order.Id, nameof(IOrderDto.DateOfReceipt), DateTime.Parse(dateOfReceipt));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public async Task HandleEventAsync(AdminMenuCreatedEvent eventMessage)
|
||||
{
|
||||
var rootNode = eventMessage.RootMenuItem;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,43 @@
|
|||
@using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
|
||||
@model OrderAttributesModel
|
||||
|
||||
<!-- InnVoice Invoice Section -->
|
||||
<div class="card card-default mb-3">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-file-invoice"></i>
|
||||
InnVoice Invoice Management
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12">
|
||||
<div id="invoiceStatus" class="alert alert-info" style="display: none;">
|
||||
<i class="fas fa-info-circle"></i> <span id="invoiceStatusMessage"></span>
|
||||
</div>
|
||||
<div id="invoiceDetails" style="display: none;">
|
||||
<p><strong>Invoice Number:</strong> <span id="invoiceNumber"></span></p>
|
||||
<p><strong>Table ID:</strong> <span id="invoiceTableId"></span></p>
|
||||
<p>
|
||||
<a id="invoicePdfLink" href="#" target="_blank" class="btn btn-sm btn-info">
|
||||
<i class="fas fa-file-pdf"></i> View PDF
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12 text-right">
|
||||
<button type="button" id="createInvoiceBtn" class="btn btn-success">
|
||||
<i class="fas fa-file-invoice-dollar"></i> Create & Upload Invoice
|
||||
</button>
|
||||
<button type="button" id="checkInvoiceBtn" class="btn btn-secondary" style="display: none;">
|
||||
<i class="fas fa-sync"></i> Check Invoice Status
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Order Attributes Section -->
|
||||
<div class="card card-default">
|
||||
<div class="card-header">
|
||||
<i class="fas fa-tags"></i>
|
||||
|
|
@ -16,7 +53,6 @@
|
|||
<span asp-validation-for="IsMeasurable"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<nop-label asp-for="DateOfReceipt" />
|
||||
|
|
@ -26,7 +62,6 @@
|
|||
<span asp-validation-for="DateOfReceipt"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-12 text-right">
|
||||
<button type="button" id="saveAttributesBtn" class="btn btn-primary">
|
||||
|
|
@ -39,6 +74,7 @@
|
|||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// Save Order Attributes
|
||||
$("#saveAttributesBtn").click(function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
|
|
@ -46,7 +82,7 @@
|
|||
data: {
|
||||
orderId: "@Model.OrderId",
|
||||
isMeasurable: $("#@Html.IdFor(m => m.IsMeasurable)").is(":checked"),
|
||||
pickupDateTimeUtc: $("#@Html.IdFor(m => m.DateOfReceipt)").val(),
|
||||
dateOfReceipt: $("#@Html.IdFor(m => m.DateOfReceipt)").val(),
|
||||
__RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val()
|
||||
},
|
||||
success: function () {
|
||||
|
|
@ -57,6 +93,106 @@
|
|||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
// Create Invoice
|
||||
$("#createInvoiceBtn").click(function () {
|
||||
var btn = $(this);
|
||||
btn.prop("disabled", true).html('<i class="fas fa-spinner fa-spin"></i> Creating Invoice...');
|
||||
|
||||
showInvoiceStatus("Creating invoice, please wait...", "info");
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "@Url.Action("CreateInvoice", "InnVoice")",
|
||||
data: {
|
||||
orderId: "@Model.OrderId",
|
||||
__RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val()
|
||||
},
|
||||
success: function (response) {
|
||||
btn.prop("disabled", false).html('<i class="fas fa-file-invoice-dollar"></i> Create & Upload Invoice');
|
||||
|
||||
if (response.success) {
|
||||
showInvoiceStatus("Invoice created successfully!", "success");
|
||||
displayInvoiceDetails(response.data);
|
||||
$("#checkInvoiceBtn").show();
|
||||
} else {
|
||||
showInvoiceStatus("Error: " + response.message, "danger");
|
||||
}
|
||||
},
|
||||
error: function (xhr) {
|
||||
btn.prop("disabled", false).html('<i class="fas fa-file-invoice-dollar"></i> Create & Upload Invoice');
|
||||
|
||||
var errorMessage = "Error creating invoice";
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
errorMessage = xhr.responseJSON.message;
|
||||
}
|
||||
showInvoiceStatus(errorMessage, "danger");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Check Invoice Status
|
||||
$("#checkInvoiceBtn").click(function () {
|
||||
var btn = $(this);
|
||||
btn.prop("disabled", true).html('<i class="fas fa-spinner fa-spin"></i> Checking...');
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "@Url.Action("GetInvoiceStatus", "InnVoice")",
|
||||
data: {
|
||||
orderId: "@Model.OrderId"
|
||||
},
|
||||
success: function (response) {
|
||||
btn.prop("disabled", false).html('<i class="fas fa-sync"></i> Check Invoice Status');
|
||||
|
||||
if (response.success && response.data) {
|
||||
displayInvoiceDetails(response.data);
|
||||
showInvoiceStatus("Invoice details loaded", "success");
|
||||
} else {
|
||||
showInvoiceStatus("No invoice found for this order", "warning");
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
btn.prop("disabled", false).html('<i class="fas fa-sync"></i> Check Invoice Status');
|
||||
showInvoiceStatus("Error checking invoice status", "danger");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function showInvoiceStatus(message, type) {
|
||||
var statusDiv = $("#invoiceStatus");
|
||||
statusDiv.removeClass("alert-info alert-success alert-warning alert-danger")
|
||||
.addClass("alert-" + type);
|
||||
$("#invoiceStatusMessage").text(message);
|
||||
statusDiv.show();
|
||||
}
|
||||
|
||||
function displayInvoiceDetails(data) {
|
||||
$("#invoiceNumber").text(data.invoiceNumber || data.sorszam || "N/A");
|
||||
$("#invoiceTableId").text(data.tableId || "N/A");
|
||||
|
||||
if (data.printUrl) {
|
||||
$("#invoicePdfLink").attr("href", data.printUrl).show();
|
||||
} else {
|
||||
$("#invoicePdfLink").hide();
|
||||
}
|
||||
|
||||
$("#invoiceDetails").show();
|
||||
}
|
||||
|
||||
// Check if invoice exists on page load
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "@Url.Action("GetInvoiceStatus", "InnVoice")",
|
||||
data: {
|
||||
orderId: "@Model.OrderId"
|
||||
},
|
||||
success: function (response) {
|
||||
if (response.success && response.data) {
|
||||
displayInvoiceDetails(response.data);
|
||||
$("#checkInvoiceBtn").show();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in New Issue