Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/Services/InnvoiceApiService.cs

496 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Xml.Linq;
using System.Linq;
using System.Text;
namespace Nop.Plugin.Misc.FruitBankPlugin.Services
{
/// <summary>
/// Service for interacting with InnVoice Invoice API
/// API Documentation: https://help.innvoice.hu/hc/hu/articles/360003142839
/// </summary>
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('/');
_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);
}
/// <summary>
/// Get all invoices
/// Rate limit: 20 times per hour without ID parameter
/// </summary>
public async Task<List<Invoice>> GetAllInvoicesAsync()
{
var url = $"{_baseUrl}/{_companyName}/invoice";
return await GetInvoicesFromUrlAsync(url);
}
/// <summary>
/// Get invoice by internal table ID
/// </summary>
public async Task<Invoice> GetInvoiceByIdAsync(int tableId)
{
var url = $"{_baseUrl}/{_companyName}/invoice/id/{tableId}";
var invoices = await GetInvoicesFromUrlAsync(url);
return invoices.Count > 0 ? invoices[0] : null;
}
/// <summary>
/// Get invoice by invoice number
/// </summary>
public async Task<List<Invoice>> GetInvoiceByNumberAsync(string invoiceNumber)
{
var url = $"{_baseUrl}/{_companyName}/invoice/invoicenumber/{Uri.EscapeDataString(invoiceNumber)}";
return await GetInvoicesFromUrlAsync(url);
}
/// <summary>
/// Get invoices by creation date
/// Format: YYYYMMDD
/// </summary>
public async Task<List<Invoice>> GetInvoicesByCreationDateAsync(DateTime date)
{
var dateStr = date.ToString("yyyyMMdd");
var url = $"{_baseUrl}/{_companyName}/invoice/created/{dateStr}";
return await GetInvoicesFromUrlAsync(url);
}
/// <summary>
/// Get invoices by fulfillment date
/// Format: YYYYMMDD
/// </summary>
public async Task<List<Invoice>> GetInvoicesByFulfillmentDateAsync(DateTime date)
{
var dateStr = date.ToString("yyyyMMdd");
var url = $"{_baseUrl}/{_companyName}/invoice/fulfillment/{dateStr}";
return await GetInvoicesFromUrlAsync(url);
}
/// <summary>
/// Get invoices by due date
/// Format: YYYYMMDD
/// </summary>
public async Task<List<Invoice>> GetInvoicesByDueDateAsync(DateTime date)
{
var dateStr = date.ToString("yyyyMMdd");
var url = $"{_baseUrl}/{_companyName}/invoice/duedate/{dateStr}";
return await GetInvoicesFromUrlAsync(url);
}
/// <summary>
/// Get invoices by payment date
/// Format: YYYYMMDD
/// </summary>
public async Task<List<Invoice>> GetInvoicesByPaymentDateAsync(DateTime date)
{
var dateStr = date.ToString("yyyyMMdd");
var url = $"{_baseUrl}/{_companyName}/invoice/paymentdate/{dateStr}";
return await GetInvoicesFromUrlAsync(url);
}
/// <summary>
/// Get invoices by customer tax number
/// </summary>
public async Task<List<Invoice>> GetInvoicesByCustomerTaxNumberAsync(string taxNumber)
{
var url = $"{_baseUrl}/{_companyName}/invoice/taxnumber/{Uri.EscapeDataString(taxNumber)}";
return await GetInvoicesFromUrlAsync(url);
}
/// <summary>
/// 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
/// </summary>
public async Task<List<Invoice>> GetInvoicesByUpdateTimeAsync(DateTime updateTime)
{
var timeStr = updateTime.ToString("yyyyMMddHHmmss");
var url = $"{_baseUrl}/{_companyName}/invoice/updatedtime/{timeStr}";
return await GetInvoicesFromUrlAsync(url);
}
private async Task<List<Invoice>> GetInvoicesFromUrlAsync(string url)
{
try
{
var response = await _httpClient.GetAsync(url);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var invoices = JsonSerializer.Deserialize<List<Invoice>>(content, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
return invoices ?? new List<Invoice>();
}
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);
}
}
}
// Models
public class Invoice
{
[JsonPropertyName("TABLE_ID")]
public int TableId { get; set; }
[JsonPropertyName("InvoiceNumber")]
public string InvoiceNumber { get; set; }
[JsonPropertyName("Created")]
public string Created { get; set; }
[JsonPropertyName("Fulfillment")]
public string Fulfillment { get; set; }
[JsonPropertyName("DueDate")]
public string DueDate { get; set; }
[JsonPropertyName("PaymentDate")]
public string PaymentDate { get; set; }
[JsonPropertyName("CustomerName")]
public string CustomerName { get; set; }
[JsonPropertyName("CustomerTaxNumber")]
public string CustomerTaxNumber { get; set; }
[JsonPropertyName("CustomerAddress")]
public string CustomerAddress { get; set; }
[JsonPropertyName("TotalNet")]
public decimal TotalNet { get; set; }
[JsonPropertyName("TotalGross")]
public decimal TotalGross { get; set; }
[JsonPropertyName("Currency")]
public string Currency { get; set; }
[JsonPropertyName("Status")]
public string Status { get; set; }
[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<InvoiceItem> Items { get; set; } = new List<InvoiceItem>();
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()
};
}
}
/// <summary>
/// Create a new invoice
/// </summary>
public async Task<InvoiceCreateResponse> CreateInvoiceAsync(InvoiceCreateRequest request)
{
var url = $"{_baseUrl}/{_companyName}/invoice";
var xml = request.ToXml();
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("data", xml)
});
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);
}
}
/// <summary>
/// Update an existing invoice
/// </summary>
public async Task<InvoiceCreateResponse> 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<string, string>("data", xml),
new KeyValuePair<string, string>("id", tableId.ToString())
});
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);
}
}