From f5b27f8c18503420704b269cc0ff32909246cc21 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 20 Oct 2025 17:51:01 +0200 Subject: [PATCH] innvoice --- .../Controllers/CustomOrderController.cs | 7 +- .../Controllers/InnVoiceOrderController.cs | 397 +++++++ .../Admin/Controllers/InvoiceController.cs | 217 +++- .../Infrastructure/PluginNopStartup.cs | 2 + .../Infrastructure/RouteProvider.cs | 5 + .../Nop.Plugin.Misc.FruitBankPlugin.csproj | 7 - .../Services/InnVoiceOrderService.cs | 417 +++++++ .../Services/InnvoiceApiService.cs | 1029 ++++++++++------- .../Views/OrderAttributes.cshtml | 338 ++++-- 9 files changed, 1884 insertions(+), 535 deletions(-) create mode 100644 Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InnVoiceOrderController.cs create mode 100644 Nop.Plugin.Misc.AIPlugin/Services/InnVoiceOrderService.cs diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs index 72ac2c1..d447290 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/CustomOrderController.cs @@ -5,6 +5,7 @@ using FruitBank.Common.Entities; using FruitBank.Common.Interfaces; using FruitBank.Common.Server.Interfaces; using FruitBank.Common.SignalRs; +using Mango.Nop.Core.Extensions; using Mango.Nop.Core.Loggers; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; @@ -176,7 +177,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers var order = new Order { OrderGuid = Guid.NewGuid(), - CustomOrderNumber = "", + CustomOrderNumber = null, CustomerId = customerId, CustomerLanguageId = customer.LanguageId ?? 1, CustomerTaxDisplayType = TaxDisplayType.IncludingTax, @@ -217,7 +218,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers DiscountAmountInclTax = 0, DiscountAmountExclTax = 0 }; - + //var valami = product.GenericAttributes.GetValueOrNull(nameof(IOrderDto.DateOfReceipt)); TEST - A. await _orderService.InsertOrderItemAsync(orderItem); } else _logger.Error($"(productDtosById.TryGetValue(item.Id, out var product) == false); {item}"); @@ -226,7 +227,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers return true; }); - if (transactionSuccess) return RedirectToAction("Edit", new { id = order.Id }); + if (transactionSuccess) return RedirectToAction("Edit", "Order", new { id = order.Id }); _logger.Error($"(transactionSuccess == false)"); return RedirectToAction("Error", new { id = order.Id }); diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InnVoiceOrderController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InnVoiceOrderController.cs new file mode 100644 index 0000000..fd3152d --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InnVoiceOrderController.cs @@ -0,0 +1,397 @@ +using Microsoft.AspNetCore.Mvc; +using Nop.Core; +using Nop.Core.Domain.Orders; +using Nop.Plugin.Misc.FruitBankPlugin.Services; +using Nop.Services.Catalog; +using Nop.Services.Common; +using Nop.Services.Customers; +using Nop.Services.Directory; +using Nop.Services.Orders; +using Nop.Web.Framework; +using Nop.Web.Framework.Controllers; +using Nop.Web.Framework.Mvc.Filters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers +{ + [Area(AreaNames.ADMIN)] + [AuthorizeAdmin] + public class InnVoiceOrderController : BasePluginController + { + private readonly IOrderService _orderService; + private readonly IWorkContext _workContext; + private readonly IStoreContext _storeContext; + private readonly ICustomerService _customerService; + private readonly ICountryService _countryService; + private readonly IProductService _productService; + private readonly InnVoiceOrderService _innVoiceOrderService; + private readonly IGenericAttributeService _genericAttributeService; + + public InnVoiceOrderController( + IOrderService orderService, + IWorkContext workContext, + IStoreContext storeContext, + ICustomerService customerService, + ICountryService countryService, + IProductService productService, + InnVoiceOrderService innVoiceOrderService, + IGenericAttributeService genericAttributeService) + { + _orderService = orderService; + _workContext = workContext; + _storeContext = storeContext; + _customerService = customerService; + _countryService = countryService; + _productService = productService; + _innVoiceOrderService = innVoiceOrderService; + _genericAttributeService = genericAttributeService; + } + + /// + /// Create an order in InnVoice from a NopCommerce order + /// + [HttpPost] + [IgnoreAntiforgeryToken] + public async Task CreateOrder(int orderId) + { + try + { + var order = await _orderService.GetOrderByIdAsync(orderId); + if (order == null) + return Json(new { success = false, message = "Order not found" }); + + var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId); + + // Get billing address + var billingAddress = await _customerService.GetCustomerBillingAddressAsync(customer); + if (billingAddress == null) + return Json(new { success = false, message = "Billing address not found" }); + + var billingCountry = await _countryService.GetCountryByAddressAsync(billingAddress); + var billingCountryCode = billingCountry?.TwoLetterIsoCode ?? "HU"; + + // Get shipping address + var shippingAddress = await _customerService.GetCustomerShippingAddressAsync(customer); + var shippingCountry = shippingAddress != null + ? await _countryService.GetCountryByAddressAsync(shippingAddress) + : null; + var shippingCountryCode = shippingCountry?.TwoLetterIsoCode ?? billingCountryCode; + + // Create order request + var orderRequest = new OrderCreateRequest + { + VevoNev = $"{billingAddress.FirstName} {billingAddress.LastName}", + VevoIrsz = billingAddress.ZipPostalCode ?? "", + VevoTelep = billingAddress.City ?? "", + VevoUtcaHsz = $"{billingAddress.Address1} {billingAddress.Address2}".Trim(), + VevoOrszag = billingCountryCode, + VevoAdoszam = billingAddress.Company, // Or a custom field for tax number + MegrendelestombID = 1, // Configure this based on your setup + MegrendelesKelte = order.CreatedOnUtc.ToLocalTime(), + Hatarido = DateTime.Now.AddDays(7), // 7 days delivery time + Devizanem = order.CustomerCurrencyCode, + FizetesiMod = order.PaymentMethodSystemName, + Email = billingAddress.Email, + Telefon = billingAddress.PhoneNumber, + MegrendelesSzamStr = order.Id.ToString() + }; + + // Add shipping address if different + if (shippingAddress != null) + { + orderRequest.SzallNev = $"{shippingAddress.FirstName} {shippingAddress.LastName}"; + orderRequest.SzallIrsz = shippingAddress.ZipPostalCode ?? ""; + orderRequest.SzallTelep = shippingAddress.City ?? ""; + orderRequest.SzallUtcaHsz = $"{shippingAddress.Address1} {shippingAddress.Address2}".Trim(); + orderRequest.SzallOrszag = shippingCountryCode; + } + + // Add order items + var orderItems = await _orderService.GetOrderItemsAsync(order.Id); + foreach (var item in orderItems) + { + var product = await _productService.GetProductByIdAsync(item.ProductId); + + orderRequest.AddItem(new InnVoiceOrderItem + { + 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 order via API + var response = await _innVoiceOrderService.CreateOrderAsync(orderRequest); + + if (response.IsSuccess) + { + // Save the TechId, TableId, and PrintUrl to the order for future reference + await _genericAttributeService.SaveAttributeAsync( + order, + "InnVoiceOrderTechId", + response.TechId, + (await _storeContext.GetCurrentStoreAsync()).Id + ); + + await _genericAttributeService.SaveAttributeAsync( + order, + "InnVoiceOrderTableId", + response.TableId?.ToString(), + (await _storeContext.GetCurrentStoreAsync()).Id + ); + + await _genericAttributeService.SaveAttributeAsync( + order, + "InnVoiceOrderPrintLink", + response.PrintUrl?.ToString(), + (await _storeContext.GetCurrentStoreAsync()).Id + ); + + return Json(new + { + success = true, + message = "Order created successfully in InnVoice", + data = new + { + tableId = response.TableId, + techId = response.TechId, + 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}" + }); + } + } + + /// + /// Get InnVoice order status for a NopCommerce order + /// + /// + /// Get InnVoice order status for a NopCommerce order + /// + [HttpGet] + public async Task GetOrderStatus(int orderId) + { + try + { + var order = await _orderService.GetOrderByIdAsync(orderId); + if (order == null) + return Json(new { success = false, message = "Order not found" }); + + var storeId = (await _storeContext.GetCurrentStoreAsync()).Id; + + // Get saved InnVoice order TechId + var techId = await _genericAttributeService.GetAttributeAsync( + order, + "InnVoiceOrderTechId", + storeId + ); + + var printLink = await _genericAttributeService.GetAttributeAsync( + order, + "InnVoiceOrderPrintLink", + storeId + ); + + if (string.IsNullOrEmpty(techId)) + { + return Json(new + { + success = false, + message = "No InnVoice order found for this order" + }); + } + + // Fetch the order details from InnVoice API using TechId + var innVoiceOrders = await _innVoiceOrderService.GetOrderByTechIdAsync(techId); + + if (innVoiceOrders == null || innVoiceOrders.Count == 0) + { + return Json(new + { + success = false, + message = "Order not found in InnVoice" + }); + } + + var innVoiceOrder = innVoiceOrders.FirstOrDefault(); + + return Json(new + { + success = true, + data = new + { + tableId = innVoiceOrder.TableId, + techId = innVoiceOrder.TechId, + printUrl = printLink, + customerName = innVoiceOrder.CustomerName, + totalGross = innVoiceOrder.TotalGross, + currency = innVoiceOrder.Currency, + orderDate = innVoiceOrder.OrderDate + } + }); + } + catch (Exception ex) + { + return Json(new + { + success = false, + message = $"Error: {ex.Message}" + }); + } + } + + /// + /// Create multiple orders in InnVoice from multiple NopCommerce orders + /// + [HttpPost] + [IgnoreAntiforgeryToken] + public async Task CreateMultipleOrders([FromBody] int[] orderIds) + { + try + { + if (orderIds == null || orderIds.Length == 0) + return Json(new { success = false, message = "No order IDs provided" }); + + var orderRequests = new List(); + + foreach (var orderId in orderIds) + { + var order = await _orderService.GetOrderByIdAsync(orderId); + if (order == null) + continue; + + var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId); + var billingAddress = await _customerService.GetCustomerBillingAddressAsync(customer); + + if (billingAddress == null) + continue; + + var billingCountry = await _countryService.GetCountryByAddressAsync(billingAddress); + var billingCountryCode = billingCountry?.TwoLetterIsoCode ?? "HU"; + + var orderRequest = new OrderCreateRequest + { + VevoNev = $"{billingAddress.FirstName} {billingAddress.LastName}", + VevoIrsz = billingAddress.ZipPostalCode ?? "", + VevoTelep = billingAddress.City ?? "", + VevoUtcaHsz = $"{billingAddress.Address1} {billingAddress.Address2}".Trim(), + VevoOrszag = billingCountryCode, + MegrendelestombID = 1, + MegrendelesKelte = order.CreatedOnUtc.ToLocalTime(), + Hatarido = DateTime.Now.AddDays(7), + Devizanem = order.CustomerCurrencyCode, + FizetesiMod = order.PaymentMethodSystemName, + Email = billingAddress.Email, + Telefon = billingAddress.PhoneNumber, + MegrendelesSzamStr = order.Id.ToString() + }; + + // Add order items + var orderItems = await _orderService.GetOrderItemsAsync(order.Id); + foreach (var item in orderItems) + { + var product = await _productService.GetProductByIdAsync(item.ProductId); + + orderRequest.AddItem(new InnVoiceOrderItem + { + TetelNev = product?.Name ?? "Product", + AfaSzoveg = "27%", + Brutto = true, + EgysegAr = item.UnitPriceInclTax, + Mennyiseg = item.Quantity, + MennyisegEgyseg = "db", + CikkSzam = product?.Sku + }); + } + + orderRequests.Add(orderRequest); + } + + if (orderRequests.Count == 0) + return Json(new { success = false, message = "No valid orders to create" }); + + // Create orders via API + var responses = await _innVoiceOrderService.CreateOrdersAsync(orderRequests); + + var successCount = responses.Count(r => r.IsSuccess); + var failureCount = responses.Count - successCount; + + // Save TechIds for successful orders + var storeId = (await _storeContext.GetCurrentStoreAsync()).Id; + for (int i = 0; i < responses.Count && i < orderIds.Length; i++) + { + if (responses[i].IsSuccess) + { + var order = await _orderService.GetOrderByIdAsync(orderIds[i]); + if (order != null) + { + await _genericAttributeService.SaveAttributeAsync( + order, + "InnVoiceOrderTechId", + responses[i].TechId, + storeId + ); + + await _genericAttributeService.SaveAttributeAsync( + order, + "InnVoiceOrderTableId", + responses[i].TableId?.ToString(), + storeId + ); + } + } + } + + return Json(new + { + success = successCount > 0, + message = $"Created {successCount} orders successfully. {failureCount} failed.", + data = new + { + successCount, + failureCount, + responses = responses.Select(r => new + { + success = r.IsSuccess, + tableId = r.TableId, + techId = r.TechId, + message = r.Message, + printUrl = r.PrintUrl + }) + } + }); + } + catch (Exception ex) + { + return Json(new + { + success = false, + message = $"Error: {ex.Message}" + }); + } + } + } +} diff --git a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InvoiceController.cs b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InvoiceController.cs index 0273ff2..15133aa 100644 --- a/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InvoiceController.cs +++ b/Nop.Plugin.Misc.AIPlugin/Areas/Admin/Controllers/InvoiceController.cs @@ -1,28 +1,219 @@ -using Microsoft.AspNetCore.Mvc; -using Nop.Web.Areas.Admin.Controllers; -using Nop.Web.Framework.Mvc.Filters; + +using Microsoft.AspNetCore.Mvc; +using Nop.Core; +using Nop.Core.Domain.Orders; +using Nop.Plugin.Misc.FruitBankPlugin.Services; +using Nop.Services.Catalog; +using Nop.Services.Common; +using Nop.Services.Customers; +using Nop.Services.Directory; +using Nop.Services.Orders; using Nop.Web.Framework; -using Nop.Services.Security; +using Nop.Web.Framework.Controllers; +using Nop.Web.Framework.Mvc.Filters; namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers { [Area(AreaNames.ADMIN)] - [AuthorizeAdmin] - public class InvoiceController : BaseAdminController + [AuthorizeAdmin] + //[AutoValidateAntiforgeryToken] + public class InvoiceController : BasePluginController { - private readonly IPermissionService _permissionService; + private readonly IOrderService _orderService; + private readonly IWorkContext _workContext; + private readonly IStoreContext _storeContext; + private readonly ICustomerService _customerService; + private readonly ICountryService _countryService; + private readonly IStateProvinceService _stateProvinceService; + // Add your InnVoice API service + private readonly InnVoiceApiService _innVoiceApiService; + private readonly IProductService _productService; + private readonly FruitBankAttributeService _fruitBankAttributeService; - public InvoiceController(IPermissionService permissionService) + public InvoiceController( + IOrderService orderService, + IWorkContext workContext, + IStoreContext storeContext, + ICustomerService customerService, + ICountryService countryService, + IStateProvinceService stateProvinceService, + InnVoiceApiService innVoiceApiService, + IProductService productService, + FruitBankAttributeService fruitBankAttributeService) { - _permissionService = permissionService; + _orderService = orderService; + _workContext = workContext; + _storeContext = storeContext; + _customerService = customerService; + _countryService = countryService; + _stateProvinceService = stateProvinceService; + _innVoiceApiService = innVoiceApiService; + _productService = productService; + _fruitBankAttributeService = fruitBankAttributeService; } - public async Task List() + [HttpPost] + public async Task CreateInvoice(int orderId) { - if (!await _permissionService.AuthorizeAsync(StandardPermission.Security.ACCESS_ADMIN_PANEL)) - return AccessDeniedView(); + try + { + var order = await _orderService.GetOrderByIdAsync(orderId); + if (order == null) + return Json(new { success = false, message = "Order not found" }); - return View("~/Plugins/Misc.FruitBankPlugin/Areas/Admin/Views/Invoice/List.cshtml"); + var customer = await _customerService.GetCustomerByIdAsync(order.CustomerId); + + var billingAddress = await _customerService.GetCustomerBillingAddressAsync(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 = true, + Email = billingAddress.Email, + MegrendelesSzamStr = order.CustomOrderNumber, + 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 + + await _fruitBankAttributeService.InsertOrUpdateGenericAttributeAsync(orderId, nameof(InvoiceCreateResponse.TechId), response.TechId); + + 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 GetInvoiceStatus(int orderId) + { + try + { + // Get the invoice from InnVoice using the saved TechId + var techId = await _fruitBankAttributeService.GetGenericAttributeValueAsync( + orderId, + nameof(InvoiceCreateResponse.TechId), + _storeContext.GetCurrentStore().Id + ); + + if (string.IsNullOrEmpty(techId)) + { + return Json(new + { + success = false, + message = "No invoice TechId found for this order" + }); + } + + var invoices = await _innVoiceApiService.GetInvoiceByTechIdAsync(techId); + + if (invoices != null && invoices.Count > 0) + { + var invoice = invoices.FirstOrDefault(); + return Json(new + { + success = true, + data = new + { + tableId = invoice.TableId, + invoiceNumber = invoice.InvoiceNumberFormatted, // or invoice.InvoiceNumber + sorszam = invoice.InvoiceNumberFormatted, + printUrl = invoice.PrintUrl, + customerName = invoice.CustomerName, + totalGross = invoice.TotalGross, + currency = invoice.Currency, + isProforma = invoice.IsProforma, + isDraft = invoice.IsDraft + } + }); + } + + return Json(new + { + success = false, + message = "No invoice found for this order" + }); + } + catch (Exception ex) + { + return Json(new + { + success = false, + message = $"Error: {ex.Message}" + }); + } } } } \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs index 9639bf0..3082552 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/PluginNopStartup.cs @@ -81,6 +81,8 @@ public class PluginNopStartup : INopStartup services.AddScoped, EventConsumer>(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); //services.AddScoped(); //services.AddScoped(); diff --git a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs index ebd5829..f5a9aee 100644 --- a/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs +++ b/Nop.Plugin.Misc.AIPlugin/Infrastructure/RouteProvider.cs @@ -136,6 +136,11 @@ public class RouteProvider : IRouteProvider name: "Plugin.FruitBank.Admin.Orders.CustomerSearchAutoComplete", pattern: "Admin/CustomOrder/CustomerSearchAutoComplete", defaults: new { controller = "CustomOrder", action = "CustomerSearchAutoComplete", area = AreaNames.ADMIN }); + + endpointRouteBuilder.MapControllerRoute( + name: "Plugin.FruitBank.Admin.Invoice.CreateInvoice", + pattern: "Admin/Invoice/CreateInvoice", + defaults: new { controller = "Invoice", action = "CreateInvoice", area = AreaNames.ADMIN }); } /// diff --git a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj index e9fae48..0283609 100644 --- a/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj +++ b/Nop.Plugin.Misc.AIPlugin/Nop.Plugin.Misc.FruitBankPlugin.csproj @@ -7,9 +7,6 @@ true true - - - @@ -637,10 +634,6 @@ - - - - diff --git a/Nop.Plugin.Misc.AIPlugin/Services/InnVoiceOrderService.cs b/Nop.Plugin.Misc.AIPlugin/Services/InnVoiceOrderService.cs new file mode 100644 index 0000000..522b59f --- /dev/null +++ b/Nop.Plugin.Misc.AIPlugin/Services/InnVoiceOrderService.cs @@ -0,0 +1,417 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using System.Xml.Linq; +using System.Linq; + +namespace Nop.Plugin.Misc.FruitBankPlugin.Services +{ + /// + /// Service for managing orders in InnVoice API + /// + public class InnVoiceOrderService + { + private readonly HttpClient _httpClient; + private readonly string _companyName; + private readonly string _username; + private readonly string _password; + private readonly string _baseUrl; + + public InnVoiceOrderService(string companyName = "apiteszt", string username = "apiteszt", string password = "dsjfluio4324hjhjfdhkjskjh213kjgsd", string baseUrl = "https://api.innvoice.hu") + { + _companyName = companyName ?? throw new ArgumentNullException(nameof(companyName)); + _username = username ?? throw new ArgumentNullException(nameof(username)); + _password = password ?? throw new ArgumentNullException(nameof(password)); + _baseUrl = baseUrl.TrimEnd('/'); + + _httpClient = new HttpClient(); + SetupAuthentication(); + } + + private void SetupAuthentication() + { + var authToken = Convert.ToBase64String( + System.Text.Encoding.ASCII.GetBytes($"{_username}:{_password}") + ); + _httpClient.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authToken); + } + + /// + /// Create one or more orders + /// + public async Task> CreateOrdersAsync(List orders) + { + var url = $"{_baseUrl}/{_companyName}/order"; + + var xml = BuildOrdersXml(orders); + var content = new FormUrlEncodedContent(new[] + { + new KeyValuePair("data", xml) + }); + + try + { + var response = await _httpClient.PostAsync(url, content); + response.EnsureSuccessStatusCode(); + + var responseContent = await response.Content.ReadAsStringAsync(); + return ParseOrderResponses(responseContent); + } + catch (HttpRequestException ex) + { + throw new InnVoiceApiException($"Error creating orders: {ex.Message}", ex); + } + } + + /// + /// Create a single order + /// + public async Task CreateOrderAsync(OrderCreateRequest order) + { + var responses = await CreateOrdersAsync(new List { order }); + return responses.FirstOrDefault(); + } + + private string BuildOrdersXml(List orders) + { + var ordersElement = new XElement("orders"); + + foreach (var order in orders) + { + var orderElement = new XElement("order"); + + orderElement.Add(new XElement("VevoNev", new XCData(order.VevoNev ?? ""))); + orderElement.Add(new XElement("VevoIrsz", new XCData(order.VevoIrsz ?? ""))); + orderElement.Add(new XElement("VevoTelep", new XCData(order.VevoTelep ?? ""))); + orderElement.Add(new XElement("VevoUtcaHsz", new XCData(order.VevoUtcaHsz ?? ""))); + + if (!string.IsNullOrEmpty(order.VevoOrszag)) + orderElement.Add(new XElement("VevoOrszag", new XCData(order.VevoOrszag))); + + if (!string.IsNullOrEmpty(order.VevoAdoszam)) + orderElement.Add(new XElement("VevoAdoszam", new XCData(order.VevoAdoszam))); + + if (!string.IsNullOrEmpty(order.SzallNev)) + orderElement.Add(new XElement("SzallNev", new XCData(order.SzallNev))); + if (!string.IsNullOrEmpty(order.SzallIrsz)) + orderElement.Add(new XElement("SzallIrsz", new XCData(order.SzallIrsz))); + if (!string.IsNullOrEmpty(order.SzallTelep)) + orderElement.Add(new XElement("SzallTelep", new XCData(order.SzallTelep))); + if (!string.IsNullOrEmpty(order.SzallUtcaHsz)) + orderElement.Add(new XElement("SzallUtcaHsz", new XCData(order.SzallUtcaHsz))); + if (!string.IsNullOrEmpty(order.SzallOrszag)) + orderElement.Add(new XElement("SzallOrszag", new XCData(order.SzallOrszag))); + + orderElement.Add(new XElement("MegrendelestombID", new XCData(order.MegrendelestombID.ToString()))); + orderElement.Add(new XElement("MegrendelesKelte", new XCData(order.MegrendelesKelte.ToString("yyyy.MM.dd.")))); + orderElement.Add(new XElement("Hatarido", new XCData(order.Hatarido.ToString("yyyy.MM.dd.")))); + orderElement.Add(new XElement("Devizanem", new XCData(order.Devizanem ?? ""))); + + if (!string.IsNullOrEmpty(order.FizetesiMod)) + orderElement.Add(new XElement("FizetesiMod", new XCData(order.FizetesiMod))); + if (!string.IsNullOrEmpty(order.Megjegyzes)) + orderElement.Add(new XElement("Megjegyzes", new XCData(order.Megjegyzes))); + if (!string.IsNullOrEmpty(order.Email)) + orderElement.Add(new XElement("Email", new XCData(order.Email))); + if (!string.IsNullOrEmpty(order.Telefon)) + orderElement.Add(new XElement("Telefon", new XCData(order.Telefon))); + if (!string.IsNullOrEmpty(order.MegrendelesSzamStr)) + orderElement.Add(new XElement("MegrendelesSzamStr", new XCData(order.MegrendelesSzamStr))); + + // Add items + foreach (var item in order.Items) + { + var tetelElement = new XElement("tetel"); + tetelElement.Add(new XElement("TetelNev", item.TetelNev ?? "")); + tetelElement.Add(new XElement("AfaSzoveg", item.AfaSzoveg ?? "")); + tetelElement.Add(new XElement("Brutto", item.Brutto ? "1" : "0")); + tetelElement.Add(new XElement("EgysegAr", item.EgysegAr.ToString())); + tetelElement.Add(new XElement("Mennyiseg", item.Mennyiseg.ToString())); + tetelElement.Add(new XElement("MennyisegEgyseg", new XCData(item.MennyisegEgyseg ?? ""))); + + if (!string.IsNullOrEmpty(item.CikkSzam)) + tetelElement.Add(new XElement("CikkSzam", new XCData(item.CikkSzam))); + + orderElement.Add(tetelElement); + } + + ordersElement.Add(orderElement); + } + + return new XDeclaration("1.0", "UTF-8", null).ToString() + "\n" + ordersElement.ToString(); + } + + private List ParseOrderResponses(string xml) + { + var responses = new List(); + + try + { + var doc = XDocument.Parse(xml); + var orderElements = doc.Descendants("order"); + + foreach (var orderElement in orderElements) + { + var response = new OrderCreateResponse + { + ErrorCode = orderElement.Element("error")?.Value?.Trim(), + Message = orderElement.Element("message")?.Value?.Trim(), + TableId = int.TryParse(orderElement.Element("TABLE_ID")?.Value?.Trim(), out var tid) ? tid : (int?)null, + TechId = orderElement.Element("techid")?.Value?.Trim(), + PrintUrl = orderElement.Element("PrintUrl")?.Value?.Trim() + }; + + responses.Add(response); + } + } + catch (Exception ex) + { + throw new InnVoiceApiException($"Error parsing order response XML: {ex.Message}", ex); + } + + return responses; + } + + /// + /// Get order by technical ID + /// + public async Task> GetOrderByTechIdAsync(string techId) + { + var url = $"{_baseUrl}/{_companyName}/order/techid/{Uri.EscapeDataString(techId)}"; + return await GetOrdersFromUrlAsync(url); + } + + /// + /// Get order by table ID + /// + public async Task GetOrderByIdAsync(int tableId) + { + var url = $"{_baseUrl}/{_companyName}/order/id/{tableId}"; + var orders = await GetOrdersFromUrlAsync(url); + return orders.Count > 0 ? orders[0] : null; + } + + private async Task> GetOrdersFromUrlAsync(string url) + { + try + { + var response = await _httpClient.GetAsync(url); + response.EnsureSuccessStatusCode(); + + var content = await response.Content.ReadAsStringAsync(); + + // Parse XML response + return ParseOrdersFromXml(content); + } + catch (HttpRequestException ex) + { + throw new InnVoiceApiException($"Error calling InnVoice API: {ex.Message}", ex); + } + catch (Exception ex) + { + throw new InnVoiceApiException($"Error parsing API response: {ex.Message}", ex); + } + } + + private List ParseOrdersFromXml(string xml) + { + var orders = new List(); + + try + { + var doc = XDocument.Parse(xml); + var orderElements = doc.Descendants("order"); + + foreach (var orderElement in orderElements) + { + var order = new InnVoiceOrder + { + TableId = GetIntValue(orderElement, "TABLE_ID"), + VevoID = GetIntValue(orderElement, "VevoID"), + CustomerName = GetStringValue(orderElement, "VevoNev"), + CustomerZipCode = GetStringValue(orderElement, "VevoIrsz"), + CustomerCity = GetStringValue(orderElement, "VevoTelep"), + CustomerAddress = GetStringValue(orderElement, "VevoUtcaHsz"), + CustomerCountry = GetStringValue(orderElement, "VevoOrszag"), + CustomerTaxNumber = GetStringValue(orderElement, "VevoAdoszam"), + ShippingName = GetStringValue(orderElement, "SzallNev"), + ShippingZipCode = GetStringValue(orderElement, "SzallIrsz"), + ShippingCity = GetStringValue(orderElement, "SzallTelep"), + ShippingAddress = GetStringValue(orderElement, "SzallUtcaHsz"), + ShippingCountry = GetStringValue(orderElement, "SzallOrszag"), + OrderBookId = GetIntValue(orderElement, "MegrendelestombID"), + OrderDate = GetStringValue(orderElement, "MegrendelesKelte"), + DueDate = GetStringValue(orderElement, "Hatarido"), + Currency = GetStringValue(orderElement, "Devizanem"), + PaymentMethod = GetStringValue(orderElement, "FizetesiMod"), + Notes = GetStringValue(orderElement, "Megjegyzes"), + Email = GetStringValue(orderElement, "Email"), + Phone = GetStringValue(orderElement, "Telefon"), + ExternalOrderNumber = GetStringValue(orderElement, "MegrendelesSzamStr"), + TotalNet = GetDecimalValue(orderElement, "NettoErtek"), + TotalVAT = GetDecimalValue(orderElement, "AFAErtek"), + TotalGross = GetDecimalValue(orderElement, "BruttoErtek"), + TechId = GetStringValue(orderElement, "techid"), + PrintUrl = GetStringValue(orderElement, "PrintLink"), + UpdateTime = GetStringValue(orderElement, "_UpdateTime"), + QueryTime = GetStringValue(orderElement, "QueryTime") + }; + + // Parse line items + var itemElements = orderElement.Descendants("tetel"); + foreach (var itemElement in itemElements) + { + order.Items.Add(new InnVoiceOrderLineItem + { + TableId = GetIntValue(itemElement, "TABLE_ID"), + ItemName = GetStringValue(itemElement, "TetelNev"), + ArticleNumber = GetStringValue(itemElement, "CikkSzam"), + VATText = GetStringValue(itemElement, "AfaSzoveg"), + VATRate = GetDecimalValue(itemElement, "AfaKulcs"), + IsGross = GetIntValue(itemElement, "Brutto") == 1, + UnitPrice = GetDecimalValue(itemElement, "EgysegAr"), + Quantity = GetDecimalValue(itemElement, "Mennyiseg"), + Unit = GetStringValue(itemElement, "MennyisegEgyseg") + }); + } + + orders.Add(order); + } + } + catch (Exception ex) + { + throw new InnVoiceApiException($"Error parsing XML: {ex.Message}", ex); + } + + return orders; + } + + private string GetStringValue(XElement element, string name) + { + return element.Element(name)?.Value?.Trim() ?? string.Empty; + } + + private int GetIntValue(XElement element, string name) + { + var value = GetStringValue(element, name); + return int.TryParse(value, out var result) ? result : 0; + } + + private decimal GetDecimalValue(XElement element, string name) + { + var value = GetStringValue(element, name); + return decimal.TryParse(value, out var result) ? result : 0m; + } + } + + // Order Models + public class OrderCreateRequest + { + public string VevoNev { get; set; } + public string VevoIrsz { get; set; } + public string VevoTelep { get; set; } + public string VevoUtcaHsz { get; set; } + public string VevoOrszag { get; set; } + public string VevoAdoszam { get; set; } + public string SzallNev { get; set; } + public string SzallIrsz { get; set; } + public string SzallTelep { get; set; } + public string SzallUtcaHsz { get; set; } + public string SzallOrszag { get; set; } + public int MegrendelestombID { get; set; } + public DateTime MegrendelesKelte { get; set; } + public DateTime Hatarido { get; set; } + public string Devizanem { get; set; } + public string FizetesiMod { get; set; } + public string Megjegyzes { get; set; } + public string Email { get; set; } + public string Telefon { get; set; } + public string MegrendelesSzamStr { get; set; } + + public List Items { get; set; } = new List(); + + public void AddItem(InnVoiceOrderItem item) + { + Items.Add(item); + } + } + + public class InnVoiceOrderItem + { + public string TetelNev { get; set; } + public string AfaSzoveg { get; set; } + public bool Brutto { get; set; } + public decimal EgysegAr { get; set; } + public decimal Mennyiseg { get; set; } + public string MennyisegEgyseg { get; set; } + public string CikkSzam { get; set; } + } + + public class OrderCreateResponse + { + public string ErrorCode { get; set; } + public string Message { get; set; } + public int? TableId { get; set; } + public string TechId { get; set; } + public string PrintUrl { get; set; } + + public bool IsSuccess => ErrorCode == "200"; + } + + public class InnVoiceApiException : Exception + { + public InnVoiceApiException(string message) : base(message) { } + public InnVoiceApiException(string message, Exception innerException) : base(message, innerException) { } + } + + // Order GET response models + public class InnVoiceOrder + { + public int TableId { get; set; } + public int VevoID { get; set; } + public string CustomerName { get; set; } + public string CustomerZipCode { get; set; } + public string CustomerCity { get; set; } + public string CustomerAddress { get; set; } + public string CustomerCountry { get; set; } + public string CustomerTaxNumber { get; set; } + public string ShippingName { get; set; } + public string ShippingZipCode { get; set; } + public string ShippingCity { get; set; } + public string ShippingAddress { get; set; } + public string ShippingCountry { get; set; } + public int OrderBookId { get; set; } + public string OrderDate { get; set; } + public string DueDate { get; set; } + public string Currency { get; set; } + public string PaymentMethod { get; set; } + public string Notes { get; set; } + public string Email { get; set; } + public string Phone { get; set; } + public string ExternalOrderNumber { get; set; } + public decimal TotalNet { get; set; } + public decimal TotalVAT { get; set; } + public decimal TotalGross { get; set; } + public string TechId { get; set; } + public string PrintUrl { get; set; } + public string UpdateTime { get; set; } + public string QueryTime { get; set; } + + public List Items { get; set; } = new List(); + } + + public class InnVoiceOrderLineItem + { + public int TableId { get; set; } + public string ItemName { get; set; } + public string ArticleNumber { get; set; } + public string VATText { get; set; } + public decimal VATRate { get; set; } + public bool IsGross { get; set; } + public decimal UnitPrice { get; set; } + public decimal Quantity { get; set; } + public string Unit { get; set; } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Services/InnvoiceApiService.cs b/Nop.Plugin.Misc.AIPlugin/Services/InnvoiceApiService.cs index ad3a08b..628b52f 100644 --- a/Nop.Plugin.Misc.AIPlugin/Services/InnvoiceApiService.cs +++ b/Nop.Plugin.Misc.AIPlugin/Services/InnvoiceApiService.cs @@ -1,4 +1,5 @@ -using System; + +using System; using System.Collections.Generic; using System.Net.Http; using System.Threading.Tasks; @@ -10,487 +11,627 @@ using System.Text; namespace Nop.Plugin.Misc.FruitBankPlugin.Services { - /// - /// Service for interacting with InnVoice Invoice API - /// API Documentation: https://help.innvoice.hu/hc/hu/articles/360003142839 - /// - public class InnVoiceApiService - { - private readonly HttpClient _httpClient; - private readonly string _companyName; - private readonly string _username; - private readonly string _password; - private readonly string _baseUrl; + /// + /// Service for interacting with InnVoice Invoice API + /// API Documentation: https://help.innvoice.hu/hc/hu/articles/360003142839 + /// + public class InnVoiceApiService + { + private readonly HttpClient _httpClient; + private readonly string _companyName; + private readonly string _username; + private readonly string _password; + private readonly string _baseUrl; - public InnVoiceApiService(string companyName, string username, string password, string baseUrl = "https://api.innvoice.hu") - { - _companyName = companyName ?? throw new ArgumentNullException(nameof(companyName)); - _username = username ?? throw new ArgumentNullException(nameof(username)); - _password = password ?? throw new ArgumentNullException(nameof(password)); - _baseUrl = baseUrl.TrimEnd('/'); + public InnVoiceApiService(string companyName = "apiteszt", string username = "apiteszt", string password = "dsjfluio4324hjhjfdhkjskjh213kjgsd", string baseUrl = "https://api.innvoice.hu") + { + _companyName = companyName ?? throw new ArgumentNullException(nameof(companyName)); + _username = username ?? throw new ArgumentNullException(nameof(username)); + _password = password ?? throw new ArgumentNullException(nameof(password)); + _baseUrl = baseUrl.TrimEnd('/'); - _httpClient = new HttpClient(); - SetupAuthentication(); - } + _httpClient = new HttpClient(); + SetupAuthentication(); + } - private void SetupAuthentication() - { - var authToken = Convert.ToBase64String( - System.Text.Encoding.ASCII.GetBytes($"{_username}:{_password}") - ); - _httpClient.DefaultRequestHeaders.Authorization = - new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authToken); - } + private void SetupAuthentication() + { + var authToken = Convert.ToBase64String( + System.Text.Encoding.ASCII.GetBytes($"{_username}:{_password}") + ); + _httpClient.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authToken); + } - /// - /// Get all invoices - /// Rate limit: 20 times per hour without ID parameter - /// - public async Task> GetAllInvoicesAsync() - { - var url = $"{_baseUrl}/{_companyName}/invoice"; - return await GetInvoicesFromUrlAsync(url); - } + /// + /// Get all invoices + /// Rate limit: 20 times per hour without ID parameter + /// + public async Task> GetAllInvoicesAsync() + { + var url = $"{_baseUrl}/{_companyName}/invoice"; + return await GetInvoicesFromUrlAsync(url); + } - /// - /// Get invoice by internal table ID - /// - public async Task GetInvoiceByIdAsync(int tableId) - { - var url = $"{_baseUrl}/{_companyName}/invoice/id/{tableId}"; - var invoices = await GetInvoicesFromUrlAsync(url); - return invoices.Count > 0 ? invoices[0] : null; - } + /// + /// Get invoice by internal table ID + /// + public async Task GetInvoiceByIdAsync(int tableId) + { + var url = $"{_baseUrl}/{_companyName}/invoice/id/{tableId}"; + var invoices = await GetInvoicesFromUrlAsync(url); + return invoices.Count > 0 ? invoices[0] : null; + } - /// - /// Get invoice by invoice number - /// - public async Task> GetInvoiceByNumberAsync(string invoiceNumber) - { - var url = $"{_baseUrl}/{_companyName}/invoice/invoicenumber/{Uri.EscapeDataString(invoiceNumber)}"; - return await GetInvoicesFromUrlAsync(url); - } + /// + /// Get invoice by invoice number + /// + public async Task> GetInvoiceByNumberAsync(string invoiceNumber) + { + var url = $"{_baseUrl}/{_companyName}/invoice/invoicenumber/{Uri.EscapeDataString(invoiceNumber)}"; + return await GetInvoicesFromUrlAsync(url); + } - /// - /// Get invoices by creation date - /// Format: YYYYMMDD - /// - public async Task> GetInvoicesByCreationDateAsync(DateTime date) - { - var dateStr = date.ToString("yyyyMMdd"); - var url = $"{_baseUrl}/{_companyName}/invoice/created/{dateStr}"; - return await GetInvoicesFromUrlAsync(url); - } + /// + /// Get invoice by invoice number + /// + public async Task> GetInvoiceByTechIdAsync(string techId) + { + //api.innvoice.hu/%regnev%/invoice/techid/%TECHID% + var url = $"{_baseUrl}/{_companyName}/invoice/techid/{Uri.EscapeDataString(techId)}"; + return await GetInvoicesFromUrlAsync(url); + } - /// - /// Get invoices by fulfillment date - /// Format: YYYYMMDD - /// - public async Task> GetInvoicesByFulfillmentDateAsync(DateTime date) - { - var dateStr = date.ToString("yyyyMMdd"); - var url = $"{_baseUrl}/{_companyName}/invoice/fulfillment/{dateStr}"; - return await GetInvoicesFromUrlAsync(url); - } - /// - /// Get invoices by due date - /// Format: YYYYMMDD - /// - public async Task> GetInvoicesByDueDateAsync(DateTime date) - { - var dateStr = date.ToString("yyyyMMdd"); - var url = $"{_baseUrl}/{_companyName}/invoice/duedate/{dateStr}"; - return await GetInvoicesFromUrlAsync(url); - } + /// + /// Get invoices by creation date + /// Format: YYYYMMDD + /// + public async Task> GetInvoicesByCreationDateAsync(DateTime date) + { + var dateStr = date.ToString("yyyyMMdd"); + var url = $"{_baseUrl}/{_companyName}/invoice/created/{dateStr}"; + return await GetInvoicesFromUrlAsync(url); + } - /// - /// Get invoices by payment date - /// Format: YYYYMMDD - /// - public async Task> GetInvoicesByPaymentDateAsync(DateTime date) - { - var dateStr = date.ToString("yyyyMMdd"); - var url = $"{_baseUrl}/{_companyName}/invoice/paymentdate/{dateStr}"; - return await GetInvoicesFromUrlAsync(url); - } + /// + /// Get invoices by fulfillment date + /// Format: YYYYMMDD + /// + public async Task> GetInvoicesByFulfillmentDateAsync(DateTime date) + { + var dateStr = date.ToString("yyyyMMdd"); + var url = $"{_baseUrl}/{_companyName}/invoice/fulfillment/{dateStr}"; + return await GetInvoicesFromUrlAsync(url); + } - /// - /// Get invoices by customer tax number - /// - public async Task> GetInvoicesByCustomerTaxNumberAsync(string taxNumber) - { - var url = $"{_baseUrl}/{_companyName}/invoice/taxnumber/{Uri.EscapeDataString(taxNumber)}"; - return await GetInvoicesFromUrlAsync(url); - } + /// + /// Get invoices by due date + /// Format: YYYYMMDD + /// + public async Task> GetInvoicesByDueDateAsync(DateTime date) + { + var dateStr = date.ToString("yyyyMMdd"); + var url = $"{_baseUrl}/{_companyName}/invoice/duedate/{dateStr}"; + return await GetInvoicesFromUrlAsync(url); + } - /// - /// Get invoices modified since a specific timestamp - /// Format: YYYYMMDDHHmmss (year, month, day, hour, minute, second) - /// Recommended for tracking changes every 10 minutes - /// Rate limit: Full queries or queries older than current month limited to 10 times per 30 days - /// Recommended: Only use current month dates - /// - public async Task> GetInvoicesByUpdateTimeAsync(DateTime updateTime) - { - var timeStr = updateTime.ToString("yyyyMMddHHmmss"); - var url = $"{_baseUrl}/{_companyName}/invoice/updatedtime/{timeStr}"; - return await GetInvoicesFromUrlAsync(url); - } + /// + /// Get invoices by payment date + /// Format: YYYYMMDD + /// + public async Task> GetInvoicesByPaymentDateAsync(DateTime date) + { + var dateStr = date.ToString("yyyyMMdd"); + var url = $"{_baseUrl}/{_companyName}/invoice/paymentdate/{dateStr}"; + return await GetInvoicesFromUrlAsync(url); + } - private async Task> GetInvoicesFromUrlAsync(string url) - { - try - { - var response = await _httpClient.GetAsync(url); - response.EnsureSuccessStatusCode(); + /// + /// Get invoices by customer tax number + /// + public async Task> GetInvoicesByCustomerTaxNumberAsync(string taxNumber) + { + var url = $"{_baseUrl}/{_companyName}/invoice/taxnumber/{Uri.EscapeDataString(taxNumber)}"; + return await GetInvoicesFromUrlAsync(url); + } - var content = await response.Content.ReadAsStringAsync(); - var invoices = JsonSerializer.Deserialize>(content, new JsonSerializerOptions - { - PropertyNameCaseInsensitive = true - }); + /// + /// Get invoices modified since a specific timestamp + /// Format: YYYYMMDDHHmmss (year, month, day, hour, minute, second) + /// Recommended for tracking changes every 10 minutes + /// Rate limit: Full queries or queries older than current month limited to 10 times per 30 days + /// Recommended: Only use current month dates + /// + public async Task> GetInvoicesByUpdateTimeAsync(DateTime updateTime) + { + var timeStr = updateTime.ToString("yyyyMMddHHmmss"); + var url = $"{_baseUrl}/{_companyName}/invoice/updatedtime/{timeStr}"; + return await GetInvoicesFromUrlAsync(url); + } - return invoices ?? new List(); - } - catch (HttpRequestException ex) - { - throw new InnVoiceApiException($"Error calling InnVoice API: {ex.Message}", ex); - } - catch (JsonException ex) - { - throw new InnVoiceApiException($"Error parsing API response: {ex.Message}", ex); - } - } - } + private async Task> GetInvoicesFromUrlAsync(string url) + { + try + { + var response = await _httpClient.GetAsync(url); + response.EnsureSuccessStatusCode(); - // Models - public class Invoice - { - [JsonPropertyName("TABLE_ID")] - public int TableId { get; set; } + var content = await response.Content.ReadAsStringAsync(); - [JsonPropertyName("InvoiceNumber")] - public string InvoiceNumber { get; set; } + // Parse XML response + return ParseInvoicesFromXml(content); + } + catch (HttpRequestException ex) + { + throw new InnVoiceApiException($"Error calling InnVoice API: {ex.Message}", ex); + } + catch (Exception ex) + { + throw new InnVoiceApiException($"Error parsing API response: {ex.Message}", ex); + } + } - [JsonPropertyName("Created")] - public string Created { get; set; } - [JsonPropertyName("Fulfillment")] - public string Fulfillment { get; set; } - [JsonPropertyName("DueDate")] - public string DueDate { get; set; } + private List ParseInvoicesFromXml(string xml) + { + var invoices = new List(); - [JsonPropertyName("PaymentDate")] - public string PaymentDate { get; set; } + try + { + var doc = XDocument.Parse(xml); + var invoiceElements = doc.Descendants("invoice"); - [JsonPropertyName("CustomerName")] - public string CustomerName { get; set; } + foreach (var invoiceElement in invoiceElements) + { + var invoice = new Invoice + { + TableId = GetIntValue(invoiceElement, "TABLE_ID"), + VevoID = GetIntValue(invoiceElement, "VevoID"), + CustomerName = GetStringValue(invoiceElement, "VevoNev"), + CustomerZipCode = GetStringValue(invoiceElement, "VevoIrsz"), + CustomerCity = GetStringValue(invoiceElement, "VevoTelep"), + CustomerAddress = GetStringValue(invoiceElement, "VevoUtcaHsz"), + CustomerCountry = GetStringValue(invoiceElement, "VevoOrszag"), + CustomerTaxNumber = GetStringValue(invoiceElement, "VevoAdoszam"), + CustomerGroupTaxNumber = GetStringValue(invoiceElement, "VevoCsAdoszam"), + PaymentMethod = GetStringValue(invoiceElement, "FizetesiMod"), + FulfillmentDate = GetStringValue(invoiceElement, "TeljesitesKelte"), + InvoiceDate = GetStringValue(invoiceElement, "SzamlaKelte"), + DueDate = GetStringValue(invoiceElement, "Hatarido"), + Notes = GetStringValue(invoiceElement, "Megjegyzes"), + Currency = GetStringValue(invoiceElement, "Devizanem"), + IsDraft = GetIntValue(invoiceElement, "Felretett") == 1, + IsCancelled = GetIntValue(invoiceElement, "Storno") == 1, + IsAdvance = GetIntValue(invoiceElement, "Eloleg") == 1, + IsProforma = GetIntValue(invoiceElement, "Proforma") == 1, + TotalNet = GetDecimalValue(invoiceElement, "NettoErtek"), + TotalVAT = GetDecimalValue(invoiceElement, "AFAErtek"), + TotalGross = GetDecimalValue(invoiceElement, "BruttoErtek"), + OutstandingAmount = GetDecimalValue(invoiceElement, "Hatralek"), + OverdueAmount = GetDecimalValue(invoiceElement, "LejartHatralek"), + PaidAmount = GetDecimalValue(invoiceElement, "Befizetes"), + InvoiceNumber = GetStringValue(invoiceElement, "Sorszam"), + InvoiceNumberFormatted = GetStringValue(invoiceElement, "SorszamFormatted"), + InvoiceId = GetIntValue(invoiceElement, "SzamlaID"), + UniqueId = GetStringValue(invoiceElement, "_UniqueID"), + ExchangeRate = GetDecimalValue(invoiceElement, "Arfolyam"), + InvoiceBookId = GetIntValue(invoiceElement, "SzamlatombID"), + InvoiceBookName = GetStringValue(invoiceElement, "SzamlatombNev"), + IsElectronic = GetIntValue(invoiceElement, "Eszamla") == 1, + Phone = GetStringValue(invoiceElement, "Telefon"), + Email = GetStringValue(invoiceElement, "Email"), + UpdateTime = GetStringValue(invoiceElement, "_UpdateTime"), + QueryTime = GetStringValue(invoiceElement, "QueryTime"), + OrderNumber = GetStringValue(invoiceElement, "MegrendelesSzamStr"), + OrderDate = GetStringValue(invoiceElement, "MegrendelesIdopontStr"), + ShippingName = GetStringValue(invoiceElement, "SzallNev"), + ShippingZipCode = GetStringValue(invoiceElement, "SzallIrsz"), + ShippingCity = GetStringValue(invoiceElement, "SzallTelep"), + ShippingAddress = GetStringValue(invoiceElement, "SzallUtcaHsz"), + ShippingCountry = GetStringValue(invoiceElement, "SzallOrszag"), + PrintUrl = GetStringValue(invoiceElement, "PrintLink"), + InvoiceLink = GetStringValue(invoiceElement, "SzamlaLink"), + HealthFundName = GetStringValue(invoiceElement, "VevoEPNev"), + HealthFundCode = GetStringValue(invoiceElement, "VevoEPKod") + }; - [JsonPropertyName("CustomerTaxNumber")] - public string CustomerTaxNumber { get; set; } + // Parse line items + var itemElements = invoiceElement.Descendants("tetel"); + foreach (var itemElement in itemElements) + { + invoice.Items.Add(new InvoiceLineItem + { + TableId = GetIntValue(itemElement, "TABLE_ID"), + ItemName = GetStringValue(itemElement, "TetelNev"), + ArticleNumber = GetStringValue(itemElement, "CikkSzam"), + VTSZSZJ = GetStringValue(itemElement, "VTSZSZJ"), + VATText = GetStringValue(itemElement, "AfaSzoveg"), + VATRate = GetDecimalValue(itemElement, "AfaKulcs"), + IsGross = GetIntValue(itemElement, "Brutto") == 1, + UnitPrice = GetDecimalValue(itemElement, "EgysegAr"), + Quantity = GetDecimalValue(itemElement, "Mennyiseg"), + Unit = GetStringValue(itemElement, "MennyisegEgyseg"), + ProductId = GetIntValue(itemElement, "TermekID"), + DiscountAmount = GetDecimalValue(itemElement, "KedvezmenyOsszeg") + }); + } - [JsonPropertyName("CustomerAddress")] - public string CustomerAddress { get; set; } + invoices.Add(invoice); + } + } + catch (Exception ex) + { + throw new InnVoiceApiException($"Error parsing XML: {ex.Message}", ex); + } - [JsonPropertyName("TotalNet")] - public decimal TotalNet { get; set; } + return invoices; + } - [JsonPropertyName("TotalGross")] - public decimal TotalGross { get; set; } + private string GetStringValue(XElement element, string name) + { + return element.Element(name)?.Value?.Trim() ?? string.Empty; + } - [JsonPropertyName("Currency")] - public string Currency { get; set; } + private int GetIntValue(XElement element, string name) + { + var value = GetStringValue(element, name); + return int.TryParse(value, out var result) ? result : 0; + } - [JsonPropertyName("Status")] - public string Status { get; set; } + private decimal GetDecimalValue(XElement element, string name) + { + var value = GetStringValue(element, name); + return decimal.TryParse(value, out var result) ? result : 0m; + } - [JsonPropertyName("InvoiceType")] - public string InvoiceType { get; set; } - - [JsonPropertyName("PaymentMethod")] - public string PaymentMethod { get; set; } - - // Add more properties as needed based on actual API response - } - - public class InnVoiceApiException : Exception - { - public InnVoiceApiException(string message) : base(message) { } - public InnVoiceApiException(string message, Exception innerException) : base(message, innerException) { } - } - - // Invoice Creation Models - public class InvoiceCreateRequest - { - public int VevoID { get; set; } = 0; - public string VevoNev { get; set; } - public string VevoIrsz { get; set; } - public string VevoOrszag { get; set; } - public string VevoTelep { get; set; } - public string VevoUtcaHsz { get; set; } - public string VevoEPNev { get; set; } - public string VevoEPKod { get; set; } - public string SzallNev { get; set; } - public string SzallIrsz { get; set; } - public string SzallTelep { get; set; } - public string SzallUtcaHsz { get; set; } - public string SzallOrszag { get; set; } - public int SzamlatombID { get; set; } - public DateTime SzamlaKelte { get; set; } - public DateTime TeljesitesKelte { get; set; } - public DateTime Hatarido { get; set; } - public string Devizanem { get; set; } - public string FizetesiMod { get; set; } - public string Megjegyzes { get; set; } - public string Nyelv1 { get; set; } - public string Nyelv2 { get; set; } - public decimal? Arfolyam { get; set; } - public string ArfolyamDeviza { get; set; } - public bool Fizetve { get; set; } - public bool Eszamla { get; set; } - public string VevoAdoszam { get; set; } - public string VevoCsAdoszam { get; set; } - public string Telefon { get; set; } - public string Email { get; set; } - public string MegrendelesSzamStr { get; set; } - public string MegrendelesIdopontStr { get; set; } - public bool Felretett { get; set; } - public bool Proforma { get; set; } - public bool AutomatikusAr { get; set; } - public bool Eloleg { get; set; } - public bool Sendmail { get; set; } - public string MailSubject { get; set; } - public string MailBody { get; set; } - public string Eredetiszamla { get; set; } - - public List Items { get; set; } = new List(); - - public void AddItem(InvoiceItem item) - { - Items.Add(item); - } - - public string ToXml() - { - var invoices = new XElement("invoices"); - var invoice = new XElement("invoice"); - - if (VevoID > 0) - invoice.Add(new XElement("VevoID", new XCData(VevoID.ToString()))); - - invoice.Add(new XElement("VevoNev", new XCData(VevoNev ?? ""))); - invoice.Add(new XElement("VevoIrsz", new XCData(VevoIrsz ?? ""))); - invoice.Add(new XElement("VevoTelep", new XCData(VevoTelep ?? ""))); - invoice.Add(new XElement("VevoOrszag", new XCData(VevoOrszag ?? ""))); - invoice.Add(new XElement("VevoUtcaHsz", new XCData(VevoUtcaHsz ?? ""))); - - if (!string.IsNullOrEmpty(VevoEPNev)) - invoice.Add(new XElement("VevoEPNev", new XCData(VevoEPNev))); - if (!string.IsNullOrEmpty(VevoEPKod)) - invoice.Add(new XElement("VevoEPKod", new XCData(VevoEPKod))); - if (!string.IsNullOrEmpty(SzallNev)) - invoice.Add(new XElement("SzallNev", new XCData(SzallNev))); - if (!string.IsNullOrEmpty(SzallIrsz)) - invoice.Add(new XElement("SzallIrsz", new XCData(SzallIrsz))); - if (!string.IsNullOrEmpty(SzallTelep)) - invoice.Add(new XElement("SzallTelep", new XCData(SzallTelep))); - if (!string.IsNullOrEmpty(SzallUtcaHsz)) - invoice.Add(new XElement("SzallUtcaHsz", new XCData(SzallUtcaHsz))); - if (!string.IsNullOrEmpty(SzallOrszag)) - invoice.Add(new XElement("SzallOrszag", new XCData(SzallOrszag))); - - invoice.Add(new XElement("SzamlatombID", new XCData(SzamlatombID.ToString()))); - invoice.Add(new XElement("SzamlaKelte", new XCData(SzamlaKelte.ToString("yyyy.MM.dd.")))); - invoice.Add(new XElement("TeljesitesKelte", new XCData(TeljesitesKelte.ToString("yyyy.MM.dd.")))); - invoice.Add(new XElement("Hatarido", new XCData(Hatarido.ToString("yyyy.MM.dd.")))); - invoice.Add(new XElement("Devizanem", new XCData(Devizanem ?? ""))); - invoice.Add(new XElement("FizetesiMod", new XCData(FizetesiMod ?? ""))); - - if (!string.IsNullOrEmpty(Megjegyzes)) - invoice.Add(new XElement("Megjegyzes", new XCData(Megjegyzes))); - if (!string.IsNullOrEmpty(Nyelv1)) - invoice.Add(new XElement("Nyelv1", new XCData(Nyelv1))); - if (!string.IsNullOrEmpty(Nyelv2)) - invoice.Add(new XElement("Nyelv2", new XCData(Nyelv2))); - if (Arfolyam.HasValue) - invoice.Add(new XElement("Arfolyam", new XCData(Arfolyam.Value.ToString()))); - if (!string.IsNullOrEmpty(ArfolyamDeviza)) - invoice.Add(new XElement("ArfolyamDeviza", new XCData(ArfolyamDeviza))); - - invoice.Add(new XElement("Fizetve", Fizetve ? "1" : "0")); - invoice.Add(new XElement("Eszamla", Eszamla ? "1" : "0")); - - if (!string.IsNullOrEmpty(VevoAdoszam)) - invoice.Add(new XElement("VevoAdoszam", new XCData(VevoAdoszam))); - if (!string.IsNullOrEmpty(VevoCsAdoszam)) - invoice.Add(new XElement("VevoCsAdoszam", new XCData(VevoCsAdoszam))); - if (!string.IsNullOrEmpty(Telefon)) - invoice.Add(new XElement("Telefon", new XCData(Telefon))); - if (!string.IsNullOrEmpty(Email)) - invoice.Add(new XElement("Email", new XCData(Email))); - if (!string.IsNullOrEmpty(MegrendelesSzamStr)) - invoice.Add(new XElement("MegrendelesSzamStr", new XCData(MegrendelesSzamStr))); - if (!string.IsNullOrEmpty(MegrendelesIdopontStr)) - invoice.Add(new XElement("MegrendelesIdopontStr", new XCData(MegrendelesIdopontStr))); - - invoice.Add(new XElement("Felretett", Felretett ? "1" : "0")); - invoice.Add(new XElement("Proforma", Proforma ? "1" : "0")); - - if (AutomatikusAr) - invoice.Add(new XElement("AutomatikusAr", "1")); - if (Eloleg) - invoice.Add(new XElement("Eloleg", "1")); - if (Sendmail) - { - invoice.Add(new XElement("Sendmail", "1")); - if (!string.IsNullOrEmpty(MailSubject)) - invoice.Add(new XElement("MailSubject", new XCData(MailSubject))); - if (!string.IsNullOrEmpty(MailBody)) - invoice.Add(new XElement("MailBody", new XCData(MailBody))); - } - if (!string.IsNullOrEmpty(Eredetiszamla)) - invoice.Add(new XElement("Eredetiszamla", new XCData(Eredetiszamla))); - - // Add items - foreach (var item in Items) - { - var tetel = new XElement("tetel"); - tetel.Add(new XElement("TetelNev", new XCData(item.TetelNev ?? ""))); - tetel.Add(new XElement("AfaSzoveg", item.AfaSzoveg ?? "")); - tetel.Add(new XElement("Brutto", item.Brutto ? "1" : "0")); - tetel.Add(new XElement("EgysegAr", item.EgysegAr.ToString())); - tetel.Add(new XElement("Mennyiseg", item.Mennyiseg.ToString())); - tetel.Add(new XElement("MennyisegEgyseg", new XCData(item.MennyisegEgyseg ?? ""))); - - if (item.KedvezmenyOsszeg.HasValue) - tetel.Add(new XElement("KedvezmenyOsszeg", item.KedvezmenyOsszeg.Value.ToString())); - if (item.TermekID.HasValue) - tetel.Add(new XElement("TermekID", item.TermekID.Value.ToString())); - if (!string.IsNullOrEmpty(item.Megjegyzes)) - tetel.Add(new XElement("Megjegyzes", new XCData(item.Megjegyzes))); - if (!string.IsNullOrEmpty(item.CikkSzam)) - tetel.Add(new XElement("CikkSzam", new XCData(item.CikkSzam))); - if (!string.IsNullOrEmpty(item.VTSZSZJ)) - tetel.Add(new XElement("VTSZSZJ", new XCData(item.VTSZSZJ))); - if (item.ElolegSzamlaTABLE_ID.HasValue) - tetel.Add(new XElement("ElolegSzamlaTABLE_ID", item.ElolegSzamlaTABLE_ID.Value.ToString())); - if (!string.IsNullOrEmpty(item.ElolegSzamlaSorszam)) - tetel.Add(new XElement("ElolegSzamlaSorszam", new XCData(item.ElolegSzamlaSorszam))); - - invoice.Add(tetel); - } - - invoices.Add(invoice); - return new XDeclaration("1.0", "UTF-8", null).ToString() + "\n" + invoices.ToString(); - } - } - - public class InvoiceItem - { - public string TetelNev { get; set; } - public string AfaSzoveg { get; set; } - public bool Brutto { get; set; } - public decimal EgysegAr { get; set; } - public decimal Mennyiseg { get; set; } - public string MennyisegEgyseg { get; set; } - public decimal? KedvezmenyOsszeg { get; set; } - public int? TermekID { get; set; } - public string Megjegyzes { get; set; } - public string CikkSzam { get; set; } - public string VTSZSZJ { get; set; } - public int? ElolegSzamlaTABLE_ID { get; set; } - public string ElolegSzamlaSorszam { get; set; } - } - - public class InvoiceCreateResponse - { - public string ErrorCode { get; set; } - public string Message { get; set; } - public int? TableId { get; set; } - public int? VevoID { get; set; } - public string TechId { get; set; } - public string Sorszam { get; set; } - public string PrintUrl { get; set; } - - public bool IsSuccess => ErrorCode == "200"; - - public static InvoiceCreateResponse FromXml(string xml) - { - var doc = XDocument.Parse(xml); - var invoice = doc.Descendants("invoice").FirstOrDefault(); - - if (invoice == null) - { - throw new InnVoiceApiException("Invalid XML response format"); - } - - return new InvoiceCreateResponse - { - ErrorCode = invoice.Element("error")?.Value, - Message = invoice.Element("message")?.Value?.Trim(), - TableId = int.TryParse(invoice.Element("TABLE_ID")?.Value?.Trim(), out var tid) ? tid : (int?)null, - VevoID = int.TryParse(invoice.Element("VevoID")?.Value?.Trim(), out var vid) ? vid : (int?)null, - TechId = invoice.Element("techid")?.Value?.Trim(), - Sorszam = invoice.Element("Sorszam")?.Value?.Trim(), - PrintUrl = invoice.Element("PrintUrl")?.Value?.Trim() - }; - } - } /// /// Create a new invoice /// public async Task CreateInvoiceAsync(InvoiceCreateRequest request) - { - var url = $"{_baseUrl}/{_companyName}/invoice"; + { + var url = $"{_baseUrl}/{_companyName}/invoice"; - var xml = request.ToXml(); - var content = new FormUrlEncodedContent(new[] - { - new KeyValuePair("data", xml) - }); + var xml = request.ToXml(); + var content = new FormUrlEncodedContent(new[] + { + new KeyValuePair("data", xml) + }); - try - { - var response = await _httpClient.PostAsync(url, content); - response.EnsureSuccessStatusCode(); + try + { + var response = await _httpClient.PostAsync(url, content); + response.EnsureSuccessStatusCode(); - var responseContent = await response.Content.ReadAsStringAsync(); - return InvoiceCreateResponse.FromXml(responseContent); - } - catch (HttpRequestException ex) - { - throw new InnVoiceApiException($"Error creating invoice: {ex.Message}", ex); - } - } + var responseContent = await response.Content.ReadAsStringAsync(); + return InvoiceCreateResponse.FromXml(responseContent); + } + catch (HttpRequestException ex) + { + throw new InnVoiceApiException($"Error creating invoice: {ex.Message}", ex); + } + } - /// - /// Update an existing invoice - /// - public async Task UpdateInvoiceAsync(int tableId, InvoiceCreateRequest request) - { - // Set the VevoID if updating customer information - var url = $"{_baseUrl}/{_companyName}/invoice"; + /// + /// Update an existing invoice + /// + public async Task UpdateInvoiceAsync(int tableId, InvoiceCreateRequest request) + { + // Set the VevoID if updating customer information + var url = $"{_baseUrl}/{_companyName}/invoice"; - var xml = request.ToXml(); - var content = new FormUrlEncodedContent(new[] - { - new KeyValuePair("data", xml), - new KeyValuePair("id", tableId.ToString()) - }); + var xml = request.ToXml(); + var content = new FormUrlEncodedContent(new[] + { + new KeyValuePair("data", xml), + new KeyValuePair("id", tableId.ToString()) + }); - try - { - var response = await _httpClient.PostAsync(url, content); - response.EnsureSuccessStatusCode(); + try + { + var response = await _httpClient.PostAsync(url, content); + response.EnsureSuccessStatusCode(); - var responseContent = await response.Content.ReadAsStringAsync(); - return InvoiceCreateResponse.FromXml(responseContent); - } - catch (HttpRequestException ex) - { - throw new InnVoiceApiException($"Error updating invoice: {ex.Message}", ex); - } - } \ No newline at end of file + var responseContent = await response.Content.ReadAsStringAsync(); + return InvoiceCreateResponse.FromXml(responseContent); + } + catch (HttpRequestException ex) + { + throw new InnVoiceApiException($"Error updating invoice: {ex.Message}", ex); + } + } + + } + + // Models + public class Invoice + { + public int TableId { get; set; } + public int VevoID { get; set; } + public string CustomerName { get; set; } + public string CustomerZipCode { get; set; } + public string CustomerCity { get; set; } + public string CustomerAddress { get; set; } + public string CustomerCountry { get; set; } + public string CustomerTaxNumber { get; set; } + public string CustomerGroupTaxNumber { get; set; } + public string PaymentMethod { get; set; } + public string FulfillmentDate { get; set; } + public string InvoiceDate { get; set; } + public string DueDate { get; set; } + public string Notes { get; set; } + public string Currency { get; set; } + public bool IsDraft { get; set; } + public bool IsCancelled { get; set; } + public bool IsAdvance { get; set; } + public bool IsProforma { get; set; } + public decimal TotalNet { get; set; } + public decimal TotalVAT { get; set; } + public decimal TotalGross { get; set; } + public decimal OutstandingAmount { get; set; } + public decimal OverdueAmount { get; set; } + public decimal PaidAmount { get; set; } + public string InvoiceNumber { get; set; } + public string InvoiceNumberFormatted { get; set; } + public int InvoiceId { get; set; } + public string UniqueId { get; set; } + public decimal ExchangeRate { get; set; } + public int InvoiceBookId { get; set; } + public string InvoiceBookName { get; set; } + public bool IsElectronic { get; set; } + public string Phone { get; set; } + public string Email { get; set; } + public string UpdateTime { get; set; } + public string QueryTime { get; set; } + public string OrderNumber { get; set; } + public string OrderDate { get; set; } + public string ShippingName { get; set; } + public string ShippingZipCode { get; set; } + public string ShippingCity { get; set; } + public string ShippingAddress { get; set; } + public string ShippingCountry { get; set; } + public string PrintUrl { get; set; } + public string InvoiceLink { get; set; } + public string HealthFundName { get; set; } + public string HealthFundCode { get; set; } + + public List Items { get; set; } = new List(); + } + + public class InvoiceLineItem + { + public int TableId { get; set; } + public string ItemName { get; set; } + public string ArticleNumber { get; set; } + public string VTSZSZJ { get; set; } + public string VATText { get; set; } + public decimal VATRate { get; set; } + public bool IsGross { get; set; } + public decimal UnitPrice { get; set; } + public decimal Quantity { get; set; } + public string Unit { get; set; } + public int ProductId { get; set; } + public decimal DiscountAmount { get; set; } + } + + + // Invoice Creation Models + public class InvoiceCreateRequest + { + public int VevoID { get; set; } = 0; + public string VevoNev { get; set; } + public string VevoIrsz { get; set; } + public string VevoOrszag { get; set; } + public string VevoTelep { get; set; } + public string VevoUtcaHsz { get; set; } + public string VevoEPNev { get; set; } + public string VevoEPKod { get; set; } + public string SzallNev { get; set; } + public string SzallIrsz { get; set; } + public string SzallTelep { get; set; } + public string SzallUtcaHsz { get; set; } + public string SzallOrszag { get; set; } + public int SzamlatombID { get; set; } + public DateTime SzamlaKelte { get; set; } + public DateTime TeljesitesKelte { get; set; } + public DateTime Hatarido { get; set; } + public string Devizanem { get; set; } + public string FizetesiMod { get; set; } + public string Megjegyzes { get; set; } + public string Nyelv1 { get; set; } + public string Nyelv2 { get; set; } + public decimal? Arfolyam { get; set; } + public string ArfolyamDeviza { get; set; } + public bool Fizetve { get; set; } + public bool Eszamla { get; set; } + public string VevoAdoszam { get; set; } + public string VevoCsAdoszam { get; set; } + public string Telefon { get; set; } + public string Email { get; set; } + public string MegrendelesSzamStr { get; set; } + public string MegrendelesIdopontStr { get; set; } + public bool Felretett { get; set; } + public bool Proforma { get; set; } + public bool AutomatikusAr { get; set; } + public bool Eloleg { get; set; } + public bool Sendmail { get; set; } + public string MailSubject { get; set; } + public string MailBody { get; set; } + public string Eredetiszamla { get; set; } + + public List Items { get; set; } = new List(); + + public void AddItem(InvoiceItem item) + { + Items.Add(item); + } + + public string ToXml() + { + var invoices = new XElement("invoices"); + var invoice = new XElement("invoice"); + + if (VevoID > 0) + invoice.Add(new XElement("VevoID", new XCData(VevoID.ToString()))); + + invoice.Add(new XElement("VevoNev", new XCData(VevoNev ?? ""))); + invoice.Add(new XElement("VevoIrsz", new XCData(VevoIrsz ?? ""))); + invoice.Add(new XElement("VevoTelep", new XCData(VevoTelep ?? ""))); + invoice.Add(new XElement("VevoOrszag", new XCData(VevoOrszag ?? ""))); + invoice.Add(new XElement("VevoUtcaHsz", new XCData(VevoUtcaHsz ?? ""))); + + if (!string.IsNullOrEmpty(VevoEPNev)) + invoice.Add(new XElement("VevoEPNev", new XCData(VevoEPNev))); + if (!string.IsNullOrEmpty(VevoEPKod)) + invoice.Add(new XElement("VevoEPKod", new XCData(VevoEPKod))); + if (!string.IsNullOrEmpty(SzallNev)) + invoice.Add(new XElement("SzallNev", new XCData(SzallNev))); + if (!string.IsNullOrEmpty(SzallIrsz)) + invoice.Add(new XElement("SzallIrsz", new XCData(SzallIrsz))); + if (!string.IsNullOrEmpty(SzallTelep)) + invoice.Add(new XElement("SzallTelep", new XCData(SzallTelep))); + if (!string.IsNullOrEmpty(SzallUtcaHsz)) + invoice.Add(new XElement("SzallUtcaHsz", new XCData(SzallUtcaHsz))); + if (!string.IsNullOrEmpty(SzallOrszag)) + invoice.Add(new XElement("SzallOrszag", new XCData(SzallOrszag))); + + invoice.Add(new XElement("SzamlatombID", new XCData(SzamlatombID.ToString()))); + invoice.Add(new XElement("SzamlaKelte", new XCData(SzamlaKelte.ToString("yyyy.MM.dd.")))); + invoice.Add(new XElement("TeljesitesKelte", new XCData(TeljesitesKelte.ToString("yyyy.MM.dd.")))); + invoice.Add(new XElement("Hatarido", new XCData(Hatarido.ToString("yyyy.MM.dd.")))); + invoice.Add(new XElement("Devizanem", new XCData(Devizanem ?? ""))); + invoice.Add(new XElement("FizetesiMod", new XCData(FizetesiMod ?? ""))); + + if (!string.IsNullOrEmpty(Megjegyzes)) + invoice.Add(new XElement("Megjegyzes", new XCData(Megjegyzes))); + if (!string.IsNullOrEmpty(Nyelv1)) + invoice.Add(new XElement("Nyelv1", new XCData(Nyelv1))); + if (!string.IsNullOrEmpty(Nyelv2)) + invoice.Add(new XElement("Nyelv2", new XCData(Nyelv2))); + if (Arfolyam.HasValue) + invoice.Add(new XElement("Arfolyam", new XCData(Arfolyam.Value.ToString()))); + if (!string.IsNullOrEmpty(ArfolyamDeviza)) + invoice.Add(new XElement("ArfolyamDeviza", new XCData(ArfolyamDeviza))); + + invoice.Add(new XElement("Fizetve", Fizetve ? "1" : "0")); + invoice.Add(new XElement("Eszamla", Eszamla ? "1" : "0")); + + if (!string.IsNullOrEmpty(VevoAdoszam)) + invoice.Add(new XElement("VevoAdoszam", new XCData(VevoAdoszam))); + if (!string.IsNullOrEmpty(VevoCsAdoszam)) + invoice.Add(new XElement("VevoCsAdoszam", new XCData(VevoCsAdoszam))); + if (!string.IsNullOrEmpty(Telefon)) + invoice.Add(new XElement("Telefon", new XCData(Telefon))); + if (!string.IsNullOrEmpty(Email)) + invoice.Add(new XElement("Email", new XCData(Email))); + if (!string.IsNullOrEmpty(MegrendelesSzamStr)) + invoice.Add(new XElement("MegrendelesSzamStr", new XCData(MegrendelesSzamStr))); + if (!string.IsNullOrEmpty(MegrendelesIdopontStr)) + invoice.Add(new XElement("MegrendelesIdopontStr", new XCData(MegrendelesIdopontStr))); + + invoice.Add(new XElement("Felretett", Felretett ? "1" : "0")); + invoice.Add(new XElement("Proforma", Proforma ? "1" : "0")); + + if (AutomatikusAr) + invoice.Add(new XElement("AutomatikusAr", "1")); + if (Eloleg) + invoice.Add(new XElement("Eloleg", "1")); + if (Sendmail) + { + invoice.Add(new XElement("Sendmail", "1")); + if (!string.IsNullOrEmpty(MailSubject)) + invoice.Add(new XElement("MailSubject", new XCData(MailSubject))); + if (!string.IsNullOrEmpty(MailBody)) + invoice.Add(new XElement("MailBody", new XCData(MailBody))); + } + if (!string.IsNullOrEmpty(Eredetiszamla)) + invoice.Add(new XElement("Eredetiszamla", new XCData(Eredetiszamla))); + + // Add items + foreach (var item in Items) + { + var tetel = new XElement("tetel"); + tetel.Add(new XElement("TetelNev", new XCData(item.TetelNev ?? ""))); + tetel.Add(new XElement("AfaSzoveg", item.AfaSzoveg ?? "")); + tetel.Add(new XElement("Brutto", item.Brutto ? "1" : "0")); + tetel.Add(new XElement("EgysegAr", item.EgysegAr.ToString())); + tetel.Add(new XElement("Mennyiseg", item.Mennyiseg.ToString())); + tetel.Add(new XElement("MennyisegEgyseg", new XCData(item.MennyisegEgyseg ?? ""))); + + if (item.KedvezmenyOsszeg.HasValue) + tetel.Add(new XElement("KedvezmenyOsszeg", item.KedvezmenyOsszeg.Value.ToString())); + if (item.TermekID.HasValue) + tetel.Add(new XElement("TermekID", item.TermekID.Value.ToString())); + if (!string.IsNullOrEmpty(item.Megjegyzes)) + tetel.Add(new XElement("Megjegyzes", new XCData(item.Megjegyzes))); + if (!string.IsNullOrEmpty(item.CikkSzam)) + tetel.Add(new XElement("CikkSzam", new XCData(item.CikkSzam))); + if (!string.IsNullOrEmpty(item.VTSZSZJ)) + tetel.Add(new XElement("VTSZSZJ", new XCData(item.VTSZSZJ))); + if (item.ElolegSzamlaTABLE_ID.HasValue) + tetel.Add(new XElement("ElolegSzamlaTABLE_ID", item.ElolegSzamlaTABLE_ID.Value.ToString())); + if (!string.IsNullOrEmpty(item.ElolegSzamlaSorszam)) + tetel.Add(new XElement("ElolegSzamlaSorszam", new XCData(item.ElolegSzamlaSorszam))); + + invoice.Add(tetel); + } + + invoices.Add(invoice); + return new XDeclaration("1.0", "UTF-8", null).ToString() + "\n" + invoices.ToString(); + } + } + + public class InvoiceItem + { + public string TetelNev { get; set; } + public string AfaSzoveg { get; set; } + public bool Brutto { get; set; } + public decimal EgysegAr { get; set; } + public decimal Mennyiseg { get; set; } + public string MennyisegEgyseg { get; set; } + public decimal? KedvezmenyOsszeg { get; set; } + public int? TermekID { get; set; } + public string Megjegyzes { get; set; } + public string CikkSzam { get; set; } + public string VTSZSZJ { get; set; } + public int? ElolegSzamlaTABLE_ID { get; set; } + public string ElolegSzamlaSorszam { get; set; } + } + + public class InvoiceCreateResponse + { + public string ErrorCode { get; set; } + public string Message { get; set; } + public int? TableId { get; set; } + public int? VevoID { get; set; } + public string TechId { get; set; } + public string Sorszam { get; set; } + public string PrintUrl { get; set; } + + public bool IsSuccess => ErrorCode == "200"; + + public static InvoiceCreateResponse FromXml(string xml) + { + var doc = XDocument.Parse(xml); + var invoice = doc.Descendants("invoice").FirstOrDefault(); + + if (invoice == null) + { + throw new InnVoiceApiException("Invalid XML response format"); + } + + return new InvoiceCreateResponse + { + ErrorCode = invoice.Element("error")?.Value, + Message = invoice.Element("message")?.Value?.Trim(), + TableId = int.TryParse(invoice.Element("TABLE_ID")?.Value?.Trim(), out var tid) ? tid : (int?)null, + VevoID = int.TryParse(invoice.Element("VevoID")?.Value?.Trim(), out var vid) ? vid : (int?)null, + TechId = invoice.Element("techid")?.Value?.Trim(), + Sorszam = invoice.Element("Sorszam")?.Value?.Trim(), + PrintUrl = invoice.Element("PrintUrl")?.Value?.Trim() + }; + } + } +} \ No newline at end of file diff --git a/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml b/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml index 217b16f..f8438cb 100644 --- a/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml +++ b/Nop.Plugin.Misc.AIPlugin/Views/OrderAttributes.cshtml @@ -1,15 +1,44 @@ @using Nop.Plugin.Misc.FruitBankPlugin.Models.Orders @model OrderAttributesModel - +
- InnVoice Invoice Management + InnVoice Management
+ + +
-
+
+
Order
+ + +
+ +
+ + +
+ +
+
Invoice
@@ -18,62 +47,66 @@

Table ID:

- View PDF + View Invoice PDF

-
-
-
+ +
+
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+ + +
+
+
+
+ +
+
+
+
- -
-
- - Custom Order Attributes -
-
-
-
- -
-
- - -
-
-
-
- -
-
- - -
-
-
-
- -
-
-
-
+ + \ No newline at end of file +