csc-cia-stne 0.0.43__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.
- csc_cia_stne/__init__.py +89 -0
- csc_cia_stne/bc_correios.py +529 -0
- csc_cia_stne/bc_sta.py +416 -0
- csc_cia_stne/email.py +239 -0
- csc_cia_stne/gcp_bigquery.py +224 -0
- csc_cia_stne/google_drive.py +268 -0
- csc_cia_stne/karavela.py +135 -0
- csc_cia_stne/logger_json.py +92 -0
- csc_cia_stne/logger_rich.py +249 -0
- csc_cia_stne/provio.py +103 -0
- csc_cia_stne/servicenow.py +689 -0
- csc_cia_stne/slack.py +227 -0
- csc_cia_stne/stne_admin.py +597 -0
- csc_cia_stne/utilitarios/__init__.py +9 -0
- csc_cia_stne/utilitarios/functions/__init__.py +14 -0
- csc_cia_stne/utilitarios/functions/func_b64.py +50 -0
- csc_cia_stne/utilitarios/functions/func_converters.py +18 -0
- csc_cia_stne/utilitarios/functions/func_recriar_pastas.py +29 -0
- csc_cia_stne/utilitarios/functions/func_settings.py +66 -0
- csc_cia_stne/utilitarios/functions/func_titulo.py +141 -0
- csc_cia_stne/utilitarios/validations/GcpBigQueryValidator.py +133 -0
- csc_cia_stne/utilitarios/validations/GoogleDriveValidator.py +144 -0
- csc_cia_stne/utilitarios/validations/ServiceNowValidator.py +403 -0
- csc_cia_stne/utilitarios/validations/__init__.py +3 -0
- csc_cia_stne-0.0.43.dist-info/LICENCE +21 -0
- csc_cia_stne-0.0.43.dist-info/METADATA +33 -0
- csc_cia_stne-0.0.43.dist-info/RECORD +29 -0
- csc_cia_stne-0.0.43.dist-info/WHEEL +5 -0
- csc_cia_stne-0.0.43.dist-info/top_level.txt +1 -0
@@ -0,0 +1,597 @@
|
|
1
|
+
import requests
|
2
|
+
import jwt
|
3
|
+
from datetime import datetime, timedelta
|
4
|
+
import time
|
5
|
+
#import subprocess
|
6
|
+
import json
|
7
|
+
#from modules.pdf import gerar_pdf_extrato
|
8
|
+
#import csv
|
9
|
+
#import io
|
10
|
+
#import sys
|
11
|
+
#import logging
|
12
|
+
from pydantic import BaseModel, StrictStr, StrictInt, ValidationError, field_validator, FieldValidationInfo
|
13
|
+
from typing import Literal
|
14
|
+
|
15
|
+
# Validações dos inputs
|
16
|
+
class InitParamsValidator(BaseModel):
|
17
|
+
client_id: str
|
18
|
+
user_agent: str
|
19
|
+
private_key: str
|
20
|
+
ambiente: Literal["prd", "sdx"] # Aceita apenas "prd" ou "sdx"
|
21
|
+
|
22
|
+
# Validação para garantir que cada parâmetro é uma string não vazia
|
23
|
+
@field_validator('client_id', 'user_agent', 'private_key')
|
24
|
+
def check_non_empty_string(cls, value, info):
|
25
|
+
if not isinstance(value, str) or not value.strip():
|
26
|
+
|
27
|
+
raise ValueError(f"O parâmetro '{info.field_name}' deve ser uma string não vazia.")
|
28
|
+
|
29
|
+
return value
|
30
|
+
|
31
|
+
class DocumentoValidator(BaseModel):
|
32
|
+
|
33
|
+
documento: StrictStr | StrictInt # Aceita apenas str ou int
|
34
|
+
|
35
|
+
# Valida se 'documento' não é vazio
|
36
|
+
@field_validator('documento')
|
37
|
+
def documento_nao_vazio(cls, value: StrictStr | StrictInt, info: FieldValidationInfo):
|
38
|
+
|
39
|
+
if isinstance(value, str) and not value.strip():
|
40
|
+
|
41
|
+
raise ValueError("O parâmetro 'documento' não pode ser uma string vazia.")
|
42
|
+
|
43
|
+
return value
|
44
|
+
|
45
|
+
class AccountIDValidator(BaseModel):
|
46
|
+
|
47
|
+
account_id: StrictStr # Aceita apenas str
|
48
|
+
|
49
|
+
# Valida se 'client_id' não é vazio
|
50
|
+
@field_validator('account_id')
|
51
|
+
def account_id_nao_vazio(cls, value: StrictStr, info: FieldValidationInfo):
|
52
|
+
|
53
|
+
if isinstance(value, str) and not value.strip():
|
54
|
+
|
55
|
+
raise ValueError("O parâmetro 'account_id' não pode ser uma string vazia.")
|
56
|
+
|
57
|
+
return value
|
58
|
+
|
59
|
+
class ExtratoParamsValidator(BaseModel):
|
60
|
+
|
61
|
+
account_id: str
|
62
|
+
data_inicio: datetime
|
63
|
+
data_fim: datetime
|
64
|
+
async_mode: bool
|
65
|
+
|
66
|
+
# Valida se 'client_id' não é vazio
|
67
|
+
@field_validator('account_id')
|
68
|
+
def account_id_nao_vazio(cls, value: StrictStr, info: FieldValidationInfo):
|
69
|
+
|
70
|
+
if isinstance(value, str) and not value.strip():
|
71
|
+
|
72
|
+
raise ValueError("O parâmetro 'account_id' não pode ser uma string vazia.")
|
73
|
+
|
74
|
+
return value
|
75
|
+
|
76
|
+
# Valida se 'data_inicio' está no formato datetime
|
77
|
+
@field_validator('data_inicio', 'data_fim')
|
78
|
+
def check_datetime_format(cls, value, info: FieldValidationInfo):
|
79
|
+
|
80
|
+
if not isinstance(value, datetime):
|
81
|
+
|
82
|
+
raise ValueError(f"O parâmetro '{info.field_name}' deve estar no formato datetime.")
|
83
|
+
|
84
|
+
return value
|
85
|
+
|
86
|
+
# Valida se 'data_fim' é posterior a 'data_inicio'
|
87
|
+
@field_validator('data_fim')
|
88
|
+
def check_data_fim_posterior(cls, data_fim, values):
|
89
|
+
data_inicio = values.get('data_inicio')
|
90
|
+
|
91
|
+
if data_inicio and data_fim and data_fim <= data_inicio:
|
92
|
+
|
93
|
+
raise ValueError("O parâmetro 'data_fim' deve ser posterior a data_inicio.")
|
94
|
+
|
95
|
+
return data_fim
|
96
|
+
|
97
|
+
# Valida se 'async_mode' é um valor booleano
|
98
|
+
@field_validator('async_mode')
|
99
|
+
def check_async_mode(cls, async_mode):
|
100
|
+
|
101
|
+
if not isinstance(async_mode, bool):
|
102
|
+
|
103
|
+
raise ValueError("O parâmetro 'async_mode' deve ser um valor booleano.")
|
104
|
+
|
105
|
+
return async_mode
|
106
|
+
|
107
|
+
class ReceiptIDValidator(BaseModel):
|
108
|
+
|
109
|
+
receipt_id: StrictStr # Aceita apenas str
|
110
|
+
|
111
|
+
# Valida se 'receipt_id' não é vazio
|
112
|
+
@field_validator('receipt_id')
|
113
|
+
def receipt_id_nao_vazio(cls, value: StrictStr, info: FieldValidationInfo):
|
114
|
+
|
115
|
+
if isinstance(value, str) and not value.strip():
|
116
|
+
|
117
|
+
raise ValueError("O parâmetro 'receipt_id' não pode ser uma string vazia.")
|
118
|
+
|
119
|
+
return value
|
120
|
+
|
121
|
+
class StoneAdmin:
|
122
|
+
|
123
|
+
def __init__(self, client_id:str, user_agent:str, private_key:str, ambiente:str):
|
124
|
+
"""
|
125
|
+
Inicializa uma instância da classe STNEAdmin.
|
126
|
+
Parâmetros:
|
127
|
+
- client_id (str): O ID do cliente.
|
128
|
+
- user_agent (str): O agente do usuário.
|
129
|
+
- private_key (str): A chave privada.
|
130
|
+
- ambiente (str): O ambiente de execução ('prd' para produção ou qualquer outro valor para sandbox).
|
131
|
+
Exemplo de uso:
|
132
|
+
```
|
133
|
+
client_id = '123456789'
|
134
|
+
user_agent = 'MyApp/1.0'
|
135
|
+
private_key = 'my_private_key'
|
136
|
+
ambiente = 'prd'
|
137
|
+
admin = STNEAdmin(client_id, user_agent, private_key, ambiente)
|
138
|
+
```
|
139
|
+
"""
|
140
|
+
|
141
|
+
try:
|
142
|
+
|
143
|
+
InitParamsValidator(client_id=client_id, user_agent=user_agent, private_key=private_key, ambiente=ambiente)
|
144
|
+
|
145
|
+
except ValidationError as e:
|
146
|
+
|
147
|
+
raise ValueError("Erro na validação dos dados de input da inicialização da instância:", e.errors())
|
148
|
+
|
149
|
+
# Produção
|
150
|
+
if ambiente == 'prd':
|
151
|
+
|
152
|
+
self.base_url = 'https://api.openbank.stone.com.br/resources/v1'
|
153
|
+
self.base_auth_url = 'https://accounts.openbank.stone.com.br'
|
154
|
+
|
155
|
+
# Sandbox
|
156
|
+
else:
|
157
|
+
|
158
|
+
self.base_url = 'https://sandbox-api.openbank.stone.com.br/resources/v1'
|
159
|
+
self.base_auth_url = 'https://sandbox-accounts.openbank.stone.com.br'
|
160
|
+
|
161
|
+
self.client_id = client_id
|
162
|
+
self.user_agent = user_agent
|
163
|
+
self.private_key = private_key
|
164
|
+
self.token = self.__get_token()
|
165
|
+
self.authenticated_header = {
|
166
|
+
'Authorization' : f"Bearer {self.token}",
|
167
|
+
'User-Agent': self.user_agent,
|
168
|
+
#'Client-ID': self.client_id
|
169
|
+
}
|
170
|
+
|
171
|
+
def __get_token(self):
|
172
|
+
"""
|
173
|
+
Obtém um token de autenticação para acessar a API do Stone Bank.
|
174
|
+
Returns:
|
175
|
+
str: O token de acesso gerado.
|
176
|
+
Raises:
|
177
|
+
requests.exceptions.RequestException: Se ocorrer um erro durante a solicitação HTTP.
|
178
|
+
KeyError: Se a resposta da solicitação não contiver a chave 'access_token'.
|
179
|
+
Exemplo:
|
180
|
+
>>> token = self.__get_token()
|
181
|
+
"""
|
182
|
+
base_url = f'{self.base_auth_url}/auth/realms/stone_bank'
|
183
|
+
auth_url = f'{base_url}/protocol/openid-connect/token'
|
184
|
+
payload = {
|
185
|
+
'exp': int(time.time()) + 3600,
|
186
|
+
'nbf': int(time.time()),
|
187
|
+
'aud': base_url,
|
188
|
+
'realm': 'stone_bank',
|
189
|
+
'sub': self.client_id,
|
190
|
+
'clientId': self.client_id,
|
191
|
+
'jti': str(time.time()),
|
192
|
+
'iat': int(time.time()),
|
193
|
+
'iss': self.client_id
|
194
|
+
}
|
195
|
+
|
196
|
+
token = jwt.encode(payload, self.private_key, algorithm='RS256')
|
197
|
+
|
198
|
+
headers = {
|
199
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
200
|
+
'User-Agent': self.user_agent
|
201
|
+
}
|
202
|
+
|
203
|
+
token_payload = {
|
204
|
+
'client_id': self.client_id,
|
205
|
+
'grant_type': 'client_credentials',
|
206
|
+
'client_assertion': token,
|
207
|
+
'client_assertion_type': 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
|
208
|
+
}
|
209
|
+
|
210
|
+
response = requests.post(auth_url, data=token_payload, headers=headers, timeout=60)
|
211
|
+
#print(response.json())
|
212
|
+
return response.json()['access_token']
|
213
|
+
|
214
|
+
def renew_authorization(self):
|
215
|
+
"""
|
216
|
+
Renova a autorização do usuário.
|
217
|
+
|
218
|
+
Esta função renova a autorização do usuário, obtendo um novo token de autenticação
|
219
|
+
e atualizando o cabeçalho de autenticação.
|
220
|
+
|
221
|
+
Parâmetros:
|
222
|
+
- Nenhum
|
223
|
+
|
224
|
+
Retorno:
|
225
|
+
- Nenhum
|
226
|
+
|
227
|
+
Exemplo de uso:
|
228
|
+
```
|
229
|
+
obj = ClassName()
|
230
|
+
obj.renew_authorization()
|
231
|
+
```
|
232
|
+
"""
|
233
|
+
self.token = self.__get_token()
|
234
|
+
self.authenticated_header = {
|
235
|
+
'Authorization' : f"Bearer {self.token}",
|
236
|
+
'User-Agent': self.user_agent,
|
237
|
+
#'Client-ID': self.client_id
|
238
|
+
}
|
239
|
+
|
240
|
+
def verificar_cliente(self,documento:str)->dict:
|
241
|
+
"""
|
242
|
+
Verifica se um cliente com o documento fornecido existe e se a conta associada está ativa.
|
243
|
+
Args:
|
244
|
+
documento (str): O número do documento do cliente.
|
245
|
+
Returns:
|
246
|
+
dict: Um dicionário contendo as informações do cliente e da conta, se encontrado.
|
247
|
+
- success (bool): Indica se a operação foi bem-sucedida.
|
248
|
+
- status_code (int): O código de status da resposta da API.
|
249
|
+
- error (Exception): O erro ocorrido, se houver.
|
250
|
+
- encontrado (bool): Indica se o cliente foi encontrado.
|
251
|
+
- detalhes (list): Uma lista de dicionários contendo os detalhes das contas encontradas.
|
252
|
+
- account_code (str): O código da conta.
|
253
|
+
- account_id (str): O ID da conta.
|
254
|
+
- owner_id (str): O ID do proprietário da conta.
|
255
|
+
- closed_at (str): A data de encerramento da conta, se estiver fechada.
|
256
|
+
- created_at (str): A data de criação da conta.
|
257
|
+
- conta_ativa (bool): Indica se a conta está ativa.
|
258
|
+
Raises:
|
259
|
+
ValueError: Se ocorrer um erro na validação dos dados de entrada.
|
260
|
+
ValueError: Se ocorrer um erro na requisição à API Stone Admin.
|
261
|
+
Example:
|
262
|
+
# Instanciar o objeto da classe
|
263
|
+
stne_admin = StneAdmin()
|
264
|
+
# Chamar a função verificar_cliente
|
265
|
+
resultado = stne_admin.verificar_cliente("123456789")
|
266
|
+
# Verificar se a operação foi bem-sucedida
|
267
|
+
if resultado["success"]:
|
268
|
+
# Verificar se o cliente foi encontrado
|
269
|
+
if resultado["encontrado"]:
|
270
|
+
# Acessar os detalhes das contas encontradas
|
271
|
+
detalhes = resultado["detalhes"]
|
272
|
+
for conta in detalhes:
|
273
|
+
print(f"Conta: {conta['account_code']}")
|
274
|
+
print(f"Status: {'Ativa' if conta['conta_ativa'] else 'Inativa'}")
|
275
|
+
print("Cliente não encontrado.")
|
276
|
+
print(f"Erro: {resultado['error']}")
|
277
|
+
"""
|
278
|
+
|
279
|
+
try:
|
280
|
+
|
281
|
+
DocumentoValidator(documento=documento)
|
282
|
+
|
283
|
+
except ValidationError as e:
|
284
|
+
|
285
|
+
raise ValueError("Erro na validação dos dados de input do método:", e.errors())
|
286
|
+
|
287
|
+
params_conta_ativa = {'owner_document': documento}
|
288
|
+
params_conta_inativa = {'owner_document': documento,'status':'closed'}
|
289
|
+
|
290
|
+
try:
|
291
|
+
|
292
|
+
# Verificando se existe cliente com esse documento, com a conta ativa
|
293
|
+
response = requests.get(f"{self.base_url}/accounts", params=params_conta_ativa, headers=self.authenticated_header, timeout=60)
|
294
|
+
|
295
|
+
# Retorno esperado pela API Stone Admin - consulta cliente ativo
|
296
|
+
if response.status_code == 200:
|
297
|
+
|
298
|
+
# Não existe cliente com esse documento e com a conta ativa
|
299
|
+
if len(response.json()) == 0:
|
300
|
+
|
301
|
+
# Verificando se existe cliente com esse documento, com a conta inativa
|
302
|
+
encontrado = False
|
303
|
+
response = requests.get(f"{self.base_url}/accounts", params=params_conta_inativa, headers=self.authenticated_header, timeout=60)
|
304
|
+
|
305
|
+
# Retorno esperado pela API Stone Admin - consulta cliente inativo
|
306
|
+
if response.status_code == 200:
|
307
|
+
|
308
|
+
resposta = response.json()
|
309
|
+
|
310
|
+
# Existe cliente com esse documento, mas com a conta inativa
|
311
|
+
if len(resposta) != 0:
|
312
|
+
|
313
|
+
encontrado = True
|
314
|
+
|
315
|
+
# Algum erro na API Stone Admin - retorna erro
|
316
|
+
else:
|
317
|
+
|
318
|
+
return False, ValueError(response.json())
|
319
|
+
|
320
|
+
# Cliente econtrado e com a conta ativa
|
321
|
+
else:
|
322
|
+
|
323
|
+
encontrado = True
|
324
|
+
resposta = response.json()
|
325
|
+
|
326
|
+
retorno = []
|
327
|
+
|
328
|
+
# Monta JSON , pode ter mais de uma conta
|
329
|
+
for registro in resposta:
|
330
|
+
|
331
|
+
retorno_item = {}
|
332
|
+
account_code = registro["account_code"]
|
333
|
+
account_id = registro["id"]
|
334
|
+
owner_id = registro["owner_id"]
|
335
|
+
closed_at = registro["closed_at"]
|
336
|
+
created_at = registro["created_at"]
|
337
|
+
|
338
|
+
# Status atual da conta
|
339
|
+
if closed_at is None:
|
340
|
+
|
341
|
+
registro["conta_ativa"] = True
|
342
|
+
|
343
|
+
else:
|
344
|
+
|
345
|
+
registro["conta_ativa"] = False
|
346
|
+
|
347
|
+
retorno.append(registro)
|
348
|
+
|
349
|
+
retorno_json = {
|
350
|
+
"success":True,
|
351
|
+
"status_code": response.status_code,
|
352
|
+
"error": None,
|
353
|
+
"encontrado": encontrado,
|
354
|
+
"detalhes": retorno
|
355
|
+
}
|
356
|
+
return retorno_json
|
357
|
+
|
358
|
+
# Retorno inesperado pela API Stone Admin - consulta cliente ativo, retorna erro
|
359
|
+
else:
|
360
|
+
|
361
|
+
retorno_json = {
|
362
|
+
"success":False,
|
363
|
+
"status_code": response.status_code,
|
364
|
+
"error": ValueError(response.json())
|
365
|
+
}
|
366
|
+
return retorno_json
|
367
|
+
|
368
|
+
# Erro inesperado como a requisição à API Stone Admin - consulta cliente ativo, retorna erro
|
369
|
+
except Exception as e:
|
370
|
+
|
371
|
+
retorno_json = {
|
372
|
+
"success":False,
|
373
|
+
"status_code": response.status_code,
|
374
|
+
"error": e
|
375
|
+
}
|
376
|
+
return retorno_json
|
377
|
+
|
378
|
+
def balance_da_conta(self,account_id:str):
|
379
|
+
"""
|
380
|
+
Retorna o saldo da conta especificada.
|
381
|
+
Parâmetros:
|
382
|
+
- account_id (str): O ID da conta.
|
383
|
+
Retorna:
|
384
|
+
- response (objeto): A resposta da requisição GET contendo o saldo da conta.
|
385
|
+
Exemplo de uso:
|
386
|
+
```
|
387
|
+
account_id = "123456789"
|
388
|
+
response = balance_da_conta(account_id)
|
389
|
+
print(response)
|
390
|
+
```
|
391
|
+
"""
|
392
|
+
try:
|
393
|
+
|
394
|
+
AccountIDValidator(account_id=account_id)
|
395
|
+
|
396
|
+
except ValidationError as e:
|
397
|
+
|
398
|
+
raise ValueError("Erro na validação dos dados de input do método:", e.errors())
|
399
|
+
|
400
|
+
# Captura o balance da conta
|
401
|
+
response = requests.get(f"{self.base_url}/accounts/{account_id}", headers=self.authenticated_header)
|
402
|
+
return response
|
403
|
+
|
404
|
+
def detalhar_titular_cpf(self,documento:str):
|
405
|
+
"""
|
406
|
+
Detalha o titular do CPF fornecido.
|
407
|
+
Args:
|
408
|
+
documento (str): O número do CPF do titular.
|
409
|
+
Returns:
|
410
|
+
requests.Response: A resposta da requisição HTTP.
|
411
|
+
Raises:
|
412
|
+
ValueError: Se houver um erro na validação dos dados de input.
|
413
|
+
Example:
|
414
|
+
>>> admin = StneAdmin()
|
415
|
+
>>> response = admin.detalhar_titular_cpf('12345678900')
|
416
|
+
"""
|
417
|
+
|
418
|
+
try:
|
419
|
+
|
420
|
+
DocumentoValidator(documento=documento)
|
421
|
+
|
422
|
+
except ValidationError as e:
|
423
|
+
|
424
|
+
raise ValueError("Erro na validação dos dados de input do método:", e.errors())
|
425
|
+
|
426
|
+
# Detalha o titular
|
427
|
+
|
428
|
+
# Verificar na rota /users (CPF)
|
429
|
+
filtro = {'document': documento}
|
430
|
+
param = {
|
431
|
+
'filter': json.dumps(filtro) # Transforma o dicionário em uma string JSON
|
432
|
+
}
|
433
|
+
response = requests.get(f"{self.base_url}/users", params=param, headers=self.authenticated_header)
|
434
|
+
return response
|
435
|
+
|
436
|
+
def detalhar_titular_cnpj(self,documento:str):
|
437
|
+
"""
|
438
|
+
Detalha o titular de um CNPJ.
|
439
|
+
Args:
|
440
|
+
documento (str): O número do CNPJ a ser consultado.
|
441
|
+
Returns:
|
442
|
+
requests.Response: A resposta da requisição HTTP.
|
443
|
+
Raises:
|
444
|
+
ValueError: Se houver um erro na validação dos dados de input.
|
445
|
+
Example:
|
446
|
+
>>> admin = StneAdmin()
|
447
|
+
>>> response = admin.detalhar_titular_cnpj('12345678901234')
|
448
|
+
"""
|
449
|
+
|
450
|
+
try:
|
451
|
+
|
452
|
+
DocumentoValidator(documento=documento)
|
453
|
+
|
454
|
+
except ValidationError as e:
|
455
|
+
|
456
|
+
raise ValueError("Erro na validação dos dados de input do método:", e.errors())
|
457
|
+
|
458
|
+
# Verificar na rota /organizations (CNPJ)
|
459
|
+
filtro = {'document': documento}
|
460
|
+
param = {
|
461
|
+
'filter': json.dumps(filtro) # Transforma o dicionário em uma string JSON
|
462
|
+
}
|
463
|
+
response = requests.get(f"{self.base_url}/organizations", params=param, headers=self.authenticated_header)
|
464
|
+
return response
|
465
|
+
|
466
|
+
def extrair_extrato(self,account_id:str,data_inicio:datetime,data_fim:datetime,async_mode:bool=False):
|
467
|
+
"""
|
468
|
+
Extrai o extrato de uma conta.
|
469
|
+
Args:
|
470
|
+
account_id (str): O ID da conta.
|
471
|
+
data_inicio (datetime): A data de início do extrato.
|
472
|
+
data_fim (datetime): A data de fim do extrato.
|
473
|
+
async_mode (bool, optional): Modo assíncrono. Defaults to False.
|
474
|
+
Returns:
|
475
|
+
dict: Um dicionário contendo informações sobre o resultado da operação.
|
476
|
+
- Se async_mode for False e a resposta for bem-sucedida, retorna:
|
477
|
+
{
|
478
|
+
"success": True,
|
479
|
+
"status_code": int,
|
480
|
+
"error": None,
|
481
|
+
"pdf_content": bytes
|
482
|
+
}
|
483
|
+
- Se async_mode for True e a resposta for bem-sucedida, retorna:
|
484
|
+
{
|
485
|
+
"success": True,
|
486
|
+
"status_code": int,
|
487
|
+
"error": None,
|
488
|
+
"receipt_id": str
|
489
|
+
}
|
490
|
+
- Se a resposta não for bem-sucedida, retorna:
|
491
|
+
{
|
492
|
+
"success": False,
|
493
|
+
"status_code": int,
|
494
|
+
"error": str,
|
495
|
+
"pdf_content": None
|
496
|
+
}
|
497
|
+
- Se ocorrer uma exceção, retorna:
|
498
|
+
{
|
499
|
+
"success": False,
|
500
|
+
"status_code": int,
|
501
|
+
"error": Exception,
|
502
|
+
"pdf_content": None
|
503
|
+
}
|
504
|
+
"""
|
505
|
+
|
506
|
+
try:
|
507
|
+
|
508
|
+
ExtratoParamsValidator(account_id=account_id, data_inicio=data_inicio, data_fim=data_fim, async_mode=async_mode)
|
509
|
+
|
510
|
+
except ValidationError as e:
|
511
|
+
|
512
|
+
raise ValueError("Erro na validação dos dados de input do método:", e.errors())
|
513
|
+
|
514
|
+
# Validação do async_mode
|
515
|
+
if not isinstance(async_mode, bool):
|
516
|
+
|
517
|
+
raise ValueError("async_mode deve ser um valor booleano.")
|
518
|
+
|
519
|
+
|
520
|
+
data_inicio = data_inicio.strftime('%Y-%m-%d')
|
521
|
+
data_fim = data_fim + timedelta(days=1)
|
522
|
+
data_fim = data_fim.strftime('%Y-%m-%d')
|
523
|
+
|
524
|
+
try:
|
525
|
+
|
526
|
+
if async_mode:
|
527
|
+
|
528
|
+
response = requests.get(f"{self.base_url}/exports/accounts/{account_id}/statement?start_date={data_inicio}&end_date={data_fim}&format=pdf&async=true", headers=self.authenticated_header, timeout=60)
|
529
|
+
|
530
|
+
else:
|
531
|
+
|
532
|
+
response = requests.get(f"{self.base_url}/exports/accounts/{account_id}/statement?start_date={data_inicio}&end_date={data_fim}&format=pdf&async=false", headers=self.authenticated_header, timeout=120)
|
533
|
+
|
534
|
+
if response.status_code == 200 and not async_mode:
|
535
|
+
|
536
|
+
return {"success":True, "status_code": response.status_code, "error": None, "pdf_content": response.content}
|
537
|
+
|
538
|
+
elif response.status_code == 202 and async_mode:
|
539
|
+
|
540
|
+
return {"success":True, "status_code": response.status_code, "error": None, "receipt_id":response.json()["id"]}
|
541
|
+
|
542
|
+
else:
|
543
|
+
|
544
|
+
return {"success":False, "status_code": response.status_code, "error": str(response.text), "pdf_content": None}
|
545
|
+
|
546
|
+
except Exception as e:
|
547
|
+
|
548
|
+
return {"success": False, "status_code": response.status_code, "error": e, "pdf_content": None}
|
549
|
+
|
550
|
+
def download_receipt(self,receipt_id:str):
|
551
|
+
"""
|
552
|
+
Faz o download de um recibo a partir de um ID de recibo.
|
553
|
+
Args:
|
554
|
+
receipt_id (str): O ID do recibo a ser baixado.
|
555
|
+
Returns:
|
556
|
+
dict: Um dicionário contendo os seguintes campos:
|
557
|
+
- 'result' (bool): Indica se o download foi bem-sucedido.
|
558
|
+
- 'status_code' (int): O código de status da resposta HTTP.
|
559
|
+
- 'error' (str ou dict): O erro retornado, se houver.
|
560
|
+
- 'pdf_content' (bytes): O conteúdo do arquivo PDF, se o download for bem-sucedido.
|
561
|
+
Raises:
|
562
|
+
ValueError: Se ocorrer um erro na validação dos dados de entrada.
|
563
|
+
Example:
|
564
|
+
>>> admin = STNEAdmin()
|
565
|
+
>>> result = admin.download_receipt("123456")
|
566
|
+
>>> print(result)
|
567
|
+
{'result': True, 'status_code': 200, 'error': None, 'pdf_content': b'%PDF-1.7\n%����\n1 0 obj\n<<\n/Type /Catalog\n/Pages 2 0 R\n>>\nendobj\n2 0 obj\n<<\n/Type /Pages\n/Kids [3 0 R]\n/Count 1\n>>\nendobj\n3 0 obj\n<<\n/Type /Page\n/Parent 2 0 R\n/Resources <<\n/Font <<\n/F1 4 0 R\n>>\n/ProcSet 5 0 R\n>>\n/MediaBox [0 0 595.276 841.890]\n/Contents 6 0 R\n>>\nendobj\n4 0 obj\n<<\n/Type /Font\n/Subtype /Type1\n/Name /F1\n/BaseFont /Helvetica\n/Encoding /MacRomanEncoding\n>>\nendobj\n5 0 obj\n[/PDF /Text]\nendobj\n6 0 obj\n<<\n/Length 44\n>>\nstream\nBT\n/F1 24 Tf\n100 100 Td\n(Hello, World!) Tj\nET\nendstream\nendobj\nxref\n0 7\n0000000000 65535 f \n0000000010 00000 n \n0000000077 00000 n \n0000000178 00000 n \n0000000302 00000 n \n0000000406 00000 n \n0000000519 00000 n \ntrailer\n<<\n/Size 7\n/Root 1 0 R\n>>\nstartxref\n614\n%%EOF\n'}
|
568
|
+
"""
|
569
|
+
|
570
|
+
try:
|
571
|
+
|
572
|
+
ReceiptIDValidator(receipt_id=receipt_id)
|
573
|
+
|
574
|
+
except ValidationError as e:
|
575
|
+
|
576
|
+
raise ValueError("Erro na validação dos dados de input do método:", e.errors())
|
577
|
+
|
578
|
+
try:
|
579
|
+
|
580
|
+
response = requests.get(f"https://api.openbank.stone.com.br/resources/v1/exports/receipt_requests/download/{receipt_id}", headers=self.authenticated_header, timeout=120)
|
581
|
+
|
582
|
+
if response.status_code == 200:
|
583
|
+
|
584
|
+
# Decodificando o conteúdo usando UTF-8
|
585
|
+
#decoded_content = response.content.decode('utf-8')
|
586
|
+
print("header:",response.headers['Content-Type'])
|
587
|
+
print(f"type do content: {type(response.content)}")
|
588
|
+
return {'result':True, 'status_code': response.status_code, 'error': None, 'pdf_content':response.content}
|
589
|
+
#return {'result':True, 'status_code': response.status_code, 'error': None, 'pdf_content':decoded_content}
|
590
|
+
|
591
|
+
else:
|
592
|
+
|
593
|
+
return {'result': False, 'status_code': response.status_code, 'error': response.json(), 'pdf_content': None}
|
594
|
+
|
595
|
+
except Exception as e:
|
596
|
+
|
597
|
+
return {'result': False, 'status_code': None, 'error': e, 'pdf_content': None}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
from .func_titulo import titulo
|
2
|
+
from .func_recriar_pastas import recriar_pasta
|
3
|
+
from .func_b64 import b64decode, b64encode
|
4
|
+
from .func_converters import convert_bquery_result_to_json
|
5
|
+
from .func_settings import get_config
|
6
|
+
|
7
|
+
__all__ = [
|
8
|
+
"titulo",
|
9
|
+
"recriar_pasta",
|
10
|
+
"b64encode",
|
11
|
+
"b64decode",
|
12
|
+
"convert_bquery_result_to_json",
|
13
|
+
"get_config"
|
14
|
+
]
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import base64
|
2
|
+
|
3
|
+
def b64decode(b64_string:str=None)->str:
|
4
|
+
"""Faz o decode de uma string em base64
|
5
|
+
|
6
|
+
Args:
|
7
|
+
b64_string (str): string em base64
|
8
|
+
|
9
|
+
Returns:
|
10
|
+
str: string em texto
|
11
|
+
"""
|
12
|
+
|
13
|
+
if b64_string is None or b64_string == "":
|
14
|
+
|
15
|
+
raise ValueError("Uma string Base64 precisa ser informada com o parâmetro 'b64_string'")
|
16
|
+
|
17
|
+
try:
|
18
|
+
|
19
|
+
b64_decode_output = base64.b64decode(b64_string).decode('utf-8')
|
20
|
+
|
21
|
+
except:
|
22
|
+
|
23
|
+
raise TypeError("A string informada não está em formato Base64")
|
24
|
+
|
25
|
+
return b64_decode_output
|
26
|
+
|
27
|
+
def b64encode(string_to_convert:str=None)->base64:
|
28
|
+
"""Faz o encode de uma string para base64
|
29
|
+
|
30
|
+
Args:
|
31
|
+
string_to_convert (str): string para converter em base64
|
32
|
+
|
33
|
+
Returns:
|
34
|
+
base64: string convertida para base64
|
35
|
+
"""
|
36
|
+
|
37
|
+
if string_to_convert is None or string_to_convert == "":
|
38
|
+
|
39
|
+
raise ValueError("Uma string precisa ser informada com o parâmetro 'string_to_convert'")
|
40
|
+
|
41
|
+
try:
|
42
|
+
|
43
|
+
b64_encode_output = base64.b64encode(str(string_to_convert).encode('utf-8')).decode('utf-8')
|
44
|
+
|
45
|
+
except Exception as e:
|
46
|
+
|
47
|
+
raise TypeError(f"Erro ao converter a string para Base64: {e}")
|
48
|
+
|
49
|
+
return b64_encode_output
|
50
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
def convert_bquery_result_to_json(query_result:list)->list:
|
2
|
+
"""Converte o resultado de uma query do BigQuery em uma lista de dicionários JSON
|
3
|
+
|
4
|
+
Args:
|
5
|
+
query_result (list): resultado da query do BigQuery (query.result)
|
6
|
+
|
7
|
+
Returns:
|
8
|
+
list: lista de dicionários JSON
|
9
|
+
"""
|
10
|
+
|
11
|
+
try:
|
12
|
+
|
13
|
+
#return [dict(row) for row in query_result]
|
14
|
+
return [dict(row._asdict()) for row in query_result]
|
15
|
+
|
16
|
+
except Exception as e:
|
17
|
+
|
18
|
+
raise e
|