stackin-python-sdk 0.1.1__py3-none-any.whl → 0.1.2__py3-none-any.whl
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.
- stackin/__init__.py +1 -1
- stackin/br/__init__.py +2 -0
- stackin/br/product.py +30 -1
- stackin/br/tax.py +23 -1
- stackin/core/client.py +9 -5
- stackin/core/exceptions.py +1 -2
- stackin/core/types.py +1 -4
- {stackin_python_sdk-0.1.1.dist-info → stackin_python_sdk-0.1.2.dist-info}/METADATA +2 -2
- stackin_python_sdk-0.1.2.dist-info/RECORD +13 -0
- stackin_python_sdk-0.1.1.dist-info/RECORD +0 -13
- {stackin_python_sdk-0.1.1.dist-info → stackin_python_sdk-0.1.2.dist-info}/WHEEL +0 -0
- {stackin_python_sdk-0.1.1.dist-info → stackin_python_sdk-0.1.2.dist-info}/licenses/LICENSE +0 -0
stackin/__init__.py
CHANGED
stackin/br/__init__.py
CHANGED
|
@@ -10,6 +10,7 @@ from stackin.br.tax import (
|
|
|
10
10
|
Icms60,
|
|
11
11
|
IcmsSn101,
|
|
12
12
|
IcmsSn102,
|
|
13
|
+
IcmsSn900,
|
|
13
14
|
IcmsUfDest,
|
|
14
15
|
Ipi,
|
|
15
16
|
IpiNt,
|
|
@@ -29,6 +30,7 @@ __all__ = [
|
|
|
29
30
|
"Icms60",
|
|
30
31
|
"IcmsSn101",
|
|
31
32
|
"IcmsSn102",
|
|
33
|
+
"IcmsSn900",
|
|
32
34
|
"IcmsUfDest",
|
|
33
35
|
"Ipi",
|
|
34
36
|
"IpiTrib",
|
stackin/br/product.py
CHANGED
|
@@ -73,11 +73,36 @@ class Product(BaseModel):
|
|
|
73
73
|
extra_groups: dict[str, Any] | None = Field(default=None)
|
|
74
74
|
tax: Tax | dict[str, Any] | None = Field(default=None)
|
|
75
75
|
|
|
76
|
+
service_code: str | None = Field(
|
|
77
|
+
default=None, description="LC 116/2003 item.subitem, nfse only."
|
|
78
|
+
)
|
|
79
|
+
service_discount: float | None = Field(
|
|
80
|
+
default=None, description="Unconditional discount, nfse only."
|
|
81
|
+
)
|
|
82
|
+
tax_retained: bool = Field(
|
|
83
|
+
default=False, description="ISSQN retained by the tomador, nfse only."
|
|
84
|
+
)
|
|
85
|
+
observations: str | None = Field(
|
|
86
|
+
default=None, max_length=2000, description="nfse only."
|
|
87
|
+
)
|
|
88
|
+
|
|
76
89
|
def to_dict(self) -> dict:
|
|
77
90
|
"""Returns the item as a plain dict, ready for the request body."""
|
|
91
|
+
_NFSE_FIELDS = {
|
|
92
|
+
"service_code",
|
|
93
|
+
"service_discount",
|
|
94
|
+
"tax_retained",
|
|
95
|
+
"observations",
|
|
96
|
+
}
|
|
78
97
|
data = self.model_dump(
|
|
79
98
|
exclude_none=True,
|
|
80
|
-
exclude={
|
|
99
|
+
exclude={
|
|
100
|
+
"description",
|
|
101
|
+
"amount",
|
|
102
|
+
"tax",
|
|
103
|
+
*_BR_FIELDS,
|
|
104
|
+
*_NFSE_FIELDS,
|
|
105
|
+
},
|
|
81
106
|
)
|
|
82
107
|
br = self.model_dump(exclude_none=True, include=_BR_FIELDS - {"tax"})
|
|
83
108
|
if isinstance(self.tax, Tax):
|
|
@@ -90,4 +115,8 @@ class Product(BaseModel):
|
|
|
90
115
|
"description": self.description,
|
|
91
116
|
"amount": self.amount,
|
|
92
117
|
"product": data,
|
|
118
|
+
"service_code": self.service_code,
|
|
119
|
+
"discount": self.service_discount,
|
|
120
|
+
"tax_retained": self.tax_retained,
|
|
121
|
+
"observations": self.observations,
|
|
93
122
|
}
|
stackin/br/tax.py
CHANGED
|
@@ -77,6 +77,19 @@ class IcmsSn102(BaseModel):
|
|
|
77
77
|
csosn: str = Field(alias="CSOSN")
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
class IcmsSn900(BaseModel):
|
|
81
|
+
"""Simples Nacional ICMS, other cases (used with interstate partilha)."""
|
|
82
|
+
|
|
83
|
+
model_config = _CONFIG
|
|
84
|
+
|
|
85
|
+
orig: str | None = None
|
|
86
|
+
csosn: str = Field(default="900", alias="CSOSN")
|
|
87
|
+
mod_bc: str | None = Field(default=None, alias="modBC")
|
|
88
|
+
v_bc: str | None = Field(default=None, alias="vBC")
|
|
89
|
+
p_icms: str | None = Field(default=None, alias="pICMS")
|
|
90
|
+
v_icms: str | None = Field(default=None, alias="vICMS")
|
|
91
|
+
|
|
92
|
+
|
|
80
93
|
class IcmsUfDest(BaseModel):
|
|
81
94
|
"""Interstate ICMS share owed to the destination state."""
|
|
82
95
|
|
|
@@ -193,7 +206,15 @@ class CofinsOutr(BaseModel):
|
|
|
193
206
|
v_cofins: str = Field(alias="vCOFINS")
|
|
194
207
|
|
|
195
208
|
|
|
196
|
-
IcmsGroup =
|
|
209
|
+
IcmsGroup = (
|
|
210
|
+
Icms00
|
|
211
|
+
| Icms40
|
|
212
|
+
| Icms60
|
|
213
|
+
| IcmsSn101
|
|
214
|
+
| IcmsSn102
|
|
215
|
+
| IcmsSn900
|
|
216
|
+
| dict[str, Any]
|
|
217
|
+
)
|
|
197
218
|
PisGroup = PisAliq | PisNt | PisOutr | dict[str, Any]
|
|
198
219
|
CofinsGroup = CofinsAliq | CofinsNt | CofinsOutr | dict[str, Any]
|
|
199
220
|
|
|
@@ -203,6 +224,7 @@ _ICMS_TAGS = {
|
|
|
203
224
|
Icms60: "ICMS60",
|
|
204
225
|
IcmsSn101: "ICMSSN101",
|
|
205
226
|
IcmsSn102: "ICMSSN102",
|
|
227
|
+
IcmsSn900: "ICMSSN900",
|
|
206
228
|
}
|
|
207
229
|
_PIS_TAGS = {PisAliq: "PISAliq", PisNt: "PISNT", PisOutr: "PISOutr"}
|
|
208
230
|
_COFINS_TAGS = {
|
stackin/core/client.py
CHANGED
|
@@ -23,9 +23,7 @@ _ENVIRONMENT_URLS = {
|
|
|
23
23
|
def _resolve_base_url(
|
|
24
24
|
base_url: str | None, environment: Environment | str | None
|
|
25
25
|
) -> str:
|
|
26
|
-
"""Resolution order
|
|
27
|
-
then env var, then the environment's default — `base_url` always
|
|
28
|
-
wins over `environment` at each step."""
|
|
26
|
+
"""Resolution order: explicit param, then env var, then environment's default."""
|
|
29
27
|
if base_url:
|
|
30
28
|
return base_url
|
|
31
29
|
if url := os.environ.get("STACKIN_BASE_URL"):
|
|
@@ -61,6 +59,8 @@ class Invoice:
|
|
|
61
59
|
tax_id: str,
|
|
62
60
|
items: list[Product],
|
|
63
61
|
recipient_address: Address | None = None,
|
|
62
|
+
series: str | None = None,
|
|
63
|
+
number: str | None = None,
|
|
64
64
|
) -> dict:
|
|
65
65
|
"""Issues a fiscal document."""
|
|
66
66
|
if not items:
|
|
@@ -81,8 +81,12 @@ class Invoice:
|
|
|
81
81
|
"tax_id": tax_id,
|
|
82
82
|
"items": [item.to_dict() for item in items],
|
|
83
83
|
}
|
|
84
|
-
if recipient_address
|
|
85
|
-
payload["
|
|
84
|
+
if recipient_address:
|
|
85
|
+
payload["recipient_address"] = recipient_address.to_dict()
|
|
86
|
+
if series:
|
|
87
|
+
payload["series"] = series
|
|
88
|
+
if number:
|
|
89
|
+
payload["number"] = number
|
|
86
90
|
|
|
87
91
|
return self._request("POST", "/invoices", json=payload)
|
|
88
92
|
|
stackin/core/exceptions.py
CHANGED
stackin/core/types.py
CHANGED
|
@@ -13,10 +13,7 @@ class DocumentType(str, Enum):
|
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class Environment(str, Enum):
|
|
16
|
-
"""Which host to talk to — pass to `Invoice(environment=...)`
|
|
17
|
-
instead of a raw `base_url`. `TEST` and `PRODUCTION` resolve to the
|
|
18
|
-
same host: homologation vs. production invoicing is a per-company
|
|
19
|
-
setting on the platform side, not a different SDK host."""
|
|
16
|
+
"""Which host to talk to — pass to `Invoice(environment=...)` instead of a raw `base_url`."""
|
|
20
17
|
|
|
21
18
|
LOCAL = "local"
|
|
22
19
|
TEST = "test"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stackin-python-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: Python SDK for issuing, consulting and cancelling electronic invoices.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -104,5 +104,5 @@ Building the full fiscal document (issuer data, service code, tax groups, schema
|
|
|
104
104
|
|
|
105
105
|
## Examples
|
|
106
106
|
|
|
107
|
-
Runnable end-to-end scripts in [`examples/`](examples/)
|
|
107
|
+
Runnable end-to-end scripts in [`examples/nfe/`](examples/nfe/) and [`examples/nfse/`](examples/nfse/) — one file per field/variant, from the bare minimum to every field filled.
|
|
108
108
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
stackin/__init__.py,sha256=zY-ivw5Pckpw-tXwUmn4hwJzU4TZ6MJrW8RLEpPDG_c,495
|
|
2
|
+
stackin/br/__init__.py,sha256=Nw5VyeoFFJPqweZI60uADtBrxilXqkpzpkYtR5WZ630,656
|
|
3
|
+
stackin/br/product.py,sha256=nuHQkWMz0eRmWYWPoduwmAfibBQvrcajEMxerwHGa9E,4004
|
|
4
|
+
stackin/br/tax.py,sha256=PdUkg5JdNKy7AHlt5ZpM9svvilZA93FPMr2QNi0SRlw,7716
|
|
5
|
+
stackin/core/__init__.py,sha256=dxtOP1DopMSNFYGh3Hf12RVcs58R9xwLn-gqfKFo-HE,19
|
|
6
|
+
stackin/core/address.py,sha256=-w5R3nzfU5T3VoxIeygu5Dq4DRb_lJHAGc0lmSMwEMU,616
|
|
7
|
+
stackin/core/client.py,sha256=abXZ71nz-skXM2afUOFNwY1j0_zlJRssihOQT4hwrXg,4591
|
|
8
|
+
stackin/core/exceptions.py,sha256=enl8GS_WlDV6PyH1M_KTxzLG9Qdr-9MJrMXB_AlCtYg,521
|
|
9
|
+
stackin/core/types.py,sha256=SYpLS2EnqKcudm7wFwCTOVAQe1IYT5T0TGSD0E6z8bo,416
|
|
10
|
+
stackin_python_sdk-0.1.2.dist-info/METADATA,sha256=PRoF8vxBg3HZFqW3exHsmqeYT6JNLUuTnElpnyUJhwQ,5200
|
|
11
|
+
stackin_python_sdk-0.1.2.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
12
|
+
stackin_python_sdk-0.1.2.dist-info/licenses/LICENSE,sha256=D-XHDcKFFhzEo9C5Dp4QPvt00pb_u__hi4Bc08zpxyU,1072
|
|
13
|
+
stackin_python_sdk-0.1.2.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
stackin/__init__.py,sha256=aRGcx1scidLojq96GQ5z9GwM9fsB1rI0XqTGwyRKJd0,495
|
|
2
|
-
stackin/br/__init__.py,sha256=6XSFt3fa9zxY2ToZ_BY3b1zjsFaeJpkNBuPwNtp2SQE,624
|
|
3
|
-
stackin/br/product.py,sha256=OgImk2y31zrGJ4q6vVcULQjH92w6Z1yOUos7mGLR7IU,3091
|
|
4
|
-
stackin/br/tax.py,sha256=MX1cUy06lwOsPj1czGhBh0qkDb0zmwpwuh6xfcA0lQY,7190
|
|
5
|
-
stackin/core/__init__.py,sha256=dxtOP1DopMSNFYGh3Hf12RVcs58R9xwLn-gqfKFo-HE,19
|
|
6
|
-
stackin/core/address.py,sha256=-w5R3nzfU5T3VoxIeygu5Dq4DRb_lJHAGc0lmSMwEMU,616
|
|
7
|
-
stackin/core/client.py,sha256=PRQILpsFjmU-2U1YwR-i8BhAFxCMRyc_tLucFXDEbgI,4525
|
|
8
|
-
stackin/core/exceptions.py,sha256=DaSktECbcddGEkHyTBRN_w1S8dRzv8bRY1pHQoly6X0,525
|
|
9
|
-
stackin/core/types.py,sha256=10Ygq-CS8CHDzA4pJPqujUTvv2cx1-LUdf_V-_oh_-8,589
|
|
10
|
-
stackin_python_sdk-0.1.1.dist-info/METADATA,sha256=AfNLYLHka9HId5K0hTvefdaNzI8Wsez9yy_1iDzLFtU,5205
|
|
11
|
-
stackin_python_sdk-0.1.1.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
12
|
-
stackin_python_sdk-0.1.1.dist-info/licenses/LICENSE,sha256=D-XHDcKFFhzEo9C5Dp4QPvt00pb_u__hi4Bc08zpxyU,1072
|
|
13
|
-
stackin_python_sdk-0.1.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|