cetustek 0.11.0 → 0.12.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/CHANGELOG.md +34 -0
- data/README.md +1 -1
- data/config/mutant.yml +13 -0
- data/lib/cetustek/configuration.rb +0 -10
- data/lib/cetustek/create_allowance.rb +2 -1
- data/lib/cetustek/models/allowance_data.rb +8 -3
- data/lib/cetustek/models/invoice_data.rb +12 -6
- data/lib/cetustek/queries.rb +4 -4
- data/lib/cetustek/version.rb +1 -1
- data/lib/cetustek/xml.rb +12 -6
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f87e7bbfa74ad309c50b741139f8cfd66f7a197813c2e2b8d515d4e810bd0ef2
|
|
4
|
+
data.tar.gz: 89e531c88d9aff213eae2e6c75a089a7121abd5a8604d0abeadbed3bc255a0c5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0af507e99327c3b63d4aee2f04398722cb7cf167604027fdacbaccdf374aa2e0300c21bf904dbd366f2390c129df0a34d36a9d46f9fb7e1471a941f11b1b3f0b
|
|
7
|
+
data.tar.gz: 2be9f5c9c30991581dc725b53235abdd608ea5b1e3c6ac356ec7f2f52dc7dde0d0874bdfcc630f508548a8c578f9806372e3ae80703cc2141203f1f6a6443148
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,40 @@ 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.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.12.0] - 2026-09-09
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- 亂填的代碼欄位不再被當成 0 靜靜送出去。`'x'.to_i` 是 `0`,而 `0` 是
|
|
13
|
+
`donate_mark` 的「載具」與 `round_num` 的「四捨五入」有效代碼,所以
|
|
14
|
+
`donate_mark: 'x'` 原本不會被擋,會被當成載具發票開出去。`tax_type: '1abc'`
|
|
15
|
+
會變成應稅,`CreateAllowance` 的 `check_allowance: 'x'` 也能通過「必須為 0」
|
|
16
|
+
的檢查。發票與折讓兩條路徑改用共用的字串比對。
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Breaking**: 最低 Ruby 版本提高到 3.3.0。3.0 已於 2024-04 EOL,不再收安全更新。
|
|
21
|
+
- **Breaking**: 上述代碼欄位原本會被默默接受的值現在會 raise `ArgumentError`。
|
|
22
|
+
影響的是本來就送錯值的呼叫端 —— 那些請求送到平台也是失敗,只是現在提早失敗。
|
|
23
|
+
- `Cetustek::Xml` 從 `module_function` 改為 `extend self`。`Xml.foo` 與
|
|
24
|
+
include 後的呼叫方式都不變。
|
|
25
|
+
|
|
26
|
+
### Internal
|
|
27
|
+
|
|
28
|
+
- 導入 mutant 做 mutation testing(`bundle exec mutant run`)。mutation coverage
|
|
29
|
+
從 57.42% 提升到 97.13%,行覆蓋率 100%。上面那個 bug 就是這樣挖出來的。
|
|
30
|
+
- 每週排程跑 mutation testing,掉出門檻才失敗(`.github/workflows/mutant.yml`)。
|
|
31
|
+
- 移除 `configuration.rb` 裡與 `lib/cetustek.rb` 重複的 `Cetustek.config` 定義
|
|
32
|
+
(被後者蓋掉的死碼)。
|
|
33
|
+
|
|
34
|
+
## [0.11.1] - 2026-09-04
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- `QueryAllowance.find` 查無折讓單時會 raise `ResultError: nodata`,而不是回 nil。
|
|
39
|
+
`nodata` 的判斷原本是 `Xml.parse_response` 的 `nil_values:` 參數,只有
|
|
40
|
+
`QueryInvoice` 有傳。改成 `Xml::NIL_VALUES` 常數,所有查詢共用同一個判斷。
|
|
41
|
+
|
|
8
42
|
## [0.11.0] - 2026-08-24
|
|
9
43
|
|
|
10
44
|
### Added
|
data/README.md
CHANGED
data/config/mutant.yml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# 對 lib/ 全部 subject 跑 mutation testing:bundle exec mutant run
|
|
2
|
+
# 開源專案用 --usage opensource,不需要 license key。
|
|
3
|
+
usage: opensource
|
|
4
|
+
integration:
|
|
5
|
+
name: rspec
|
|
6
|
+
requires:
|
|
7
|
+
- cetustek
|
|
8
|
+
matcher:
|
|
9
|
+
subjects:
|
|
10
|
+
- Cetustek*
|
|
11
|
+
coverage_criteria:
|
|
12
|
+
timeout: false
|
|
13
|
+
process_abort: true
|
|
@@ -21,7 +21,8 @@ module Cetustek
|
|
|
21
21
|
# check_allowance 只剩 0(已確認的折讓單):規格 §2.9 已刪除 1,
|
|
22
22
|
# 114/01/01 起上傳的折讓單皆為已確認。
|
|
23
23
|
def initialize(allowance_data, check_allowance: 0)
|
|
24
|
-
|
|
24
|
+
# to_i 會把 'x' 也變成 0,字串比對才擋得住亂填的值。
|
|
25
|
+
raise ArgumentError, 'check_allowance must be 0 (114/01/01 起折讓單皆為已確認)' unless check_allowance.to_s == '0'
|
|
25
26
|
|
|
26
27
|
@data = allowance_data
|
|
27
28
|
@check_allowance = 0
|
|
@@ -53,7 +53,7 @@ module Cetustek
|
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def validate_tax_type!
|
|
56
|
-
return if
|
|
56
|
+
return if code_in?(TAX_TYPES, @tax_type)
|
|
57
57
|
|
|
58
58
|
raise ArgumentError, "tax_type must be 1 (應稅), 2 (零稅率) or 3 (免稅), got #{@tax_type.inspect}"
|
|
59
59
|
end
|
|
@@ -65,14 +65,19 @@ module Cetustek
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def validate_round_num!
|
|
68
|
-
return if @round_num.nil? ||
|
|
68
|
+
return if @round_num.nil? || code_in?(ROUND_NUMS, @round_num)
|
|
69
69
|
|
|
70
70
|
raise ArgumentError, "round_num must be between #{ROUND_NUMS.first} and #{ROUND_NUMS.last}, " \
|
|
71
71
|
"got #{@round_num.inspect}"
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
# 見 InvoiceData#code_in?:to_i 會把亂填的代碼變成 0,而 0 是有效代碼。
|
|
75
|
+
def code_in?(codes, value)
|
|
76
|
+
codes.map(&:to_s).include?(value.to_s)
|
|
77
|
+
end
|
|
78
|
+
|
|
74
79
|
def blank?(value)
|
|
75
|
-
value.
|
|
80
|
+
value.to_s.strip.empty?
|
|
76
81
|
end
|
|
77
82
|
end
|
|
78
83
|
end
|
|
@@ -160,7 +160,7 @@ module Cetustek
|
|
|
160
160
|
def validate_donate_mark!
|
|
161
161
|
raise ArgumentError, 'donate_mark is required (0 載具, 1 捐贈, 2 紙本)' if blank?(@donate_mark)
|
|
162
162
|
|
|
163
|
-
unless
|
|
163
|
+
unless code_in?(DONATE_MARKS, @donate_mark)
|
|
164
164
|
raise ArgumentError, "donate_mark must be 0 (載具), 1 (捐贈) or 2 (紙本), got #{@donate_mark.inspect}"
|
|
165
165
|
end
|
|
166
166
|
|
|
@@ -197,7 +197,7 @@ module Cetustek
|
|
|
197
197
|
end
|
|
198
198
|
|
|
199
199
|
def validate_tax!
|
|
200
|
-
unless
|
|
200
|
+
unless code_in?(TAX_TYPES, @tax_type)
|
|
201
201
|
raise ArgumentError, "tax_type must be one of #{TAX_TYPES.join(', ')}, got #{@tax_type.inspect}"
|
|
202
202
|
end
|
|
203
203
|
|
|
@@ -217,20 +217,26 @@ module Cetustek
|
|
|
217
217
|
end
|
|
218
218
|
|
|
219
219
|
def validate_remark!
|
|
220
|
-
return if @remark.
|
|
220
|
+
return if @remark.to_s.length <= MAX_REMARK_LENGTH
|
|
221
221
|
|
|
222
222
|
raise ArgumentError, "remark must not exceed #{MAX_REMARK_LENGTH} characters"
|
|
223
223
|
end
|
|
224
224
|
|
|
225
225
|
def validate_round_num!
|
|
226
|
-
return if @round_num.nil? ||
|
|
226
|
+
return if @round_num.nil? || code_in?(ROUND_NUMS, @round_num)
|
|
227
227
|
|
|
228
228
|
raise ArgumentError, "round_num must be between #{ROUND_NUMS.first} and #{ROUND_NUMS.last}, " \
|
|
229
229
|
"got #{@round_num.inspect}"
|
|
230
230
|
end
|
|
231
231
|
|
|
232
|
+
# 'x'.to_i 是 0,而 0 是「載具」「四捨五入」的有效代碼 —— 用 to_i 比對,
|
|
233
|
+
# 亂填的值會被當成 0 靜靜送出去。代碼一律轉字串比對。
|
|
234
|
+
def code_in?(codes, value)
|
|
235
|
+
codes.map(&:to_s).include?(value.to_s)
|
|
236
|
+
end
|
|
237
|
+
|
|
232
238
|
def blank?(value)
|
|
233
|
-
value.
|
|
239
|
+
value.to_s.strip.empty?
|
|
234
240
|
end
|
|
235
241
|
end
|
|
236
242
|
|
|
@@ -273,7 +279,7 @@ module Cetustek
|
|
|
273
279
|
end
|
|
274
280
|
|
|
275
281
|
def blank?(value)
|
|
276
|
-
value.
|
|
282
|
+
value.to_s.strip.empty?
|
|
277
283
|
end
|
|
278
284
|
end
|
|
279
285
|
end
|
data/lib/cetustek/queries.rb
CHANGED
|
@@ -30,7 +30,7 @@ module Cetustek
|
|
|
30
30
|
# §2.5 QueryInvoicebyOrderid shares this exact XML shape (Table 13), so
|
|
31
31
|
# QueryInvoiceByOrderId#find reuses this instead of duplicating it.
|
|
32
32
|
def self.parse(xml)
|
|
33
|
-
root = Xml.parse_response(xml
|
|
33
|
+
root = Xml.parse_response(xml)
|
|
34
34
|
return nil unless root
|
|
35
35
|
|
|
36
36
|
data = Xml.parse_fields(root, FIELDS)
|
|
@@ -66,7 +66,7 @@ module Cetustek
|
|
|
66
66
|
# Query* classes — it used to be returned as if it were the number.
|
|
67
67
|
def self.find(order_id)
|
|
68
68
|
body = soap_return(query(order_id), :query_invoice_number_by_orderid).to_s.strip
|
|
69
|
-
return nil if body.empty? || body
|
|
69
|
+
return nil if body.empty? || Xml::NIL_VALUES.include?(body)
|
|
70
70
|
return body if body.match?(FORMAT)
|
|
71
71
|
|
|
72
72
|
ResultCode.raise!(body, ResultCode::COMMON)
|
|
@@ -90,8 +90,8 @@ module Cetustek
|
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
# Same query, with the returned XML parsed into a Hash of snake_case keys
|
|
93
|
-
# plus a :details array. Values are the raw strings from the XML;
|
|
94
|
-
#
|
|
93
|
+
# plus a :details array. Values are the raw strings from the XML; nil when
|
|
94
|
+
# the platform answers with nothing at all, or the documented "nodata".
|
|
95
95
|
def self.find(allowance_number)
|
|
96
96
|
parse(soap_return(query(allowance_number), :query_allowance))
|
|
97
97
|
end
|
data/lib/cetustek/version.rb
CHANGED
data/lib/cetustek/xml.rb
CHANGED
|
@@ -8,8 +8,13 @@ module Cetustek
|
|
|
8
8
|
# and a single <Invoice>/<Allowance> root carrying XSDVersion.
|
|
9
9
|
module Xml
|
|
10
10
|
XSD_VERSION = '2.8'
|
|
11
|
+
# 查無資料時平台回的字串,不是結果代碼。
|
|
12
|
+
NIL_VALUES = %w[nodata].freeze
|
|
11
13
|
|
|
12
|
-
module_function
|
|
14
|
+
# extend self(而不是 module_function):module_function 會把方法複製一份到
|
|
15
|
+
# singleton,mutation testing 改到的是 instance method 那份,正式碼呼叫的
|
|
16
|
+
# 卻是複本,整個模組的變異都殺不掉。
|
|
17
|
+
extend self
|
|
13
18
|
|
|
14
19
|
def document(root_name)
|
|
15
20
|
doc = Ox::Document.new
|
|
@@ -41,12 +46,13 @@ module Cetustek
|
|
|
41
46
|
end
|
|
42
47
|
|
|
43
48
|
# Shared guard every Query* class needs before it can parse a response
|
|
44
|
-
# body: nil for an empty answer or
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
|
|
49
|
+
# body: nil for an empty answer or the documented "nodata" sentinel;
|
|
50
|
+
# ResultError for a bare result code; otherwise the parsed root element,
|
|
51
|
+
# ready for parse_fields. "nodata" is not per-query — every 查詢 answers
|
|
52
|
+
# with it when the number simply isn't there, so it lives here.
|
|
53
|
+
def parse_response(body)
|
|
48
54
|
text = body.to_s.strip
|
|
49
|
-
return nil if text.empty? ||
|
|
55
|
+
return nil if text.empty? || NIL_VALUES.include?(text)
|
|
50
56
|
|
|
51
57
|
ResultCode.raise!(text, ResultCode::COMMON) unless text.start_with?('<')
|
|
52
58
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cetustek
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zac
|
|
@@ -78,6 +78,7 @@ files:
|
|
|
78
78
|
- CHANGELOG.md
|
|
79
79
|
- README.md
|
|
80
80
|
- Rakefile
|
|
81
|
+
- config/mutant.yml
|
|
81
82
|
- lib/cetustek.rb
|
|
82
83
|
- lib/cetustek/cancel_allowance.rb
|
|
83
84
|
- lib/cetustek/cancel_invoice.rb
|
|
@@ -110,7 +111,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
110
111
|
requirements:
|
|
111
112
|
- - ">="
|
|
112
113
|
- !ruby/object:Gem::Version
|
|
113
|
-
version: 3.
|
|
114
|
+
version: 3.3.0
|
|
114
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
115
116
|
requirements:
|
|
116
117
|
- - ">="
|