digifact-sdk 2.0.1__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.
- digifact_sdk-2.0.1/PKG-INFO +207 -0
- digifact_sdk-2.0.1/README.md +183 -0
- digifact_sdk-2.0.1/digifact_sdk/__init__.py +20 -0
- digifact_sdk-2.0.1/digifact_sdk/builder.py +911 -0
- digifact_sdk-2.0.1/digifact_sdk/client.py +704 -0
- digifact_sdk-2.0.1/digifact_sdk/exceptions.py +76 -0
- digifact_sdk-2.0.1/digifact_sdk/tax.py +133 -0
- digifact_sdk-2.0.1/digifact_sdk.egg-info/PKG-INFO +207 -0
- digifact_sdk-2.0.1/digifact_sdk.egg-info/SOURCES.txt +13 -0
- digifact_sdk-2.0.1/digifact_sdk.egg-info/dependency_links.txt +1 -0
- digifact_sdk-2.0.1/digifact_sdk.egg-info/requires.txt +7 -0
- digifact_sdk-2.0.1/digifact_sdk.egg-info/top_level.txt +1 -0
- digifact_sdk-2.0.1/pyproject.toml +52 -0
- digifact_sdk-2.0.1/setup.cfg +4 -0
- digifact_sdk-2.0.1/tests/test_integration.py +432 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: digifact-sdk
|
|
3
|
+
Version: 2.0.1
|
|
4
|
+
Summary: Python SDK for Digifact FEL Guatemala e-invoicing API
|
|
5
|
+
Author-email: AALONZOLU <noreply@github.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: digifact,fel,guatemala,factura,e-invoicing,sat
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: requests>=2.28
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-timeout; extra == "dev"
|
|
23
|
+
Requires-Dist: python-dotenv; extra == "dev"
|
|
24
|
+
|
|
25
|
+
# Digifact FEL Guatemala — Python SDK
|
|
26
|
+
|
|
27
|
+
Python SDK for the [Digifact](https://www.digifact.com.gt/) FEL (Factura Electrónica en Línea) Guatemala e-invoicing API.
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install digifact-sdk
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or from source:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install -e sdk/python/
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from digifact_sdk import DigifactClient
|
|
45
|
+
|
|
46
|
+
client = DigifactClient(
|
|
47
|
+
taxid="12345678",
|
|
48
|
+
username="FELUSER",
|
|
49
|
+
password="secret",
|
|
50
|
+
environment="test", # or "production"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# FACT CF — consumer final, IVA calculated automatically
|
|
54
|
+
result = client.invoice(
|
|
55
|
+
buyer="CF",
|
|
56
|
+
items=[{"description": "Consultoría", "qty": 1, "price": 100.00}]
|
|
57
|
+
)
|
|
58
|
+
print(result.auth_number)
|
|
59
|
+
|
|
60
|
+
# FACT to NIT — buyer name fetched from SAT automatically
|
|
61
|
+
result = client.invoice(
|
|
62
|
+
buyer="12345678",
|
|
63
|
+
items=[
|
|
64
|
+
{"description": "Laptop", "qty": 1, "price": 5000.00, "type": "Bien"},
|
|
65
|
+
{"description": "Soporte anual", "qty": 1, "price": 500.00},
|
|
66
|
+
]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# FACT to CUI buyer
|
|
70
|
+
result = client.invoice(
|
|
71
|
+
buyer={"taxid": "3730617490101", "type": "CUI", "name": "Juan Pérez"},
|
|
72
|
+
items=[{"description": "Producto", "qty": 2, "price": 50.00}]
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# FCAM (Factura Cambiaria)
|
|
76
|
+
result = client.invoice(
|
|
77
|
+
buyer="12345678",
|
|
78
|
+
items=[{"description": "Servicio", "qty": 1, "price": 500.00}],
|
|
79
|
+
doc_type="FCAM",
|
|
80
|
+
payment_terms=[{"date": "2026-04-18", "amount": 500.00}]
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Credit note (NCRE)
|
|
84
|
+
result = client.credit_note(
|
|
85
|
+
buyer="12345678",
|
|
86
|
+
items=[{"description": "Devolución", "qty": 1, "price": 100.00}],
|
|
87
|
+
origin={
|
|
88
|
+
"auth_number": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
|
|
89
|
+
"date": "2026-03-18",
|
|
90
|
+
"series": "XXXXXXXX",
|
|
91
|
+
"number": "123456",
|
|
92
|
+
},
|
|
93
|
+
reason="Producto defectuoso"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Debit note (NDEB)
|
|
97
|
+
result = client.debit_note(
|
|
98
|
+
buyer="12345678",
|
|
99
|
+
items=[{"description": "Cargo adicional", "qty": 1, "price": 50.00}],
|
|
100
|
+
origin={...},
|
|
101
|
+
reason="Cargo por entrega express"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Cancel a DTE
|
|
105
|
+
result = client.cancel(
|
|
106
|
+
auth_number="XXXXXXXX-...",
|
|
107
|
+
receiver_id="CF",
|
|
108
|
+
issue_datetime="2026-03-18 21:40:14",
|
|
109
|
+
reason="Error en monto"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Total credit note
|
|
113
|
+
result = client.credit_note_total(
|
|
114
|
+
auth_number="XXXXXXXX-...",
|
|
115
|
+
issue_datetime="2026-03-18 21:40:14",
|
|
116
|
+
reason="Nota de crédito total"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
# NIT lookup
|
|
120
|
+
info = client.lookup_nit("12345678")
|
|
121
|
+
print(info["name"])
|
|
122
|
+
|
|
123
|
+
# Retrieve DTE
|
|
124
|
+
doc = client.get_dte("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Document types
|
|
128
|
+
|
|
129
|
+
| Type | Description | IVA |
|
|
130
|
+
|------|-------------|-----|
|
|
131
|
+
| `FACT` | Standard invoice | Yes |
|
|
132
|
+
| `FCAM` | Factura Cambiaria (installments) | Yes |
|
|
133
|
+
| `NDEB` | Debit note | Yes |
|
|
134
|
+
| `NCRE` | Credit note | Yes |
|
|
135
|
+
| `NABN` | Nota de Abono | No |
|
|
136
|
+
| `FESP` | Factura Especial | Yes |
|
|
137
|
+
| `RDON` | Recibo por Donación | No |
|
|
138
|
+
| `FPEQ` | Factura Pequeño Contribuyente | No |
|
|
139
|
+
| `RECI` | Recibo universitario | No |
|
|
140
|
+
| `CCA` | Cobro por Cuenta Ajena | Yes |
|
|
141
|
+
|
|
142
|
+
## IVA calculation
|
|
143
|
+
|
|
144
|
+
Prices are IVA-inclusive (what the customer pays):
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
line_total = qty × price
|
|
148
|
+
taxable_amount = line_total / 1.12
|
|
149
|
+
iva_amount = line_total − taxable_amount
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
All money values are formatted as strings with 6 decimal places.
|
|
153
|
+
|
|
154
|
+
## Item dict keys
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
{
|
|
158
|
+
"description": str, # required
|
|
159
|
+
"price": float | Decimal, # required — unit price, IVA-inclusive
|
|
160
|
+
"qty": float | Decimal, # optional, default 1
|
|
161
|
+
"type": str, # optional: "Servicio" (default) | "Bien"
|
|
162
|
+
"unit_of_measure": str, # optional, default "UNI"
|
|
163
|
+
"discount": float | None, # optional — line discount amount
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Running tests
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Unit tests (no credentials needed)
|
|
171
|
+
python -m pytest tests/ -v
|
|
172
|
+
|
|
173
|
+
# Integration tests
|
|
174
|
+
export DIGIFACT_TAXID=12345678
|
|
175
|
+
export DIGIFACT_USERNAME=FELUSER
|
|
176
|
+
export DIGIFACT_PASSWORD=your_password
|
|
177
|
+
python -m pytest tests/ -v
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Environment variables
|
|
181
|
+
|
|
182
|
+
| Variable | Description |
|
|
183
|
+
|----------|-------------|
|
|
184
|
+
| `DIGIFACT_TAXID` | Fiscal ID (e.g. `12345678`) |
|
|
185
|
+
| `DIGIFACT_USERNAME` | Username (e.g. `FELUSER`) |
|
|
186
|
+
| `DIGIFACT_PASSWORD` | Account password |
|
|
187
|
+
|
|
188
|
+
## Error handling
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
from digifact_sdk import (
|
|
192
|
+
DigifactError, # base
|
|
193
|
+
DigifactAuthError, # auth failure
|
|
194
|
+
DigifactApiError, # HTTP / API error
|
|
195
|
+
DigifactValidationError, # SAT rejection
|
|
196
|
+
DigifactNitNotFoundError, # NIT not found
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
result = client.invoice("CF", [...])
|
|
201
|
+
except DigifactValidationError as exc:
|
|
202
|
+
print(f"SAT rejected: {exc}")
|
|
203
|
+
print(f"Code: {exc.code}")
|
|
204
|
+
print(f"Raw: {exc.raw}")
|
|
205
|
+
except DigifactError as exc:
|
|
206
|
+
print(f"SDK error: {exc}")
|
|
207
|
+
```
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Digifact FEL Guatemala — Python SDK
|
|
2
|
+
|
|
3
|
+
Python SDK for the [Digifact](https://www.digifact.com.gt/) FEL (Factura Electrónica en Línea) Guatemala e-invoicing API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install digifact-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or from source:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install -e sdk/python/
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from digifact_sdk import DigifactClient
|
|
21
|
+
|
|
22
|
+
client = DigifactClient(
|
|
23
|
+
taxid="12345678",
|
|
24
|
+
username="FELUSER",
|
|
25
|
+
password="secret",
|
|
26
|
+
environment="test", # or "production"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# FACT CF — consumer final, IVA calculated automatically
|
|
30
|
+
result = client.invoice(
|
|
31
|
+
buyer="CF",
|
|
32
|
+
items=[{"description": "Consultoría", "qty": 1, "price": 100.00}]
|
|
33
|
+
)
|
|
34
|
+
print(result.auth_number)
|
|
35
|
+
|
|
36
|
+
# FACT to NIT — buyer name fetched from SAT automatically
|
|
37
|
+
result = client.invoice(
|
|
38
|
+
buyer="12345678",
|
|
39
|
+
items=[
|
|
40
|
+
{"description": "Laptop", "qty": 1, "price": 5000.00, "type": "Bien"},
|
|
41
|
+
{"description": "Soporte anual", "qty": 1, "price": 500.00},
|
|
42
|
+
]
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# FACT to CUI buyer
|
|
46
|
+
result = client.invoice(
|
|
47
|
+
buyer={"taxid": "3730617490101", "type": "CUI", "name": "Juan Pérez"},
|
|
48
|
+
items=[{"description": "Producto", "qty": 2, "price": 50.00}]
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# FCAM (Factura Cambiaria)
|
|
52
|
+
result = client.invoice(
|
|
53
|
+
buyer="12345678",
|
|
54
|
+
items=[{"description": "Servicio", "qty": 1, "price": 500.00}],
|
|
55
|
+
doc_type="FCAM",
|
|
56
|
+
payment_terms=[{"date": "2026-04-18", "amount": 500.00}]
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Credit note (NCRE)
|
|
60
|
+
result = client.credit_note(
|
|
61
|
+
buyer="12345678",
|
|
62
|
+
items=[{"description": "Devolución", "qty": 1, "price": 100.00}],
|
|
63
|
+
origin={
|
|
64
|
+
"auth_number": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
|
|
65
|
+
"date": "2026-03-18",
|
|
66
|
+
"series": "XXXXXXXX",
|
|
67
|
+
"number": "123456",
|
|
68
|
+
},
|
|
69
|
+
reason="Producto defectuoso"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Debit note (NDEB)
|
|
73
|
+
result = client.debit_note(
|
|
74
|
+
buyer="12345678",
|
|
75
|
+
items=[{"description": "Cargo adicional", "qty": 1, "price": 50.00}],
|
|
76
|
+
origin={...},
|
|
77
|
+
reason="Cargo por entrega express"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Cancel a DTE
|
|
81
|
+
result = client.cancel(
|
|
82
|
+
auth_number="XXXXXXXX-...",
|
|
83
|
+
receiver_id="CF",
|
|
84
|
+
issue_datetime="2026-03-18 21:40:14",
|
|
85
|
+
reason="Error en monto"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
# Total credit note
|
|
89
|
+
result = client.credit_note_total(
|
|
90
|
+
auth_number="XXXXXXXX-...",
|
|
91
|
+
issue_datetime="2026-03-18 21:40:14",
|
|
92
|
+
reason="Nota de crédito total"
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# NIT lookup
|
|
96
|
+
info = client.lookup_nit("12345678")
|
|
97
|
+
print(info["name"])
|
|
98
|
+
|
|
99
|
+
# Retrieve DTE
|
|
100
|
+
doc = client.get_dte("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Document types
|
|
104
|
+
|
|
105
|
+
| Type | Description | IVA |
|
|
106
|
+
|------|-------------|-----|
|
|
107
|
+
| `FACT` | Standard invoice | Yes |
|
|
108
|
+
| `FCAM` | Factura Cambiaria (installments) | Yes |
|
|
109
|
+
| `NDEB` | Debit note | Yes |
|
|
110
|
+
| `NCRE` | Credit note | Yes |
|
|
111
|
+
| `NABN` | Nota de Abono | No |
|
|
112
|
+
| `FESP` | Factura Especial | Yes |
|
|
113
|
+
| `RDON` | Recibo por Donación | No |
|
|
114
|
+
| `FPEQ` | Factura Pequeño Contribuyente | No |
|
|
115
|
+
| `RECI` | Recibo universitario | No |
|
|
116
|
+
| `CCA` | Cobro por Cuenta Ajena | Yes |
|
|
117
|
+
|
|
118
|
+
## IVA calculation
|
|
119
|
+
|
|
120
|
+
Prices are IVA-inclusive (what the customer pays):
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
line_total = qty × price
|
|
124
|
+
taxable_amount = line_total / 1.12
|
|
125
|
+
iva_amount = line_total − taxable_amount
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
All money values are formatted as strings with 6 decimal places.
|
|
129
|
+
|
|
130
|
+
## Item dict keys
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
{
|
|
134
|
+
"description": str, # required
|
|
135
|
+
"price": float | Decimal, # required — unit price, IVA-inclusive
|
|
136
|
+
"qty": float | Decimal, # optional, default 1
|
|
137
|
+
"type": str, # optional: "Servicio" (default) | "Bien"
|
|
138
|
+
"unit_of_measure": str, # optional, default "UNI"
|
|
139
|
+
"discount": float | None, # optional — line discount amount
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Running tests
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# Unit tests (no credentials needed)
|
|
147
|
+
python -m pytest tests/ -v
|
|
148
|
+
|
|
149
|
+
# Integration tests
|
|
150
|
+
export DIGIFACT_TAXID=12345678
|
|
151
|
+
export DIGIFACT_USERNAME=FELUSER
|
|
152
|
+
export DIGIFACT_PASSWORD=your_password
|
|
153
|
+
python -m pytest tests/ -v
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Environment variables
|
|
157
|
+
|
|
158
|
+
| Variable | Description |
|
|
159
|
+
|----------|-------------|
|
|
160
|
+
| `DIGIFACT_TAXID` | Fiscal ID (e.g. `12345678`) |
|
|
161
|
+
| `DIGIFACT_USERNAME` | Username (e.g. `FELUSER`) |
|
|
162
|
+
| `DIGIFACT_PASSWORD` | Account password |
|
|
163
|
+
|
|
164
|
+
## Error handling
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from digifact_sdk import (
|
|
168
|
+
DigifactError, # base
|
|
169
|
+
DigifactAuthError, # auth failure
|
|
170
|
+
DigifactApiError, # HTTP / API error
|
|
171
|
+
DigifactValidationError, # SAT rejection
|
|
172
|
+
DigifactNitNotFoundError, # NIT not found
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
try:
|
|
176
|
+
result = client.invoice("CF", [...])
|
|
177
|
+
except DigifactValidationError as exc:
|
|
178
|
+
print(f"SAT rejected: {exc}")
|
|
179
|
+
print(f"Code: {exc.code}")
|
|
180
|
+
print(f"Raw: {exc.raw}")
|
|
181
|
+
except DigifactError as exc:
|
|
182
|
+
print(f"SDK error: {exc}")
|
|
183
|
+
```
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Digifact FEL Guatemala SDK — Python package."""
|
|
2
|
+
from .client import DigifactClient, DteResult
|
|
3
|
+
from .exceptions import (
|
|
4
|
+
DigifactApiError,
|
|
5
|
+
DigifactAuthError,
|
|
6
|
+
DigifactError,
|
|
7
|
+
DigifactNitNotFoundError,
|
|
8
|
+
DigifactValidationError,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__version__ = "2.0.0"
|
|
12
|
+
__all__ = [
|
|
13
|
+
"DigifactClient",
|
|
14
|
+
"DteResult",
|
|
15
|
+
"DigifactError",
|
|
16
|
+
"DigifactAuthError",
|
|
17
|
+
"DigifactApiError",
|
|
18
|
+
"DigifactValidationError",
|
|
19
|
+
"DigifactNitNotFoundError",
|
|
20
|
+
]
|