stackin-python-sdk 0.9.0__py3-none-any.whl → 0.10.0__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 +7 -1
- stackin/core/client.py +78 -69
- stackin/core/reference.py +173 -0
- stackin/core/taxpayer.py +49 -0
- {stackin_python_sdk-0.9.0.dist-info → stackin_python_sdk-0.10.0.dist-info}/METADATA +33 -2
- stackin_python_sdk-0.10.0.dist-info/RECORD +15 -0
- stackin_python_sdk-0.9.0.dist-info/RECORD +0 -13
- {stackin_python_sdk-0.9.0.dist-info → stackin_python_sdk-0.10.0.dist-info}/WHEEL +0 -0
- {stackin_python_sdk-0.9.0.dist-info → stackin_python_sdk-0.10.0.dist-info}/licenses/LICENSE +0 -0
stackin/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Invoice __init__ module."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.
|
|
3
|
+
__version__ = "0.10.0"
|
|
4
4
|
__description__ = (
|
|
5
5
|
"Python SDK for issuing, consulting and cancelling electronic invoices."
|
|
6
6
|
)
|
|
@@ -8,10 +8,16 @@ __description__ = (
|
|
|
8
8
|
from .core.address import Address
|
|
9
9
|
from .core.client import Invoice
|
|
10
10
|
from .core.exceptions import APIError, ConnectionFailedError, InvoiceError
|
|
11
|
+
from .core.reference import KINDS, FiscalReference, Kind
|
|
12
|
+
from .core.taxpayer import Taxpayer
|
|
11
13
|
from .core.types import DocumentType, Environment, Manifestation
|
|
12
14
|
|
|
13
15
|
__all__ = [
|
|
14
16
|
"Invoice",
|
|
17
|
+
"FiscalReference",
|
|
18
|
+
"Kind",
|
|
19
|
+
"KINDS",
|
|
20
|
+
"Taxpayer",
|
|
15
21
|
"DocumentType",
|
|
16
22
|
"Environment",
|
|
17
23
|
"Manifestation",
|
stackin/core/client.py
CHANGED
|
@@ -63,8 +63,13 @@ def _validate_nfe_address(address: Address | None) -> None:
|
|
|
63
63
|
)
|
|
64
64
|
|
|
65
65
|
|
|
66
|
-
class
|
|
67
|
-
"""
|
|
66
|
+
class _Client:
|
|
67
|
+
"""Where an api_key becomes an HTTP call, for every entry point.
|
|
68
|
+
|
|
69
|
+
Lives here rather than in a module of its own because the tests
|
|
70
|
+
patch `stackin.core.client.requests`, and so every request the SDK
|
|
71
|
+
makes has to be issued from this module.
|
|
72
|
+
"""
|
|
68
73
|
|
|
69
74
|
def __init__(
|
|
70
75
|
self,
|
|
@@ -78,6 +83,77 @@ class Invoice:
|
|
|
78
83
|
self.api_key = api_key or os.environ.get("STACKIN_API_KEY")
|
|
79
84
|
self.timeout = timeout
|
|
80
85
|
|
|
86
|
+
def _headers(self, idempotency_key: str | None = None) -> dict:
|
|
87
|
+
headers = {}
|
|
88
|
+
if self.api_key:
|
|
89
|
+
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
90
|
+
if idempotency_key:
|
|
91
|
+
headers["Idempotency-Key"] = idempotency_key
|
|
92
|
+
return headers
|
|
93
|
+
|
|
94
|
+
def _request(
|
|
95
|
+
self,
|
|
96
|
+
method: str,
|
|
97
|
+
path: str,
|
|
98
|
+
*,
|
|
99
|
+
json: dict | None = None,
|
|
100
|
+
params: dict | None = None,
|
|
101
|
+
idempotency_key: str | None = None,
|
|
102
|
+
) -> Any:
|
|
103
|
+
response = self._send(
|
|
104
|
+
method,
|
|
105
|
+
path,
|
|
106
|
+
json=json,
|
|
107
|
+
params=params,
|
|
108
|
+
idempotency_key=idempotency_key,
|
|
109
|
+
)
|
|
110
|
+
try:
|
|
111
|
+
body = response.json() if response.content else {}
|
|
112
|
+
except ValueError:
|
|
113
|
+
body = {}
|
|
114
|
+
if isinstance(body, dict):
|
|
115
|
+
return body.get("result", body)
|
|
116
|
+
return body
|
|
117
|
+
|
|
118
|
+
def _send(
|
|
119
|
+
self,
|
|
120
|
+
method: str,
|
|
121
|
+
path: str,
|
|
122
|
+
*,
|
|
123
|
+
json: dict | None = None,
|
|
124
|
+
params: dict | None = None,
|
|
125
|
+
idempotency_key: str | None = None,
|
|
126
|
+
) -> requests.Response:
|
|
127
|
+
url = f"{self.base_url}/api/v1{path}"
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
response = requests.request(
|
|
131
|
+
method,
|
|
132
|
+
url,
|
|
133
|
+
json=json,
|
|
134
|
+
params=params,
|
|
135
|
+
headers=self._headers(idempotency_key),
|
|
136
|
+
timeout=self.timeout,
|
|
137
|
+
)
|
|
138
|
+
except requests.RequestException as error:
|
|
139
|
+
raise ConnectionFailedError(str(error)) from error
|
|
140
|
+
|
|
141
|
+
if not response.ok:
|
|
142
|
+
try:
|
|
143
|
+
body = response.json() if response.content else {}
|
|
144
|
+
except ValueError:
|
|
145
|
+
body = {}
|
|
146
|
+
raise APIError(
|
|
147
|
+
status_code=response.status_code,
|
|
148
|
+
detail=body.get("detail", response.text),
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return response
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class Invoice(_Client):
|
|
155
|
+
"""Client for issuing, consulting, and cancelling fiscal documents."""
|
|
156
|
+
|
|
81
157
|
def issue(
|
|
82
158
|
self,
|
|
83
159
|
*,
|
|
@@ -328,70 +404,3 @@ class Invoice:
|
|
|
328
404
|
f"/received-invoices/{access_key}/manifestation",
|
|
329
405
|
json=payload,
|
|
330
406
|
)
|
|
331
|
-
|
|
332
|
-
def _headers(self, idempotency_key: str | None = None) -> dict:
|
|
333
|
-
headers = {}
|
|
334
|
-
if self.api_key:
|
|
335
|
-
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
336
|
-
if idempotency_key:
|
|
337
|
-
headers["Idempotency-Key"] = idempotency_key
|
|
338
|
-
return headers
|
|
339
|
-
|
|
340
|
-
def _request(
|
|
341
|
-
self,
|
|
342
|
-
method: str,
|
|
343
|
-
path: str,
|
|
344
|
-
*,
|
|
345
|
-
json: dict | None = None,
|
|
346
|
-
params: dict | None = None,
|
|
347
|
-
idempotency_key: str | None = None,
|
|
348
|
-
) -> Any:
|
|
349
|
-
response = self._send(
|
|
350
|
-
method,
|
|
351
|
-
path,
|
|
352
|
-
json=json,
|
|
353
|
-
params=params,
|
|
354
|
-
idempotency_key=idempotency_key,
|
|
355
|
-
)
|
|
356
|
-
try:
|
|
357
|
-
body = response.json() if response.content else {}
|
|
358
|
-
except ValueError:
|
|
359
|
-
body = {}
|
|
360
|
-
if isinstance(body, dict):
|
|
361
|
-
return body.get("result", body)
|
|
362
|
-
return body
|
|
363
|
-
|
|
364
|
-
def _send(
|
|
365
|
-
self,
|
|
366
|
-
method: str,
|
|
367
|
-
path: str,
|
|
368
|
-
*,
|
|
369
|
-
json: dict | None = None,
|
|
370
|
-
params: dict | None = None,
|
|
371
|
-
idempotency_key: str | None = None,
|
|
372
|
-
) -> requests.Response:
|
|
373
|
-
url = f"{self.base_url}/api/v1{path}"
|
|
374
|
-
|
|
375
|
-
try:
|
|
376
|
-
response = requests.request(
|
|
377
|
-
method,
|
|
378
|
-
url,
|
|
379
|
-
json=json,
|
|
380
|
-
params=params,
|
|
381
|
-
headers=self._headers(idempotency_key),
|
|
382
|
-
timeout=self.timeout,
|
|
383
|
-
)
|
|
384
|
-
except requests.RequestException as error:
|
|
385
|
-
raise ConnectionFailedError(str(error)) from error
|
|
386
|
-
|
|
387
|
-
if not response.ok:
|
|
388
|
-
try:
|
|
389
|
-
body = response.json() if response.content else {}
|
|
390
|
-
except ValueError:
|
|
391
|
-
body = {}
|
|
392
|
-
raise APIError(
|
|
393
|
-
status_code=response.status_code,
|
|
394
|
-
detail=body.get("detail", response.text),
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
return response
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""The published classification tables — CFOP, NCM, CEST and the rest.
|
|
2
|
+
|
|
3
|
+
None of this is the company's own data and none of it is writable. The
|
|
4
|
+
eight named accessors are ergonomics; `kind()` is the contract, and it
|
|
5
|
+
reaches a classification published after this release without one.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, cast
|
|
11
|
+
from urllib.parse import quote
|
|
12
|
+
|
|
13
|
+
from stackin.core.client import _Client
|
|
14
|
+
from stackin.core.types import Environment
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _segment(value: str) -> str:
|
|
18
|
+
"""One path segment, escaped, refusing what would leave it.
|
|
19
|
+
|
|
20
|
+
`safe=""` because a code the caller typed may hold a `/`. The dot
|
|
21
|
+
segments need refusing rather than escaping: RFC 3986 calls `.`
|
|
22
|
+
unreserved, so `quote` leaves it alone and the HTTP client then
|
|
23
|
+
collapses `..` into a different endpoint.
|
|
24
|
+
"""
|
|
25
|
+
if value in ("", ".", ".."):
|
|
26
|
+
raise ValueError(f"{value!r} is not a usable path segment")
|
|
27
|
+
return quote(value, safe="")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
KINDS = (
|
|
31
|
+
"cfop",
|
|
32
|
+
"ncm",
|
|
33
|
+
"cest",
|
|
34
|
+
"cst",
|
|
35
|
+
"csosn",
|
|
36
|
+
"iss_service",
|
|
37
|
+
"icms_fuel",
|
|
38
|
+
"ibs_cbs_class",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class Kind:
|
|
43
|
+
"""One classification, bound to a country."""
|
|
44
|
+
|
|
45
|
+
def __init__(self, client: _Client, name: str, country: str) -> None:
|
|
46
|
+
self._client = client
|
|
47
|
+
self.name = name
|
|
48
|
+
self.country = country
|
|
49
|
+
|
|
50
|
+
def get(self, code: str, *, country: str | None = None) -> dict:
|
|
51
|
+
"""One code. A code that does not exist raises APIError (404)."""
|
|
52
|
+
return cast(
|
|
53
|
+
dict,
|
|
54
|
+
self._client._request(
|
|
55
|
+
"GET",
|
|
56
|
+
f"/fiscal-references/{_segment(self.name)}"
|
|
57
|
+
f"/{_segment(code)}",
|
|
58
|
+
params={"country": country or self.country},
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def search(
|
|
63
|
+
self,
|
|
64
|
+
term: str | None = None,
|
|
65
|
+
*,
|
|
66
|
+
country: str | None = None,
|
|
67
|
+
limit: int | None = None,
|
|
68
|
+
offset: int | None = None,
|
|
69
|
+
) -> dict:
|
|
70
|
+
"""This classification, searched or simply paged through.
|
|
71
|
+
|
|
72
|
+
Rows come back ordered by kind then code; the API takes no
|
|
73
|
+
sort_by or order_by here, unlike Invoice.history().
|
|
74
|
+
"""
|
|
75
|
+
return _search(
|
|
76
|
+
self._client,
|
|
77
|
+
country=country or self.country,
|
|
78
|
+
kind=self.name,
|
|
79
|
+
term=term,
|
|
80
|
+
limit=limit,
|
|
81
|
+
offset=offset,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _search(
|
|
86
|
+
client: _Client,
|
|
87
|
+
*,
|
|
88
|
+
country: str,
|
|
89
|
+
kind: str | None,
|
|
90
|
+
term: str | None,
|
|
91
|
+
limit: int | None,
|
|
92
|
+
offset: int | None,
|
|
93
|
+
) -> dict:
|
|
94
|
+
"""Shared by Kind.search and FiscalReference.search."""
|
|
95
|
+
params: dict[str, Any] = {"country": country}
|
|
96
|
+
if kind is not None:
|
|
97
|
+
params["kind"] = kind
|
|
98
|
+
if term:
|
|
99
|
+
params["search"] = term
|
|
100
|
+
if limit is not None:
|
|
101
|
+
params["limit"] = limit
|
|
102
|
+
if offset is not None:
|
|
103
|
+
params["offset"] = offset
|
|
104
|
+
|
|
105
|
+
return cast(
|
|
106
|
+
dict,
|
|
107
|
+
client._request("GET", "/fiscal-references", params=params),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class FiscalReference(_Client):
|
|
112
|
+
"""Client for the published fiscal classification tables."""
|
|
113
|
+
|
|
114
|
+
def __init__(
|
|
115
|
+
self,
|
|
116
|
+
base_url: str | None = None,
|
|
117
|
+
environment: Environment | str | None = None,
|
|
118
|
+
api_key: str | None = None,
|
|
119
|
+
timeout: int = 30,
|
|
120
|
+
country: str = "BR",
|
|
121
|
+
) -> None:
|
|
122
|
+
super().__init__(base_url, environment, api_key, timeout)
|
|
123
|
+
self.country = country
|
|
124
|
+
|
|
125
|
+
self.cfop = Kind(self, "cfop", country)
|
|
126
|
+
self.ncm = Kind(self, "ncm", country)
|
|
127
|
+
self.cest = Kind(self, "cest", country)
|
|
128
|
+
self.cst = Kind(self, "cst", country)
|
|
129
|
+
self.csosn = Kind(self, "csosn", country)
|
|
130
|
+
self.iss_service = Kind(self, "iss_service", country)
|
|
131
|
+
self.icms_fuel = Kind(self, "icms_fuel", country)
|
|
132
|
+
self.ibs_cbs_class = Kind(self, "ibs_cbs_class", country)
|
|
133
|
+
|
|
134
|
+
def kinds(self, *, country: str | None = None) -> list[str]:
|
|
135
|
+
"""Which classifications this country has data for.
|
|
136
|
+
|
|
137
|
+
The only honest answer to "what else is there" — a hard-coded
|
|
138
|
+
list in a README goes stale the next time the ETL grows one.
|
|
139
|
+
"""
|
|
140
|
+
return cast(
|
|
141
|
+
list[str],
|
|
142
|
+
self._request(
|
|
143
|
+
"GET",
|
|
144
|
+
"/fiscal-references/kinds",
|
|
145
|
+
params={"country": country or self.country},
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
def kind(self, name: str, *, country: str | None = None) -> Kind:
|
|
150
|
+
"""Any classification by name, including one with no accessor."""
|
|
151
|
+
return Kind(self, name, country or self.country)
|
|
152
|
+
|
|
153
|
+
def search(
|
|
154
|
+
self,
|
|
155
|
+
term: str | None = None,
|
|
156
|
+
*,
|
|
157
|
+
country: str | None = None,
|
|
158
|
+
limit: int | None = None,
|
|
159
|
+
offset: int | None = None,
|
|
160
|
+
) -> dict:
|
|
161
|
+
"""Every classification at once, which no accessor can express.
|
|
162
|
+
|
|
163
|
+
The most expensive call the endpoint accepts: it is the whole
|
|
164
|
+
country's tables, not one of them.
|
|
165
|
+
"""
|
|
166
|
+
return _search(
|
|
167
|
+
self,
|
|
168
|
+
country=country or self.country,
|
|
169
|
+
kind=None,
|
|
170
|
+
term=term,
|
|
171
|
+
limit=limit,
|
|
172
|
+
offset=offset,
|
|
173
|
+
)
|
stackin/core/taxpayer.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""The taxpayer registry, looked up one tax id at a time.
|
|
2
|
+
|
|
3
|
+
One method, and it stays one method. The registry names and addresses
|
|
4
|
+
real people and companies: an exact lookup answers the question an
|
|
5
|
+
issuer has — who is this CNPJ I am about to invoice — and answers
|
|
6
|
+
nothing else. A prefix or wildcard search over the same table is a bulk
|
|
7
|
+
export wearing the costume of a query. Do not add one, not for parity
|
|
8
|
+
with FiscalReference, not "just by name", not "just within one state".
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from typing import cast
|
|
14
|
+
|
|
15
|
+
from stackin.core.client import _Client
|
|
16
|
+
from stackin.core.reference import _segment
|
|
17
|
+
from stackin.core.types import Environment
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Taxpayer(_Client):
|
|
21
|
+
"""Client for the taxpayer registry."""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
base_url: str | None = None,
|
|
26
|
+
environment: Environment | str | None = None,
|
|
27
|
+
api_key: str | None = None,
|
|
28
|
+
timeout: int = 30,
|
|
29
|
+
country: str = "BR",
|
|
30
|
+
) -> None:
|
|
31
|
+
super().__init__(base_url, environment, api_key, timeout)
|
|
32
|
+
self.country = country
|
|
33
|
+
|
|
34
|
+
def get(self, tax_id: str, *, country: str | None = None) -> dict:
|
|
35
|
+
"""One taxpayer, by exact tax id.
|
|
36
|
+
|
|
37
|
+
A 404 raises APIError and does not mean the taxpayer does not
|
|
38
|
+
exist: the registry reloads monthly, so a company registered in
|
|
39
|
+
the last few weeks is simply not in it yet. Never build a
|
|
40
|
+
validation rule on top of it.
|
|
41
|
+
"""
|
|
42
|
+
return cast(
|
|
43
|
+
dict,
|
|
44
|
+
self._request(
|
|
45
|
+
"GET",
|
|
46
|
+
f"/taxpayers/{_segment(tax_id)}",
|
|
47
|
+
params={"country": country or self.country},
|
|
48
|
+
),
|
|
49
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: stackin-python-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Python SDK for issuing, consulting and cancelling electronic invoices.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -313,6 +313,37 @@ Realizada. Only the last one takes a reason, and it requires one — both rules
|
|
|
313
313
|
checked locally, before the request goes out, because a round trip to be told a
|
|
314
314
|
fixed rule is a round trip wasted.
|
|
315
315
|
|
|
316
|
+
## Looking up a code, or who a CNPJ belongs to
|
|
317
|
+
|
|
318
|
+
Two more clients, for the tables an issuer reads while filling a document. Neither writes anything.
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
from stackin import FiscalReference, Taxpayer
|
|
322
|
+
|
|
323
|
+
ref = FiscalReference(api_key="...") # country="BR" by default
|
|
324
|
+
|
|
325
|
+
ref.ncm.get("84716052") # one code
|
|
326
|
+
ref.ncm.search("teclado", limit=5) # a page of matches
|
|
327
|
+
ref.cfop.get("5102")
|
|
328
|
+
ref.kinds() # what this country has
|
|
329
|
+
ref.kind("ibs_cbs_class").get("000001") # any kind, named or not
|
|
330
|
+
ref.search("teclado") # every kind at once
|
|
331
|
+
|
|
332
|
+
Taxpayer(api_key="...").get("00000000000191")
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
`cfop`, `ncm`, `cest`, `cst`, `csosn`, `iss_service`, `icms_fuel` and `ibs_cbs_class` have accessors. **`kind(name)` reaches anything else**, including a classification published after this release — ask `kinds()` rather than trusting this list.
|
|
336
|
+
|
|
337
|
+
`metadata` is passed through as the API sends it and differs per kind: `utrib` on an NCM, `ncm_code` on a CEST, `tax_type` on a CST, `None` on an ISS service.
|
|
338
|
+
|
|
339
|
+
Three things worth knowing before you loop:
|
|
340
|
+
|
|
341
|
+
- **These share the invoice read allowance** — 600 calls a minute per key, the same bucket `consult()`, `history()`, `received()` and `pdf()` draw from. One `search()` page beats N `get()` calls.
|
|
342
|
+
- **Ordering is fixed** (kind, then code, ascending). Unlike `history()`, these take no `sort_by`/`order_by`.
|
|
343
|
+
- **A 404 from `Taxpayer` does not mean the company does not exist.** That registry reloads monthly, so a recently registered CNPJ is simply not in it yet. Do not build a validation rule on it.
|
|
344
|
+
|
|
345
|
+
`Taxpayer` has one method and keeps one: the registry holds the names and addresses of real people, so there is no search over it, by design.
|
|
346
|
+
|
|
316
347
|
## Errors
|
|
317
348
|
|
|
318
349
|
- `stackin.APIError` — the API responded with a non-2xx status (`status_code`, `detail`) — a 401 here means `api_key` is missing, wrong, or was rotated.
|
|
@@ -323,5 +354,5 @@ Building the full fiscal document (issuer data, service code, tax groups, schema
|
|
|
323
354
|
|
|
324
355
|
## Examples
|
|
325
356
|
|
|
326
|
-
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. `examples/consult_invoice.py`, `examples/cancel_invoice.py`, and `examples/reissue_invoice.py` cover the operations that act on an already-issued document.
|
|
357
|
+
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. `examples/consult_invoice.py`, `examples/cancel_invoice.py`, and `examples/reissue_invoice.py` cover the operations that act on an already-issued document. `examples/lookup_fiscal_reference.py` and `examples/lookup_taxpayer.py` cover the two read-only clients.
|
|
327
358
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
stackin/__init__.py,sha256=mssUlglaIcPNx4tAm_JRW_NamEYTyRUdUOSwxRXsASY,689
|
|
2
|
+
stackin/br/__init__.py,sha256=-s-2-ZyCmGZA8jTR8KFgNOSoPjjgV57Ss_cdI-FjRRY,1206
|
|
3
|
+
stackin/br/product.py,sha256=4T5urNCi0LJrxs03Kt2vbXs1DBzYlRijBULWxi6bZ44,4830
|
|
4
|
+
stackin/br/tax.py,sha256=UxcYX-kJw1HxUixB0hRABSgoHqT6CLoEjup4xIRFaYg,54747
|
|
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=Iipq9V9J4nbQ4LBmxqJeYphIWe1sWcPNVO8RxicAiCc,12374
|
|
8
|
+
stackin/core/exceptions.py,sha256=ELn-uL8rwjAFsD1PgM8Zr-1if3Z5yOWU81chUs4k6tU,505
|
|
9
|
+
stackin/core/reference.py,sha256=xNAcaRuiiHYF5M7l42Jr5W9GFfzqh3CeJaqZAwomkdw,5096
|
|
10
|
+
stackin/core/taxpayer.py,sha256=4YN7Oi5PDHGhLBW4M7eZlw9ARQn5sk4GLrC2_Z7-NhA,1669
|
|
11
|
+
stackin/core/types.py,sha256=NpfYVN58Ug9fEY7VYeQ2TC1eekoFGHZq_habvrqEeTY,604
|
|
12
|
+
stackin_python_sdk-0.10.0.dist-info/METADATA,sha256=8dGhwQSagI7jHluUufFtoW9K3qyU2dq3WK8QDoBuy74,15049
|
|
13
|
+
stackin_python_sdk-0.10.0.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
14
|
+
stackin_python_sdk-0.10.0.dist-info/licenses/LICENSE,sha256=D-XHDcKFFhzEo9C5Dp4QPvt00pb_u__hi4Bc08zpxyU,1072
|
|
15
|
+
stackin_python_sdk-0.10.0.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
stackin/__init__.py,sha256=3t_IUn9nn3GSfJp-qb-h5xisouQu1jXmits5PONx1yA,531
|
|
2
|
-
stackin/br/__init__.py,sha256=-s-2-ZyCmGZA8jTR8KFgNOSoPjjgV57Ss_cdI-FjRRY,1206
|
|
3
|
-
stackin/br/product.py,sha256=4T5urNCi0LJrxs03Kt2vbXs1DBzYlRijBULWxi6bZ44,4830
|
|
4
|
-
stackin/br/tax.py,sha256=UxcYX-kJw1HxUixB0hRABSgoHqT6CLoEjup4xIRFaYg,54747
|
|
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=G80o4ORwNtazasIlmJBEhyrwG5VgTAsit9MTxK7FcEI,12086
|
|
8
|
-
stackin/core/exceptions.py,sha256=ELn-uL8rwjAFsD1PgM8Zr-1if3Z5yOWU81chUs4k6tU,505
|
|
9
|
-
stackin/core/types.py,sha256=NpfYVN58Ug9fEY7VYeQ2TC1eekoFGHZq_habvrqEeTY,604
|
|
10
|
-
stackin_python_sdk-0.9.0.dist-info/METADATA,sha256=zbT293Pa4yPTsivPQS46yx5NcIZU_YaT1npTt9dh8Jc,13160
|
|
11
|
-
stackin_python_sdk-0.9.0.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
12
|
-
stackin_python_sdk-0.9.0.dist-info/licenses/LICENSE,sha256=D-XHDcKFFhzEo9C5Dp4QPvt00pb_u__hi4Bc08zpxyU,1072
|
|
13
|
-
stackin_python_sdk-0.9.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|