|
@@ -0,0 +1,415 @@
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.ComponentModel;
|
|
|
+using System.IO;
|
|
|
+using System.Linq;
|
|
|
+using System.Net.Http;
|
|
|
+using System.Net.Http.Json;
|
|
|
+using System.Text;
|
|
|
+using System.Threading.Tasks;
|
|
|
+using System.Xml.Linq;
|
|
|
+
|
|
|
+namespace TCCInvoice
|
|
|
+{
|
|
|
+ internal class InvoiceGenerator
|
|
|
+ {
|
|
|
+ #region Fields
|
|
|
+ private string dataNumber;
|
|
|
+ private DateTime dataDate = DateTime.Today;
|
|
|
+ private string sellerId = SELLER_ID;
|
|
|
+ private string buyerId;
|
|
|
+ private string buyerName;
|
|
|
+ private string customsClearanceMark;
|
|
|
+ private int salesAmount;
|
|
|
+ private int freeTaxSalesAmount = FREE_ZERO_TAX_SALES_AMOUNT;
|
|
|
+ private int zeroTaxSalesAmount = FREE_ZERO_TAX_SALES_AMOUNT;
|
|
|
+ private string invoiceType = INVOICE_TYPE;
|
|
|
+ private int taxType = TAX_TYPE;
|
|
|
+ private double taxRate = TAX_RATE;
|
|
|
+ private int taxAmount;
|
|
|
+ private int totalAmount;
|
|
|
+ private string printMark = DEFAULT_PRINT_MARK;
|
|
|
+ private string carrierType;
|
|
|
+ private string carrierId1;
|
|
|
+ private string carrierId2;
|
|
|
+ private string mainRemark;
|
|
|
+ private int donateMark;
|
|
|
+ private string nPOBAN;
|
|
|
+ private string contactEmail;
|
|
|
+ private string contactAddress;
|
|
|
+ private string contactPhone;
|
|
|
+ private List<InvoiceItem> invoiceItems = new List<InvoiceItem>();
|
|
|
+
|
|
|
+ private FileSystemWatcher xmlWatcher;
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Constants
|
|
|
+ private const string SELLER_ID = "83196607";
|
|
|
+ private const string UNKNOWN_BUYER_NAME = " "; // 未查詢到的營業人名稱
|
|
|
+ private const string DEFAULT_BUYER_NAME = "xxxx"; // 一般消費者
|
|
|
+ private const string DEFAULT_BUYER_ID = "0000000000";
|
|
|
+ private const int FREE_ZERO_TAX_SALES_AMOUNT = 0;
|
|
|
+ private const string INVOICE_TYPE = "07";
|
|
|
+ private const int TAX_TYPE = 1;
|
|
|
+ private const double TAX_RATE = 0.05;
|
|
|
+ private const string DEFAULT_PRINT_MARK = "N";
|
|
|
+ /// <summary>
|
|
|
+ /// WEB : http://gcis.nat.g0v.tw/id/30435973
|
|
|
+ /// API : http://gcis.nat.g0v.tw/api/show/30435973
|
|
|
+ /// </summary>
|
|
|
+ private const string BUSINESS_NAME_API = "http://gcis.nat.g0v.tw/api/show/";
|
|
|
+ private const string CARRIER_TYPE_BARCODE = "3J0002";
|
|
|
+ private const string DEFAULT_LOVECODE = "";
|
|
|
+
|
|
|
+ private const string PREINVOICE_PATH = @"C:\UXB2B_EIVO\PreInvoice\";
|
|
|
+ private const string INVOICE_RESPONSE_PATH = @"C:\UXB2B_EIVO\PreInvoice(Response)\";
|
|
|
+ private const string INVOICE_FAILURE_PATH = @"C:\UXB2B_EIVO\PreInvoice(Failure)\";
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Properties
|
|
|
+ /// <summary>
|
|
|
+ /// (必填)單據號碼,須為唯一值,長度上限20
|
|
|
+ /// </summary>
|
|
|
+ public string DataNumber
|
|
|
+ {
|
|
|
+ set => dataNumber = value;
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (必填)發票日期
|
|
|
+ /// </summary>
|
|
|
+ public DateTime DataDate
|
|
|
+ {
|
|
|
+ set => dataDate = value;
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (選填)賣方統編,預設值83196607
|
|
|
+ /// </summary>
|
|
|
+ public string SellerId
|
|
|
+ {
|
|
|
+ set => sellerId = value;
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (必填)買方統編,若無統編請填null或空字串,系統自動帶入預設值0000000000
|
|
|
+ /// </summary>
|
|
|
+ public string BuyerId
|
|
|
+ {
|
|
|
+ set
|
|
|
+ {
|
|
|
+ buyerId = string.IsNullOrEmpty(value) ? DEFAULT_BUYER_ID : value;
|
|
|
+
|
|
|
+ if (buyerId.Equals(DEFAULT_BUYER_ID)) // 一般消費者
|
|
|
+ {
|
|
|
+ buyerName = DEFAULT_BUYER_NAME;
|
|
|
+ }
|
|
|
+ else // 有統編
|
|
|
+ {
|
|
|
+ GetBuyerNameFromBuyerIdAsync().Wait();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (必填)含稅總額
|
|
|
+ /// </summary>
|
|
|
+ public int TotalAmount
|
|
|
+ {
|
|
|
+ set
|
|
|
+ {
|
|
|
+ totalAmount = value;
|
|
|
+
|
|
|
+ if (string.IsNullOrEmpty(buyerId))
|
|
|
+ {
|
|
|
+ BuyerId = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (buyerId.Equals(DEFAULT_BUYER_ID)) // 一般消費者
|
|
|
+ {
|
|
|
+ salesAmount = totalAmount;
|
|
|
+ taxAmount = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ salesAmount = (int)Math.Round((double)totalAmount / (1 + taxRate), 0, MidpointRounding.AwayFromZero);
|
|
|
+ taxAmount = totalAmount - salesAmount;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (必填)手機條碼載具,若無手機條碼請填null
|
|
|
+ /// </summary>
|
|
|
+ public string CarrierId1
|
|
|
+ {
|
|
|
+ set
|
|
|
+ {
|
|
|
+ carrierId1 = value;
|
|
|
+ carrierId2 = carrierId1;
|
|
|
+ carrierType = String.IsNullOrEmpty(carrierId1) ? null : CARRIER_TYPE_BARCODE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (選填)總備註,最多200字
|
|
|
+ /// </summary>
|
|
|
+ public string MainRemark
|
|
|
+ {
|
|
|
+ set => mainRemark = value;
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (必填)不捐贈發票填0,捐贈發票填1
|
|
|
+ /// </summary>
|
|
|
+ public int DonateMark
|
|
|
+ {
|
|
|
+ set
|
|
|
+ {
|
|
|
+ donateMark = value;
|
|
|
+ nPOBAN = donateMark == 1 ? DEFAULT_LOVECODE : null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (選填)消費者的email address
|
|
|
+ /// </summary>
|
|
|
+ public string ContactEmail
|
|
|
+ {
|
|
|
+ set => contactEmail = value;
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (選填)消費者的聯絡地址
|
|
|
+ /// </summary>
|
|
|
+ public string ContactAddress
|
|
|
+ {
|
|
|
+ set => contactAddress = value;
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// (選填)消費者的聯絡電話
|
|
|
+ /// </summary>
|
|
|
+ public string ContactPhone
|
|
|
+ {
|
|
|
+ set => contactPhone = value;
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Constructors
|
|
|
+ public InvoiceGenerator()
|
|
|
+ {
|
|
|
+ xmlWatcher = new FileSystemWatcher();
|
|
|
+ xmlWatcher.Path = INVOICE_RESPONSE_PATH;
|
|
|
+ xmlWatcher.Filter = "*.xml";
|
|
|
+ xmlWatcher.IncludeSubdirectories = false;
|
|
|
+ xmlWatcher.EnableRaisingEvents = true;
|
|
|
+ xmlWatcher.Created += InvoiceXmlCreated;
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Events & EventHandlers
|
|
|
+ public delegate void InvoiceGenerateEventHandler(string dataNumber, string invoiceNumber,
|
|
|
+ string invoiceDate, string invoiceTime, string carrierNumber);
|
|
|
+ public event InvoiceGenerateEventHandler InvoiceGenerated;
|
|
|
+
|
|
|
+ private void InvoiceXmlCreated(object sender, FileSystemEventArgs e)
|
|
|
+ {
|
|
|
+ string datanumber = null;
|
|
|
+ string invoicenumber = null;
|
|
|
+ string invoicedate = null;
|
|
|
+ string invoicetime = null;
|
|
|
+ string carriernumber = null;
|
|
|
+
|
|
|
+ XDocument xDoc = XDocument.Load(e.FullPath);
|
|
|
+
|
|
|
+ string status = xDoc.Descendants("Status").ElementAt(0).Value;
|
|
|
+ if (status == "1")
|
|
|
+ {
|
|
|
+ invoicenumber = xDoc.Descendants("InvoiceNumber").ElementAt(0).Value;
|
|
|
+ datanumber = xDoc.Descendants("DataNumber").ElementAt(0).Value;
|
|
|
+ invoicedate = xDoc.Descendants("InvoiceDate").ElementAt(0).Value;
|
|
|
+ invoicetime = xDoc.Descendants("InvoiceTime").ElementAt(0).Value;
|
|
|
+ // carriernumber = xDoc.Descendants("CarrierId1").ElementAt(0).Value;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Failure
|
|
|
+ }
|
|
|
+
|
|
|
+ InvoiceGenerated?.Invoke(datanumber, invoicenumber, invoicedate, invoicetime, carriernumber);
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Instance Methods
|
|
|
+ /// <summary>
|
|
|
+ /// 加入S消費品項明細
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="sequenceNumber">發票明細之排列序號</param>
|
|
|
+ /// <param name="description">產品名稱</param>
|
|
|
+ /// <param name="quantity">數量 decimal(16,4)</param>
|
|
|
+ /// <param name="unit">單位,若不填請輸入null</param>
|
|
|
+ /// <param name="unitPrice">單價 decimal(16,4)</param>
|
|
|
+ /// <param name="amount">總金額 decimal(16,4)</param>
|
|
|
+ /// <param name="remark">單一欄位備註,若不填請輸入null</param>
|
|
|
+ public void AddInvoiceItem(int sequenceNumber, string description, double quantity,
|
|
|
+ string unit, double unitPrice, double amount, string remark)
|
|
|
+ {
|
|
|
+ InvoiceItem item = new InvoiceItem()
|
|
|
+ {
|
|
|
+ SequenceNumber = sequenceNumber,
|
|
|
+ Description = description,
|
|
|
+ Quantity = quantity,
|
|
|
+ Unit = unit,
|
|
|
+ UnitPrice = unitPrice,
|
|
|
+ Amount = amount,
|
|
|
+ Remark = remark
|
|
|
+ };
|
|
|
+
|
|
|
+ invoiceItems.Add(item);
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 產生Preinvoice檔案,交由網優Gateway開立發票,並重置InvoiceGenerator物件的欄位值
|
|
|
+ /// </summary>
|
|
|
+ public void GetInvoiceResponse()
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ GeneratePreinvoiceXML();
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ ;
|
|
|
+ }
|
|
|
+
|
|
|
+ ResetPreInvoiceInfo();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void GeneratePreinvoiceXML()
|
|
|
+ {
|
|
|
+ XDocument xDoc = new XDocument();
|
|
|
+ xDoc.Declaration = new XDeclaration("1.0", "UTF-8", "");
|
|
|
+
|
|
|
+ XElement root = new XElement("InvoiceRoot");
|
|
|
+ root.Add(new XElement("Invoice",
|
|
|
+ new XElement("DataNumber", dataNumber),
|
|
|
+ new XElement("DataDate", dataDate.ToString("yyyy/MM/dd")),
|
|
|
+ new XElement("SellerId", sellerId),
|
|
|
+ new XElement("BuyerId", buyerId),
|
|
|
+ new XElement("BuyerName", buyerName),
|
|
|
+ new XElement("CustomsClearanceMark", customsClearanceMark),
|
|
|
+ new XElement("SalesAmount", salesAmount),
|
|
|
+ new XElement("FreeTaxSalesAmount", freeTaxSalesAmount),
|
|
|
+ new XElement("ZeroTaxSalesAmount", zeroTaxSalesAmount),
|
|
|
+ new XElement("InvoiceType", invoiceType),
|
|
|
+ new XElement("TaxType", taxType),
|
|
|
+ new XElement("TaxRate", taxRate),
|
|
|
+ new XElement("TaxAmount", taxAmount),
|
|
|
+ new XElement("TotalAmount", totalAmount),
|
|
|
+ new XElement("PrintMark", printMark),
|
|
|
+ new XElement("CarrierType", carrierType),
|
|
|
+ new XElement("CarrierId1", carrierId1),
|
|
|
+ new XElement("CarrierId2", carrierId2),
|
|
|
+ new XElement("MainRemark", mainRemark),
|
|
|
+ new XElement("DonateMark", donateMark),
|
|
|
+ new XElement("NPOBAN", nPOBAN),
|
|
|
+ new XElement("Contact",
|
|
|
+ new XElement("Email", contactEmail),
|
|
|
+ new XElement("Address", contactAddress),
|
|
|
+ new XElement("TEL", contactPhone)
|
|
|
+ )
|
|
|
+ )
|
|
|
+ );
|
|
|
+
|
|
|
+ foreach (InvoiceItem item in invoiceItems)
|
|
|
+ {
|
|
|
+ XElement invoice = root.Element("Invoice");
|
|
|
+ invoice.Add(new XElement("InvoiceItem",
|
|
|
+ new XElement("SequenceNumber", item.SequenceNumber),
|
|
|
+ new XElement("Description", item.Description),
|
|
|
+ new XElement("Quantity", item.Quantity),
|
|
|
+ new XElement("Unit", item.Unit),
|
|
|
+ new XElement("UnitPrice", item.UnitPrice),
|
|
|
+ new XElement("Amount", item.Amount),
|
|
|
+ new XElement("Remark", item.Remark)
|
|
|
+ )
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ xDoc.Add(root);
|
|
|
+ xDoc.Save(PREINVOICE_PATH + dataNumber + ".xml");
|
|
|
+ }
|
|
|
+
|
|
|
+ private void ResetPreInvoiceInfo()
|
|
|
+ {
|
|
|
+ dataNumber = null;
|
|
|
+ dataDate = DateTime.Today;
|
|
|
+ buyerId = null;
|
|
|
+ buyerName = null;
|
|
|
+ customsClearanceMark = null;
|
|
|
+ salesAmount = 0;
|
|
|
+ taxAmount = 0;
|
|
|
+ totalAmount = 0;
|
|
|
+ carrierType = null;
|
|
|
+ carrierId1 = null;
|
|
|
+ carrierId2 = null;
|
|
|
+ mainRemark = null;
|
|
|
+ donateMark = 0;
|
|
|
+ nPOBAN = null;
|
|
|
+ contactEmail = null;
|
|
|
+ contactAddress = null;
|
|
|
+ contactPhone = null;
|
|
|
+ invoiceItems.Clear();
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task GetBuyerNameFromBuyerIdAsync()
|
|
|
+ {
|
|
|
+ using (var client = new HttpClient())
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ var result = await client.GetFromJsonAsync<BusinessName>(BUSINESS_NAME_API + buyerId);
|
|
|
+
|
|
|
+ buyerName = UNKNOWN_BUYER_NAME;
|
|
|
+ if (result.data != null && result.data.財政部 != null)
|
|
|
+ {
|
|
|
+ buyerName = result.data.財政部.營業人名稱; // 1st try
|
|
|
+ if (string.IsNullOrEmpty(buyerName))
|
|
|
+ {
|
|
|
+ buyerName = result.data.財政部.單位名稱; // 2nd try
|
|
|
+ if (string.IsNullOrEmpty(buyerName))
|
|
|
+ {
|
|
|
+ buyerName = result.data.名稱; // last try
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception)
|
|
|
+ {
|
|
|
+ buyerName = UNKNOWN_BUYER_NAME;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Nested Classes
|
|
|
+ internal class InvoiceItem
|
|
|
+ {
|
|
|
+ public int SequenceNumber { get; set; }
|
|
|
+ public string Description { get; set; }
|
|
|
+ public double Quantity { get; set; }
|
|
|
+ public string Unit { get; set; }
|
|
|
+ public double UnitPrice { get; set; }
|
|
|
+ public double Amount { get; set; }
|
|
|
+ public string Remark { get; set; }
|
|
|
+ }
|
|
|
+ internal class BusinessName
|
|
|
+ {
|
|
|
+ public Data data { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal class Data
|
|
|
+ {
|
|
|
+ public string 名稱 { get; set; }
|
|
|
+ public 財政部 財政部 { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ internal class 財政部
|
|
|
+ {
|
|
|
+ public string 營業人名稱 { get; set; }
|
|
|
+ public string 單位名稱 { get; set; }
|
|
|
+ }
|
|
|
+ #endregion
|
|
|
+ }
|
|
|
+}
|