attime-star-api-python-sdk 1.3.2__tar.gz → 1.4.0__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.
Files changed (19) hide show
  1. {attime_star_api_python_sdk-1.3.2/src/attime_star_api_python_sdk.egg-info → attime_star_api_python_sdk-1.4.0}/PKG-INFO +1 -1
  2. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/pyproject.toml +1 -1
  3. attime_star_api_python_sdk-1.4.0/src/attime/star/apis/attime001.py +76 -0
  4. attime_star_api_python_sdk-1.4.0/src/attime/star/dtos/attime001.py +92 -0
  5. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0/src/attime_star_api_python_sdk.egg-info}/PKG-INFO +1 -1
  6. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime_star_api_python_sdk.egg-info/SOURCES.txt +4 -1
  7. attime_star_api_python_sdk-1.4.0/tests/test_attime001_cancelamento.py +87 -0
  8. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/LICENSE +0 -0
  9. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/README.md +0 -0
  10. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/setup.cfg +0 -0
  11. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime/star/api.py +0 -0
  12. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime/star/apis/__init__.py +0 -0
  13. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime/star/apis/bvix.py +0 -0
  14. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime/star/apis/infocar.py +0 -0
  15. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime/star/dtos/__init__.py +0 -0
  16. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime/star/dtos/bvix.py +0 -0
  17. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime/star/dtos/infocar.py +0 -0
  18. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime_star_api_python_sdk.egg-info/dependency_links.txt +0 -0
  19. {attime_star_api_python_sdk-1.3.2 → attime_star_api_python_sdk-1.4.0}/src/attime_star_api_python_sdk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: attime-star-api-python-sdk
3
- Version: 1.3.2
3
+ Version: 1.4.0
4
4
  Summary: Client HTTP de comunicacao com a API da ATTime Star
5
5
  Author-email: Filipe Coelho <filipe@fmconsult.com.br>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "attime-star-api-python-sdk"
7
- version = "1.3.2"
7
+ version = "1.4.0"
8
8
  description = "Client HTTP de comunicacao com a API da ATTime Star"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -0,0 +1,76 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Cliente HTTP ApiAttime001 (ABTP0004)."""
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+
7
+ import jsonpickle
8
+ from fmconsult.utils.url import UrlUtil
9
+ from http import HTTPMethod
10
+
11
+ from attime.star.api import ATTimeStarApi
12
+ from attime.star.dtos.attime001 import ApiAttime001CancelamentoDTO
13
+
14
+
15
+ class Attime001Api(ATTimeStarApi):
16
+ """Rotinas ApiAttime001 — cancelamento de contrato/item/cobertura/serviço/beneficiário."""
17
+
18
+ def cancelamento(
19
+ self, item: ApiAttime001CancelamentoDTO | dict
20
+ ) -> list[ApiAttime001CancelamentoDTO]:
21
+ """POST /btc/ABTP0004/ApiAttime001/Cancelamento."""
22
+ logging.info('cancelling via ApiAttime001 Cancelamento...')
23
+ try:
24
+ if isinstance(item, ApiAttime001CancelamentoDTO):
25
+ payload = item.to_dict()
26
+ else:
27
+ payload = dict(item)
28
+
29
+ escopo = payload.get('escopoOperacao')
30
+ numero = payload.get('numeroContrato')
31
+ if escopo in (None, ''):
32
+ raise ValueError('escopoOperacao é obrigatório')
33
+ if numero in (None, ''):
34
+ raise ValueError('numeroContrato é obrigatório')
35
+
36
+ url = UrlUtil().make_url(
37
+ self.base_url,
38
+ ['btc', 'ABTP0004', 'ApiAttime001', 'Cancelamento'],
39
+ )
40
+ res = self.call_request(
41
+ http_method=HTTPMethod.POST,
42
+ request_url=url,
43
+ payload=payload,
44
+ )
45
+ res = jsonpickle.decode(res)
46
+
47
+ if isinstance(res, list):
48
+ return [
49
+ row
50
+ for row in (ApiAttime001CancelamentoDTO.from_dict(r) for r in res)
51
+ if row is not None
52
+ ]
53
+
54
+ if isinstance(res, dict):
55
+ if 'dados' in res:
56
+ dados = res['dados']
57
+ if isinstance(dados, list):
58
+ return [
59
+ row
60
+ for row in (ApiAttime001CancelamentoDTO.from_dict(r) for r in dados)
61
+ if row is not None
62
+ ]
63
+ if isinstance(dados, dict):
64
+ parsed = ApiAttime001CancelamentoDTO.from_dict(dados)
65
+ return [parsed] if parsed is not None else []
66
+ raise Exception(res.get('mensagem', 'Resposta inesperada em dados'))
67
+
68
+ if 'codigo' in res:
69
+ raise Exception(res.get('mensagem', 'Erro desconhecido'))
70
+
71
+ parsed = ApiAttime001CancelamentoDTO.from_dict(res)
72
+ return [parsed] if parsed is not None else []
73
+
74
+ raise Exception('Resposta inesperada da API Attime001 Cancelamento')
75
+ except Exception:
76
+ raise
@@ -0,0 +1,92 @@
1
+ # -*- coding: utf-8 -*-
2
+ """DTOs ApiAttime001 (ABTP0004).
3
+
4
+ Cancelamento: POST /btc/ABTP0004/ApiAttime001/Cancelamento
5
+ Doc: cancelamentos de contrato / item / cobertura / serviço / beneficiário.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from dataclasses import asdict, dataclass
10
+ from typing import Optional
11
+
12
+
13
+ class EscopoOperacao:
14
+ """Valores sugeridos de ``escopoOperacao`` (OpenAPI tipa como string livre).
15
+
16
+ Alinhados à descrição do endpoint (contrato/item/cobertura/serviço/beneficiário).
17
+ Confirmar no homolog se a STAR exigir código diferente.
18
+ """
19
+
20
+ CONTRATO = 'CONTRATO'
21
+ ITEM = 'ITEM'
22
+ COBERTURA = 'COBERTURA'
23
+ SERVICO = 'SERVICO'
24
+ BENEFICIARIO = 'BENEFICIARIO'
25
+
26
+
27
+ @dataclass
28
+ class ApiAttime001CancelamentoDTO:
29
+ """Payload/resposta de Cancelamento (ApiAttime001CancelamentoDTO)."""
30
+
31
+ escopoOperacao: str
32
+ numeroContrato: str
33
+ manutencaoUsuarioCpf: Optional[str] = None
34
+ manutencaoUsuarioDataHora: Optional[str] = None # ISO date-time
35
+ identificadorItem: Optional[str] = None
36
+ caracteristicaCodigo: Optional[int] = None
37
+ coberturaCodigo: Optional[int] = None
38
+ servicoCodigo: Optional[int] = None
39
+ beneficiarioCodigo: Optional[int] = None
40
+
41
+ def to_dict(self) -> dict:
42
+ return {k: v for k, v in asdict(self).items() if v is not None}
43
+
44
+ @classmethod
45
+ def from_dict(
46
+ cls, data: dict | ApiAttime001CancelamentoDTO | None
47
+ ) -> ApiAttime001CancelamentoDTO | None:
48
+ if data is None:
49
+ return None
50
+ if isinstance(data, cls):
51
+ return data
52
+ if not isinstance(data, dict):
53
+ raise TypeError(
54
+ f'ApiAttime001CancelamentoDTO.from_dict espera dict, recebeu {type(data)!r}'
55
+ )
56
+ return cls(**{k: data.get(k) for k in cls.__dataclass_fields__})
57
+
58
+ @classmethod
59
+ def for_item(
60
+ cls,
61
+ *,
62
+ numeroContrato: str,
63
+ identificadorItem: str,
64
+ manutencaoUsuarioCpf: Optional[str] = None,
65
+ manutencaoUsuarioDataHora: Optional[str] = None,
66
+ escopoOperacao: str = EscopoOperacao.ITEM,
67
+ ) -> ApiAttime001CancelamentoDTO:
68
+ """Atalho para cancelar um item do contrato (caso típico de saída de vida)."""
69
+ return cls(
70
+ escopoOperacao=escopoOperacao,
71
+ numeroContrato=numeroContrato,
72
+ identificadorItem=identificadorItem,
73
+ manutencaoUsuarioCpf=manutencaoUsuarioCpf,
74
+ manutencaoUsuarioDataHora=manutencaoUsuarioDataHora,
75
+ )
76
+
77
+ @classmethod
78
+ def for_contrato(
79
+ cls,
80
+ *,
81
+ numeroContrato: str,
82
+ manutencaoUsuarioCpf: Optional[str] = None,
83
+ manutencaoUsuarioDataHora: Optional[str] = None,
84
+ escopoOperacao: str = EscopoOperacao.CONTRATO,
85
+ ) -> ApiAttime001CancelamentoDTO:
86
+ """Atalho para cancelar o contrato inteiro."""
87
+ return cls(
88
+ escopoOperacao=escopoOperacao,
89
+ numeroContrato=numeroContrato,
90
+ manutencaoUsuarioCpf=manutencaoUsuarioCpf,
91
+ manutencaoUsuarioDataHora=manutencaoUsuarioDataHora,
92
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: attime-star-api-python-sdk
3
- Version: 1.3.2
3
+ Version: 1.4.0
4
4
  Summary: Client HTTP de comunicacao com a API da ATTime Star
5
5
  Author-email: Filipe Coelho <filipe@fmconsult.com.br>
6
6
  License: MIT License
@@ -3,12 +3,15 @@ README.md
3
3
  pyproject.toml
4
4
  src/attime/star/api.py
5
5
  src/attime/star/apis/__init__.py
6
+ src/attime/star/apis/attime001.py
6
7
  src/attime/star/apis/bvix.py
7
8
  src/attime/star/apis/infocar.py
8
9
  src/attime/star/dtos/__init__.py
10
+ src/attime/star/dtos/attime001.py
9
11
  src/attime/star/dtos/bvix.py
10
12
  src/attime/star/dtos/infocar.py
11
13
  src/attime_star_api_python_sdk.egg-info/PKG-INFO
12
14
  src/attime_star_api_python_sdk.egg-info/SOURCES.txt
13
15
  src/attime_star_api_python_sdk.egg-info/dependency_links.txt
14
- src/attime_star_api_python_sdk.egg-info/top_level.txt
16
+ src/attime_star_api_python_sdk.egg-info/top_level.txt
17
+ tests/test_attime001_cancelamento.py
@@ -0,0 +1,87 @@
1
+ # -*- coding: utf-8 -*-
2
+ """Testes unitários ApiAttime001 Cancelamento (sem HTTP)."""
3
+ from __future__ import annotations
4
+
5
+ import unittest
6
+ from unittest.mock import MagicMock, patch
7
+
8
+ from attime.star.dtos.attime001 import (
9
+ ApiAttime001CancelamentoDTO,
10
+ EscopoOperacao,
11
+ )
12
+
13
+
14
+ class TestApiAttime001CancelamentoDTO(unittest.TestCase):
15
+ def test_to_dict_omits_none(self):
16
+ dto = ApiAttime001CancelamentoDTO.for_item(
17
+ numeroContrato='002501000089',
18
+ identificadorItem='12345678901',
19
+ )
20
+ data = dto.to_dict()
21
+ self.assertEqual(data['escopoOperacao'], EscopoOperacao.ITEM)
22
+ self.assertEqual(data['numeroContrato'], '002501000089')
23
+ self.assertEqual(data['identificadorItem'], '12345678901')
24
+ self.assertNotIn('coberturaCodigo', data)
25
+ self.assertNotIn('manutencaoUsuarioCpf', data)
26
+
27
+ def test_from_dict_roundtrip(self):
28
+ raw = {
29
+ 'escopoOperacao': 'ITEM',
30
+ 'numeroContrato': '002501000089',
31
+ 'identificadorItem': '999',
32
+ 'coberturaCodigo': 81,
33
+ }
34
+ dto = ApiAttime001CancelamentoDTO.from_dict(raw)
35
+ self.assertIsNotNone(dto)
36
+ self.assertEqual(dto.coberturaCodigo, 81)
37
+ self.assertEqual(dto.to_dict()['coberturaCodigo'], 81)
38
+
39
+ def test_for_contrato(self):
40
+ dto = ApiAttime001CancelamentoDTO.for_contrato(numeroContrato='002501000001')
41
+ self.assertEqual(dto.escopoOperacao, EscopoOperacao.CONTRATO)
42
+ self.assertIsNone(dto.identificadorItem)
43
+
44
+
45
+ class TestAttime001ApiCancelamento(unittest.TestCase):
46
+ def test_requires_escopo_and_numero(self):
47
+ from attime.star.apis.attime001 import Attime001Api
48
+
49
+ with patch.object(Attime001Api, '__init__', lambda self: None):
50
+ api = Attime001Api()
51
+ with self.assertRaises(ValueError):
52
+ api.cancelamento({'numeroContrato': '002501000001'})
53
+ with self.assertRaises(ValueError):
54
+ api.cancelamento({'escopoOperacao': 'ITEM'})
55
+
56
+ def test_posts_to_cancelamento_path(self):
57
+ from attime.star.apis.attime001 import Attime001Api
58
+
59
+ with patch.object(Attime001Api, '__init__', lambda self: None):
60
+ api = Attime001Api()
61
+ api.base_url = 'https://starbackhom.attime.inf.br'
62
+ api.call_request = MagicMock(
63
+ return_value='[{"escopoOperacao":"ITEM","numeroContrato":"002501000089","identificadorItem":"1"}]'
64
+ )
65
+ with patch('attime.star.apis.attime001.jsonpickle.decode') as decode:
66
+ decode.return_value = [
67
+ {
68
+ 'escopoOperacao': 'ITEM',
69
+ 'numeroContrato': '002501000089',
70
+ 'identificadorItem': '1',
71
+ }
72
+ ]
73
+ rows = api.cancelamento(
74
+ ApiAttime001CancelamentoDTO.for_item(
75
+ numeroContrato='002501000089',
76
+ identificadorItem='1',
77
+ )
78
+ )
79
+ self.assertEqual(len(rows), 1)
80
+ self.assertEqual(rows[0].identificadorItem, '1')
81
+ kwargs = api.call_request.call_args.kwargs
82
+ self.assertIn('/ABTP0004/ApiAttime001/Cancelamento', kwargs['request_url'])
83
+ self.assertEqual(kwargs['payload']['escopoOperacao'], 'ITEM')
84
+
85
+
86
+ if __name__ == '__main__':
87
+ unittest.main()