attime-star-api-python-sdk 1.8.0__tar.gz → 1.9.1__tar.gz
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.
- {attime_star_api_python_sdk-1.8.0/src/attime_star_api_python_sdk.egg-info → attime_star_api_python_sdk-1.9.1}/PKG-INFO +1 -1
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/pyproject.toml +1 -1
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/apis/attime001.py +20 -20
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/attime001.py +21 -36
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/ecommerce.py +25 -2
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1/src/attime_star_api_python_sdk.egg-info}/PKG-INFO +1 -1
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/tests/test_attime001_atualizacoes.py +9 -11
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/tests/test_attime001_cancelamento.py +16 -28
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/tests/test_attime001_importar_ecommerce.py +36 -5
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/LICENSE +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/README.md +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/setup.cfg +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/api.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/apis/__init__.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/apis/bvix.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/apis/infocar.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/__init__.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/bvix.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/enums.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/infocar.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/serde.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime_star_api_python_sdk.egg-info/SOURCES.txt +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime_star_api_python_sdk.egg-info/dependency_links.txt +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime_star_api_python_sdk.egg-info/top_level.txt +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/tests/test_attime001_insere_item.py +0 -0
- {attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/tests/test_attime001_relacao.py +0 -0
|
@@ -16,24 +16,27 @@ from attime.star.dtos.attime001 import (
|
|
|
16
16
|
ApiAttime001EmitirItemDTO,
|
|
17
17
|
ApiAttime001RelacaoContratoCpfItemDTO,
|
|
18
18
|
ApiAttime001RelacaoItemDTO,
|
|
19
|
-
EscopoOperacao,
|
|
20
19
|
)
|
|
21
20
|
from attime.star.dtos.ecommerce import ApiAttime001ImportarEcommerceDTO
|
|
22
21
|
|
|
23
22
|
T = TypeVar('T')
|
|
24
23
|
|
|
25
24
|
|
|
26
|
-
def
|
|
27
|
-
if payload.get('escopoOperacao') in (None, ''):
|
|
28
|
-
raise ValueError('escopoOperacao é obrigatório')
|
|
25
|
+
def _require_numero_contrato(payload: dict) -> None:
|
|
29
26
|
if payload.get('numeroContrato') in (None, ''):
|
|
30
27
|
raise ValueError('numeroContrato é obrigatório')
|
|
31
28
|
|
|
32
29
|
|
|
33
|
-
def
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
def _require_cancelamento_payload(payload: dict) -> None:
|
|
31
|
+
"""Escopo via campos: declinio, ou identificadorItem ("" = contrato)."""
|
|
32
|
+
_require_numero_contrato(payload)
|
|
33
|
+
if payload.get('declinio'):
|
|
34
|
+
return
|
|
35
|
+
if 'identificadorItem' not in payload:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
'identificadorItem é obrigatório ("" para contrato; CAC 25/nº para item) '
|
|
38
|
+
'ou informe o bloco declinio'
|
|
39
|
+
)
|
|
37
40
|
|
|
38
41
|
|
|
39
42
|
def _parse_attime001_list_response(
|
|
@@ -96,27 +99,23 @@ class Attime001Api(ATTimeStarApi):
|
|
|
96
99
|
def cancelamento(
|
|
97
100
|
self, item: ApiAttime001CancelamentoDTO | dict
|
|
98
101
|
) -> list[ApiAttime001CancelamentoDTO]:
|
|
99
|
-
"""
|
|
102
|
+
"""PUT /btc/ABTP0004/ApiAttime001/Cancelamento."""
|
|
100
103
|
logging.info('cancelling via ApiAttime001 Cancelamento...')
|
|
101
104
|
if isinstance(item, ApiAttime001CancelamentoDTO):
|
|
102
105
|
payload = item.to_dict()
|
|
103
106
|
else:
|
|
104
107
|
payload = dict(item)
|
|
108
|
+
# Compat: ignore legacy field if caller still sends it.
|
|
109
|
+
payload.pop('escopoOperacao', None)
|
|
105
110
|
|
|
106
|
-
|
|
107
|
-
if _escopo_is(payload.get('escopoOperacao'), EscopoOperacao.DECLINIO) and not payload.get(
|
|
108
|
-
'declinio'
|
|
109
|
-
):
|
|
110
|
-
raise ValueError(
|
|
111
|
-
f'declinio é obrigatório quando escopoOperacao={EscopoOperacao.DECLINIO}'
|
|
112
|
-
)
|
|
111
|
+
_require_cancelamento_payload(payload)
|
|
113
112
|
|
|
114
113
|
url = UrlUtil().make_url(
|
|
115
114
|
self.base_url,
|
|
116
115
|
['btc', 'ABTP0004', 'ApiAttime001', 'Cancelamento'],
|
|
117
116
|
)
|
|
118
117
|
res = self.call_request(
|
|
119
|
-
http_method=HTTPMethod.
|
|
118
|
+
http_method=HTTPMethod.PUT,
|
|
120
119
|
request_url=url,
|
|
121
120
|
payload=payload,
|
|
122
121
|
)
|
|
@@ -130,21 +129,22 @@ class Attime001Api(ATTimeStarApi):
|
|
|
130
129
|
def atualizacoes(
|
|
131
130
|
self, item: ApiAttime001AtualizacoesDTO | dict
|
|
132
131
|
) -> list[ApiAttime001AtualizacoesDTO]:
|
|
133
|
-
"""
|
|
132
|
+
"""PUT /btc/ABTP0004/ApiAttime001/Atualizacoes."""
|
|
134
133
|
logging.info('updating via ApiAttime001 Atualizacoes...')
|
|
135
134
|
if isinstance(item, ApiAttime001AtualizacoesDTO):
|
|
136
135
|
payload = item.to_dict()
|
|
137
136
|
else:
|
|
138
137
|
payload = dict(item)
|
|
138
|
+
payload.pop('escopoOperacao', None)
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
_require_numero_contrato(payload)
|
|
141
141
|
|
|
142
142
|
url = UrlUtil().make_url(
|
|
143
143
|
self.base_url,
|
|
144
144
|
['btc', 'ABTP0004', 'ApiAttime001', 'Atualizacoes'],
|
|
145
145
|
)
|
|
146
146
|
res = self.call_request(
|
|
147
|
-
http_method=HTTPMethod.
|
|
147
|
+
http_method=HTTPMethod.PUT,
|
|
148
148
|
request_url=url,
|
|
149
149
|
payload=payload,
|
|
150
150
|
)
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""DTOs ApiAttime001 (ABTP0004).
|
|
3
3
|
|
|
4
|
-
Cancelamento:
|
|
5
|
-
Atualizacoes:
|
|
4
|
+
Cancelamento: PUT /btc/ABTP0004/ApiAttime001/Cancelamento
|
|
5
|
+
Atualizacoes: PUT /btc/ABTP0004/ApiAttime001/Atualizacoes
|
|
6
6
|
InsereItem: POST /btc/ABTP0004/ApiAttime001/InsereItem
|
|
7
7
|
RelacaoContrato: GET /btc/ABTP0004/ApiAttime001/RelacaoContrato
|
|
8
8
|
RelacaoItens: GET /btc/ABTP0004/ApiAttime001/RelacaoItens
|
|
9
9
|
|
|
10
10
|
Enums de domínio compartilhados: ``attime.star.dtos.enums``.
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
Escopo de Cancelamento/Atualizacoes é inferido pelos campos preenchidos
|
|
13
|
+
(STAR removeu ``escopoOperacao`` do JSON).
|
|
12
14
|
"""
|
|
13
15
|
from __future__ import annotations
|
|
14
16
|
|
|
15
17
|
from dataclasses import dataclass
|
|
16
|
-
from enum import StrEnum
|
|
17
18
|
from typing import Optional
|
|
18
19
|
|
|
19
20
|
from attime.star.dtos.enums import (
|
|
@@ -36,21 +37,6 @@ _WIRE_CARACTERISTICA = {
|
|
|
36
37
|
_WIRE_CARACTERISTICA_REV = {v: k for k, v in _WIRE_CARACTERISTICA.items()}
|
|
37
38
|
|
|
38
39
|
|
|
39
|
-
class EscopoOperacao(StrEnum):
|
|
40
|
-
"""Literais de ``escopoOperacao`` conforme description do OpenAPI (Title Case).
|
|
41
|
-
|
|
42
|
-
Cancelamento inclui DECLINIO; Atualizacoes não. Específico deste endpoint.
|
|
43
|
-
"""
|
|
44
|
-
|
|
45
|
-
CONTRATO = 'Contrato'
|
|
46
|
-
ITEM = 'Item'
|
|
47
|
-
CARACTERISTICA = 'Caracteristica'
|
|
48
|
-
COBERTURA = 'Cobertura'
|
|
49
|
-
SERVICO = 'Servico'
|
|
50
|
-
BENEFICIARIO = 'Beneficiario'
|
|
51
|
-
DECLINIO = 'Declinio'
|
|
52
|
-
|
|
53
|
-
|
|
54
40
|
# ---------------------------------------------------------------------------
|
|
55
41
|
# Cancelamento
|
|
56
42
|
# ---------------------------------------------------------------------------
|
|
@@ -103,9 +89,13 @@ class DeclinioDto:
|
|
|
103
89
|
|
|
104
90
|
@dataclass
|
|
105
91
|
class ApiAttime001CancelamentoDTO:
|
|
106
|
-
"""Payload/resposta de Cancelamento (ApiAttime001CancelamentoDTO).
|
|
92
|
+
"""Payload/resposta de Cancelamento (ApiAttime001CancelamentoDTO).
|
|
93
|
+
|
|
94
|
+
Escopo é inferido pelos campos: contrato inteiro usa ``identificadorItem=""``;
|
|
95
|
+
item/parcial exige ``identificadorItem`` (CAC 25 ou nº do item); Declínio
|
|
96
|
+
exige o bloco ``declinio``.
|
|
97
|
+
"""
|
|
107
98
|
|
|
108
|
-
escopoOperacao: str | EscopoOperacao
|
|
109
99
|
numeroContrato: str
|
|
110
100
|
manutencaoUsuarioCpf: Optional[str] = None
|
|
111
101
|
manutencaoUsuarioDataHora: Optional[str] = None # ISO date-time
|
|
@@ -118,7 +108,6 @@ class ApiAttime001CancelamentoDTO:
|
|
|
118
108
|
|
|
119
109
|
def to_dict(self) -> dict:
|
|
120
110
|
return omit_none({
|
|
121
|
-
'escopoOperacao': to_wire(self.escopoOperacao),
|
|
122
111
|
'numeroContrato': self.numeroContrato,
|
|
123
112
|
'manutencaoUsuarioCpf': self.manutencaoUsuarioCpf,
|
|
124
113
|
'manutencaoUsuarioDataHora': self.manutencaoUsuarioDataHora,
|
|
@@ -143,7 +132,6 @@ class ApiAttime001CancelamentoDTO:
|
|
|
143
132
|
f'ApiAttime001CancelamentoDTO.from_dict espera dict, recebeu {type(data)!r}'
|
|
144
133
|
)
|
|
145
134
|
return cls(
|
|
146
|
-
escopoOperacao=data.get('escopoOperacao'),
|
|
147
135
|
numeroContrato=data.get('numeroContrato'),
|
|
148
136
|
manutencaoUsuarioCpf=data.get('manutencaoUsuarioCpf'),
|
|
149
137
|
manutencaoUsuarioDataHora=data.get('manutencaoUsuarioDataHora'),
|
|
@@ -163,11 +151,9 @@ class ApiAttime001CancelamentoDTO:
|
|
|
163
151
|
identificadorItem: str,
|
|
164
152
|
manutencaoUsuarioCpf: Optional[str] = None,
|
|
165
153
|
manutencaoUsuarioDataHora: Optional[str] = None,
|
|
166
|
-
escopoOperacao: str = EscopoOperacao.ITEM,
|
|
167
154
|
) -> ApiAttime001CancelamentoDTO:
|
|
168
|
-
"""Atalho para cancelar um item do
|
|
155
|
+
"""Atalho para cancelar um item (CAC 25 ou número do item)."""
|
|
169
156
|
return cls(
|
|
170
|
-
escopoOperacao=escopoOperacao,
|
|
171
157
|
numeroContrato=numeroContrato,
|
|
172
158
|
identificadorItem=identificadorItem,
|
|
173
159
|
manutencaoUsuarioCpf=manutencaoUsuarioCpf,
|
|
@@ -181,12 +167,11 @@ class ApiAttime001CancelamentoDTO:
|
|
|
181
167
|
numeroContrato: str,
|
|
182
168
|
manutencaoUsuarioCpf: Optional[str] = None,
|
|
183
169
|
manutencaoUsuarioDataHora: Optional[str] = None,
|
|
184
|
-
escopoOperacao: str = EscopoOperacao.CONTRATO,
|
|
185
170
|
) -> ApiAttime001CancelamentoDTO:
|
|
186
|
-
"""Atalho para cancelar o contrato inteiro."""
|
|
171
|
+
"""Atalho para cancelar o contrato inteiro (``identificadorItem=""``)."""
|
|
187
172
|
return cls(
|
|
188
|
-
escopoOperacao=escopoOperacao,
|
|
189
173
|
numeroContrato=numeroContrato,
|
|
174
|
+
identificadorItem='',
|
|
190
175
|
manutencaoUsuarioCpf=manutencaoUsuarioCpf,
|
|
191
176
|
manutencaoUsuarioDataHora=manutencaoUsuarioDataHora,
|
|
192
177
|
)
|
|
@@ -199,16 +184,14 @@ class ApiAttime001CancelamentoDTO:
|
|
|
199
184
|
declinio_itens: list[DeclinioItemCobertura] | list[dict],
|
|
200
185
|
manutencaoUsuarioCpf: Optional[str] = None,
|
|
201
186
|
manutencaoUsuarioDataHora: Optional[str] = None,
|
|
202
|
-
escopoOperacao: str = EscopoOperacao.DECLINIO,
|
|
203
187
|
) -> ApiAttime001CancelamentoDTO:
|
|
204
|
-
"""Atalho para
|
|
188
|
+
"""Atalho para Declínio (bloco ``declinio`` obrigatório)."""
|
|
205
189
|
itens: list[DeclinioItemCobertura] = []
|
|
206
190
|
for row in declinio_itens:
|
|
207
191
|
parsed = DeclinioItemCobertura.from_dict(row)
|
|
208
192
|
if parsed is not None:
|
|
209
193
|
itens.append(parsed)
|
|
210
194
|
return cls(
|
|
211
|
-
escopoOperacao=escopoOperacao,
|
|
212
195
|
numeroContrato=numeroContrato,
|
|
213
196
|
manutencaoUsuarioCpf=manutencaoUsuarioCpf,
|
|
214
197
|
manutencaoUsuarioDataHora=manutencaoUsuarioDataHora,
|
|
@@ -391,9 +374,13 @@ class AtualizacaoItemBeneficiarioDto:
|
|
|
391
374
|
|
|
392
375
|
@dataclass
|
|
393
376
|
class ApiAttime001AtualizacoesDTO:
|
|
394
|
-
"""Payload/resposta de Atualizacoes (ApiAttime001AtualizacoesDTO).
|
|
377
|
+
"""Payload/resposta de Atualizacoes (ApiAttime001AtualizacoesDTO).
|
|
378
|
+
|
|
379
|
+
Escopo é inferido pelos blocos presentes (``contrato``, ``item``,
|
|
380
|
+
``itemCaracteristica``, ``itemCobertura``, ``itemCoberturaServico``,
|
|
381
|
+
``itemBeneficiario``).
|
|
382
|
+
"""
|
|
395
383
|
|
|
396
|
-
escopoOperacao: str | EscopoOperacao
|
|
397
384
|
numeroContrato: str
|
|
398
385
|
manutencaoUsuarioCpf: Optional[str] = None
|
|
399
386
|
manutencaoUsuarioDataHora: Optional[str] = None
|
|
@@ -406,7 +393,6 @@ class ApiAttime001AtualizacoesDTO:
|
|
|
406
393
|
|
|
407
394
|
def to_dict(self) -> dict:
|
|
408
395
|
return omit_none({
|
|
409
|
-
'escopoOperacao': to_wire(self.escopoOperacao),
|
|
410
396
|
'numeroContrato': self.numeroContrato,
|
|
411
397
|
'manutencaoUsuarioCpf': self.manutencaoUsuarioCpf,
|
|
412
398
|
'manutencaoUsuarioDataHora': self.manutencaoUsuarioDataHora,
|
|
@@ -438,7 +424,6 @@ class ApiAttime001AtualizacoesDTO:
|
|
|
438
424
|
return [nested_cls.from_dict(i) for i in rows if i is not None]
|
|
439
425
|
|
|
440
426
|
return cls(
|
|
441
|
-
escopoOperacao=data.get('escopoOperacao'),
|
|
442
427
|
numeroContrato=data.get('numeroContrato'),
|
|
443
428
|
manutencaoUsuarioCpf=data.get('manutencaoUsuarioCpf'),
|
|
444
429
|
manutencaoUsuarioDataHora=data.get('manutencaoUsuarioDataHora'),
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
POST /btc/ABTP0004/ApiAttime001/ImportarEcommerce
|
|
5
5
|
|
|
6
|
-
``formaPagamento.dadosPagamento``
|
|
6
|
+
``formaPagamento.dadosPagamento`` é ``dict`` livre:
|
|
7
|
+
boleto → ``{EnderecoCobranca: ""}``; cartão/PIX tipagem ainda pendente.
|
|
7
8
|
"""
|
|
8
9
|
from __future__ import annotations
|
|
9
10
|
|
|
@@ -67,6 +68,18 @@ class EcommerceTelefoneDto:
|
|
|
67
68
|
return from_dataclass(cls, data)
|
|
68
69
|
|
|
69
70
|
|
|
71
|
+
# Alinhamento STAR Homol: para PJ estes campos devem ir no JSON como null
|
|
72
|
+
# (não omitidos). PF continua com omit_none quando vazios.
|
|
73
|
+
# ``nomeSocial`` em PJ vai como string vazia (``""``), não null.
|
|
74
|
+
_PJ_NULL_WIRE_FIELDS = (
|
|
75
|
+
'codigoSexo',
|
|
76
|
+
'codigoEstadoCivil',
|
|
77
|
+
'tipoDocumentoIdentificacao',
|
|
78
|
+
'numeroDocumento',
|
|
79
|
+
'codigoProfissao',
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
70
83
|
@dataclass
|
|
71
84
|
class EcommerceClienteDto:
|
|
72
85
|
cpfCnpj: Optional[str] = None
|
|
@@ -82,8 +95,13 @@ class EcommerceClienteDto:
|
|
|
82
95
|
endereco: Optional[list[EcommerceEnderecoDto]] = None
|
|
83
96
|
telefone: Optional[list[EcommerceTelefoneDto]] = None
|
|
84
97
|
|
|
98
|
+
@staticmethod
|
|
99
|
+
def _is_cnpj(cpf_cnpj: Optional[str]) -> bool:
|
|
100
|
+
digits = ''.join(ch for ch in str(cpf_cnpj or '') if ch.isdigit())
|
|
101
|
+
return len(digits) >= 14
|
|
102
|
+
|
|
85
103
|
def to_dict(self) -> dict:
|
|
86
|
-
|
|
104
|
+
data = omit_none({
|
|
87
105
|
'cpfCnpj': self.cpfCnpj,
|
|
88
106
|
'nomeCompleto': self.nomeCompleto,
|
|
89
107
|
'dataNascimentoFundacao': self.dataNascimentoFundacao,
|
|
@@ -97,6 +115,11 @@ class EcommerceClienteDto:
|
|
|
97
115
|
'endereco': to_wire(self.endereco),
|
|
98
116
|
'telefone': to_wire(self.telefone),
|
|
99
117
|
})
|
|
118
|
+
if self._is_cnpj(self.cpfCnpj):
|
|
119
|
+
for key in _PJ_NULL_WIRE_FIELDS:
|
|
120
|
+
data[key] = None
|
|
121
|
+
data['nomeSocial'] = ''
|
|
122
|
+
return data
|
|
100
123
|
|
|
101
124
|
@classmethod
|
|
102
125
|
def from_dict(cls, data: dict | EcommerceClienteDto | None):
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import unittest
|
|
6
|
+
from http import HTTPMethod
|
|
6
7
|
from unittest.mock import MagicMock, patch
|
|
7
8
|
|
|
8
9
|
from attime.star.dtos.attime001 import (
|
|
@@ -10,7 +11,6 @@ from attime.star.dtos.attime001 import (
|
|
|
10
11
|
AtualizacaoContratoDto,
|
|
11
12
|
AtualizacaoItemCaracteristicaDto,
|
|
12
13
|
AtualizacaoItemCoberturaDto,
|
|
13
|
-
EscopoOperacao,
|
|
14
14
|
)
|
|
15
15
|
from attime.star.dtos.enums import Sexo, SimNao
|
|
16
16
|
|
|
@@ -18,7 +18,6 @@ from attime.star.dtos.enums import Sexo, SimNao
|
|
|
18
18
|
class TestApiAttime001AtualizacoesDTO(unittest.TestCase):
|
|
19
19
|
def test_to_dict_omits_none_and_maps_caracteristica_wire(self):
|
|
20
20
|
dto = ApiAttime001AtualizacoesDTO(
|
|
21
|
-
escopoOperacao=EscopoOperacao.CARACTERISTICA,
|
|
22
21
|
numeroContrato='016501000280',
|
|
23
22
|
itemCaracteristica=[
|
|
24
23
|
AtualizacaoItemCaracteristicaDto(
|
|
@@ -29,6 +28,7 @@ class TestApiAttime001AtualizacoesDTO(unittest.TestCase):
|
|
|
29
28
|
],
|
|
30
29
|
)
|
|
31
30
|
data = dto.to_dict()
|
|
31
|
+
self.assertNotIn('escopoOperacao', data)
|
|
32
32
|
self.assertNotIn('contrato', data)
|
|
33
33
|
self.assertNotIn('itemCobertura', data)
|
|
34
34
|
row = data['itemCaracteristica'][0]
|
|
@@ -38,7 +38,6 @@ class TestApiAttime001AtualizacoesDTO(unittest.TestCase):
|
|
|
38
38
|
|
|
39
39
|
def test_from_dict_accepts_openapi_typo(self):
|
|
40
40
|
raw = {
|
|
41
|
-
'escopoOperacao': 'CARACTERISTICA',
|
|
42
41
|
'numeroContrato': '016501000280',
|
|
43
42
|
'itemCaracteristica': [
|
|
44
43
|
{
|
|
@@ -56,7 +55,6 @@ class TestApiAttime001AtualizacoesDTO(unittest.TestCase):
|
|
|
56
55
|
|
|
57
56
|
def test_contrato_and_cobertura_roundtrip(self):
|
|
58
57
|
dto = ApiAttime001AtualizacoesDTO(
|
|
59
|
-
escopoOperacao=EscopoOperacao.CONTRATO,
|
|
60
58
|
numeroContrato='016501000280',
|
|
61
59
|
manutencaoUsuarioCpf='12345678901',
|
|
62
60
|
contrato=AtualizacaoContratoDto(
|
|
@@ -74,7 +72,7 @@ class TestApiAttime001AtualizacoesDTO(unittest.TestCase):
|
|
|
74
72
|
],
|
|
75
73
|
)
|
|
76
74
|
data = dto.to_dict()
|
|
77
|
-
self.
|
|
75
|
+
self.assertNotIn('escopoOperacao', data)
|
|
78
76
|
self.assertEqual(data['contrato']['sexo'], 'M')
|
|
79
77
|
self.assertEqual(data['contrato']['contratoGarantia'], 'N')
|
|
80
78
|
again = ApiAttime001AtualizacoesDTO.from_dict(data)
|
|
@@ -83,17 +81,17 @@ class TestApiAttime001AtualizacoesDTO(unittest.TestCase):
|
|
|
83
81
|
|
|
84
82
|
|
|
85
83
|
class TestAttime001ApiAtualizacoes(unittest.TestCase):
|
|
86
|
-
def
|
|
84
|
+
def test_requires_numero(self):
|
|
87
85
|
from attime.star.apis.attime001 import Attime001Api
|
|
88
86
|
|
|
89
87
|
with patch.object(Attime001Api, '__init__', lambda self: None):
|
|
90
88
|
api = Attime001Api()
|
|
91
89
|
with self.assertRaises(ValueError):
|
|
92
|
-
api.atualizacoes({
|
|
90
|
+
api.atualizacoes({})
|
|
93
91
|
with self.assertRaises(ValueError):
|
|
94
92
|
api.atualizacoes({'escopoOperacao': 'ITEM'})
|
|
95
93
|
|
|
96
|
-
def
|
|
94
|
+
def test_puts_to_atualizacoes_path(self):
|
|
97
95
|
from attime.star.apis.attime001 import Attime001Api
|
|
98
96
|
|
|
99
97
|
with patch.object(Attime001Api, '__init__', lambda self: None):
|
|
@@ -103,22 +101,22 @@ class TestAttime001ApiAtualizacoes(unittest.TestCase):
|
|
|
103
101
|
with patch('attime.star.apis.attime001.jsonpickle.decode') as decode:
|
|
104
102
|
decode.return_value = [
|
|
105
103
|
{
|
|
106
|
-
'escopoOperacao': 'ITEM',
|
|
107
104
|
'numeroContrato': '016501000280',
|
|
108
105
|
'item': [{'itemNumero': 1, 'dataFimVigencia': '2027-01-01'}],
|
|
109
106
|
}
|
|
110
107
|
]
|
|
111
108
|
rows = api.atualizacoes(
|
|
112
109
|
ApiAttime001AtualizacoesDTO(
|
|
113
|
-
escopoOperacao=EscopoOperacao.ITEM,
|
|
114
110
|
numeroContrato='016501000280',
|
|
111
|
+
item=[{'itemNumero': 1, 'dataFimVigencia': '2027-01-01'}],
|
|
115
112
|
)
|
|
116
113
|
)
|
|
117
114
|
self.assertEqual(len(rows), 1)
|
|
118
115
|
self.assertEqual(rows[0].item[0].itemNumero, 1)
|
|
119
116
|
kwargs = api.call_request.call_args.kwargs
|
|
117
|
+
self.assertEqual(kwargs['http_method'], HTTPMethod.PUT)
|
|
120
118
|
self.assertIn('/ABTP0004/ApiAttime001/Atualizacoes', kwargs['request_url'])
|
|
121
|
-
self.
|
|
119
|
+
self.assertNotIn('escopoOperacao', kwargs['payload'])
|
|
122
120
|
|
|
123
121
|
|
|
124
122
|
if __name__ == '__main__':
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import unittest
|
|
6
|
+
from http import HTTPMethod
|
|
6
7
|
from unittest.mock import MagicMock, patch
|
|
7
8
|
|
|
8
9
|
from attime.star.dtos.attime001 import (
|
|
9
10
|
ApiAttime001CancelamentoDTO,
|
|
10
11
|
DeclinioItemCobertura,
|
|
11
|
-
EscopoOperacao,
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
|
|
@@ -19,7 +19,7 @@ class TestApiAttime001CancelamentoDTO(unittest.TestCase):
|
|
|
19
19
|
identificadorItem='12345678901',
|
|
20
20
|
)
|
|
21
21
|
data = dto.to_dict()
|
|
22
|
-
self.
|
|
22
|
+
self.assertNotIn('escopoOperacao', data)
|
|
23
23
|
self.assertEqual(data['numeroContrato'], '002501000089')
|
|
24
24
|
self.assertEqual(data['identificadorItem'], '12345678901')
|
|
25
25
|
self.assertNotIn('coberturaCodigo', data)
|
|
@@ -28,7 +28,6 @@ class TestApiAttime001CancelamentoDTO(unittest.TestCase):
|
|
|
28
28
|
|
|
29
29
|
def test_from_dict_roundtrip(self):
|
|
30
30
|
raw = {
|
|
31
|
-
'escopoOperacao': 'ITEM',
|
|
32
31
|
'numeroContrato': '002501000089',
|
|
33
32
|
'identificadorItem': '999',
|
|
34
33
|
'coberturaCodigo': 81,
|
|
@@ -37,11 +36,13 @@ class TestApiAttime001CancelamentoDTO(unittest.TestCase):
|
|
|
37
36
|
self.assertIsNotNone(dto)
|
|
38
37
|
self.assertEqual(dto.coberturaCodigo, 81)
|
|
39
38
|
self.assertEqual(dto.to_dict()['coberturaCodigo'], 81)
|
|
39
|
+
self.assertNotIn('escopoOperacao', dto.to_dict())
|
|
40
40
|
|
|
41
|
-
def
|
|
41
|
+
def test_for_contrato_uses_empty_identificador(self):
|
|
42
42
|
dto = ApiAttime001CancelamentoDTO.for_contrato(numeroContrato='002501000001')
|
|
43
|
-
|
|
44
|
-
self.
|
|
43
|
+
data = dto.to_dict()
|
|
44
|
+
self.assertEqual(data['identificadorItem'], '')
|
|
45
|
+
self.assertNotIn('escopoOperacao', data)
|
|
45
46
|
|
|
46
47
|
def test_for_declinio_serializes_nested(self):
|
|
47
48
|
dto = ApiAttime001CancelamentoDTO.for_declinio(
|
|
@@ -56,8 +57,7 @@ class TestApiAttime001CancelamentoDTO(unittest.TestCase):
|
|
|
56
57
|
],
|
|
57
58
|
)
|
|
58
59
|
data = dto.to_dict()
|
|
59
|
-
self.
|
|
60
|
-
self.assertEqual(data['escopoOperacao'], EscopoOperacao.DECLINIO)
|
|
60
|
+
self.assertNotIn('escopoOperacao', data)
|
|
61
61
|
self.assertEqual(
|
|
62
62
|
data['declinio']['declinioItemCobertura'][0]['coberturaCodigo'], 101
|
|
63
63
|
)
|
|
@@ -67,7 +67,6 @@ class TestApiAttime001CancelamentoDTO(unittest.TestCase):
|
|
|
67
67
|
|
|
68
68
|
def test_declinio_from_dict_roundtrip(self):
|
|
69
69
|
raw = {
|
|
70
|
-
'escopoOperacao': 'DECLINIO',
|
|
71
70
|
'numeroContrato': '016501000280',
|
|
72
71
|
'declinio': {
|
|
73
72
|
'declinioItemCobertura': [
|
|
@@ -86,41 +85,28 @@ class TestApiAttime001CancelamentoDTO(unittest.TestCase):
|
|
|
86
85
|
|
|
87
86
|
|
|
88
87
|
class TestAttime001ApiCancelamento(unittest.TestCase):
|
|
89
|
-
def
|
|
88
|
+
def test_requires_numero_and_identificador_or_declinio(self):
|
|
90
89
|
from attime.star.apis.attime001 import Attime001Api
|
|
91
90
|
|
|
92
91
|
with patch.object(Attime001Api, '__init__', lambda self: None):
|
|
93
92
|
api = Attime001Api()
|
|
94
93
|
with self.assertRaises(ValueError):
|
|
95
|
-
api.cancelamento({
|
|
94
|
+
api.cancelamento({})
|
|
96
95
|
with self.assertRaises(ValueError):
|
|
97
|
-
api.cancelamento({'
|
|
98
|
-
|
|
99
|
-
def test_requires_declinio_block(self):
|
|
100
|
-
from attime.star.apis.attime001 import Attime001Api
|
|
101
|
-
|
|
102
|
-
with patch.object(Attime001Api, '__init__', lambda self: None):
|
|
103
|
-
api = Attime001Api()
|
|
104
|
-
with self.assertRaises(ValueError) as ctx:
|
|
105
|
-
api.cancelamento({
|
|
106
|
-
'escopoOperacao': 'DECLINIO',
|
|
107
|
-
'numeroContrato': '016501000280',
|
|
108
|
-
})
|
|
109
|
-
self.assertIn('declinio', str(ctx.exception))
|
|
96
|
+
api.cancelamento({'numeroContrato': '002501000001'})
|
|
110
97
|
|
|
111
|
-
def
|
|
98
|
+
def test_puts_to_cancelamento_path(self):
|
|
112
99
|
from attime.star.apis.attime001 import Attime001Api
|
|
113
100
|
|
|
114
101
|
with patch.object(Attime001Api, '__init__', lambda self: None):
|
|
115
102
|
api = Attime001Api()
|
|
116
103
|
api.base_url = 'https://starbackhom.attime.inf.br'
|
|
117
104
|
api.call_request = MagicMock(
|
|
118
|
-
return_value='[{"
|
|
105
|
+
return_value='[{"numeroContrato":"002501000089","identificadorItem":"1"}]'
|
|
119
106
|
)
|
|
120
107
|
with patch('attime.star.apis.attime001.jsonpickle.decode') as decode:
|
|
121
108
|
decode.return_value = [
|
|
122
109
|
{
|
|
123
|
-
'escopoOperacao': 'ITEM',
|
|
124
110
|
'numeroContrato': '002501000089',
|
|
125
111
|
'identificadorItem': '1',
|
|
126
112
|
}
|
|
@@ -134,8 +120,10 @@ class TestAttime001ApiCancelamento(unittest.TestCase):
|
|
|
134
120
|
self.assertEqual(len(rows), 1)
|
|
135
121
|
self.assertEqual(rows[0].identificadorItem, '1')
|
|
136
122
|
kwargs = api.call_request.call_args.kwargs
|
|
123
|
+
self.assertEqual(kwargs['http_method'], HTTPMethod.PUT)
|
|
137
124
|
self.assertIn('/ABTP0004/ApiAttime001/Cancelamento', kwargs['request_url'])
|
|
138
|
-
self.
|
|
125
|
+
self.assertNotIn('escopoOperacao', kwargs['payload'])
|
|
126
|
+
self.assertEqual(kwargs['payload']['identificadorItem'], '1')
|
|
139
127
|
|
|
140
128
|
|
|
141
129
|
if __name__ == '__main__':
|
|
@@ -68,7 +68,7 @@ def _minimal_payload() -> ApiAttime001ImportarEcommerceDTO:
|
|
|
68
68
|
tipoItem=TipoItem.AUTOMOVEL,
|
|
69
69
|
formaPagamento=EcommerceFormaPagamentoDto(
|
|
70
70
|
diaVencimentoParcelaFatura=10,
|
|
71
|
-
dadosPagamento={'
|
|
71
|
+
dadosPagamento={'EnderecoCobranca': ''},
|
|
72
72
|
),
|
|
73
73
|
itens=[
|
|
74
74
|
EcommerceItemDto(
|
|
@@ -102,13 +102,44 @@ class TestApiAttime001ImportarEcommerceDTO(unittest.TestCase):
|
|
|
102
102
|
)
|
|
103
103
|
self.assertEqual(data['emissao']['contratos']['tipoItem'], 20)
|
|
104
104
|
self.assertEqual(
|
|
105
|
-
data['emissao']['contratos']['formaPagamento']['dadosPagamento']
|
|
106
|
-
'
|
|
105
|
+
data['emissao']['contratos']['formaPagamento']['dadosPagamento'],
|
|
106
|
+
{'EnderecoCobranca': ''},
|
|
107
107
|
)
|
|
108
108
|
again = ApiAttime001ImportarEcommerceDTO.from_dict(data)
|
|
109
109
|
self.assertEqual(again.emissao.clientes.cpfCnpj, '12345678901')
|
|
110
110
|
self.assertEqual(again.emissao.contratos.itens[0].caracteristicas[0].codigo, 39)
|
|
111
111
|
|
|
112
|
+
def test_pj_cliente_wires_pf_attrs_as_null(self):
|
|
113
|
+
"""STAR Homol: PJ deve enviar as chaves PF com null (não omitir)."""
|
|
114
|
+
cliente = EcommerceClienteDto(
|
|
115
|
+
cpfCnpj='41413609000170',
|
|
116
|
+
nomeCompleto='RMA TRANSPORTE',
|
|
117
|
+
dataNascimentoFundacao='2020-01-01T00:00:00',
|
|
118
|
+
)
|
|
119
|
+
data = cliente.to_dict()
|
|
120
|
+
for key in (
|
|
121
|
+
'codigoSexo',
|
|
122
|
+
'codigoEstadoCivil',
|
|
123
|
+
'tipoDocumentoIdentificacao',
|
|
124
|
+
'numeroDocumento',
|
|
125
|
+
'codigoProfissao',
|
|
126
|
+
):
|
|
127
|
+
self.assertIn(key, data)
|
|
128
|
+
self.assertIsNone(data[key])
|
|
129
|
+
self.assertIn('nomeSocial', data)
|
|
130
|
+
self.assertEqual(data['nomeSocial'], '')
|
|
131
|
+
|
|
132
|
+
def test_pf_cliente_omits_empty_optional_pf_attrs(self):
|
|
133
|
+
cliente = EcommerceClienteDto(
|
|
134
|
+
cpfCnpj='12345678901',
|
|
135
|
+
nomeCompleto='JOAO',
|
|
136
|
+
codigoSexo=Sexo.MASCULINO,
|
|
137
|
+
)
|
|
138
|
+
data = cliente.to_dict()
|
|
139
|
+
self.assertEqual(data['codigoSexo'], 'M')
|
|
140
|
+
self.assertNotIn('tipoDocumentoIdentificacao', data)
|
|
141
|
+
self.assertNotIn('numeroDocumento', data)
|
|
142
|
+
|
|
112
143
|
def test_dados_pagamento_must_be_dict(self):
|
|
113
144
|
with self.assertRaises(TypeError):
|
|
114
145
|
EcommerceFormaPagamentoDto.from_dict({
|
|
@@ -149,8 +180,8 @@ class TestAttime001ApiImportarEcommerce(unittest.TestCase):
|
|
|
149
180
|
self.assertEqual(
|
|
150
181
|
kwargs['payload']['emissao']['contratos']['formaPagamento'][
|
|
151
182
|
'dadosPagamento'
|
|
152
|
-
]
|
|
153
|
-
'
|
|
183
|
+
],
|
|
184
|
+
{'EnderecoCobranca': ''},
|
|
154
185
|
)
|
|
155
186
|
|
|
156
187
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/api.py
RENAMED
|
File without changes
|
|
File without changes
|
{attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/apis/bvix.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/bvix.py
RENAMED
|
File without changes
|
{attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/enums.py
RENAMED
|
File without changes
|
|
File without changes
|
{attime_star_api_python_sdk-1.8.0 → attime_star_api_python_sdk-1.9.1}/src/attime/star/dtos/serde.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|