8
0

17 کامیت‌ها 2e24283c9b ... 298f08412e

نویسنده SHA1 پیام تاریخ
  7515 2e24283c9b Initial commit 2 سال پیش
  Snoopy 298f08412e Reconstruct 2 سال پیش
  Snoopy 89672f6084 <Bug Fix> Random Number 2 سال پیش
  Snoopy 71e303eb27 1. sellerInvoiceFailureWatcher 增加 LastAccess filter 2 سال پیش
  Snoopy d5af0f6a2b (Improve) 加入網優因定期維護重啟SQL Server造成Gateway發票上傳失敗時的自動化後處理機制 2 سال پیش
  Snoopy 72ab15b95d (Improve) Change BUSINESS_NAME_API URL to https 3 سال پیش
  Snoopy d3a9533a5d (Fix Bug)當查不到統一編號對應的營業人名稱時,需填入任意的四位數字 3 سال پیش
  Snoopy 742c3c26b5 1.預設不捐贈發票; 2.新開DonateMark屬性可設定是否捐贈 3 سال پیش
  Snoopy 923e1cb09b Improve SellerInvoiceResponse_Changed() 3 سال پیش
  Snoopy 202e8c377d 1.一次性回傳發票處理完成通知;2.修正同時開立多張發票造成的檔案存取權限問題 3 سال پیش
  Snoopy 8c51f17032 增加可藉由DataNumber查詢發票開立結果的函式 3 سال پیش
  Snoopy 728930f5ef 1.新增隨機碼; 2.事件通知修改為發票開立通知、配號成功通知、配號成功但上傳失敗通知等三種; 3.台泥指定愛心碼 3 سال پیش
  Snoopy 4db413ea11 <Bug Fix> ResetPreInvoiceInfo() Let nPOBAN reset to DEFAULT_LOVECODE; 3 سال پیش
  Snoopy def549bf47 1.預設捐贈,若有填統編或手機條碼就不捐贈; 2.未知的營業人名稱改為隨機四位數字; 3.發票開立事件通知中的carriernumber欄位,若有填手機條碼就回傳手機條碼,否則回傳null 3 سال پیش
  Snoopy db679d76dc Add the implementation of InvoiceGenerationFailed Event 3 سال پیش
  Snoopy 618c76222e 加入專案檔案。 3 سال پیش
  Snoopy f3748d5a54 加入 .gitignore 與 .gitattributes。 3 سال پیش
10فایلهای تغییر یافته به همراه1210 افزوده شده و 32 حذف شده
  1. 63 0
      .gitattributes
  2. 282 29
      .gitignore
  3. 0 3
      README.md
  4. 25 0
      TCCInvoice.sln
  5. 14 0
      TCCInvoice/App.config
  6. 604 0
      TCCInvoice/InvoiceGenerator.cs
  7. 73 0
      TCCInvoice/Program.cs
  8. 36 0
      TCCInvoice/Properties/AssemblyInfo.cs
  9. 100 0
      TCCInvoice/TCCInvoice.csproj
  10. 13 0
      TCCInvoice/packages.config

+ 63 - 0
.gitattributes

@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs     diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following 
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln       merge=binary
+#*.csproj    merge=binary
+#*.vbproj    merge=binary
+#*.vcxproj   merge=binary
+#*.vcproj    merge=binary
+#*.dbproj    merge=binary
+#*.fsproj    merge=binary
+#*.lsproj    merge=binary
+#*.wixproj   merge=binary
+#*.modelproj merge=binary
+#*.sqlproj   merge=binary
+#*.wwaproj   merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg   binary
+#*.png   binary
+#*.gif   binary
+
+###############################################################################
+# diff behavior for common document formats
+# 
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the 
+# entries below.
+###############################################################################
+#*.doc   diff=astextplain
+#*.DOC   diff=astextplain
+#*.docx  diff=astextplain
+#*.DOCX  diff=astextplain
+#*.dot   diff=astextplain
+#*.DOT   diff=astextplain
+#*.pdf   diff=astextplain
+#*.PDF   diff=astextplain
+#*.rtf   diff=astextplain
+#*.RTF   diff=astextplain

+ 282 - 29
.gitignore

@@ -1,30 +1,85 @@
-# ---> C Sharp
-# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
-[Bb]in/
-[Oo]bj/
-
-# mstest test results
-TestResults
-
 ## Ignore Visual Studio temporary files, build results, and
 ## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
 
 # User-specific files
+*.rsuser
 *.suo
 *.user
+*.userosscache
 *.sln.docstates
 
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
 # Build results
 [Dd]ebug/
+[Dd]ebugPublic/
 [Rr]elease/
+[Rr]eleases/
 x64/
+x86/
+[Ww][Ii][Nn]32/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Oo]ut/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# ASP.NET Scaffolding
+ScaffoldingReadMe.txt
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
 *_i.c
 *_p.c
+*_h.h
 *.ilk
 *.meta
 *.obj
+*.iobj
 *.pch
 *.pdb
+*.ipdb
 *.pgc
 *.pgd
 *.rsp
@@ -33,35 +88,83 @@ x64/
 *.tli
 *.tlh
 *.tmp
+*.tmp_proj
+*_wpftmp.csproj
 *.log
 *.vspscc
 *.vssscc
 .builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
 
 # Visual C++ cache files
 ipch/
 *.aps
 *.ncb
+*.opendb
 *.opensdf
 *.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
 
 # Visual Studio profiler
 *.psess
 *.vsp
 *.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
 
 # Guidance Automation Toolkit
 *.gpState
 
 # ReSharper is a .NET coding add-in
-_ReSharper*
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Coverlet is a free, cross platform Code Coverage Tool
+coverage*.json
+coverage*.xml
+coverage*.info
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
 
 # NCrunch
-*.ncrunch*
+_NCrunch_*
 .*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
 
 # Installshield output folder
-[Ee]xpress
+[Ee]xpress/
 
 # DocProject is a documentation generator add-in
 DocProject/buildhelp/
@@ -74,37 +177,187 @@ DocProject/Help/Html2
 DocProject/Help/html
 
 # Click-Once directory
-publish
+publish/
 
 # Publish Web Output
-*.Publish.xml
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
 
-# NuGet Packages Directory
-packages
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
 
-# Windows Azure Build Output
-csx
+# Microsoft Azure Build Output
+csx/
 *.build.csdef
 
-# Windows Store app package directory
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
 AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
 
 # Others
-[Bb]in
-[Oo]bj
-sql
-TestResults
-[Tt]est[Rr]esult*
-*.Cache
-ClientBin
-[Ss]tyle[Cc]op.*
+ClientBin/
 ~$*
+*~
 *.dbmdl
-Generated_Code #added for RIA/Silverlight projects
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
 
-# Backup & report files from converting an old project file to a newer
-# Visual Studio version. Backup files are not needed, because we have git ;-)
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
 _UpgradeReport_Files/
 Backup*/
 UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
 
+# Fody - auto-generated XML schema
+FodyWeavers.xsd

+ 0 - 3
README.md

@@ -1,3 +0,0 @@
-# TCCInvoice
-
-Invoice generator for TCC

+ 25 - 0
TCCInvoice.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.32112.339
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TCCInvoice", "TCCInvoice\TCCInvoice.csproj", "{D0081CCA-054E-4BB0-8F56-12FB54B98D99}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{D0081CCA-054E-4BB0-8F56-12FB54B98D99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D0081CCA-054E-4BB0-8F56-12FB54B98D99}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D0081CCA-054E-4BB0-8F56-12FB54B98D99}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D0081CCA-054E-4BB0-8F56-12FB54B98D99}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {9C459733-FCFF-4739-9258-BD78C369DD73}
+	EndGlobalSection
+EndGlobal

+ 14 - 0
TCCInvoice/App.config

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
+    </startup>
+  <runtime>
+    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
+      <dependentAssembly>
+        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
+        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
+      </dependentAssembly>
+    </assemblyBinding>
+  </runtime>
+</configuration>

+ 604 - 0
TCCInvoice/InvoiceGenerator.cs

@@ -0,0 +1,604 @@
+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 string randomNumber;
+        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 Dictionary<string, string> DataRandomNoPair = new Dictionary<string, string>();
+
+        private FileSystemWatcher preInvoiceResponseWatcher;
+        private FileSystemWatcher sellerInvoiceResponseWatcher;
+        private FileSystemWatcher sellerInvoiceFailureWatcher;
+        private List<InvoiceResponseItem> invoiceResponseItemList = new List<InvoiceResponseItem>();
+        private string responseDataNumber;
+        private string responseInvoiceError;
+        private int totalQuantity = 0;
+        private int count = 0;
+        #endregion
+
+        #region Constants
+        private const string SELLER_ID = "83196607";
+        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 : https://gcis.nat.g0v.tw/id/30435973
+        /// API : https://gcis.nat.g0v.tw/api/show/30435973
+        /// </summary>
+        private const string BUSINESS_NAME_API = "https://gcis.nat.g0v.tw/api/show/";
+        private const string CARRIER_TYPE_BARCODE = "3J0002";
+        private const string DEFAULT_LOVECODE = "4997276";
+
+        private const string PREINVOICE_PATH = @"C:\UXB2B_EIVO\PreInvoice\";
+        private const string PREINVOICE_RESPONSE_PATH = @"C:\UXB2B_EIVO\PreInvoice(Response)\";
+        private const string SELLER_INVOICE_RESPONSE_PATH = @"C:\Gateway-83196607\logs\InvoiceNoInspector\SellerInvoice(Response)\";
+        private const string SELLER_INVOICE_PATH = @"C:\Gateway-83196607\logs\InvoiceNoInspector\SellerInvoice\";
+        private const string SELLER_INVOICE_FAILURE_PATH = @"C:\Gateway-83196607\logs\InvoiceNoInspector\SellerInvoice(Failure)\";
+        private const string RESPONSE_BACKUP_PATH = @"C:\UXB2B_EIVO\ResponseBackup\";
+        #endregion
+
+        #region Properties
+        /// <summary>
+        /// (必填)單據號碼,須為唯一值,長度上限20
+        /// </summary>
+        public string DataNumber
+        {
+            set
+            {
+                Reset();
+                dataNumber = value;
+                DataRandomNoPair.Add(value, this.randomNumber);
+                Console.WriteLine("DataNumber = " + dataNumber);
+            }
+        }
+        /// <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 = GenerateRandom4Digit();
+                }
+                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代表捐贈。預設值為0
+        /// </summary>
+        public int DonateMark
+        {
+            set
+            {
+                donateMark = value;
+
+                donateMark = donateMark != 0 ? 1 : 0;
+                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(int quantity)
+        {
+            totalQuantity = quantity;
+            preInvoiceResponseWatcher = new FileSystemWatcher()
+            {
+                Path = PREINVOICE_RESPONSE_PATH,
+                Filter = "*.xml",
+                NotifyFilter = NotifyFilters.LastWrite,
+                IncludeSubdirectories = false,
+                EnableRaisingEvents = true,
+            };
+            preInvoiceResponseWatcher.Changed += PreInvoiceResponse_Changed;
+
+            sellerInvoiceResponseWatcher = new FileSystemWatcher()
+            {
+                Path = SELLER_INVOICE_RESPONSE_PATH,
+                Filter = "*.xml",
+                NotifyFilter = NotifyFilters.LastWrite,
+                IncludeSubdirectories = false,
+                EnableRaisingEvents = true,
+            };
+            sellerInvoiceResponseWatcher.Changed += SellerInvoiceResponse_Changed;
+
+            sellerInvoiceFailureWatcher = new FileSystemWatcher()
+            {
+                Path = SELLER_INVOICE_FAILURE_PATH,
+                Filter = "*.xml",
+                NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.LastAccess,
+                IncludeSubdirectories = false,
+                EnableRaisingEvents = true,
+            };
+            sellerInvoiceFailureWatcher.Changed += SellerInvoiceFailure_Changed;
+        }
+        #endregion
+
+        #region Events & EventHandlers
+        public delegate void InvoiceCompleteEventHandler(List<InvoiceResponseItem> responseItems);
+        public event InvoiceCompleteEventHandler InvoiceCompleted;
+
+        private void PreInvoiceResponse_Changed(object sender, FileSystemEventArgs e)
+        {
+            XDocument xDoc = XDocument.Load(e.FullPath);
+
+            string status = xDoc.Descendants("Status").ElementAt(0).Value;
+            if (status == "1") // 配號成功
+            {
+                string invoicenumber = xDoc.Descendants("InvoiceNumber").ElementAt(0).Value;
+                string datanumber = xDoc.Descendants("DataNumber").ElementAt(0).Value;
+                string invoicedate = xDoc.Descendants("InvoiceDate").ElementAt(0).Value;
+                string invoicetime = xDoc.Descendants("InvoiceTime").ElementAt(0).Value;
+                string invoiceError = null;
+                string value;
+                if (DataRandomNoPair.TryGetValue(datanumber, out value))
+                {
+                    Console.WriteLine("Fetched value: {0}", value);
+                }
+                else
+                {
+                    value = "7878";
+                    Console.WriteLine("No such key: {0}", datanumber);
+                }
+                invoiceResponseItemList.Add(new InvoiceResponseItem 
+                {
+                    ResponseStatus = "配號成功",
+                    ResponseInvoiceNumber = invoicenumber,
+                    ResponseDataNumber = datanumber,
+                    ResponseInvoiceDate = invoicedate,
+                    ResponseInvoiceTime = invoicetime,
+                    ResponseRandomNumber = value,
+                    ResponseInvoiceError = invoiceError
+                });
+            }
+            else // 配號失敗
+            {
+                responseInvoiceError = xDoc.Descendants("Description").ElementAt(0).Value;
+                responseDataNumber = xDoc.Descendants("DataNumber").ElementAt(0).Value;
+                randomNumber = null;
+
+                invoiceResponseItemList.Add(new InvoiceResponseItem
+                {
+                    ResponseStatus = "配號失敗",
+                    ResponseInvoiceNumber = null,
+                    ResponseDataNumber = responseDataNumber,
+                    ResponseInvoiceDate = null,
+                    ResponseInvoiceTime = null,
+                    ResponseRandomNumber = null,
+                    ResponseInvoiceError = responseInvoiceError
+                });
+
+                count++;
+                if (count == totalQuantity)
+                {
+                    InvoiceCompleted?.Invoke(invoiceResponseItemList);
+                    TurnOffFileSystemWatcher();
+                }
+            }
+        }
+
+        private void SellerInvoiceResponse_Changed(object sender, FileSystemEventArgs e)
+        {
+            XDocument xDoc = XDocument.Load(e.FullPath);
+
+            int index = -1;
+            string status = xDoc.Descendants("Status").ElementAt(0).Value;
+            string invoicenumber = xDoc.Descendants("InvoiceNumber").ElementAt(0).Value;
+            foreach (InvoiceResponseItem item in invoiceResponseItemList)
+            {
+                if (item.ResponseInvoiceNumber == invoicenumber)
+                {
+                    index = invoiceResponseItemList.IndexOf(item);
+                }
+            }
+
+            if (index == -1)
+            {
+                return; // something wrong
+            }
+            
+            if (status == "1") // 上傳成功
+            {
+                invoiceResponseItemList[index].ResponseStatus = "上傳成功";
+
+                count++;
+                if (count == totalQuantity)
+                {
+                    InvoiceCompleted?.Invoke(invoiceResponseItemList);
+                    TurnOffFileSystemWatcher();
+                }
+            }
+            else // 上傳失敗
+            {
+                string invoiceerror = xDoc.Descendants("Description").ElementAt(0).Value;
+
+                invoiceResponseItemList[index].ResponseStatus = "上傳失敗";
+                invoiceResponseItemList[index].ResponseInvoiceError = invoiceerror;
+
+                count++;
+                if (count == totalQuantity)
+                {
+                    InvoiceCompleted?.Invoke(invoiceResponseItemList);
+                    TurnOffFileSystemWatcher();
+                }
+            }
+        }
+
+        private void SellerInvoiceFailure_Changed(object sender, FileSystemEventArgs e)
+        {
+            string failurePath = e.FullPath;
+            string responsePath = SELLER_INVOICE_RESPONSE_PATH + e.Name;
+            string sellerinvoicePath = SELLER_INVOICE_PATH + e.Name;
+
+            // 當SELLER_INVOICE_FAILURE_PATH出現檔案時,先等待500毫秒,
+            // 再去檢查SELLER_INVOICE_RESPONSE_PATH是否也出現同一個檔名的檔案,
+            // 如果沒有出現,代表應該是網優SQL連線失敗造成的發票上傳失敗,
+            // 此時會先等待180秒,再把該檔案複製到SELLER_INVOICE_PATH,讓Gateway自動重新上傳
+            Task.Run(async delegate
+            {
+                await Task.Delay(500);
+                if (!File.Exists(responsePath)) // 可能是網優SQL連線失敗,需要嘗試重新上傳
+                {
+                    await Task.Delay(30000);
+                    File.Copy(failurePath, sellerinvoicePath);
+                }
+            });
+        }
+        #endregion
+
+        #region Instance Methods
+        /// <summary>
+        /// 加入消費品項明細
+        /// </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)
+            {
+                ;
+            }
+        }
+
+        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("RandomNumber", randomNumber),
+                                  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");
+            Console.WriteLine(PREINVOICE_PATH + dataNumber + ".xml");
+        }
+
+        private void Reset()
+        {
+            dataNumber = null;
+            dataDate = DateTime.Today;
+            buyerId = null;
+            buyerName = null;
+            customsClearanceMark = null;
+            randomNumber = GenerateRandom4Digit();
+            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();
+
+            responseDataNumber = null;
+            responseInvoiceError = null;
+        }
+
+        private async Task GetBuyerNameFromBuyerIdAsync()
+        {
+            using (var client = new HttpClient())
+            {
+                try
+                {
+                    var result = await client.GetFromJsonAsync<BusinessName>(BUSINESS_NAME_API + buyerId);
+
+                    //buyerName = GenerateRandom4Digit();
+                    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
+                            }
+                        }
+                    }
+                    if (string.IsNullOrEmpty(buyerName))
+                    {
+                        buyerName = GenerateRandom4Digit();
+                    }
+                }
+                catch (Exception)
+                {
+                    buyerName = GenerateRandom4Digit();
+                }
+            };
+        }
+
+        private string GenerateRandom4Digit()
+        {
+            int min = 0;
+            int max = 9999;
+            Random rdm = new Random(Guid.NewGuid().GetHashCode());
+            return rdm.Next(min, max).ToString().PadLeft(4, '0');
+        }
+
+        private void TurnOffFileSystemWatcher()
+        {
+            if (preInvoiceResponseWatcher != null)
+            {
+                preInvoiceResponseWatcher.EnableRaisingEvents = false;
+                preInvoiceResponseWatcher.Dispose();
+                preInvoiceResponseWatcher = null;
+            }
+            if (sellerInvoiceResponseWatcher != null)
+            {
+                sellerInvoiceResponseWatcher.EnableRaisingEvents = false;
+                sellerInvoiceResponseWatcher.Dispose();
+                sellerInvoiceResponseWatcher = null;
+            }
+            if (sellerInvoiceFailureWatcher != null)
+            {
+                sellerInvoiceFailureWatcher.EnableRaisingEvents = false;
+                sellerInvoiceFailureWatcher.Dispose();
+                sellerInvoiceFailureWatcher = null;
+            }
+        }
+        #endregion
+
+        #region Nested Classes
+        internal class InvoiceResponseItem
+        {
+            public string ResponseStatus { get; set; }
+            public string ResponseInvoiceNumber { get; set; }
+            public string ResponseDataNumber { get; set; }
+            public string ResponseInvoiceDate { get; set; }
+            public string ResponseInvoiceTime { get; set; }
+            public string ResponseRandomNumber { get; set; }
+            public string ResponseInvoiceError { get; set; }
+        }
+        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
+    }
+}

+ 73 - 0
TCCInvoice/Program.cs

@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TCCInvoice
+{
+    internal class Program
+    {
+        static void Main(string[] args)
+        {
+            int count = 0;
+            int max = 30;
+            while (true)
+            {
+                if (count < max)
+                {
+                    // 建立InvoiceGenerator類別的物件
+                    InvoiceGenerator myInvoice = new InvoiceGenerator(1);
+
+                    // 註冊發票號碼已處理完畢的事件通知
+                    myInvoice.InvoiceCompleted += MyInvoice_InvoiceCompleted;
+
+                    for (int i = 0; i < 1; i++)
+                    {
+                        // 填具待開立發票的相關資料(Preinvoice)
+                        string id = DateTime.Now.ToString("yyyyMMddHHmmssffff");
+                        myInvoice.DataNumber = id;
+                        myInvoice.DataDate = DateTime.Today;
+                        myInvoice.BuyerId = null; // 一般消費者
+                        myInvoice.TotalAmount = 524;
+                        myInvoice.CarrierId1 = null;
+                        myInvoice.MainRemark = "交易訂單號:" + id;
+                        myInvoice.DonateMark = 0;
+                        myInvoice.ContactEmail = null;
+                        myInvoice.ContactPhone = null;
+                        myInvoice.AddInvoiceItem(0, "充電服務費", 41.5793, "度", 12, 499, "充電服務費每度12元");
+                        myInvoice.AddInvoiceItem(1, "占用費", 30, "分鐘", 50, 25, "占用費每小時50元");
+                        // 開立發票
+                        myInvoice.GetInvoiceResponse();
+                    }
+                    System.Threading.Thread.Sleep(20000);
+                    myInvoice.InvoiceCompleted -= MyInvoice_InvoiceCompleted;
+                }
+                else
+                {
+                    System.Threading.Thread.Sleep(1000);
+                }
+                Console.WriteLine(count++);
+            }
+
+
+            //Console.WriteLine("Press enter to exit");
+            //Console.ReadLine();
+        }
+
+        private static void MyInvoice_InvoiceCompleted(List<InvoiceGenerator.InvoiceResponseItem> responseItems)
+        {
+            List<InvoiceGenerator.InvoiceResponseItem> list = new List<InvoiceGenerator.InvoiceResponseItem>(responseItems);
+            foreach (InvoiceGenerator.InvoiceResponseItem item in list)
+            {
+                Console.WriteLine(item.ResponseStatus);
+                Console.WriteLine(item.ResponseInvoiceNumber);
+                Console.WriteLine(item.ResponseDataNumber);
+                Console.WriteLine(item.ResponseInvoiceDate);
+                Console.WriteLine(item.ResponseInvoiceTime);
+                Console.WriteLine(item.ResponseRandomNumber);
+                Console.WriteLine(item.ResponseInvoiceError);
+            }
+        }
+    }
+}

+ 36 - 0
TCCInvoice/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 組件的一般資訊是由下列的屬性集控制。
+// 變更這些屬性的值即可修改組件的相關
+// 資訊。
+[assembly: AssemblyTitle("TCCInvoice")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TCCInvoice")]
+[assembly: AssemblyCopyright("Copyright ©  2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 將 ComVisible 設為 false 可對 COM 元件隱藏
+// 組件中的類型。若必須從 COM 存取此組件中的類型,
+// 的類型,請在該類型上將 ComVisible 屬性設定為 true。
+[assembly: ComVisible(false)]
+
+// 下列 GUID 為專案公開 (Expose) 至 COM 時所要使用的 typelib ID
+[assembly: Guid("d0081cca-054e-4bb0-8f56-12fb54b98d99")]
+
+// 組件的版本資訊由下列四個值所組成:
+//
+//      主要版本
+//      次要版本
+//      組建編號
+//      修訂
+//
+// 您可以指定所有的值,也可以使用 '*' 將組建和修訂編號
+// 設為預設,如下所示:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 100 - 0
TCCInvoice/TCCInvoice.csproj

@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{D0081CCA-054E-4BB0-8F56-12FB54B98D99}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>TCCInvoice</RootNamespace>
+    <AssemblyName>TCCInvoice</AssemblyName>
+    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <DocumentationFile>bin\Debug\TCCInvoice.xml</DocumentationFile>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.2-mauipre.1.22054.8\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Core" />
+    <Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Net.Http.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Net.Http.Json.6.0.0\lib\net461\System.Net.Http.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Numerics" />
+    <Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.2-mauipre.1.22054.8\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Text.Encodings.Web, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Text.Encodings.Web.6.0.2-mauipre.1.22054.8\lib\net461\System.Text.Encodings.Web.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Text.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Text.Json.6.0.2-mauipre.1.22054.8\lib\net461\System.Text.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll</HintPath>
+    </Reference>
+    <Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
+    </Reference>
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="InvoiceGenerator.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <Import Project="..\packages\System.Text.Json.6.0.2-mauipre.1.22054.8\build\System.Text.Json.targets" Condition="Exists('..\packages\System.Text.Json.6.0.2-mauipre.1.22054.8\build\System.Text.Json.targets')" />
+  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+    <PropertyGroup>
+      <ErrorText>此專案參考這部電腦上所缺少的 NuGet 套件。請啟用 NuGet 套件還原,以下載該套件。如需詳細資訊,請參閱 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的檔案是 {0}。</ErrorText>
+    </PropertyGroup>
+    <Error Condition="!Exists('..\packages\System.Text.Json.6.0.2-mauipre.1.22054.8\build\System.Text.Json.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.Text.Json.6.0.2-mauipre.1.22054.8\build\System.Text.Json.targets'))" />
+  </Target>
+  <PropertyGroup>
+    <PreBuildEvent>
+    </PreBuildEvent>
+  </PropertyGroup>
+</Project>

+ 13 - 0
TCCInvoice/packages.config

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Microsoft.Bcl.AsyncInterfaces" version="6.0.2-mauipre.1.22054.8" targetFramework="net48" />
+  <package id="System.Buffers" version="4.5.1" targetFramework="net48" />
+  <package id="System.Memory" version="4.5.4" targetFramework="net48" />
+  <package id="System.Net.Http.Json" version="6.0.0" targetFramework="net48" />
+  <package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net48" />
+  <package id="System.Runtime.CompilerServices.Unsafe" version="6.0.2-mauipre.1.22054.8" targetFramework="net48" />
+  <package id="System.Text.Encodings.Web" version="6.0.2-mauipre.1.22054.8" targetFramework="net48" />
+  <package id="System.Text.Json" version="6.0.2-mauipre.1.22054.8" targetFramework="net48" />
+  <package id="System.Threading.Tasks.Extensions" version="4.5.4" targetFramework="net48" />
+  <package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
+</packages>