csc-cia-stne 0.0.43__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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
|