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") public InnVoiceOrderService(string companyName = "fruitbank", string username = "fruitbank", string password = "YkKzM1dwJax0HTlPWlMABSqdSA", 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); Console.WriteLine($"InnVoice API CreateOrdersAsync response status: {response.StatusCode}"); 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 }); if (responses != null && responses.Count != 1) throw new Exception($"InvoiceOrderApi responses.Count != 1!"); return responses?.FirstOrDefault() ?? new OrderCreateResponse(); } /// /// Get orders modified since a specific timestamp /// Format: YYYYMMDDHHmmss (year, month, day, hour, minute, second) /// Recommended for tracking changes /// 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> GetOrdersByUpdateTimeAsync(DateTime updateTime) { var timeStr = updateTime.ToString("yyyyMMddHHmmss"); var url = $"{_baseUrl}/{_companyName}/order/updatedtime/{timeStr}"; var result = await GetOrdersFromUrlAsync(url); return result; } public async Task> GetOrdersByOrderDateAsync(DateTime orderDate) { var timeStr = orderDate.ToString("yyyy.MM.dd."); // Format: éééé.hh.nn. var url = $"{_baseUrl}/{_companyName}/order/megrendeleskelte/{timeStr}"; Console.WriteLine($"DEBUG InnVoiceOrderService: Calling URL: {url}"); var result = await GetOrdersFromUrlAsync(url); return result; } 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.Count == 0 ? null : 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; } } }