sienge-python 0.1.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.
@@ -0,0 +1,137 @@
1
+ """
2
+ sienge.models.suprimentos — Dataclasses para Suprimentos (pedidos de compra).
3
+
4
+ Campos reais da API /purchase-orders:
5
+ id, formattedPurchaseOrderId, status, consistent, authorized,
6
+ disapproved, deliveryLate, supplierId, buildingId
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from typing import Any
11
+
12
+
13
+ @dataclass
14
+ class PedidoCompra:
15
+ """Pedido de compra no Sienge. Mapeado de GET /purchase-orders."""
16
+ id: int
17
+ numero_formatado: str = ""
18
+ status: str = ""
19
+ consistente: str = ""
20
+ autorizado: bool = False
21
+ reprovado: bool = False
22
+ entrega_atrasada: bool = False
23
+ fornecedor_id: int | None = None
24
+ building_id: int | None = None # referencia centro de custo
25
+ extras: dict[str, Any] = field(default_factory=dict)
26
+
27
+ @classmethod
28
+ def from_api(cls, data: dict) -> "PedidoCompra":
29
+ return cls(
30
+ id=data.get("id", 0),
31
+ numero_formatado=data.get("formattedPurchaseOrderId", str(data.get("number", ""))),
32
+ status=data.get("status", ""),
33
+ consistente=data.get("consistent", ""),
34
+ autorizado=data.get("authorized", False),
35
+ reprovado=data.get("disapproved", False),
36
+ entrega_atrasada=data.get("deliveryLate", False),
37
+ fornecedor_id=data.get("supplierId"),
38
+ building_id=data.get("buildingId"),
39
+ extras={k: v for k, v in data.items() if k not in {
40
+ "id", "formattedPurchaseOrderId", "number", "status",
41
+ "consistent", "authorized", "disapproved", "deliveryLate",
42
+ "supplierId", "buildingId",
43
+ }},
44
+ )
45
+
46
+
47
+ @dataclass
48
+ class ItemPedido:
49
+ """Item de um pedido de compra."""
50
+ id: int
51
+ descricao: str = ""
52
+ quantidade: float = 0.0
53
+ unidade: str = ""
54
+ valor_unitario: float = 0.0
55
+ valor_total: float = 0.0
56
+ insumo_id: int | None = None
57
+ extras: dict[str, Any] = field(default_factory=dict)
58
+
59
+ @classmethod
60
+ def from_api(cls, data: dict) -> "ItemPedido":
61
+ return cls(
62
+ id=data.get("id", 0),
63
+ descricao=data.get("description", data.get("resourceDescription", "")),
64
+ quantidade=data.get("quantity", 0.0),
65
+ unidade=data.get("unit", data.get("measurementUnit", "")),
66
+ valor_unitario=data.get("unitPrice", data.get("unitValue", 0.0)),
67
+ valor_total=data.get("totalPrice", data.get("totalValue", 0.0)),
68
+ insumo_id=data.get("resourceId"),
69
+ extras={k: v for k, v in data.items() if k not in {
70
+ "id", "description", "resourceDescription", "quantity",
71
+ "unit", "measurementUnit", "unitPrice", "unitValue",
72
+ "totalPrice", "totalValue", "resourceId",
73
+ }},
74
+ )
75
+
76
+
77
+ @dataclass
78
+ class ItemSolicitacao:
79
+ """Item de uma solicitacao de compra."""
80
+ id: int
81
+ descricao: str = ""
82
+ quantidade: float = 0.0
83
+ unidade: str = ""
84
+ insumo_id: int | None = None
85
+ extras: dict[str, Any] = field(default_factory=dict)
86
+
87
+ @classmethod
88
+ def from_api(cls, data: dict) -> "ItemSolicitacao":
89
+ return cls(
90
+ id=data.get("id", 0),
91
+ descricao=data.get("description", data.get("resourceDescription", "")),
92
+ quantidade=data.get("quantity", 0.0),
93
+ unidade=data.get("unit", data.get("measurementUnit", "")),
94
+ insumo_id=data.get("resourceId"),
95
+ extras={k: v for k, v in data.items() if k not in {
96
+ "id", "description", "resourceDescription", "quantity",
97
+ "unit", "measurementUnit", "resourceId",
98
+ }},
99
+ )
100
+
101
+
102
+ @dataclass
103
+ class SolicitacaoCompra:
104
+ """Solicitacao de compra no Sienge.
105
+
106
+ NOTA: GET /purchase-requests retorna 405 na CONIN (nao disponivel).
107
+ """
108
+ id: int
109
+ numero: str = ""
110
+ data: str | None = None
111
+ solicitante: str = ""
112
+ obra_id: int | None = None
113
+ centro_custo_id: int | None = None
114
+ status: str = ""
115
+ observacao: str = ""
116
+ itens: list[ItemSolicitacao] = field(default_factory=list)
117
+ extras: dict[str, Any] = field(default_factory=dict)
118
+
119
+ @classmethod
120
+ def from_api(cls, data: dict) -> "SolicitacaoCompra":
121
+ itens_raw = data.get("items", [])
122
+ return cls(
123
+ id=data.get("id", 0),
124
+ numero=str(data.get("number", data.get("requestNumber", ""))),
125
+ data=data.get("date", data.get("requestDate")),
126
+ solicitante=data.get("requester", data.get("requestedBy", "")),
127
+ obra_id=data.get("buildingId"),
128
+ centro_custo_id=data.get("costCenterId"),
129
+ status=data.get("status", ""),
130
+ observacao=data.get("observation", data.get("notes", "")),
131
+ itens=[ItemSolicitacao.from_api(i) for i in itens_raw] if itens_raw else [],
132
+ extras={k: v for k, v in data.items() if k not in {
133
+ "id", "number", "requestNumber", "date", "requestDate",
134
+ "requester", "requestedBy", "buildingId", "costCenterId",
135
+ "status", "observation", "notes", "items",
136
+ }},
137
+ )
sienge/rate_limiter.py ADDED
@@ -0,0 +1,84 @@
1
+ """
2
+ sienge.rate_limiter — Controle de rate limit para a API Sienge.
3
+
4
+ Limites:
5
+ - REST: 200 req/min por subdominio
6
+ - BULK: 20 req/min por subdominio
7
+ """
8
+
9
+ import threading
10
+ import time
11
+ from collections import deque
12
+
13
+
14
+ class RateLimiter:
15
+ """Rate limiter baseado em janela deslizante (sliding window)."""
16
+
17
+ def __init__(self, max_requests: int, window_seconds: float = 60.0):
18
+ self._max_requests = max_requests
19
+ self._window = window_seconds
20
+ self._timestamps: deque[float] = deque()
21
+ self._lock = threading.Lock()
22
+
23
+ def acquire(self) -> float:
24
+ """Bloqueia ate que uma requisicao possa ser feita.
25
+
26
+ Returns:
27
+ Tempo de espera em segundos (0 se nao precisou esperar).
28
+ """
29
+ total_wait = 0.0
30
+ with self._lock:
31
+ now = time.monotonic()
32
+ # Remove timestamps fora da janela
33
+ cutoff = now - self._window
34
+ while self._timestamps and self._timestamps[0] < cutoff:
35
+ self._timestamps.popleft()
36
+
37
+ if len(self._timestamps) >= self._max_requests:
38
+ # Precisa esperar ate o timestamp mais antigo sair da janela
39
+ wait_until = self._timestamps[0] + self._window
40
+ wait_time = wait_until - now
41
+ if wait_time > 0:
42
+ total_wait = wait_time
43
+
44
+ if total_wait > 0:
45
+ time.sleep(total_wait)
46
+
47
+ with self._lock:
48
+ self._timestamps.append(time.monotonic())
49
+
50
+ return total_wait
51
+
52
+ @property
53
+ def remaining(self) -> int:
54
+ """Requisicoes restantes na janela atual."""
55
+ with self._lock:
56
+ now = time.monotonic()
57
+ cutoff = now - self._window
58
+ while self._timestamps and self._timestamps[0] < cutoff:
59
+ self._timestamps.popleft()
60
+ return max(0, self._max_requests - len(self._timestamps))
61
+
62
+
63
+ # Instancias globais por tipo de API
64
+ _rest_limiter = None
65
+ _bulk_limiter = None
66
+ _lock = threading.Lock()
67
+
68
+
69
+ def get_rest_limiter() -> RateLimiter:
70
+ """Retorna o rate limiter para REST API (200 req/min)."""
71
+ global _rest_limiter
72
+ with _lock:
73
+ if _rest_limiter is None:
74
+ _rest_limiter = RateLimiter(max_requests=195, window_seconds=60.0) # margem de 5
75
+ return _rest_limiter
76
+
77
+
78
+ def get_bulk_limiter() -> RateLimiter:
79
+ """Retorna o rate limiter para Bulk API (20 req/min)."""
80
+ global _bulk_limiter
81
+ with _lock:
82
+ if _bulk_limiter is None:
83
+ _bulk_limiter = RateLimiter(max_requests=18, window_seconds=60.0) # margem de 2
84
+ return _bulk_limiter
sienge/utils.py ADDED
@@ -0,0 +1,118 @@
1
+ """
2
+ sienge.utils — Utilitarios: paginacao automatica, retry, formatacao.
3
+ """
4
+
5
+ import logging
6
+ import time
7
+ from typing import Any, Callable, Iterator, TypeVar
8
+
9
+ from .exceptions import RateLimitError, MaintenanceError, SiengeError
10
+
11
+ logger = logging.getLogger("sienge")
12
+
13
+ T = TypeVar("T")
14
+
15
+ # Limite maximo por pagina da API Sienge
16
+ MAX_PAGE_SIZE = 200
17
+
18
+
19
+ def retry_with_backoff(
20
+ func: Callable[..., T],
21
+ max_retries: int = 3,
22
+ base_delay: float = 1.0,
23
+ max_delay: float = 60.0,
24
+ retryable_exceptions: tuple = (RateLimitError, MaintenanceError, ConnectionError, TimeoutError),
25
+ ) -> T:
26
+ """Executa func com retry e backoff exponencial.
27
+
28
+ Args:
29
+ func: Callable sem argumentos a executar.
30
+ max_retries: Numero maximo de tentativas.
31
+ base_delay: Delay base em segundos.
32
+ max_delay: Delay maximo em segundos.
33
+ retryable_exceptions: Excecoes que disparam retry.
34
+
35
+ Returns:
36
+ Resultado de func().
37
+
38
+ Raises:
39
+ A excecao original apos esgotar tentativas.
40
+ """
41
+ last_exc = None
42
+ for attempt in range(max_retries + 1):
43
+ try:
44
+ return func()
45
+ except retryable_exceptions as e:
46
+ last_exc = e
47
+ if attempt == max_retries:
48
+ raise
49
+ # Se RateLimitError, usa retry_after como delay
50
+ if isinstance(e, RateLimitError) and e.retry_after:
51
+ delay = min(e.retry_after, max_delay)
52
+ else:
53
+ delay = min(base_delay * (2 ** attempt), max_delay)
54
+ logger.warning(
55
+ "Tentativa %d/%d falhou (%s). Retentando em %.1fs...",
56
+ attempt + 1, max_retries + 1, type(e).__name__, delay,
57
+ )
58
+ time.sleep(delay)
59
+ raise last_exc # type: ignore[misc]
60
+
61
+
62
+ def paginate(
63
+ fetch_page: Callable[[int, int], dict],
64
+ limit_per_page: int = 200,
65
+ max_results: int | None = None,
66
+ ) -> Iterator[dict]:
67
+ """Itera automaticamente sobre resultados paginados da API Sienge.
68
+
69
+ A API retorna: {"resultSetMetadata": {"count": N, "offset": O, "limit": L}, "results": [...]}
70
+
71
+ Args:
72
+ fetch_page: Callable(offset, limit) que retorna a resposta JSON da API.
73
+ limit_per_page: Itens por pagina (max 200).
74
+ max_results: Limite total de resultados (None = todos).
75
+
76
+ Yields:
77
+ Cada item individual dos resultados.
78
+ """
79
+ offset = 0
80
+ page_size = min(limit_per_page, MAX_PAGE_SIZE)
81
+ yielded = 0
82
+
83
+ while True:
84
+ data = fetch_page(offset, page_size)
85
+
86
+ results = data.get("results", [])
87
+ if not results:
88
+ break
89
+
90
+ for item in results:
91
+ yield item
92
+ yielded += 1
93
+ if max_results and yielded >= max_results:
94
+ return
95
+
96
+ # Verifica se ha mais paginas
97
+ metadata = data.get("resultSetMetadata", {})
98
+ total = metadata.get("count", 0)
99
+
100
+ offset += len(results)
101
+ if offset >= total:
102
+ break
103
+
104
+
105
+ def format_currency(value: float | int | None, symbol: str = "R$") -> str:
106
+ """Formata valor monetario no padrao brasileiro."""
107
+ if value is None:
108
+ return f"{symbol} 0,00"
109
+ formatted = f"{value:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
110
+ return f"{symbol} {formatted}"
111
+
112
+
113
+ def parse_sienge_date(date_str: str | None) -> str | None:
114
+ """Normaliza datas do Sienge para YYYY-MM-DD."""
115
+ if not date_str:
116
+ return None
117
+ # Sienge retorna YYYY-MM-DD normalmente
118
+ return date_str[:10] if len(date_str) >= 10 else date_str
@@ -0,0 +1,208 @@
1
+ Metadata-Version: 2.4
2
+ Name: sienge-python
3
+ Version: 0.1.0
4
+ Summary: Cliente Python para a API REST do Sienge — ERP de construcao civil
5
+ Project-URL: Homepage, https://github.com/conin-engenharia/sienge-python
6
+ Project-URL: Documentation, https://github.com/conin-engenharia/sienge-python#readme
7
+ Project-URL: Repository, https://github.com/conin-engenharia/sienge-python
8
+ Project-URL: Issues, https://github.com/conin-engenharia/sienge-python/issues
9
+ Author-email: CONIN Engenharia <amora@conin-ia.com.br>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: api,civil,construcao,erp,python,sienge,wrapper
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
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
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: requests>=2.28.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest-cov>=4.0; extra == 'dev'
28
+ Requires-Dist: pytest>=7.0; extra == 'dev'
29
+ Requires-Dist: python-dotenv>=1.0; extra == 'dev'
30
+ Description-Content-Type: text/markdown
31
+
32
+ <p align="center">
33
+ <img src="assets/logo-conin.png" alt="CONIN Engenharia" width="400">
34
+ </p>
35
+
36
+ <h1 align="center">sienge-python</h1>
37
+
38
+ <p align="center">
39
+ <strong>Primeiro cliente Python para a API REST do Sienge</strong> — o ERP mais usado na construcao civil brasileira.<br>
40
+ <em>The first Python client for the Sienge REST API — Brazil's leading construction ERP.</em>
41
+ </p>
42
+
43
+ <p align="center">
44
+ <a href="https://github.com/conin-engenharia/sienge-python/actions"><img src="https://github.com/conin-engenharia/sienge-python/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
45
+ <a href="https://github.com/conin-engenharia/sienge-python/blob/main/LICENSE"><img src="https://img.shields.io/github/license/conin-engenharia/sienge-python" alt="License"></a>
46
+ </p>
47
+
48
+ ---
49
+
50
+ ## O que e este projeto?
51
+
52
+ O [Sienge](https://www.sienge.com.br/) e o sistema de gestao (ERP) mais utilizado por construtoras no Brasil. Ele gerencia obras, financeiro, compras, contabilidade e outros processos da construcao civil. A plataforma disponibiliza uma **API REST** que permite integrar esses dados com sistemas externos.
53
+
54
+ O **sienge-python** e um cliente Python que simplifica essa integracao. A biblioteca cuida de autenticacao, paginacao, controle de limites de requisicao e tratamento de erros — permitindo que o desenvolvedor acesse os dados do Sienge com poucas linhas de codigo.
55
+
56
+ ```python
57
+ from sienge import SiengeClient
58
+
59
+ client = SiengeClient("sua-empresa", "usuario-api", "senha-api")
60
+ titulos = client.financeiro.list_titulos(start_date="2026-01-01")
61
+
62
+ for t in titulos:
63
+ print(f"{t.numero_documento}: R$ {t.valor_total:,.2f}")
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Instalacao / Installation
69
+
70
+ ```bash
71
+ pip install sienge-python
72
+ ```
73
+
74
+ Ou direto do repositorio / Or from source:
75
+
76
+ ```bash
77
+ pip install git+https://github.com/conin-engenharia/sienge-python.git
78
+ ```
79
+
80
+ ## Uso Rapido / Quick Start
81
+
82
+ ```python
83
+ from sienge import SiengeClient
84
+
85
+ client = SiengeClient("sua-empresa", "usuario-api", "senha-api")
86
+
87
+ # Listar obras / List buildings
88
+ obras = client.engenharia.list_obras()
89
+ for obra in obras:
90
+ print(f"{obra.nome} — {obra.tipo}")
91
+
92
+ # Titulos a pagar / Bills
93
+ titulos = client.financeiro.list_titulos(start_date="2026-01-01")
94
+ total = sum(t.valor_total for t in titulos)
95
+ print(f"Total a pagar: R$ {total:,.2f}")
96
+
97
+ # Centros de custo / Cost centers
98
+ centros = client.financeiro.search_centros_custo("Obra Centro")
99
+
100
+ # Pedidos de compra / Purchase orders
101
+ pedidos = client.suprimentos.list_pedidos(building_id=123)
102
+
103
+ # Fornecedores / Suppliers
104
+ fornecedores = client.credores.list_credores()
105
+
106
+ # Contabilidade / Accounting
107
+ lancamentos = client.contabilidade.list_lancamentos(company_id=1, start_date="2026-01-01")
108
+ ```
109
+
110
+ ### Via variaveis de ambiente / Environment variables
111
+
112
+ ```bash
113
+ export SIENGE_SUBDOMAIN=sua-empresa
114
+ export SIENGE_USERNAME=usuario-api
115
+ export SIENGE_PASSWORD=senha-api
116
+ ```
117
+
118
+ ```python
119
+ from sienge import SiengeClient
120
+ client = SiengeClient.from_env()
121
+ ```
122
+
123
+ ## Modulos Disponiveis / Available Modules
124
+
125
+ | Modulo | Acesso | Funcionalidades |
126
+ |--------|--------|-----------------|
127
+ | **Engenharia** | `client.engenharia` | Obras, progresso, orcamento, diarios, canteiros, bases de custo |
128
+ | **Financeiro** | `client.financeiro` | Titulos, fluxo de caixa, contas bancarias, saldos, centros de custo, NF-e |
129
+ | **Suprimentos** | `client.suprimentos` | Pedidos de compra (CRUD), notas fiscais, contratos, estoque, cotacoes |
130
+ | **Comercial** | `client.comercial` | Clientes, contratos, unidades, mapa imobiliario, tipos de imovel |
131
+ | **Contabilidade** | `client.contabilidade` | Lancamentos, plano de contas, lotes, fechamento, empresas |
132
+ | **Credores** | `client.credores` | Fornecedores, info bancaria |
133
+ | **Patrimonio** | `client.patrimonio` | Ativos fixos, moveis, alugueis |
134
+ | **Webhooks** | `client.webhooks` | Cadastro e gerenciamento de notificacoes (10 eventos) |
135
+ | **Tabelas** | `client.tabelas` | Cidades, profissoes, marcas, unidades de medida, indexadores |
136
+ | **Bulk Data** | `client.bulk` | 12 endpoints de exportacao em massa (plano Ultimate) |
137
+
138
+ ## Features
139
+
140
+ - **Type hints completos** — Dataclasses tipadas para todos os recursos
141
+ - **Rate limiting automatico** — 200 req/min REST, 20 req/min Bulk
142
+ - **Retry com backoff exponencial** — Resiliencia contra erros transientes
143
+ - **Paginacao automatica** — Iterators que buscam todas as paginas
144
+ - **Tratamento de erros** — Excecoes especificas: `AuthError`, `RateLimitError`, `NotFoundError`
145
+ - **Thread-safe** — Rate limiter com locks
146
+
147
+ ## Excecoes / Exceptions
148
+
149
+ ```python
150
+ from sienge import SiengeError, AuthError, RateLimitError, NotFoundError
151
+
152
+ try:
153
+ obra = client.engenharia.get_obra(999)
154
+ except AuthError:
155
+ print("Credenciais invalidas ou recurso nao liberado")
156
+ except NotFoundError:
157
+ print("Obra nao encontrada")
158
+ except RateLimitError as e:
159
+ print(f"Rate limit — retry em {e.retry_after}s")
160
+ except SiengeError as e:
161
+ print(f"Erro: {e}")
162
+ ```
163
+
164
+ ## Paginacao Automatica / Auto-pagination
165
+
166
+ ```python
167
+ # Itera sobre TODOS os centros de custo (3000+) automaticamente
168
+ for cc in client.financeiro.iter_centros_custo():
169
+ print(f"{cc.codigo}: {cc.nome}")
170
+
171
+ # Ou limitar resultados
172
+ primeiros_50 = client.financeiro.list_centros_custo(limit=50)
173
+ ```
174
+
175
+ ## Pre-requisitos / Prerequisites
176
+
177
+ - Python 3.10+
178
+ - Conta de cliente **Data Center** do Sienge
179
+ - Usuario de API criado no painel Sienge (Menu > APIs e Conectores > Usuarios de API)
180
+ - Recursos necessarios liberados para o usuario (aba "Autorizacoes")
181
+
182
+ ## Planos de API / API Plans
183
+
184
+ | Plano | Limite REST/dia | Limite Bulk/dia |
185
+ |-------|-----------------|-----------------|
186
+ | Free | 100 | 10 |
187
+ | Start | 1.000 | 100 |
188
+ | Essential | 5.000 | 500 |
189
+ | Enterprise | 10.000 | 1.000 |
190
+ | Ultimate | 75.000 | 7.500 |
191
+
192
+ ## Contribuindo / Contributing
193
+
194
+ 1. Fork o repositorio
195
+ 2. Crie uma branch (`git checkout -b feature/minha-feature`)
196
+ 3. Commit (`git commit -m 'Adiciona minha feature'`)
197
+ 4. Push (`git push origin feature/minha-feature`)
198
+ 5. Abra um Pull Request
199
+
200
+ ## Licenca / License
201
+
202
+ MIT License — veja [LICENSE](LICENSE).
203
+
204
+ ## Sobre / About
205
+
206
+ Desenvolvido pela [CONIN Engenharia](https://conin-ia.com.br) para automatizar operacoes de construcao civil com o Sienge.
207
+
208
+ *Built by CONIN Engenharia to automate construction operations with Sienge ERP.*
@@ -0,0 +1,28 @@
1
+ sienge/__init__.py,sha256=2mOuDEt_wg6j4JQuxs6nqCODk_tJOeUxJU_hsuyxKig,1199
2
+ sienge/auth.py,sha256=LXxEarJG6AoDPHtsZ2xqu1WYOKvC9K9lLycITeFgJBQ,728
3
+ sienge/client.py,sha256=-qmuePjD1pUm3-zHqw4Yl24OGy6B6CO2fgep3nxVCpI,5021
4
+ sienge/exceptions.py,sha256=nkAkTtNjKjsc2VwnFskWMHJHxGzvYPTWtVxSLevVcPc,1086
5
+ sienge/rate_limiter.py,sha256=cGre8k16jyOD6-KTNE0UQlvQi6CyE61aleWzIme8ctU,2691
6
+ sienge/utils.py,sha256=-hMO6GdubLFIkr9U4Mw3EuxIa5PoT64PDfFLtux2atk,3646
7
+ sienge/endpoints/__init__.py,sha256=qpOF9ZrgXQgUmrnZ5MrOUAPcLgOrmSidcg4QhDknSgo,803
8
+ sienge/endpoints/base.py,sha256=rrSfBtR5o1HpCZtX0SnKuHUQ3rmKytj54lLY2tSTkWQ,5699
9
+ sienge/endpoints/bulk.py,sha256=pRZW2Yd-rI1MZuyNBjwT0eENtgfn-QPUT1xSPyAkOGY,10403
10
+ sienge/endpoints/comercial.py,sha256=5lcKqhN6GkG-v6TElG2rtAu8WH3Qmp3TScTuFGNhVk4,7832
11
+ sienge/endpoints/contabilidade.py,sha256=y0NGCjvlsELaFAoLnmjScBH7cWcr7r00nbefewjmNZw,5957
12
+ sienge/endpoints/credores.py,sha256=-GRvygildYH2Daddghrwga39OeSDtnnsGONZKjwuPN8,2207
13
+ sienge/endpoints/engenharia.py,sha256=0yrU0kTv1hZYwqF9Wfre5Df8IzgP6MWR9zM-4JSFnsY,7159
14
+ sienge/endpoints/financeiro.py,sha256=uPcXLWPscR8-c6fly3cAlxA9hbOiNP_AqudPR-zb7ps,12832
15
+ sienge/endpoints/patrimonio.py,sha256=ZJdCmG1T5UjGeYyoMYfAHI7qy4KElKfTn0g3fAnTRfQ,1744
16
+ sienge/endpoints/suprimentos.py,sha256=r6iVrrVjx2YBCA3NL-xUxrReK94f-koE6lXBHO612I4,9714
17
+ sienge/endpoints/tabelas.py,sha256=WwVDgS9vv4ULigXnrn49BPJyXUPWLEL73jmDCHxD-Vs,3868
18
+ sienge/endpoints/webhooks.py,sha256=XNL4-iclyrXoy2ov1g6MW2KE-C6Tw9RgTcHe3Zr4lg0,1448
19
+ sienge/models/__init__.py,sha256=9YUEu3YwCzybKl9rRSiqZwjr-YX0IYmVbmb7kyRa14A,656
20
+ sienge/models/comercial.py,sha256=eu8FebVh66-H5GpxYWXg1d0l5uveGidy7rW1Il8wlMc,3498
21
+ sienge/models/credores.py,sha256=HKc4EEbRRVRlfHbuTd9CLPZGDvV7ZvaLz1rxcNXQ4Tw,1729
22
+ sienge/models/financeiro.py,sha256=d_HzdrSMMVoQoTuRforvWL9qdQGpyJxiobXDpja1558,6337
23
+ sienge/models/obra.py,sha256=xDOePpCN7rH37WUzMhyNE71Gl68lgIGN-PTxkhm8bvI,2849
24
+ sienge/models/suprimentos.py,sha256=egCzAhb4kUW9BTjIpbvImVHwWMNFZHidVGOn2gQBoXM,5190
25
+ sienge_python-0.1.0.dist-info/METADATA,sha256=TNInlooFxXqldbdYIoVQbBT0D3augi6dG6XZwu4WmWA,7638
26
+ sienge_python-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
27
+ sienge_python-0.1.0.dist-info/licenses/LICENSE,sha256=SS6AFkKhH09gKp-UJ3dY_XvQPfZ8evQkf_h7x351Pps,1094
28
+ sienge_python-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CONIN Engenharia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.