einvoice 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.coderabbit.yaml +14 -0
- data/.github/workflows/build.yml +3 -3
- data/CHANGELOG.md +21 -0
- data/Gemfile +8 -0
- data/README.md +80 -8
- data/Rakefile +44 -6
- data/einvoice.gemspec +11 -6
- data/lib/einvoice/configuration.rb +1 -1
- data/lib/einvoice/connection.rb +1 -1
- data/lib/einvoice/donation_unit_list.json +1186 -1183
- data/lib/einvoice/provider.rb +1 -2
- data/lib/einvoice/tradevan/model/issue_data.rb +2 -2
- data/lib/einvoice/tradevan/model/issue_item.rb +1 -1
- data/lib/einvoice/tradevan/model/void_data.rb +2 -2
- data/lib/einvoice/tradevan/provider.rb +0 -1
- data/lib/einvoice/version.rb +1 -1
- data/lib/einvoice.rb +0 -1
- data/lib/faraday/response/decode_tradevan.rb +4 -5
- data/lib/faraday/response/parse_xml.rb +14 -0
- metadata +17 -40
- data/lib/einvoice/utils.rb +0 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0ce2293f378b646e5ae9ab94d5cce8b539a7dd441d8db03472ae3c6f4bbd9735
|
|
4
|
+
data.tar.gz: 0d8e775b71d10cb37f3892cc40bd8e46e1ce872721ec7cb1c0864807d6a5f24d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 233c9f4194869b5e61420c7b255f768befe0f09cb3f952f1c300c7d9b8e417d98053461f1b794cf5f7a9a7c4b4a428b38b99db0005e77429d9686bf3afad1bda
|
|
7
|
+
data.tar.gz: 11f5ba7e0132eb0dc9eb4d0fc16cd48252b950b3c189f2c45efa4757bfbc43936c3c97155692d2e3b989bf038e528e20f5ee023031817fed9ab0a3a7bbb564d7
|
data/.coderabbit.yaml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
reviews:
|
|
2
|
+
profile: chill
|
|
3
|
+
request_changes_workflow: false
|
|
4
|
+
auto_review:
|
|
5
|
+
enabled: true
|
|
6
|
+
drafts: false
|
|
7
|
+
path_filters:
|
|
8
|
+
- "!lib/einvoice/donation_unit_list.json"
|
|
9
|
+
path_instructions:
|
|
10
|
+
- path: "lib/einvoice/tradevan/**"
|
|
11
|
+
instructions: >
|
|
12
|
+
This is legacy 1.x code kept for backward compatibility.
|
|
13
|
+
Do not suggest large-scale refactors or architecture changes here.
|
|
14
|
+
Focus only on correctness bugs and security issues.
|
data/.github/workflows/build.yml
CHANGED
|
@@ -2,9 +2,9 @@ name: build
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches: [
|
|
5
|
+
branches: [ 1-x-stable ]
|
|
6
6
|
pull_request:
|
|
7
|
-
branches: [
|
|
7
|
+
branches: [ 1-x-stable ]
|
|
8
8
|
|
|
9
9
|
permissions:
|
|
10
10
|
contents: read
|
|
@@ -16,7 +16,7 @@ jobs:
|
|
|
16
16
|
ruby-version: ['3.1', '3.2', '3.3', '3.4']
|
|
17
17
|
runs-on: ubuntu-latest
|
|
18
18
|
steps:
|
|
19
|
-
- uses: actions/checkout@
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
20
|
- name: Set up Ruby
|
|
21
21
|
uses: ruby/setup-ruby@v1
|
|
22
22
|
with:
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [1.5.0] - 2026-09-11
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Tradevan `IssueData#payload` and `VoidData#payload` now exclude
|
|
12
|
+
`context_for_validation`. On Rails 8 this internal ivar was sent as a
|
|
13
|
+
business field once `valid?` had run, and Tradevan rejected the whole
|
|
14
|
+
request. Thanks @dlackty.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- **Ruby 3.1 or newer is now required.** CI covers 3.1 through 3.4.
|
|
18
|
+
- **Faraday 2 is now required** (`~> 2.1`); `faraday_middleware` is gone,
|
|
19
|
+
replaced by a built-in `Faraday::Response::ParseXml`. If you pin
|
|
20
|
+
`faraday` to 1.x, stay on 1.4.0.
|
|
21
|
+
- `Einvoice::Utils` and the `gyoku` dependency have been removed. Nothing
|
|
22
|
+
in the gem called them.
|
|
23
|
+
|
|
24
|
+
### Internal
|
|
25
|
+
- Added request-level Tradevan provider specs.
|
|
26
|
+
- CI runs on `1-x-stable`; the donation unit list task parses with `CSV`.
|
|
27
|
+
- README documents the allowance operations and 1.x maintenance mode.
|
|
28
|
+
|
|
8
29
|
## [1.4.0] - 2026-08-08
|
|
9
30
|
|
|
10
31
|
### Changed
|
data/Gemfile
CHANGED
|
@@ -3,6 +3,14 @@ source 'https://rubygems.org'
|
|
|
3
3
|
# Specify your gem's dependencies in einvoice.gemspec
|
|
4
4
|
gemspec
|
|
5
5
|
|
|
6
|
+
# Remove this pin once we drop Ruby 3.1 support. json 3.0 dropped quirks_mode,
|
|
7
|
+
# which activesupport 7.2.3.2 still passes; 7.2.3.2 is the newest activesupport
|
|
8
|
+
# that works on Ruby 3.1.
|
|
9
|
+
gem "json", "< 3.0"
|
|
10
|
+
|
|
11
|
+
# Used by the update_donation_unit rake task; csv leaves the default gems in Ruby 3.4.
|
|
12
|
+
gem "csv", require: false, group: :development
|
|
13
|
+
|
|
6
14
|
gem "ostruct", require: false, group: :development
|
|
7
15
|
gem "simplecov", require: false, group: :test
|
|
8
16
|
gem "simplecov-lcov", require: false, group: :test
|
data/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
[](https://github.com/abookyun/einvoice/actions/workflows/build.yml)
|
|
3
3
|
[](https://codecov.io/github/abookyun/einvoice)
|
|
4
4
|
|
|
5
|
+
> **The 1.x line is moving to maintenance mode.** A provider-agnostic 2.0
|
|
6
|
+
> rewrite is in progress on `main`; once it lands, 1.x continues on the
|
|
7
|
+
> [`1-x-stable`](https://github.com/abookyun/einvoice/tree/1-x-stable) branch.
|
|
8
|
+
> If you depend on the current Tradevan client, pin `~> 1.4`.
|
|
9
|
+
|
|
5
10
|
## What's E-Invoice
|
|
6
11
|
|
|
7
12
|
To support the thriving e-commerce industry and lower the business costs and barriers associated with printing paper receipts, the Taiwan Executive Yuan announced plans in August 2000 to implement electronic receipts in Taiwan and launched a comprehensive project in May 2010 to promote e-invoice applications. This initiative employs innovative approaches such as allowing consumers to claim virtual receipts via multiple devices, offering automatic checking of receipt lottery numbers, and providing a variety of channels for retailers to issue receipts.
|
|
@@ -67,7 +72,7 @@ Einvoice::Tradevan::Provider.new(ssl_verify: false)
|
|
|
67
72
|
client = Einvoice::Client.new(Einvoice::Tradevan::Provider.new)
|
|
68
73
|
```
|
|
69
74
|
|
|
70
|
-
### Issue an invoice
|
|
75
|
+
### Issue an invoice (開立電子發票)
|
|
71
76
|
|
|
72
77
|
```ruby
|
|
73
78
|
payload = {
|
|
@@ -138,7 +143,7 @@ result.data
|
|
|
138
143
|
# }
|
|
139
144
|
```
|
|
140
145
|
|
|
141
|
-
### Cancel an invoice
|
|
146
|
+
### Cancel an invoice (作廢電子發票)
|
|
142
147
|
|
|
143
148
|
```ruby
|
|
144
149
|
payload = {
|
|
@@ -165,7 +170,74 @@ result.data
|
|
|
165
170
|
# }
|
|
166
171
|
```
|
|
167
172
|
|
|
168
|
-
###
|
|
173
|
+
### Issue an allowance (開立折讓單)
|
|
174
|
+
|
|
175
|
+
An allowance against a previously issued invoice is sent through `issue` with `type: "A"`. Each item must reference the original invoice (`invoiceNumber` / `invoiceDate`) and carry its tax-exclusive amount (`itemExclude`).
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
payload = {
|
|
179
|
+
companyUn: "12345678",
|
|
180
|
+
orgId: "ABCDE",
|
|
181
|
+
type: "A",
|
|
182
|
+
allowanceIdentifier: "12345678_ABCDE_53b2a44e4b3d",
|
|
183
|
+
transactionNumber: "53b2a44e4b3d",
|
|
184
|
+
transactionDate: "20160426",
|
|
185
|
+
paperPrintMode: "0",
|
|
186
|
+
invoiceAlarmMode: "4",
|
|
187
|
+
allowanceExclusiveAmount: "95",
|
|
188
|
+
allowanceTax: "5",
|
|
189
|
+
allowanceInclusiveAmount: "100",
|
|
190
|
+
allowancePaperReturned: "N",
|
|
191
|
+
invoicePaperReturned: "N",
|
|
192
|
+
receiverName: "John Appleseed",
|
|
193
|
+
receiverEmail: "john@gmail.com",
|
|
194
|
+
itemList: [
|
|
195
|
+
{
|
|
196
|
+
saleIdentifier: "12345678_ABCDE_53b2a44e4b3c",
|
|
197
|
+
serialNumber: "0001",
|
|
198
|
+
invoiceNumber: "GX38551078",
|
|
199
|
+
invoiceDate: "20160425",
|
|
200
|
+
productName: "Coffee Latte",
|
|
201
|
+
qty: "1000",
|
|
202
|
+
price: "100",
|
|
203
|
+
itemExclude: "95",
|
|
204
|
+
tax: "5",
|
|
205
|
+
itemTotal: "100",
|
|
206
|
+
taxType: "T"
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
result = client.issue(payload)
|
|
214
|
+
result.successful?
|
|
215
|
+
#=> true
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Notes:
|
|
219
|
+
|
|
220
|
+
* `allowanceIdentifier` must be prefixed with `"#{companyUn}_#{orgId}_"`, like `saleIdentifier` on invoices.
|
|
221
|
+
* `allowanceExclusiveAmount` + `allowanceTax` = `allowanceInclusiveAmount`.
|
|
222
|
+
|
|
223
|
+
### Void an allowance (作廢折讓單)
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
payload = {
|
|
227
|
+
type: "A",
|
|
228
|
+
companyUn: "12345678",
|
|
229
|
+
allowanceNumber: "ABCDE20160426001",
|
|
230
|
+
allowancePaperReturned: "Y",
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
result = client.cancel(payload)
|
|
236
|
+
result.successful?
|
|
237
|
+
#=> true
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Search invoice by memberId/sellTargetCode (載具/會員發票查詢)
|
|
169
241
|
|
|
170
242
|
```ruby
|
|
171
243
|
payload = {
|
|
@@ -211,7 +283,7 @@ result.data
|
|
|
211
283
|
# ]
|
|
212
284
|
```
|
|
213
285
|
|
|
214
|
-
### Search invoice detail
|
|
286
|
+
### Search invoice detail (發票開立明細查詢)
|
|
215
287
|
|
|
216
288
|
```ruby
|
|
217
289
|
result = client.search_invoice_detail("GX38551078")
|
|
@@ -255,7 +327,7 @@ result.data
|
|
|
255
327
|
# }
|
|
256
328
|
```
|
|
257
329
|
|
|
258
|
-
### Send card info to customer
|
|
330
|
+
### Send card info to customer (電子發票載具歸戶/中獎通知作業)
|
|
259
331
|
|
|
260
332
|
```ruby
|
|
261
333
|
payload = {
|
|
@@ -275,7 +347,7 @@ result.successful?
|
|
|
275
347
|
result.data
|
|
276
348
|
```
|
|
277
349
|
|
|
278
|
-
### Get invoice mark info
|
|
350
|
+
### Get invoice mark info (電子發票字軌號碼配額取號/分配作業)
|
|
279
351
|
|
|
280
352
|
```ruby
|
|
281
353
|
payload = {
|
|
@@ -307,13 +379,13 @@ result.data
|
|
|
307
379
|
# ]
|
|
308
380
|
```
|
|
309
381
|
|
|
310
|
-
### Get donate unit list
|
|
382
|
+
### Get donate unit list (受贈機關/社福團體愛心碼清單查詢)
|
|
311
383
|
|
|
312
384
|
```ruby
|
|
313
385
|
client.get_donate_unit_list("12345678")
|
|
314
386
|
```
|
|
315
387
|
|
|
316
|
-
### Get invoice content
|
|
388
|
+
### Get invoice content (電子發票內容查詢)
|
|
317
389
|
|
|
318
390
|
```ruby
|
|
319
391
|
payload = {
|
data/Rakefile
CHANGED
|
@@ -7,13 +7,51 @@ task default: :spec
|
|
|
7
7
|
|
|
8
8
|
desc "Download latest e-invoice donation unit list and convert it to JSON"
|
|
9
9
|
task :update_donation_unit do
|
|
10
|
+
require "csv"
|
|
10
11
|
require "json"
|
|
11
12
|
require "faraday"
|
|
13
|
+
|
|
12
14
|
# From https://data.gov.tw/dataset/31868
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
url = "https://dataset.einvoice.nat.gov.tw/ods/portal/ODS303W/download/3886F055-EB77-4DF9-98E2-F3F49A7D3434/1/8B227A99-042A-4903-8B34-5715442A227D/0/?fileType=csv"
|
|
16
|
+
response = Faraday.get(url)
|
|
17
|
+
raise "Download failed: HTTP #{response.status}" unless response.status == 200
|
|
18
|
+
|
|
19
|
+
body = response.body
|
|
20
|
+
|
|
21
|
+
possible = ["UTF-8", "Big5"]
|
|
22
|
+
chosen = nil
|
|
23
|
+
utf8_text = nil
|
|
24
|
+
|
|
25
|
+
possible.each do |code|
|
|
26
|
+
begin
|
|
27
|
+
str = body.dup.force_encoding(code)
|
|
28
|
+
candidate = str.encode("UTF-8")
|
|
29
|
+
if candidate.valid_encoding?
|
|
30
|
+
chosen = code
|
|
31
|
+
utf8_text = candidate
|
|
32
|
+
break
|
|
33
|
+
end
|
|
34
|
+
rescue Encoding::UndefinedConversionError, Encoding::InvalidByteSequenceError
|
|
35
|
+
next
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
unless chosen
|
|
40
|
+
chosen = "fallback(replace)"
|
|
41
|
+
utf8_text = body.dup.force_encoding("UTF-8").scrub("?")
|
|
42
|
+
puts "::warning::Used fallback encoding with replacement; some characters may be lost"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
utf8_text = utf8_text.delete_prefix("\uFEFF") # remove possible UTF-8 BOM
|
|
46
|
+
csv = CSV.parse(utf8_text, headers: true)
|
|
47
|
+
|
|
48
|
+
orgs = csv.map do |row|
|
|
49
|
+
code = (row['捐贈碼'] || row[2]).to_s.strip
|
|
50
|
+
name = (row['受捐贈機關或團體名稱'] || row[1]).to_s.strip
|
|
51
|
+
[code, name]
|
|
52
|
+
end.sort_by { |code, _| [code.to_i, code] }
|
|
53
|
+
|
|
54
|
+
File.write "lib/einvoice/donation_unit_list.json", JSON.pretty_generate(orgs.to_h)
|
|
17
55
|
|
|
18
|
-
|
|
19
|
-
end
|
|
56
|
+
puts "Wrote lib/einvoice/donation_unit_list.json (#{orgs.length} entries)"
|
|
57
|
+
end
|
data/einvoice.gemspec
CHANGED
|
@@ -9,25 +9,30 @@ Gem::Specification.new do |spec|
|
|
|
9
9
|
spec.authors = ["David Yun"]
|
|
10
10
|
spec.email = ["abookyun@gmail.com"]
|
|
11
11
|
|
|
12
|
-
spec.summary = %q{
|
|
12
|
+
spec.summary = %q{An API wrapper for Taiwan e-invoice services.}
|
|
13
13
|
spec.homepage = "https://github.com/abookyun/einvoice"
|
|
14
14
|
spec.license = "MIT"
|
|
15
15
|
|
|
16
|
+
spec.metadata = {
|
|
17
|
+
"source_code_uri" => "https://github.com/abookyun/einvoice",
|
|
18
|
+
"changelog_uri" => "https://github.com/abookyun/einvoice/blob/main/CHANGELOG.md",
|
|
19
|
+
"bug_tracker_uri" => "https://github.com/abookyun/einvoice/issues",
|
|
20
|
+
"rubygems_mfa_required" => "true"
|
|
21
|
+
}
|
|
22
|
+
|
|
16
23
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
17
24
|
spec.bindir = "exe"
|
|
18
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
19
26
|
spec.require_paths = ["lib"]
|
|
20
27
|
|
|
21
|
-
spec.required_ruby_version = '>=
|
|
22
|
-
spec.add_dependency "faraday"
|
|
23
|
-
spec.add_dependency "faraday_middleware"
|
|
28
|
+
spec.required_ruby_version = '>= 3.1'
|
|
29
|
+
spec.add_dependency "faraday", "~> 2.1"
|
|
24
30
|
spec.add_dependency "multi_xml"
|
|
25
31
|
spec.add_dependency "activemodel"
|
|
26
32
|
spec.add_dependency "activesupport"
|
|
27
|
-
spec.add_dependency "gyoku"
|
|
28
33
|
|
|
29
34
|
spec.add_development_dependency "bundler", "> 1.17"
|
|
30
|
-
spec.add_development_dependency "rake", "~>
|
|
35
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
31
36
|
spec.add_development_dependency "pry"
|
|
32
37
|
spec.add_development_dependency "rspec"
|
|
33
38
|
spec.add_development_dependency "factory_bot"
|