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.
@@ -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
+ ]