cetustek 0.1.0__tar.gz → 0.2.0__tar.gz
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.
Potentially problematic release.
This version of cetustek might be problematic. Click here for more details.
- cetustek-0.2.0/.claude/settings.local.json +12 -0
- cetustek-0.2.0/PKG-INFO +315 -0
- cetustek-0.2.0/README.md +285 -0
- cetustek-0.2.0/example.py +151 -0
- {cetustek-0.1.0 → cetustek-0.2.0}/pyproject.toml +1 -1
- cetustek-0.2.0/src/cetustek/__init__.py +33 -0
- cetustek-0.2.0/src/cetustek/client.py +262 -0
- {cetustek-0.1.0 → cetustek-0.2.0}/src/cetustek/models.py +52 -0
- cetustek-0.2.0/tests/test_cetustek.py +158 -0
- cetustek-0.1.0/.claude/settings.local.json +0 -9
- cetustek-0.1.0/PKG-INFO +0 -111
- cetustek-0.1.0/README.md +0 -81
- cetustek-0.1.0/src/cetustek/__init__.py +0 -19
- cetustek-0.1.0/src/cetustek/client.py +0 -124
- cetustek-0.1.0/tests/test_cetustek.py +0 -191
- {cetustek-0.1.0 → cetustek-0.2.0}/.gitignore +0 -0
- {cetustek-0.1.0 → cetustek-0.2.0}/LICENSE +0 -0
- {cetustek-0.1.0 → cetustek-0.2.0}/src/cetustek/py.typed +0 -0
- {cetustek-0.1.0 → cetustek-0.2.0}/tests/__init__.py +0 -0
cetustek-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cetustek
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python SDK for Cetustek Taiwan e-invoice API
|
|
5
|
+
Project-URL: Homepage, https://github.com/vincelee888/cetustek
|
|
6
|
+
Project-URL: Repository, https://github.com/vincelee888/cetustek
|
|
7
|
+
Project-URL: Issues, https://github.com/vincelee888/cetustek/issues
|
|
8
|
+
Author: Vince Lee
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cetustek,e-invoice,einvoice,invoice,taiwan
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.9
|
|
25
|
+
Requires-Dist: requests>=2.25.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# Cetustek
|
|
32
|
+
|
|
33
|
+
Python SDK for [Cetustek](https://www.cetustek.com.tw/) Taiwan e-invoice API.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install cetustek
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from cetustek import Cetustek, CreateInvoiceInput, InvoiceItem
|
|
45
|
+
|
|
46
|
+
# Initialize the client
|
|
47
|
+
client = Cetustek(
|
|
48
|
+
endpoint="https://invoice.cetustek.com.tw/InvoiceMultiWeb/InvoiceAPI",
|
|
49
|
+
rent_id="YOUR_RENT_ID",
|
|
50
|
+
site_code="YOUR_SITE_CODE",
|
|
51
|
+
api_password="YOUR_API_PASSWORD",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# Create an invoice
|
|
55
|
+
result = client.createInvoice(CreateInvoiceInput(
|
|
56
|
+
order_id="12345678",
|
|
57
|
+
order_date="2026/01/15",
|
|
58
|
+
buyer_identifier="12345678",
|
|
59
|
+
buyer_name="Company Name",
|
|
60
|
+
donate_mark="0",
|
|
61
|
+
invoice_type="07",
|
|
62
|
+
tax_type="1",
|
|
63
|
+
pay_way="1",
|
|
64
|
+
items=[
|
|
65
|
+
InvoiceItem(
|
|
66
|
+
production_code="PROD001",
|
|
67
|
+
description="Product",
|
|
68
|
+
quantity=1,
|
|
69
|
+
unit_price=1000,
|
|
70
|
+
)
|
|
71
|
+
],
|
|
72
|
+
))
|
|
73
|
+
|
|
74
|
+
# Access response as dataclass
|
|
75
|
+
print(f"Invoice Number: {result.invoice_number}")
|
|
76
|
+
print(f"Random Code: {result.random_code}")
|
|
77
|
+
print(f"Invoice Year: {result.invoice_year}")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API Reference
|
|
81
|
+
|
|
82
|
+
### Cetustek Client
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
from cetustek import Cetustek
|
|
86
|
+
|
|
87
|
+
client = Cetustek(
|
|
88
|
+
endpoint="https://invoice.cetustek.com.tw/InvoiceMultiWeb/InvoiceAPI",
|
|
89
|
+
rent_id="YOUR_RENT_ID",
|
|
90
|
+
site_code="YOUR_SITE_CODE",
|
|
91
|
+
api_password="YOUR_API_PASSWORD",
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
| Parameter | Description |
|
|
96
|
+
|-----------|-------------|
|
|
97
|
+
| `endpoint` | Cetustek API endpoint URL |
|
|
98
|
+
| `rent_id` | Your Cetustek rent ID (統一編號) |
|
|
99
|
+
| `site_code` | Your site code |
|
|
100
|
+
| `api_password` | Your API password |
|
|
101
|
+
|
|
102
|
+
### Create Invoice
|
|
103
|
+
|
|
104
|
+
Create a new e-invoice. Returns `CreateInvoiceResponse`.
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from cetustek import CreateInvoiceInput, InvoiceItem, CetustekAPIError
|
|
108
|
+
|
|
109
|
+
invoice_input = CreateInvoiceInput(
|
|
110
|
+
order_id="12345678", # Unique order ID
|
|
111
|
+
order_date="2026/01/15", # Format: yyyy/MM/dd
|
|
112
|
+
donate_mark="0", # 0: No donation, 1: Donate, 2: Donate to specific org
|
|
113
|
+
invoice_type="07", # 07: B2B, 08: B2C
|
|
114
|
+
tax_type="1", # 1: Taxable, 2: Zero-rated, 3: Tax-free, 9: Mixed
|
|
115
|
+
pay_way="1", # Payment method code
|
|
116
|
+
items=[
|
|
117
|
+
InvoiceItem(
|
|
118
|
+
production_code="PROD001",
|
|
119
|
+
description="Product description",
|
|
120
|
+
quantity=1,
|
|
121
|
+
unit_price=1000,
|
|
122
|
+
unit="件", # Optional: unit name
|
|
123
|
+
)
|
|
124
|
+
],
|
|
125
|
+
# Optional fields
|
|
126
|
+
buyer_identifier="12345678", # Buyer's tax ID (統一編號)
|
|
127
|
+
buyer_name="Company Name",
|
|
128
|
+
buyer_address="Address",
|
|
129
|
+
buyer_email="email@example.com",
|
|
130
|
+
tax_rate=0.05, # Default: 0.05 (5%)
|
|
131
|
+
carrier_type="3J0002", # Carrier type code
|
|
132
|
+
carrier_id1="/ABC1234", # Carrier ID
|
|
133
|
+
carrier_id2="/ABC1234", # Carrier ID confirmation
|
|
134
|
+
npoban="12345", # NPO code for donation
|
|
135
|
+
remark="備註", # Remark
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
result = client.createInvoice(invoice_input)
|
|
140
|
+
print(f"Invoice Number: {result.invoice_number}") # e.g., "WP20260002"
|
|
141
|
+
print(f"Random Code: {result.random_code}") # e.g., "6827"
|
|
142
|
+
print(f"Invoice Year: {result.invoice_year}") # e.g., "2026"
|
|
143
|
+
except CetustekAPIError as e:
|
|
144
|
+
print(f"Error: {e.code} - {e.message}")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Query Invoice
|
|
148
|
+
|
|
149
|
+
Query an existing invoice by number and year. Returns `QueryInvoiceResponse`.
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
from cetustek import QueryInvoiceInput
|
|
153
|
+
|
|
154
|
+
query_input = QueryInvoiceInput(
|
|
155
|
+
invoice_number="AA12345678", # 10-character invoice number
|
|
156
|
+
invoice_year="2026", # 4-digit year
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
result = client.queryInvoice(query_input)
|
|
160
|
+
|
|
161
|
+
# Access invoice details
|
|
162
|
+
print(f"Invoice Number: {result.invoice_number}")
|
|
163
|
+
print(f"Order ID: {result.order_id}")
|
|
164
|
+
print(f"Random Code: {result.random_code}")
|
|
165
|
+
print(f"Buyer Name: {result.buyer_name}")
|
|
166
|
+
print(f"Seller Name: {result.seller_name}")
|
|
167
|
+
print(f"Total Amount: {result.total_amount}")
|
|
168
|
+
print(f"Invoice Status: {result.invoice_status}")
|
|
169
|
+
|
|
170
|
+
# Access raw XML for additional parsing
|
|
171
|
+
print(result.raw_xml)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Cancel Invoice
|
|
175
|
+
|
|
176
|
+
Cancel (void) an existing invoice. Returns `CancelInvoiceResponse`.
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from cetustek import CancelInvoiceInput
|
|
180
|
+
|
|
181
|
+
cancel_input = CancelInvoiceInput(
|
|
182
|
+
invoice_number="AA12345678",
|
|
183
|
+
invoice_year="2026",
|
|
184
|
+
remark="Cancellation reason",
|
|
185
|
+
return_tax_document_number=None, # Optional: tax document number
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# Standard cancellation (with validation)
|
|
189
|
+
result = client.cancelInvoice(cancel_input)
|
|
190
|
+
|
|
191
|
+
# Skip validation checks
|
|
192
|
+
result = client.cancelInvoice(cancel_input, no_check=True)
|
|
193
|
+
|
|
194
|
+
if result.success:
|
|
195
|
+
print("Invoice cancelled successfully")
|
|
196
|
+
else:
|
|
197
|
+
print(f"Failed with code: {result.code}")
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Response Models
|
|
201
|
+
|
|
202
|
+
### CreateInvoiceResponse
|
|
203
|
+
|
|
204
|
+
| Field | Type | Description |
|
|
205
|
+
|-------|------|-------------|
|
|
206
|
+
| `invoice_number` | str | 10-character invoice number (e.g., "WP20260002") |
|
|
207
|
+
| `random_code` | str | 4-digit random code (e.g., "6827") |
|
|
208
|
+
| `invoice_year` | str | Year extracted from invoice number (property) |
|
|
209
|
+
|
|
210
|
+
### QueryInvoiceResponse
|
|
211
|
+
|
|
212
|
+
| Field | Type | Description |
|
|
213
|
+
|-------|------|-------------|
|
|
214
|
+
| `invoice_number` | str | Invoice number |
|
|
215
|
+
| `invoice_date` | str | Invoice date |
|
|
216
|
+
| `invoice_time` | str | Invoice time |
|
|
217
|
+
| `order_id` | str | Order ID |
|
|
218
|
+
| `random_code` | str | Random code |
|
|
219
|
+
| `buyer_identifier` | str | Buyer tax ID |
|
|
220
|
+
| `buyer_name` | str | Buyer name |
|
|
221
|
+
| `seller_identifier` | str | Seller tax ID |
|
|
222
|
+
| `seller_name` | str | Seller name |
|
|
223
|
+
| `invoice_status` | str | Invoice status |
|
|
224
|
+
| `donate_mark` | str | Donation mark |
|
|
225
|
+
| `carrier_type` | str | Carrier type |
|
|
226
|
+
| `carrier_id` | str | Carrier ID |
|
|
227
|
+
| `npoban` | str | NPO code |
|
|
228
|
+
| `tax_type` | str | Tax type |
|
|
229
|
+
| `sales_amount` | float | Sales amount |
|
|
230
|
+
| `tax_amount` | float | Tax amount |
|
|
231
|
+
| `total_amount` | float | Total amount |
|
|
232
|
+
| `raw_xml` | str | Original XML response |
|
|
233
|
+
|
|
234
|
+
### CancelInvoiceResponse
|
|
235
|
+
|
|
236
|
+
| Field | Type | Description |
|
|
237
|
+
|-------|------|-------------|
|
|
238
|
+
| `success` | bool | Whether cancellation succeeded |
|
|
239
|
+
| `code` | str | Response code ("C0" for success) |
|
|
240
|
+
| `message` | str | Error message (if failed) |
|
|
241
|
+
|
|
242
|
+
## Input Models
|
|
243
|
+
|
|
244
|
+
### InvoiceItem
|
|
245
|
+
|
|
246
|
+
| Field | Type | Required | Description |
|
|
247
|
+
|-------|------|----------|-------------|
|
|
248
|
+
| `production_code` | str | Yes | Product code |
|
|
249
|
+
| `description` | str | Yes | Product description |
|
|
250
|
+
| `quantity` | float | Yes | Quantity |
|
|
251
|
+
| `unit_price` | float | Yes | Unit price |
|
|
252
|
+
| `unit` | str | No | Unit name (e.g., "件", "個") |
|
|
253
|
+
|
|
254
|
+
### CreateInvoiceInput
|
|
255
|
+
|
|
256
|
+
| Field | Type | Required | Description |
|
|
257
|
+
|-------|------|----------|-------------|
|
|
258
|
+
| `order_id` | str | Yes | Unique order ID |
|
|
259
|
+
| `order_date` | str | Yes | Order date (yyyy/MM/dd) |
|
|
260
|
+
| `donate_mark` | str | Yes | Donation mark: 0/1/2 |
|
|
261
|
+
| `invoice_type` | str | Yes | Invoice type: 07 (B2B) / 08 (B2C) |
|
|
262
|
+
| `pay_way` | str | Yes | Payment method code |
|
|
263
|
+
| `tax_type` | str | Yes | Tax type: 1/2/3/4/5/9 |
|
|
264
|
+
| `items` | List[InvoiceItem] | Yes | Invoice items |
|
|
265
|
+
| `buyer_identifier` | str | No | Buyer tax ID |
|
|
266
|
+
| `buyer_name` | str | No | Buyer name |
|
|
267
|
+
| `buyer_address` | str | No | Buyer address |
|
|
268
|
+
| `buyer_email` | str | No | Buyer email |
|
|
269
|
+
| `tax_rate` | float | No | Tax rate (default: 0.05) |
|
|
270
|
+
| `carrier_type` | str | No | Carrier type code |
|
|
271
|
+
| `carrier_id1` | str | No | Carrier ID |
|
|
272
|
+
| `carrier_id2` | str | No | Carrier ID confirmation |
|
|
273
|
+
| `npoban` | str | No | NPO code for donation |
|
|
274
|
+
| `remark` | str | No | Remark |
|
|
275
|
+
|
|
276
|
+
## Exception Handling
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from cetustek import CetustekError, CetustekAPIError
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
result = client.createInvoice(invoice_input)
|
|
283
|
+
except CetustekAPIError as e:
|
|
284
|
+
# API returned an error response
|
|
285
|
+
print(f"API Error Code: {e.code}")
|
|
286
|
+
print(f"API Error Message: {e.message}")
|
|
287
|
+
except CetustekError as e:
|
|
288
|
+
# General SDK error (e.g., invalid response format)
|
|
289
|
+
print(f"SDK Error: {e}")
|
|
290
|
+
except Exception as e:
|
|
291
|
+
# Network or other errors
|
|
292
|
+
print(f"Error: {e}")
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Example
|
|
296
|
+
|
|
297
|
+
See [example.py](example.py) for a complete working example.
|
|
298
|
+
|
|
299
|
+
## Development
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
# Clone the repository
|
|
303
|
+
git clone https://github.com/vincelee888/cetustek.git
|
|
304
|
+
cd cetustek
|
|
305
|
+
|
|
306
|
+
# Install in development mode
|
|
307
|
+
pip install -e ".[dev]"
|
|
308
|
+
|
|
309
|
+
# Run tests
|
|
310
|
+
pytest
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## License
|
|
314
|
+
|
|
315
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
cetustek-0.2.0/README.md
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Cetustek
|
|
2
|
+
|
|
3
|
+
Python SDK for [Cetustek](https://www.cetustek.com.tw/) Taiwan e-invoice API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install cetustek
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from cetustek import Cetustek, CreateInvoiceInput, InvoiceItem
|
|
15
|
+
|
|
16
|
+
# Initialize the client
|
|
17
|
+
client = Cetustek(
|
|
18
|
+
endpoint="https://invoice.cetustek.com.tw/InvoiceMultiWeb/InvoiceAPI",
|
|
19
|
+
rent_id="YOUR_RENT_ID",
|
|
20
|
+
site_code="YOUR_SITE_CODE",
|
|
21
|
+
api_password="YOUR_API_PASSWORD",
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Create an invoice
|
|
25
|
+
result = client.createInvoice(CreateInvoiceInput(
|
|
26
|
+
order_id="12345678",
|
|
27
|
+
order_date="2026/01/15",
|
|
28
|
+
buyer_identifier="12345678",
|
|
29
|
+
buyer_name="Company Name",
|
|
30
|
+
donate_mark="0",
|
|
31
|
+
invoice_type="07",
|
|
32
|
+
tax_type="1",
|
|
33
|
+
pay_way="1",
|
|
34
|
+
items=[
|
|
35
|
+
InvoiceItem(
|
|
36
|
+
production_code="PROD001",
|
|
37
|
+
description="Product",
|
|
38
|
+
quantity=1,
|
|
39
|
+
unit_price=1000,
|
|
40
|
+
)
|
|
41
|
+
],
|
|
42
|
+
))
|
|
43
|
+
|
|
44
|
+
# Access response as dataclass
|
|
45
|
+
print(f"Invoice Number: {result.invoice_number}")
|
|
46
|
+
print(f"Random Code: {result.random_code}")
|
|
47
|
+
print(f"Invoice Year: {result.invoice_year}")
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## API Reference
|
|
51
|
+
|
|
52
|
+
### Cetustek Client
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from cetustek import Cetustek
|
|
56
|
+
|
|
57
|
+
client = Cetustek(
|
|
58
|
+
endpoint="https://invoice.cetustek.com.tw/InvoiceMultiWeb/InvoiceAPI",
|
|
59
|
+
rent_id="YOUR_RENT_ID",
|
|
60
|
+
site_code="YOUR_SITE_CODE",
|
|
61
|
+
api_password="YOUR_API_PASSWORD",
|
|
62
|
+
)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
| Parameter | Description |
|
|
66
|
+
|-----------|-------------|
|
|
67
|
+
| `endpoint` | Cetustek API endpoint URL |
|
|
68
|
+
| `rent_id` | Your Cetustek rent ID (統一編號) |
|
|
69
|
+
| `site_code` | Your site code |
|
|
70
|
+
| `api_password` | Your API password |
|
|
71
|
+
|
|
72
|
+
### Create Invoice
|
|
73
|
+
|
|
74
|
+
Create a new e-invoice. Returns `CreateInvoiceResponse`.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from cetustek import CreateInvoiceInput, InvoiceItem, CetustekAPIError
|
|
78
|
+
|
|
79
|
+
invoice_input = CreateInvoiceInput(
|
|
80
|
+
order_id="12345678", # Unique order ID
|
|
81
|
+
order_date="2026/01/15", # Format: yyyy/MM/dd
|
|
82
|
+
donate_mark="0", # 0: No donation, 1: Donate, 2: Donate to specific org
|
|
83
|
+
invoice_type="07", # 07: B2B, 08: B2C
|
|
84
|
+
tax_type="1", # 1: Taxable, 2: Zero-rated, 3: Tax-free, 9: Mixed
|
|
85
|
+
pay_way="1", # Payment method code
|
|
86
|
+
items=[
|
|
87
|
+
InvoiceItem(
|
|
88
|
+
production_code="PROD001",
|
|
89
|
+
description="Product description",
|
|
90
|
+
quantity=1,
|
|
91
|
+
unit_price=1000,
|
|
92
|
+
unit="件", # Optional: unit name
|
|
93
|
+
)
|
|
94
|
+
],
|
|
95
|
+
# Optional fields
|
|
96
|
+
buyer_identifier="12345678", # Buyer's tax ID (統一編號)
|
|
97
|
+
buyer_name="Company Name",
|
|
98
|
+
buyer_address="Address",
|
|
99
|
+
buyer_email="email@example.com",
|
|
100
|
+
tax_rate=0.05, # Default: 0.05 (5%)
|
|
101
|
+
carrier_type="3J0002", # Carrier type code
|
|
102
|
+
carrier_id1="/ABC1234", # Carrier ID
|
|
103
|
+
carrier_id2="/ABC1234", # Carrier ID confirmation
|
|
104
|
+
npoban="12345", # NPO code for donation
|
|
105
|
+
remark="備註", # Remark
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
result = client.createInvoice(invoice_input)
|
|
110
|
+
print(f"Invoice Number: {result.invoice_number}") # e.g., "WP20260002"
|
|
111
|
+
print(f"Random Code: {result.random_code}") # e.g., "6827"
|
|
112
|
+
print(f"Invoice Year: {result.invoice_year}") # e.g., "2026"
|
|
113
|
+
except CetustekAPIError as e:
|
|
114
|
+
print(f"Error: {e.code} - {e.message}")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Query Invoice
|
|
118
|
+
|
|
119
|
+
Query an existing invoice by number and year. Returns `QueryInvoiceResponse`.
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from cetustek import QueryInvoiceInput
|
|
123
|
+
|
|
124
|
+
query_input = QueryInvoiceInput(
|
|
125
|
+
invoice_number="AA12345678", # 10-character invoice number
|
|
126
|
+
invoice_year="2026", # 4-digit year
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
result = client.queryInvoice(query_input)
|
|
130
|
+
|
|
131
|
+
# Access invoice details
|
|
132
|
+
print(f"Invoice Number: {result.invoice_number}")
|
|
133
|
+
print(f"Order ID: {result.order_id}")
|
|
134
|
+
print(f"Random Code: {result.random_code}")
|
|
135
|
+
print(f"Buyer Name: {result.buyer_name}")
|
|
136
|
+
print(f"Seller Name: {result.seller_name}")
|
|
137
|
+
print(f"Total Amount: {result.total_amount}")
|
|
138
|
+
print(f"Invoice Status: {result.invoice_status}")
|
|
139
|
+
|
|
140
|
+
# Access raw XML for additional parsing
|
|
141
|
+
print(result.raw_xml)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Cancel Invoice
|
|
145
|
+
|
|
146
|
+
Cancel (void) an existing invoice. Returns `CancelInvoiceResponse`.
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from cetustek import CancelInvoiceInput
|
|
150
|
+
|
|
151
|
+
cancel_input = CancelInvoiceInput(
|
|
152
|
+
invoice_number="AA12345678",
|
|
153
|
+
invoice_year="2026",
|
|
154
|
+
remark="Cancellation reason",
|
|
155
|
+
return_tax_document_number=None, # Optional: tax document number
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Standard cancellation (with validation)
|
|
159
|
+
result = client.cancelInvoice(cancel_input)
|
|
160
|
+
|
|
161
|
+
# Skip validation checks
|
|
162
|
+
result = client.cancelInvoice(cancel_input, no_check=True)
|
|
163
|
+
|
|
164
|
+
if result.success:
|
|
165
|
+
print("Invoice cancelled successfully")
|
|
166
|
+
else:
|
|
167
|
+
print(f"Failed with code: {result.code}")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Response Models
|
|
171
|
+
|
|
172
|
+
### CreateInvoiceResponse
|
|
173
|
+
|
|
174
|
+
| Field | Type | Description |
|
|
175
|
+
|-------|------|-------------|
|
|
176
|
+
| `invoice_number` | str | 10-character invoice number (e.g., "WP20260002") |
|
|
177
|
+
| `random_code` | str | 4-digit random code (e.g., "6827") |
|
|
178
|
+
| `invoice_year` | str | Year extracted from invoice number (property) |
|
|
179
|
+
|
|
180
|
+
### QueryInvoiceResponse
|
|
181
|
+
|
|
182
|
+
| Field | Type | Description |
|
|
183
|
+
|-------|------|-------------|
|
|
184
|
+
| `invoice_number` | str | Invoice number |
|
|
185
|
+
| `invoice_date` | str | Invoice date |
|
|
186
|
+
| `invoice_time` | str | Invoice time |
|
|
187
|
+
| `order_id` | str | Order ID |
|
|
188
|
+
| `random_code` | str | Random code |
|
|
189
|
+
| `buyer_identifier` | str | Buyer tax ID |
|
|
190
|
+
| `buyer_name` | str | Buyer name |
|
|
191
|
+
| `seller_identifier` | str | Seller tax ID |
|
|
192
|
+
| `seller_name` | str | Seller name |
|
|
193
|
+
| `invoice_status` | str | Invoice status |
|
|
194
|
+
| `donate_mark` | str | Donation mark |
|
|
195
|
+
| `carrier_type` | str | Carrier type |
|
|
196
|
+
| `carrier_id` | str | Carrier ID |
|
|
197
|
+
| `npoban` | str | NPO code |
|
|
198
|
+
| `tax_type` | str | Tax type |
|
|
199
|
+
| `sales_amount` | float | Sales amount |
|
|
200
|
+
| `tax_amount` | float | Tax amount |
|
|
201
|
+
| `total_amount` | float | Total amount |
|
|
202
|
+
| `raw_xml` | str | Original XML response |
|
|
203
|
+
|
|
204
|
+
### CancelInvoiceResponse
|
|
205
|
+
|
|
206
|
+
| Field | Type | Description |
|
|
207
|
+
|-------|------|-------------|
|
|
208
|
+
| `success` | bool | Whether cancellation succeeded |
|
|
209
|
+
| `code` | str | Response code ("C0" for success) |
|
|
210
|
+
| `message` | str | Error message (if failed) |
|
|
211
|
+
|
|
212
|
+
## Input Models
|
|
213
|
+
|
|
214
|
+
### InvoiceItem
|
|
215
|
+
|
|
216
|
+
| Field | Type | Required | Description |
|
|
217
|
+
|-------|------|----------|-------------|
|
|
218
|
+
| `production_code` | str | Yes | Product code |
|
|
219
|
+
| `description` | str | Yes | Product description |
|
|
220
|
+
| `quantity` | float | Yes | Quantity |
|
|
221
|
+
| `unit_price` | float | Yes | Unit price |
|
|
222
|
+
| `unit` | str | No | Unit name (e.g., "件", "個") |
|
|
223
|
+
|
|
224
|
+
### CreateInvoiceInput
|
|
225
|
+
|
|
226
|
+
| Field | Type | Required | Description |
|
|
227
|
+
|-------|------|----------|-------------|
|
|
228
|
+
| `order_id` | str | Yes | Unique order ID |
|
|
229
|
+
| `order_date` | str | Yes | Order date (yyyy/MM/dd) |
|
|
230
|
+
| `donate_mark` | str | Yes | Donation mark: 0/1/2 |
|
|
231
|
+
| `invoice_type` | str | Yes | Invoice type: 07 (B2B) / 08 (B2C) |
|
|
232
|
+
| `pay_way` | str | Yes | Payment method code |
|
|
233
|
+
| `tax_type` | str | Yes | Tax type: 1/2/3/4/5/9 |
|
|
234
|
+
| `items` | List[InvoiceItem] | Yes | Invoice items |
|
|
235
|
+
| `buyer_identifier` | str | No | Buyer tax ID |
|
|
236
|
+
| `buyer_name` | str | No | Buyer name |
|
|
237
|
+
| `buyer_address` | str | No | Buyer address |
|
|
238
|
+
| `buyer_email` | str | No | Buyer email |
|
|
239
|
+
| `tax_rate` | float | No | Tax rate (default: 0.05) |
|
|
240
|
+
| `carrier_type` | str | No | Carrier type code |
|
|
241
|
+
| `carrier_id1` | str | No | Carrier ID |
|
|
242
|
+
| `carrier_id2` | str | No | Carrier ID confirmation |
|
|
243
|
+
| `npoban` | str | No | NPO code for donation |
|
|
244
|
+
| `remark` | str | No | Remark |
|
|
245
|
+
|
|
246
|
+
## Exception Handling
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from cetustek import CetustekError, CetustekAPIError
|
|
250
|
+
|
|
251
|
+
try:
|
|
252
|
+
result = client.createInvoice(invoice_input)
|
|
253
|
+
except CetustekAPIError as e:
|
|
254
|
+
# API returned an error response
|
|
255
|
+
print(f"API Error Code: {e.code}")
|
|
256
|
+
print(f"API Error Message: {e.message}")
|
|
257
|
+
except CetustekError as e:
|
|
258
|
+
# General SDK error (e.g., invalid response format)
|
|
259
|
+
print(f"SDK Error: {e}")
|
|
260
|
+
except Exception as e:
|
|
261
|
+
# Network or other errors
|
|
262
|
+
print(f"Error: {e}")
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## Example
|
|
266
|
+
|
|
267
|
+
See [example.py](example.py) for a complete working example.
|
|
268
|
+
|
|
269
|
+
## Development
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# Clone the repository
|
|
273
|
+
git clone https://github.com/vincelee888/cetustek.git
|
|
274
|
+
cd cetustek
|
|
275
|
+
|
|
276
|
+
# Install in development mode
|
|
277
|
+
pip install -e ".[dev]"
|
|
278
|
+
|
|
279
|
+
# Run tests
|
|
280
|
+
pytest
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## License
|
|
284
|
+
|
|
285
|
+
MIT License - see [LICENSE](LICENSE) for details.
|