Compare commits

...

5 Commits

Author SHA1 Message Date
Adam 0bee8979b7 Documenntype? OrderId? 2025-10-19 13:57:55 +02:00
Adam 192904558d merge 2025-10-18 18:35:45 +02:00
Adam 8aa84f1e79 merge 2025-10-18 18:35:38 +02:00
Adam 3c2394e13d Merge branch '4.80' of https://git.aycode.com/Adam/Mango.Nop.Plugins into 4.80 2025-10-15 15:27:02 +02:00
Adam d87823bb41 Create Order 2025-10-15 15:26:52 +02:00
7 changed files with 1550 additions and 640 deletions

View File

@ -5,19 +5,30 @@ using FruitBank.Common.Interfaces;
using FruitBank.Common.Server.Interfaces; using FruitBank.Common.Server.Interfaces;
using FruitBank.Common.SignalRs; using FruitBank.Common.SignalRs;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Nop.Core.Domain.Customers;
using Nop.Core.Domain.Orders; 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.Domains.DataLayer;
using Nop.Plugin.Misc.FruitBankPlugin.Factories; using Nop.Plugin.Misc.FruitBankPlugin.Factories;
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders; using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
using Nop.Services.Catalog;
using Nop.Services.Common; using Nop.Services.Common;
using Nop.Services.Customers;
using Nop.Services.Messages; using Nop.Services.Messages;
using Nop.Services.Orders; using Nop.Services.Orders;
using Nop.Services.Payments;
using Nop.Services.Security; using Nop.Services.Security;
using Nop.Web.Areas.Admin.Controllers; using Nop.Web.Areas.Admin.Controllers;
using Nop.Web.Areas.Admin.Factories; using Nop.Web.Areas.Admin.Factories;
using Nop.Web.Areas.Admin.Models.Orders; using Nop.Web.Areas.Admin.Models.Orders;
using Nop.Web.Framework; using Nop.Web.Framework;
using Nop.Web.Framework.Mvc.Filters; 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 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 IPermissionService _permissionService;
private readonly IGenericAttributeService _genericAttributeService; private readonly IGenericAttributeService _genericAttributeService;
private readonly INotificationService _notificationService; private readonly INotificationService _notificationService;
private readonly ICustomerService _customerService;
private readonly IProductService _productService;
// ... other dependencies // ... 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; _orderService = orderService;
_orderModelFactory = orderModelFactory as CustomOrderModelFactory; _orderModelFactory = orderModelFactory as CustomOrderModelFactory;
@ -41,6 +54,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
_permissionService = permissionService; _permissionService = permissionService;
_genericAttributeService = genericAttributeService; _genericAttributeService = genericAttributeService;
_notificationService = notificationService; _notificationService = notificationService;
_customerService = customerService;
_productService = productService;
// ... initialize other deps // ... initialize other deps
} }
@ -134,6 +149,377 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
return RedirectToAction("Edit", "Order", new { id = model.OrderId }); 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}"
// });
// }
//}
} }
} }

View File

@ -186,6 +186,20 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
return Json(model); 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] [HttpPost]
[RequestSizeLimit(10485760)] // 10MB [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 && file.ContentType == "application/pdf")
if (file.Length > 0) if (file.Length > 0)
{ {
if (!file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)){ if (file.ContentType.Equals("application/pdf", StringComparison.OrdinalIgnoreCase)){
try try
{ {
// Open the PDF from the IFormFile's stream directly in memory // 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}"); return StatusCode(500, $"Error processing PDF file: {ex.Message}");
} }
} }
else else //read from image
{ {
try 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()) using (var stream = file.OpenReadStream())
{ {
try try
{ {
// ✅ Use the service we implemented earlier // ✅ 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) 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 " + 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. " + "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)}" + $"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 ShippingDocumentToFiles shippingDocumentToFiles = new ShippingDocumentToFiles
{ {
ShippingDocumentId = shippingDocumentId, 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); await _dbContext.ShippingDocumentToFiles.InsertAsync(shippingDocumentToFiles);
// - IF WE DON'T HAVE PARTNERID ALREADY: read partner information // - 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 // Analyze PDF with AI to extract structured data
var aiAnalysis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText( var aiAnalysis = await _aiCalculationService.GetOpenAIPDFAnalysisFromText(
pdfText.ToString(), 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) // Parse AI response (assuming it returns JSON)

File diff suppressed because it is too large Load Diff

View File

@ -24,14 +24,22 @@
}) })
.Columns(c => { .Columns(c => {
c.Add().DataField("Id").AllowEditing(false); 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() c.Add()
.Caption("Items in order") .Caption("Items in order")
.DataType(GridColumnDataType.Number) .DataType(GridColumnDataType.Number)
.CalculateCellValue("calculateItemsCount").AllowEditing(false); .CalculateCellValue("calculateItemsCount").AllowEditing(false);
c.Add().DataField("PartnerId"); @* c.Add().DataField("PartnerId"); *@
c.Add().DataField("DocumentIdNumber"); c.Add().DataField("DocumentIdNumber");
c.Add().DataField("IsAllMeasured"); c.Add().DataField("IsAllMeasured").AllowEditing(false);
c.Add() c.Add()
.Caption("Completed") .Caption("Completed")
.DataType(GridColumnDataType.Boolean) .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> </div>
@using (Html.DevExtreme().NamedTemplate("masterDetailTemplate")) @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> <script>
// Store the parent grid model as JSON // Store the parent grid model as JSON
var parentGridModel = @Html.Raw(Json.Serialize(Model)); 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) { function onRowExpanded(e) {
// Trigger loading of first tab when row expands // 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> </script>

View File

@ -131,6 +131,11 @@ public class RouteProvider : IRouteProvider
name: "Plugin.FruitBank.Admin.Orders.SaveOrderAttributes", name: "Plugin.FruitBank.Admin.Orders.SaveOrderAttributes",
pattern: "Admin/CustomOrder/SaveOrderAttributes", pattern: "Admin/CustomOrder/SaveOrderAttributes",
defaults: new { controller = "CustomOrder", action = "SaveOrderAttributes", area = AreaNames.ADMIN }); 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> /// <summary>

View File

@ -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;
using Nop.Core.Domain.Catalog; using Nop.Core.Domain.Catalog;
using Nop.Core.Domain.Orders; using Nop.Core.Domain.Orders;
using Nop.Core.Events;
using Nop.Plugin.Misc.FruitBankPlugin.Models;
using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders; using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders;
using Nop.Services.Catalog; using Nop.Services.Catalog;
using Nop.Services.Common; using Nop.Services.Common;
@ -13,10 +15,11 @@ using Nop.Services.Plugins;
using Nop.Web.Framework.Events; using Nop.Web.Framework.Events;
using Nop.Web.Framework.Menu; using Nop.Web.Framework.Menu;
using Nop.Web.Models.Sitemap; using Nop.Web.Models.Sitemap;
using System.Linq;
namespace Nop.Plugin.Misc.FruitBankPlugin.Services 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 IGenericAttributeService _genericAttributeService;
private readonly IProductService _productService; private readonly IProductService _productService;
@ -27,6 +30,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
private readonly IStoreContext _storeContext; private readonly IStoreContext _storeContext;
private readonly IAdminMenu _adminMenu; private readonly IAdminMenu _adminMenu;
private readonly ILocalizationService _localizationService; private readonly ILocalizationService _localizationService;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly FruitBankAttributeService _fruitBankAttributeService;
public EventConsumer( public EventConsumer(
IGenericAttributeService genericAttributeService, IGenericAttributeService genericAttributeService,
@ -38,7 +43,9 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
IWorkContext workContext, IWorkContext workContext,
IStoreContext storeContext, IStoreContext storeContext,
IAdminMenu adminMenu, IAdminMenu adminMenu,
ILocalizationService localizationService) : base(pluginManager) ILocalizationService localizationService,
IHttpContextAccessor httpContextAccessor,
FruitBankAttributeService fruitBankAttributeService) : base(pluginManager)
{ {
_genericAttributeService = genericAttributeService; _genericAttributeService = genericAttributeService;
_productService = productService; _productService = productService;
@ -49,6 +56,8 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Services
_storeContext = storeContext; _storeContext = storeContext;
_adminMenu = adminMenu; _adminMenu = adminMenu;
_localizationService = localizationService; _localizationService = localizationService;
_httpContextAccessor = httpContextAccessor;
_fruitBankAttributeService = fruitBankAttributeService;
} }
protected override string PluginSystemName => "Misc.FruitBankPlugin"; 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) public async Task HandleEventAsync(AdminMenuCreatedEvent eventMessage)
{ {
var rootNode = eventMessage.RootMenuItem; var rootNode = eventMessage.RootMenuItem;

View File

@ -1,6 +1,43 @@
@using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders @using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
@model OrderAttributesModel @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 card-default">
<div class="card-header"> <div class="card-header">
<i class="fas fa-tags"></i> <i class="fas fa-tags"></i>
@ -16,7 +53,6 @@
<span asp-validation-for="IsMeasurable"></span> <span asp-validation-for="IsMeasurable"></span>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<div class="col-md-3"> <div class="col-md-3">
<nop-label asp-for="DateOfReceipt" /> <nop-label asp-for="DateOfReceipt" />
@ -26,7 +62,6 @@
<span asp-validation-for="DateOfReceipt"></span> <span asp-validation-for="DateOfReceipt"></span>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<div class="col-md-12 text-right"> <div class="col-md-12 text-right">
<button type="button" id="saveAttributesBtn" class="btn btn-primary"> <button type="button" id="saveAttributesBtn" class="btn btn-primary">
@ -39,6 +74,7 @@
<script> <script>
$(document).ready(function () { $(document).ready(function () {
// Save Order Attributes
$("#saveAttributesBtn").click(function () { $("#saveAttributesBtn").click(function () {
$.ajax({ $.ajax({
type: "POST", type: "POST",
@ -46,7 +82,7 @@
data: { data: {
orderId: "@Model.OrderId", orderId: "@Model.OrderId",
isMeasurable: $("#@Html.IdFor(m => m.IsMeasurable)").is(":checked"), 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() __RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val()
}, },
success: function () { success: function () {
@ -57,6 +93,106 @@
} }
}); });
}); });
// 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> </script>