diff --git a/README.md b/README.md index 5b1c064d..4fc9a20c 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,14 @@ Please see [here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Sou >> >> ***This is not required and will operate normally without the ProxyAddress being set.*** + +### Configuration using Docker - Linux +You need to add windows-1252 encoding +```csharp + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); +``` + + ### Order List, For more orders sample please check [Here](https://github.com/abuzuhri/Amazon-SP-API-CSharp/blob/main/Source/FikaAmazonAPI.SampleCode/ReportsSample.cs). ```CSharp ParameterOrderList serachOrderList = new ParameterOrderList(); diff --git a/Source/FikaAmazonAPI/ReportGeneration/ProductsReport.cs b/Source/FikaAmazonAPI/ReportGeneration/ProductsReport.cs index 251d368e..f9c44b1f 100644 --- a/Source/FikaAmazonAPI/ReportGeneration/ProductsReport.cs +++ b/Source/FikaAmazonAPI/ReportGeneration/ProductsReport.cs @@ -7,12 +7,12 @@ namespace FikaAmazonAPI.ReportGeneration public class ProductsReport { public List Data { get; set; } = new List(); - public ProductsReport(string path, Encoding encoding = default) + public ProductsReport(string path) { if (string.IsNullOrEmpty(path)) return; - var table = Table.ConvertFromCSV(path, encoding: encoding); + var table = Table.ConvertFromCSV(path); List values = new List(); foreach (var row in table.Rows) diff --git a/Source/FikaAmazonAPI/ReportGeneration/ReportDataTable/Table.cs b/Source/FikaAmazonAPI/ReportGeneration/ReportDataTable/Table.cs index af638d1f..eed8a797 100644 --- a/Source/FikaAmazonAPI/ReportGeneration/ReportDataTable/Table.cs +++ b/Source/FikaAmazonAPI/ReportGeneration/ReportDataTable/Table.cs @@ -1,9 +1,11 @@ -using System; +using FikaAmazonAPI.Utils; +using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; +using System.Threading; namespace FikaAmazonAPI.ReportGeneration.ReportDataTable { @@ -48,13 +50,12 @@ public Table(params string[] header) this.header = header; } - public static Table ConvertFromCSV(string path, char separator = '\t', Encoding encoding = default) + public static Table ConvertFromCSV(string path, char separator = '\t') { - var lines = File.ReadAllLines(path, encoding ?? Encoding.UTF8); + var lines = File.ReadAllLines(path, EncodingHelper.GetEncodingFromCulture(Thread.CurrentThread.CurrentCulture)); var table = new Table(lines.First().Split(separator)); - lines.Skip(1).ToList().ForEach(a => ConvertFromCSVAddRow(table, a, separator)); return table; } diff --git a/Source/FikaAmazonAPI/ReportGeneration/ReportManager.cs b/Source/FikaAmazonAPI/ReportGeneration/ReportManager.cs index 5c31bc61..c94f53fc 100644 --- a/Source/FikaAmazonAPI/ReportGeneration/ReportManager.cs +++ b/Source/FikaAmazonAPI/ReportGeneration/ReportManager.cs @@ -400,6 +400,40 @@ private async Task GetOrderInvoicingDataAsync(AmazonConnection amazonCon #endregion + #region VatInvoicing + + public List GetVATInvoicingData(DateTime fromDate, DateTime toDate, + List marketplaces = null) => + Task.Run(() => GetVATInvoicingDataAsync(fromDate, toDate, marketplaces)).ConfigureAwait(false) + .GetAwaiter().GetResult(); + + public async Task> GetVATInvoicingDataAsync(DateTime fromDate, DateTime toDate, + List marketplaces = null) + { + List list = new List(); + var dateList = ReportDateRange.GetDateRange(fromDate, toDate, DAY_30); + foreach (var range in dateList) + { + var path = await GetVATInvoicingDataAsync(_amazonConnection, range.StartDate, range.EndDate, + marketplaces); + VatInvoicingReport report = new VatInvoicingReport(path, _amazonConnection.RefNumber); + list.AddRange(report.Data); + } + + return list; + } + + private async Task GetVATInvoicingDataAsync(AmazonConnection amazonConnection, DateTime fromDate, + DateTime toDate, List marketplaces = null) + { + var options = new ReportOptions(); + options.Add("ReportOption=All", "true"); + return await amazonConnection.Reports.CreateReportAndDownloadFileAsync( + ReportTypes.GET_FLAT_FILE_VAT_INVOICE_DATA_REPORT, fromDate, toDate, options, false, marketplaces); + } + + #endregion + #region Settlement public List GetLedgerDetail(int days) => diff --git a/Source/FikaAmazonAPI/ReportGeneration/VatInvoicingReport.cs b/Source/FikaAmazonAPI/ReportGeneration/VatInvoicingReport.cs new file mode 100644 index 00000000..e185be90 --- /dev/null +++ b/Source/FikaAmazonAPI/ReportGeneration/VatInvoicingReport.cs @@ -0,0 +1,229 @@ +using FikaAmazonAPI.ReportGeneration.ReportDataTable; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace FikaAmazonAPI.ReportGeneration +{ + public class VatInvoicingReport + { + public List Data { get; set; } = new List(); + public VatInvoicingReport(string path, string refNumber) + { + if (string.IsNullOrEmpty(path)) + return; + + var table = Table.ConvertFromCSV(path); + + List values = new List(); + foreach (var row in table.Rows) + { + values.Add(VatInvoicingReportRow.FromRow(row, refNumber)); + } + Data = values; + } + } + + + public class VatInvoicingReportRow + { + public string MarketplaceId { get; set; } + public string AmazonOrderId { get; set; } + public string OrderItemId { get; set; } + public string ShippingId { get; set; } + public string TransactionId { get; set; } + public string TransactionType { get; set; } + public string InvoiceNumber { get; set; } + public string InvoiceStatus { get; set; } + public string InvoiceStatusDescription { get; set; } + public string IsAmazonInvoiced { get; set; } + public DateTime? OrderDate { get; set; } + public DateTime? ShipmentDate { get; set; } + public string BuyerVatNumber { get; set; } + public string SellerVatNumber { get; set; } + public string ASIN { get; set; } + public string SKU { get; set; } + public string ProductName { get; set; } + public decimal? QuantityPurchased { get; set; } + public string Currency { get; set; } + public decimal? ItemVatInclAmount { get; set; } + public decimal? ItemVatAmount { get; set; } + public decimal? ItemVatExclAmount { get; set; } + public decimal? ItemVatRate { get; set; } + public decimal? ItemPromoVatInclAmount { get; set; } + public decimal? ItemPromoVatExclAmount { get; set; } + public decimal? ItemPromoVatAmount { get; set; } + public decimal? ItemPromoVatRate { get; set; } + public string ItemPromotionId { get; set; } + public decimal? GiftWrapVatInclAmount { get; set; } + public decimal? GiftWrapVatAmount { get; set; } + public decimal? GiftWrapVatExclAmount { get; set; } + public decimal? GiftWrapVatRate { get; set; } + public decimal? GiftPromoVatInclAmount { get; set; } + public decimal? GiftPromoVatExclAmount { get; set; } + public decimal? GiftPromoVatAmount { get; set; } + public decimal? GiftPromoVatRate { get; set; } + public string GiftPromotionId { get; set; } + public decimal? ShippingVatInclAmount { get; set; } + public decimal? ShippingVatAmount { get; set; } + public decimal? ShippingVatExclAmount { get; set; } + public decimal? ShippingVatRate { get; set; } + public decimal? ShippingPromoVatInclAmount { get; set; } + public decimal? ShippingPromoVatExclAmount { get; set; } + public decimal? ShippingPromoVatAmount { get; set; } + public decimal? ShippingPromoVatRate { get; set; } + public string ShipPromotionId { get; set; } + public string IsBusinessOrder { get; set; } + public string PriceDesignation { get; set; } + public string PurchaseOrderNumber { get; set; } + public string RecipientName { get; set; } + public string ShipServiceLevel { get; set; } + public string FulfilledBy { get; set; } + public string ShipAddress1 { get; set; } + public string ShipAddress2 { get; set; } + public string ShipAddress3 { get; set; } + public string ShipCity { get; set; } + public string ShipState { get; set; } + public string ShipPostalCode { get; set; } + public string ShipCountry { get; set; } + public string ShipPhoneNumber { get; set; } + public string ShipFromCountry { get; set; } + public string ShipFromState { get; set; } + public string ShipFromCity { get; set; } + public string ShipFromPostalCode { get; set; } + public string BillingName { get; set; } + public string BillAddress1 { get; set; } + public string BillAddress2 { get; set; } + public string BillAddress3 { get; set; } + public string BillCity { get; set; } + public string BillState { get; set; } + public string BillPostalCode { get; set; } + public string BillCountry { get; set; } + public string BillingPhoneNumber { get; set; } + public string BuyerName { get; set; } + public string BuyerCompanyName { get; set; } + public string LegacyCustomerOrderItemId { get; set; } + public string RecommendedInvoiceFormat { get; set; } + public string BuyerTaxRegistrationType { get; set; } + public string BuyerEInvoiceAccountId { get; set; } + public string IsBuyerPhysicallyPresent { get; set; } + public string IsSellerPhysicallyPresent { get; set; } + public string CitationEs { get; set; } + public string CitationIt { get; set; } + public string CitationFr { get; set; } + public string CitationDe { get; set; } + public string CitationEn { get; set; } + public string ExportOutsideEu { get; set; } + public string IsInvoiceCorrected { get; set; } + public string OriginalVatInvoiceNumber { get; set; } + public string InvoiceCorrectionDetails { get; set; } + + public string refNumber { get; set; } + + public VatInvoicingReportRow() + { + + } + + public static VatInvoicingReportRow FromRow(TableRow rowData, string refNumber) + { + var row = new VatInvoicingReportRow(); + row.MarketplaceId = rowData.GetString("marketplace-id"); + row.AmazonOrderId = rowData.GetString("order-id"); + row.OrderItemId = rowData.GetString("order-item-id"); + row.ShippingId = rowData.GetString("shipping-id"); + row.TransactionId = rowData.GetString("transaction-id"); + row.TransactionType = rowData.GetString("transaction-type"); + row.InvoiceNumber = rowData.GetString("invoice-number"); + row.InvoiceStatus = rowData.GetString("invoice-status"); + row.InvoiceStatusDescription = rowData.GetString("invoice-status-description"); + row.IsAmazonInvoiced = rowData.GetString("is-amazon-invoiced"); + row.OrderDate = DataConverter.GetDate(rowData.GetString("order-date"), DataConverter.DateTimeFormat.DATETIME_K_FORMAT); + row.ShipmentDate = DataConverter.GetDate(rowData.GetString("shipment-date"), DataConverter.DateTimeFormat.DATETIME_K_FORMAT); + row.BuyerVatNumber = rowData.GetString("buyer-vat-number"); + row.SellerVatNumber = rowData.GetString("seller-vat-number"); + row.ASIN = rowData.GetString("asin"); + row.SKU = rowData.GetString("sku"); + row.ProductName = rowData.GetString("product-name"); + row.QuantityPurchased = rowData.GetDecimal("quantity-purchased"); + row.Currency = rowData.GetString("currency"); + row.ItemVatInclAmount = rowData.GetDecimal("item-vat-incl-amount"); + row.ItemVatAmount = rowData.GetDecimal("item-vat-amount"); + row.ItemVatExclAmount = rowData.GetDecimal("item-vat-excl-amount"); + row.ItemVatRate = rowData.GetDecimal("item-vat-rate"); + row.ItemPromoVatInclAmount = rowData.GetDecimal("item-promo-vat-incl-amount"); + row.ItemPromoVatExclAmount = rowData.GetDecimal("item-promo-vat-excl-amount"); + row.ItemPromoVatAmount = rowData.GetDecimal("item-promo-vat-amount"); + row.ItemPromoVatRate = rowData.GetDecimal("item-promo-vat-rate"); + row.ItemPromotionId = rowData.GetString("item-promotion-id"); + row.GiftWrapVatInclAmount = rowData.GetDecimal("gift-wrap-vat-incl-amount"); + row.GiftWrapVatAmount = rowData.GetDecimal("gift-wrap-vat-amount"); + row.GiftWrapVatExclAmount = rowData.GetDecimal("gift-wrap-vat-excl-amount"); + row.GiftWrapVatRate = rowData.GetDecimal("gift-wrap-vat-rate"); + row.GiftPromoVatInclAmount = rowData.GetDecimal("gift-promo-vat-incl-amount"); + row.GiftPromoVatExclAmount = rowData.GetDecimal("gift-promo-vat-excl-amount"); + row.GiftPromoVatAmount = rowData.GetDecimal("gift-promo-vat-amount"); + row.GiftPromoVatRate = rowData.GetDecimal("gift-promo-vat-rate"); + row.GiftPromotionId = rowData.GetString("gift-promotion-id"); + row.ShippingVatInclAmount = rowData.GetDecimal("shipping-vat-incl-amount"); + row.ShippingVatAmount = rowData.GetDecimal("shipping-vat-amount"); + row.ShippingVatExclAmount = rowData.GetDecimal("shipping-vat-excl-amount"); + row.ShippingVatRate = rowData.GetDecimal("shipping-vat-rate"); + row.ShippingPromoVatInclAmount = rowData.GetDecimal("shipping-promo-vat-incl-amount"); + row.ShippingPromoVatExclAmount = rowData.GetDecimal("shipping-promo-vat-excl-amount"); + row.ShippingPromoVatAmount = rowData.GetDecimal("shipping-promo-vat-amount"); + row.ShippingPromoVatRate = rowData.GetDecimal("shipping-promo-vat-rate"); + row.ShipPromotionId = rowData.GetString("ship-promotion-id"); + row.IsBusinessOrder = rowData.GetString("is-business-order"); + row.PriceDesignation = rowData.GetString("price-designation"); + row.PurchaseOrderNumber = rowData.GetString("purchase-order-number"); + row.RecipientName = rowData.GetString("recipient-name"); + row.ShipServiceLevel = rowData.GetString("ship-service-level"); + row.FulfilledBy = rowData.GetString("fulfilled-by"); + row.ShipAddress1 = rowData.GetString("ship-address-1"); + row.ShipAddress2 = rowData.GetString("ship-address-2"); + row.ShipAddress3 = rowData.GetString("ship-address-3"); + row.ShipCity = rowData.GetString("ship-city"); + row.ShipState = rowData.GetString("ship-state"); + row.ShipPostalCode = rowData.GetString("ship-postal-code"); + row.ShipCountry = rowData.GetString("ship-country"); + row.ShipPhoneNumber = rowData.GetString("ship-phone-number"); + row.ShipFromCountry = rowData.GetString("ship-from-country"); + row.ShipFromState = rowData.GetString("ship-from-state"); + row.ShipFromCity = rowData.GetString("ship-from-city"); + row.ShipFromPostalCode = rowData.GetString("ship-from-postal-code"); + row.BillingName = rowData.GetString("billing-name"); + row.BillAddress1 = rowData.GetString("bill-address-1"); + row.BillAddress2 = rowData.GetString("bill-address-2"); + row.BillAddress3 = rowData.GetString("bill-address-3"); + row.BillCity = rowData.GetString("bill-city"); + row.BillState = rowData.GetString("bill-state"); + row.BillPostalCode = rowData.GetString("bill-postal-code"); + row.BillCountry = rowData.GetString("bill-country"); + row.BillingPhoneNumber = rowData.GetString("billing-phone-number"); + row.BuyerName = rowData.GetString("buyer-name"); + row.BuyerCompanyName = rowData.GetString("buyer-company-name"); + row.LegacyCustomerOrderItemId = rowData.GetString("legacy-customer-order-item-id"); + row.RecommendedInvoiceFormat = rowData.GetString("recommended-invoice-format"); + row.BuyerTaxRegistrationType = rowData.GetString("buyer-tax-registration-type"); + row.BuyerEInvoiceAccountId = rowData.GetString("buyer-e-invoice-account-id"); + row.IsBuyerPhysicallyPresent = rowData.GetString("is-buyer-physically-present"); + row.IsSellerPhysicallyPresent = rowData.GetString("is-seller-physically-present"); + row.CitationEs = rowData.GetString("citation-es"); + row.CitationIt = rowData.GetString("citation-it"); + row.CitationFr = rowData.GetString("citation-fr"); + row.CitationDe = rowData.GetString("citation-de"); + row.CitationEn = rowData.GetString("citation-en"); + row.ExportOutsideEu = rowData.GetString("export-outside-eu"); + row.IsInvoiceCorrected = rowData.GetString("is-invoice-corrected"); + row.OriginalVatInvoiceNumber = rowData.GetString("original-vat-invoice-number"); + row.InvoiceCorrectionDetails = rowData.GetString("invoice-correction-details"); + + row.refNumber = refNumber; + return row; + + return row; + } + } +} diff --git a/Source/FikaAmazonAPI/Services/ReportService.cs b/Source/FikaAmazonAPI/Services/ReportService.cs index ac528900..55f84f0b 100644 --- a/Source/FikaAmazonAPI/Services/ReportService.cs +++ b/Source/FikaAmazonAPI/Services/ReportService.cs @@ -247,6 +247,8 @@ private async Task GetFileAsync(ReportDocument reportDocument, Cancellat cancellationToken.ThrowIfCancellationRequested(); + FileTransform.SetFileEncoding(tempFilePath); + return tempFilePath; } catch (OperationCanceledException) diff --git a/Source/FikaAmazonAPI/Utils/EncodingDetector.cs b/Source/FikaAmazonAPI/Utils/EncodingDetector.cs new file mode 100644 index 00000000..f546e46f --- /dev/null +++ b/Source/FikaAmazonAPI/Utils/EncodingDetector.cs @@ -0,0 +1,111 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; + +namespace FikaAmazonAPI.Utils +{ + public class EncodingDetector + { + private static readonly Encoding[] CommonEncodings = { + Encoding.UTF8, Encoding.Unicode, Encoding.BigEndianUnicode, + Encoding.UTF32, Encoding.ASCII, Encoding.GetEncoding("ISO-8859-1") + }; + + public static Encoding DetectEncoding(string filePath, int sampleSize = 4096) + { + byte[] buffer = File.ReadAllBytes(filePath); + return DetectEncoding(buffer, sampleSize); + } + + public static Encoding DetectEncoding(byte[] buffer, int sampleSize = 4096) + { + if (buffer == null || buffer.Length == 0) + throw new ArgumentException("Buffer is empty"); + + // Schritt 1: Prüfe auf BOM + Encoding bomEncoding = CheckBOM(buffer); + if (bomEncoding != null) + return bomEncoding; + + // Schritt 2: Prüfe auf UTF-8 + if (IsUTF8(buffer)) + return Encoding.UTF8; + + // Schritt 3: Prüfe auf bekannte Encodings durch Heuristik + return HeuristicDetection(buffer); + } + + private static Encoding CheckBOM(byte[] buffer) + { + if (buffer.Length >= 3 && buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF) + return Encoding.UTF8; // UTF-8 mit BOM + + if (buffer.Length >= 2 && buffer[0] == 0xFF && buffer[1] == 0xFE) + return Encoding.Unicode; // UTF-16 LE + + if (buffer.Length >= 2 && buffer[0] == 0xFE && buffer[1] == 0xFF) + return Encoding.BigEndianUnicode; // UTF-16 BE + + if (buffer.Length >= 4 && buffer[0] == 0x00 && buffer[1] == 0x00 && buffer[2] == 0xFE && buffer[3] == 0xFF) + return Encoding.UTF32; // UTF-32 BE + + if (buffer.Length >= 4 && buffer[0] == 0xFF && buffer[1] == 0xFE && buffer[2] == 0x00 && buffer[3] == 0x00) + return Encoding.UTF32; // UTF-32 LE + + return null; + } + + private static bool IsUTF8(byte[] buffer) + { + int i = 0; + while (i < buffer.Length) + { + if (buffer[i] <= 0x7F) + { + i++; + continue; + } + + if (buffer[i] >= 0xC2 && buffer[i] <= 0xDF) + { + if (i + 1 < buffer.Length && (buffer[i + 1] & 0xC0) == 0x80) + { + i += 2; + continue; + } + } + else if (buffer[i] >= 0xE0 && buffer[i] <= 0xEF) + { + if (i + 2 < buffer.Length && (buffer[i + 1] & 0xC0) == 0x80 && (buffer[i + 2] & 0xC0) == 0x80) + { + i += 3; + continue; + } + } + else if (buffer[i] >= 0xF0 && buffer[i] <= 0xF4) + { + if (i + 3 < buffer.Length && (buffer[i + 1] & 0xC0) == 0x80 && (buffer[i + 2] & 0xC0) == 0x80 && (buffer[i + 3] & 0xC0) == 0x80) + { + i += 4; + continue; + } + } + + return false; + } + return true; + } + + private static Encoding HeuristicDetection(byte[] buffer) + { + int asciiCount = buffer.Count(b => b <= 127); + int extendedCount = buffer.Length - asciiCount; + + if (extendedCount == 0) + return Encoding.ASCII; // ASCII-Dateien enthalten keine Sonderzeichen + + return Encoding.GetEncoding("ISO-8859-1"); // Latin-1 als Fallback für ANSI + } + } +} diff --git a/Source/FikaAmazonAPI/Utils/EncodingHelper.cs b/Source/FikaAmazonAPI/Utils/EncodingHelper.cs new file mode 100644 index 00000000..1b214607 --- /dev/null +++ b/Source/FikaAmazonAPI/Utils/EncodingHelper.cs @@ -0,0 +1,26 @@ +using System.Globalization; +using System.Text; + +namespace FikaAmazonAPI.Utils +{ + public static class EncodingHelper + { + public static Encoding GetEncodingFromCulture(CultureInfo culture) + { + string cultureName = culture.Name.ToLower(); + + return cultureName switch + { + "en-us" => Encoding.GetEncoding("Windows-1252"), // Westeuropa (ANSI) + "de-de" => Encoding.GetEncoding("Windows-1252"), // Westeuropa (ANSI) + "fr-fr" => Encoding.GetEncoding("Windows-1252"), // Westeuropa (ANSI) + "ja-jp" => Encoding.GetEncoding("shift_jis"), // Japanisch + "zh-cn" => Encoding.GetEncoding("gb2312"), // Vereinfachtes Chinesisch + "ru-ru" => Encoding.GetEncoding("Windows-1251"), // Kyrillisch + "ko-kr" => Encoding.GetEncoding("ks_c_5601-1987"), // Koreanisch + "ar-sa" => Encoding.GetEncoding("Windows-1256"), // Arabisch + _ => Encoding.UTF8 // Standard als Fallback + }; + } + } +} diff --git a/Source/FikaAmazonAPI/Utils/FileTransform.cs b/Source/FikaAmazonAPI/Utils/FileTransform.cs index 3b0ea3e2..8d1a2b48 100644 --- a/Source/FikaAmazonAPI/Utils/FileTransform.cs +++ b/Source/FikaAmazonAPI/Utils/FileTransform.cs @@ -1,7 +1,10 @@ using System; +using System.Globalization; using System.IO; using System.IO.Compression; using System.Security.Cryptography; +using System.Text; +using System.Threading; namespace FikaAmazonAPI.Utils { @@ -31,8 +34,6 @@ public static string DecryptString(byte[] key, byte[] iv, byte[] cipherText) } - - public static string Decompress(string fileName) { FileInfo fileInfo = new FileInfo(fileName); @@ -54,6 +55,12 @@ public static string Decompress(string fileName) } } + public static void SetFileEncoding(string filePath) + { + var detectedEncoding = EncodingDetector.DetectEncoding(filePath); + string content = File.ReadAllText(filePath, detectedEncoding); + File.WriteAllText(filePath, content, EncodingHelper.GetEncodingFromCulture(Thread.CurrentThread.CurrentCulture)); + } } }