transdesk-importer-python-sdk 1.0.0__tar.gz → 1.1.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.
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/PKG-INFO +16 -1
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/README.md +15 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/pyproject.toml +1 -1
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/models/canonical.py +18 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/models/internal.py +18 -4
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/shared/data_normalizer.py +21 -1
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/canonical/apolice_builder.py +32 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/internal/coverage_mapper.py +3 -3
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/internal/internal_mapper.py +78 -5
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/internal/rcf_parser.py +1 -1
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk_importer_python_sdk.egg-info/PKG-INFO +16 -1
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk_importer_python_sdk.egg-info/SOURCES.txt +3 -1
- transdesk_importer_python_sdk-1.1.0/tests/test_data_normalizer.py +26 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/tests/test_importer.py +1 -0
- transdesk_importer_python_sdk-1.1.0/tests/test_vehicle_details.py +87 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/LICENSE +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/setup.cfg +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/__init__.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/client.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/exporters/__init__.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/exporters/json_exporter.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/models/__init__.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/readers/__init__.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/readers/excel_reader.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/shared/__init__.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/__init__.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/canonical/__init__.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/canonical/cobertura_builder.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/canonical/item_classifier.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/internal/__init__.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/transformers/internal/towing_parser.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/validators/__init__.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk/importer/validators/internal_policy_validator.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk_importer_python_sdk.egg-info/dependency_links.txt +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk_importer_python_sdk.egg-info/requires.txt +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/src/transdesk_importer_python_sdk.egg-info/top_level.txt +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/tests/test_excel_reader.py +0 -0
- {transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/tests/test_validator.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: transdesk-importer-python-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: SDK de leitura/transformacao de planilhas de apolices Transdesk para o formato interno 77seg
|
|
5
5
|
Author-email: FMConsult <contato@fmconsult.com.br>
|
|
6
6
|
License: MIT License
|
|
@@ -112,6 +112,21 @@ Helpers: `result.to_dict()` e `result.to_json(**kwargs)`.
|
|
|
112
112
|
cobertura e de risco estao sempre presentes; os que nao se aplicam ao tipo do
|
|
113
113
|
item (ex.: campos de vida num veiculo) vem como `null`.
|
|
114
114
|
|
|
115
|
+
Campos adicionais de veiculo/reboque (colunas apos `CHAVE_PIX` na planilha):
|
|
116
|
+
|
|
117
|
+
| Coluna planilha | Campo `InternalItem` | Observacao |
|
|
118
|
+
| --------------- | -------------------- | ---------- |
|
|
119
|
+
| `EMPRESA_RASTREAMENTO` | `vehicle_tracker_company` | Somente quando `has_vehicle_tracker` |
|
|
120
|
+
| `CATEGORIA` | `vehicle_classification` | Classificacao do veiculo (int) |
|
|
121
|
+
| `MARCA` | `brand_name` / `brand` | Veiculo; reboque usa `MARCA_REBOQUE` |
|
|
122
|
+
| `MODELO` | `model_description` / `model` | Veiculo; reboque usa `MODELO_REBOQUE` |
|
|
123
|
+
| `RENAVAM` | `renavam` | |
|
|
124
|
+
| `COR` | `color` | |
|
|
125
|
+
| `ANO` | `model_year`, `manufacture_year` | Mesmo valor nos dois campos |
|
|
126
|
+
| `EIXOS` | `axles` | |
|
|
127
|
+
| `CAMBIO` | `transmission` | |
|
|
128
|
+
| `ALIENADO` | `is_alienated` | `N/A` ou vazio = `false` |
|
|
129
|
+
|
|
115
130
|
### Validacao
|
|
116
131
|
|
|
117
132
|
O `ValidationError` cobre tres tipos:
|
|
@@ -69,6 +69,21 @@ Helpers: `result.to_dict()` e `result.to_json(**kwargs)`.
|
|
|
69
69
|
cobertura e de risco estao sempre presentes; os que nao se aplicam ao tipo do
|
|
70
70
|
item (ex.: campos de vida num veiculo) vem como `null`.
|
|
71
71
|
|
|
72
|
+
Campos adicionais de veiculo/reboque (colunas apos `CHAVE_PIX` na planilha):
|
|
73
|
+
|
|
74
|
+
| Coluna planilha | Campo `InternalItem` | Observacao |
|
|
75
|
+
| --------------- | -------------------- | ---------- |
|
|
76
|
+
| `EMPRESA_RASTREAMENTO` | `vehicle_tracker_company` | Somente quando `has_vehicle_tracker` |
|
|
77
|
+
| `CATEGORIA` | `vehicle_classification` | Classificacao do veiculo (int) |
|
|
78
|
+
| `MARCA` | `brand_name` / `brand` | Veiculo; reboque usa `MARCA_REBOQUE` |
|
|
79
|
+
| `MODELO` | `model_description` / `model` | Veiculo; reboque usa `MODELO_REBOQUE` |
|
|
80
|
+
| `RENAVAM` | `renavam` | |
|
|
81
|
+
| `COR` | `color` | |
|
|
82
|
+
| `ANO` | `model_year`, `manufacture_year` | Mesmo valor nos dois campos |
|
|
83
|
+
| `EIXOS` | `axles` | |
|
|
84
|
+
| `CAMBIO` | `transmission` | |
|
|
85
|
+
| `ALIENADO` | `is_alienated` | `N/A` ou vazio = `false` |
|
|
86
|
+
|
|
72
87
|
### Validacao
|
|
73
88
|
|
|
74
89
|
O `ValidationError` cobre tres tipos:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "transdesk-importer-python-sdk"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.1.0"
|
|
8
8
|
description = "SDK de leitura/transformacao de planilhas de apolices Transdesk para o formato interno 77seg"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -36,6 +36,16 @@ class VeiculoRisco:
|
|
|
36
36
|
placa: Optional[str] = None
|
|
37
37
|
chassi: Optional[str] = None
|
|
38
38
|
codigo_fipe: Optional[str] = None
|
|
39
|
+
empresa_rastreamento: Optional[str] = None
|
|
40
|
+
categoria: Optional[str] = None
|
|
41
|
+
modelo: Optional[str] = None
|
|
42
|
+
renavam: Optional[str] = None
|
|
43
|
+
marca: Optional[str] = None
|
|
44
|
+
cor: Optional[str] = None
|
|
45
|
+
ano: Optional[str] = None
|
|
46
|
+
eixos: Optional[str] = None
|
|
47
|
+
cambio: Optional[str] = None
|
|
48
|
+
alienado: Optional[str] = None
|
|
39
49
|
|
|
40
50
|
|
|
41
51
|
@dataclass
|
|
@@ -44,6 +54,14 @@ class ReboqueRisco:
|
|
|
44
54
|
chassi: Optional[str] = None
|
|
45
55
|
marca: Optional[str] = None
|
|
46
56
|
modelo: Optional[str] = None
|
|
57
|
+
empresa_rastreamento: Optional[str] = None
|
|
58
|
+
categoria: Optional[str] = None
|
|
59
|
+
renavam: Optional[str] = None
|
|
60
|
+
cor: Optional[str] = None
|
|
61
|
+
ano: Optional[str] = None
|
|
62
|
+
eixos: Optional[str] = None
|
|
63
|
+
cambio: Optional[str] = None
|
|
64
|
+
alienado: Optional[str] = None
|
|
47
65
|
|
|
48
66
|
|
|
49
67
|
@dataclass
|
|
@@ -23,22 +23,25 @@ class Broker:
|
|
|
23
23
|
|
|
24
24
|
@dataclass
|
|
25
25
|
class CustomerInfo:
|
|
26
|
+
cpf: Optional[str] = None
|
|
26
27
|
cnpj: Optional[str] = None
|
|
27
28
|
principal_name: Optional[str] = None
|
|
28
29
|
secondary_name: Optional[str] = None
|
|
29
30
|
email_address: Optional[str] = None
|
|
31
|
+
enrollment_number: Optional[str] = None
|
|
32
|
+
custom_fields: Optional[Dict[str, Any]] = None
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
@dataclass
|
|
33
36
|
class Telephone:
|
|
34
|
-
|
|
37
|
+
type_code: str
|
|
35
38
|
ddd: str
|
|
36
39
|
number: str
|
|
37
40
|
|
|
38
41
|
|
|
39
42
|
@dataclass
|
|
40
43
|
class Mobile:
|
|
41
|
-
|
|
44
|
+
type_code: str
|
|
42
45
|
ddd: str
|
|
43
46
|
number: str
|
|
44
47
|
allow_sms: bool = True
|
|
@@ -85,7 +88,7 @@ class InternalItem:
|
|
|
85
88
|
|
|
86
89
|
# RCF
|
|
87
90
|
has_rcf: bool = False
|
|
88
|
-
|
|
91
|
+
material_damage_value: float = 0
|
|
89
92
|
property_damage_premium: float = 0
|
|
90
93
|
bodily_injury_value: float = 0
|
|
91
94
|
bodily_injury_premium: float = 0
|
|
@@ -102,12 +105,13 @@ class InternalItem:
|
|
|
102
105
|
towing_premium: float = 0
|
|
103
106
|
|
|
104
107
|
# Vidros
|
|
105
|
-
|
|
108
|
+
glass_coverage_level: Optional[str] = None
|
|
106
109
|
glass_premium: float = 0
|
|
107
110
|
|
|
108
111
|
# Rastreador
|
|
109
112
|
has_vehicle_tracker: bool = False
|
|
110
113
|
vehicle_tracker_premium: float = 0
|
|
114
|
+
vehicle_tracker_company: Optional[str] = None
|
|
111
115
|
|
|
112
116
|
# Juridica
|
|
113
117
|
has_legal_assistance: bool = False
|
|
@@ -124,6 +128,16 @@ class InternalItem:
|
|
|
124
128
|
body_description: Optional[str] = None
|
|
125
129
|
brand: Optional[str] = None
|
|
126
130
|
model: Optional[str] = None
|
|
131
|
+
brand_name: Optional[str] = None
|
|
132
|
+
model_description: Optional[str] = None
|
|
133
|
+
vehicle_classification: Optional[int] = None
|
|
134
|
+
renavam: Optional[str] = None
|
|
135
|
+
color: Optional[str] = None
|
|
136
|
+
model_year: Optional[int] = None
|
|
137
|
+
manufacture_year: Optional[int] = None
|
|
138
|
+
axles: Optional[int] = None
|
|
139
|
+
transmission: Optional[str] = None
|
|
140
|
+
is_alienated: bool = False
|
|
127
141
|
|
|
128
142
|
# Vida
|
|
129
143
|
name: Optional[str] = None
|
|
@@ -41,7 +41,27 @@ class DataNormalizer:
|
|
|
41
41
|
|
|
42
42
|
@staticmethod
|
|
43
43
|
def to_int(value):
|
|
44
|
+
if value in (None, ""):
|
|
45
|
+
return None
|
|
46
|
+
|
|
44
47
|
try:
|
|
45
|
-
return int(value)
|
|
48
|
+
return int(str(value).strip())
|
|
46
49
|
except Exception:
|
|
47
50
|
return None
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def to_bool(value, na_values=("N/A", "n/a", "")):
|
|
54
|
+
if value is None:
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
normalized = str(value).strip()
|
|
58
|
+
if normalized in na_values:
|
|
59
|
+
return False
|
|
60
|
+
|
|
61
|
+
upper = normalized.upper()
|
|
62
|
+
if upper in ("SIM", "S", "TRUE", "1", "YES", "Y"):
|
|
63
|
+
return True
|
|
64
|
+
if upper in ("NÃO", "NAO", "N", "FALSE", "0", "NO"):
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
return False
|
|
@@ -188,6 +188,7 @@ class ApoliceBuilder:
|
|
|
188
188
|
chassi=row.get("CHASSI"),
|
|
189
189
|
marca=row.get("MARCA_REBOQUE"),
|
|
190
190
|
modelo=row.get("MODELO_REBOQUE"),
|
|
191
|
+
**self._read_vehicle_detail_fields(rows, include_brand_model=False),
|
|
191
192
|
),
|
|
192
193
|
coberturas=CoberturaBuilder(rows).build(),
|
|
193
194
|
)
|
|
@@ -205,6 +206,7 @@ class ApoliceBuilder:
|
|
|
205
206
|
placa=row.get("PLACA_VEICULO"),
|
|
206
207
|
chassi=row.get("CHASSI"),
|
|
207
208
|
codigo_fipe=row.get("CODE_FIPE"),
|
|
209
|
+
**self._read_vehicle_detail_fields(rows, include_brand_model=True),
|
|
208
210
|
),
|
|
209
211
|
complementos=self.build_complementos(rows),
|
|
210
212
|
coberturas=CoberturaBuilder(rows).build(),
|
|
@@ -214,6 +216,36 @@ class ApoliceBuilder:
|
|
|
214
216
|
|
|
215
217
|
return item
|
|
216
218
|
|
|
219
|
+
def _read_vehicle_detail_fields(self, rows, include_brand_model=False):
|
|
220
|
+
column_map = {
|
|
221
|
+
"empresa_rastreamento": "EMPRESA_RASTREAMENTO",
|
|
222
|
+
"categoria": "CATEGORIA",
|
|
223
|
+
"renavam": "RENAVAM",
|
|
224
|
+
"cor": "COR",
|
|
225
|
+
"ano": "ANO",
|
|
226
|
+
"eixos": "EIXOS",
|
|
227
|
+
"cambio": "CAMBIO",
|
|
228
|
+
"alienado": "ALIENADO",
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if include_brand_model:
|
|
232
|
+
column_map["marca"] = "MARCA"
|
|
233
|
+
column_map["modelo"] = "MODELO"
|
|
234
|
+
|
|
235
|
+
fields = {}
|
|
236
|
+
for field_name, column_name in column_map.items():
|
|
237
|
+
fields[field_name] = self._first_non_empty(rows, column_name)
|
|
238
|
+
|
|
239
|
+
return fields
|
|
240
|
+
|
|
241
|
+
@staticmethod
|
|
242
|
+
def _first_non_empty(rows, column_name):
|
|
243
|
+
for row in rows:
|
|
244
|
+
value = row.get(column_name)
|
|
245
|
+
if value is not None and str(value).strip() != "":
|
|
246
|
+
return value
|
|
247
|
+
return None
|
|
248
|
+
|
|
217
249
|
# ---------------------------------------------------------
|
|
218
250
|
# COMPLEMENTOS
|
|
219
251
|
# ---------------------------------------------------------
|
|
@@ -93,7 +93,7 @@ class CoverageMapper:
|
|
|
93
93
|
item.vehicle_tracker_premium += (coverage.valor_mensalidade or 0)
|
|
94
94
|
|
|
95
95
|
def _apply_glass(self, item, coverage):
|
|
96
|
-
item.
|
|
96
|
+
item.glass_coverage_level = "completo"
|
|
97
97
|
item.glass_premium += (coverage.valor_mensalidade or 0)
|
|
98
98
|
|
|
99
99
|
def _apply_rcf(self, item, coverage):
|
|
@@ -104,13 +104,13 @@ class CoverageMapper:
|
|
|
104
104
|
premiums = self._allocate_premium(
|
|
105
105
|
coverage.valor_mensalidade or 0,
|
|
106
106
|
{
|
|
107
|
-
"property_damage": parsed["
|
|
107
|
+
"property_damage": parsed["material_damage_value"],
|
|
108
108
|
"bodily_injury": parsed["bodily_injury_value"],
|
|
109
109
|
"moral_damage": parsed["moral_damage_value"],
|
|
110
110
|
},
|
|
111
111
|
)
|
|
112
112
|
|
|
113
|
-
item.
|
|
113
|
+
item.material_damage_value += (parsed["material_damage_value"])
|
|
114
114
|
item.property_damage_premium += (premiums["property_damage"])
|
|
115
115
|
|
|
116
116
|
item.bodily_injury_value += (parsed["bodily_injury_value"])
|
|
@@ -10,6 +10,7 @@ from transdesk.importer.models.internal import (
|
|
|
10
10
|
ResellerData,
|
|
11
11
|
Telephone,
|
|
12
12
|
)
|
|
13
|
+
from transdesk.importer.shared.data_normalizer import DataNormalizer
|
|
13
14
|
from transdesk.importer.transformers.internal.coverage_mapper import CoverageMapper
|
|
14
15
|
|
|
15
16
|
|
|
@@ -44,19 +45,61 @@ class InternalMapper:
|
|
|
44
45
|
customer = policy.cliente
|
|
45
46
|
|
|
46
47
|
email = next((e for e in customer.emails if e), None)
|
|
48
|
+
cpf, cnpj = self._route_document(customer.documento, customer.tipo_pessoa)
|
|
47
49
|
|
|
48
50
|
return Lead(
|
|
49
51
|
customer_info=CustomerInfo(
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
cpf=cpf,
|
|
53
|
+
cnpj=cnpj,
|
|
54
|
+
principal_name=customer.razao_social or customer.nome_fantasia,
|
|
55
|
+
secondary_name=customer.nome_fantasia or "",
|
|
53
56
|
email_address=email,
|
|
57
|
+
enrollment_number=customer.matricula,
|
|
58
|
+
custom_fields=self._build_bank_custom_fields(customer),
|
|
54
59
|
),
|
|
55
60
|
telphone_info=self._build_phone(customer),
|
|
56
61
|
celphone_info=self._build_mobile(customer),
|
|
57
62
|
address_info=self._build_address(customer),
|
|
58
63
|
)
|
|
59
64
|
|
|
65
|
+
def _build_bank_custom_fields(self, customer):
|
|
66
|
+
bank = customer.dados_bancarios
|
|
67
|
+
|
|
68
|
+
if bank is None:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
bank_info = {
|
|
72
|
+
'bank_code': bank.banco,
|
|
73
|
+
'agency': bank.agencia,
|
|
74
|
+
'account': bank.conta,
|
|
75
|
+
'pix_key': bank.chave_pix,
|
|
76
|
+
'holder_name': bank.titular,
|
|
77
|
+
'holder_document': bank.documento_titular,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if not any(bank_info.values()):
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
return {'bank_info': bank_info}
|
|
84
|
+
|
|
85
|
+
def _route_document(self, documento, tipo_pessoa=None):
|
|
86
|
+
digits = "".join(filter(str.isdigit, str(documento or "")))
|
|
87
|
+
|
|
88
|
+
if not digits:
|
|
89
|
+
return None, None
|
|
90
|
+
|
|
91
|
+
tipo = str(tipo_pessoa or "").strip().upper()
|
|
92
|
+
|
|
93
|
+
if tipo in ("F", "PF", "FISICA", "FÍSICA"):
|
|
94
|
+
return digits, None
|
|
95
|
+
if tipo in ("J", "PJ", "JURIDICA", "JURÍDICA"):
|
|
96
|
+
return None, digits
|
|
97
|
+
|
|
98
|
+
if len(digits) <= 11:
|
|
99
|
+
return digits.zfill(11), None
|
|
100
|
+
|
|
101
|
+
return None, digits.zfill(14)
|
|
102
|
+
|
|
60
103
|
def build_items(self, policy):
|
|
61
104
|
return [
|
|
62
105
|
self.map_item(item, index)
|
|
@@ -77,7 +120,7 @@ class InternalMapper:
|
|
|
77
120
|
|
|
78
121
|
if not numero.startswith("9"):
|
|
79
122
|
return Telephone(
|
|
80
|
-
|
|
123
|
+
type_code="03",
|
|
81
124
|
ddd=digits[:2],
|
|
82
125
|
number=numero,
|
|
83
126
|
)
|
|
@@ -98,7 +141,7 @@ class InternalMapper:
|
|
|
98
141
|
|
|
99
142
|
if numero.startswith("9"):
|
|
100
143
|
return Mobile(
|
|
101
|
-
|
|
144
|
+
type_code="02",
|
|
102
145
|
ddd=digits[:2],
|
|
103
146
|
number=numero,
|
|
104
147
|
allow_sms=True,
|
|
@@ -154,6 +197,36 @@ class InternalMapper:
|
|
|
154
197
|
if item.tipo_item == "reboque":
|
|
155
198
|
internal_item.brand = getattr(risco, "marca", None)
|
|
156
199
|
internal_item.model = getattr(risco, "modelo", None)
|
|
200
|
+
internal_item.brand_name = internal_item.brand
|
|
201
|
+
internal_item.model_description = internal_item.model
|
|
202
|
+
elif item.tipo_item == "veiculo":
|
|
203
|
+
marca = getattr(risco, "marca", None)
|
|
204
|
+
modelo = getattr(risco, "modelo", None)
|
|
205
|
+
internal_item.brand = marca
|
|
206
|
+
internal_item.model = modelo
|
|
207
|
+
internal_item.brand_name = marca
|
|
208
|
+
internal_item.model_description = modelo
|
|
209
|
+
|
|
210
|
+
internal_item.vehicle_classification = DataNormalizer.to_int(
|
|
211
|
+
getattr(risco, "categoria", None)
|
|
212
|
+
)
|
|
213
|
+
internal_item.renavam = DataNormalizer.clean_doc(
|
|
214
|
+
getattr(risco, "renavam", None)
|
|
215
|
+
)
|
|
216
|
+
internal_item.color = getattr(risco, "cor", None) or None
|
|
217
|
+
year = DataNormalizer.to_int(getattr(risco, "ano", None))
|
|
218
|
+
internal_item.model_year = year
|
|
219
|
+
internal_item.manufacture_year = year
|
|
220
|
+
internal_item.axles = DataNormalizer.to_int(getattr(risco, "eixos", None))
|
|
221
|
+
internal_item.transmission = getattr(risco, "cambio", None) or None
|
|
222
|
+
internal_item.is_alienated = DataNormalizer.to_bool(
|
|
223
|
+
getattr(risco, "alienado", None)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
if internal_item.has_vehicle_tracker:
|
|
227
|
+
internal_item.vehicle_tracker_company = (
|
|
228
|
+
getattr(risco, "empresa_rastreamento", None) or None
|
|
229
|
+
)
|
|
157
230
|
|
|
158
231
|
def _build_body_description(self, item):
|
|
159
232
|
parts = []
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: transdesk-importer-python-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: SDK de leitura/transformacao de planilhas de apolices Transdesk para o formato interno 77seg
|
|
5
5
|
Author-email: FMConsult <contato@fmconsult.com.br>
|
|
6
6
|
License: MIT License
|
|
@@ -112,6 +112,21 @@ Helpers: `result.to_dict()` e `result.to_json(**kwargs)`.
|
|
|
112
112
|
cobertura e de risco estao sempre presentes; os que nao se aplicam ao tipo do
|
|
113
113
|
item (ex.: campos de vida num veiculo) vem como `null`.
|
|
114
114
|
|
|
115
|
+
Campos adicionais de veiculo/reboque (colunas apos `CHAVE_PIX` na planilha):
|
|
116
|
+
|
|
117
|
+
| Coluna planilha | Campo `InternalItem` | Observacao |
|
|
118
|
+
| --------------- | -------------------- | ---------- |
|
|
119
|
+
| `EMPRESA_RASTREAMENTO` | `vehicle_tracker_company` | Somente quando `has_vehicle_tracker` |
|
|
120
|
+
| `CATEGORIA` | `vehicle_classification` | Classificacao do veiculo (int) |
|
|
121
|
+
| `MARCA` | `brand_name` / `brand` | Veiculo; reboque usa `MARCA_REBOQUE` |
|
|
122
|
+
| `MODELO` | `model_description` / `model` | Veiculo; reboque usa `MODELO_REBOQUE` |
|
|
123
|
+
| `RENAVAM` | `renavam` | |
|
|
124
|
+
| `COR` | `color` | |
|
|
125
|
+
| `ANO` | `model_year`, `manufacture_year` | Mesmo valor nos dois campos |
|
|
126
|
+
| `EIXOS` | `axles` | |
|
|
127
|
+
| `CAMBIO` | `transmission` | |
|
|
128
|
+
| `ALIENADO` | `is_alienated` | `N/A` ou vazio = `false` |
|
|
129
|
+
|
|
115
130
|
### Validacao
|
|
116
131
|
|
|
117
132
|
O `ValidationError` cobre tres tipos:
|
|
@@ -29,6 +29,8 @@ src/transdesk_importer_python_sdk.egg-info/SOURCES.txt
|
|
|
29
29
|
src/transdesk_importer_python_sdk.egg-info/dependency_links.txt
|
|
30
30
|
src/transdesk_importer_python_sdk.egg-info/requires.txt
|
|
31
31
|
src/transdesk_importer_python_sdk.egg-info/top_level.txt
|
|
32
|
+
tests/test_data_normalizer.py
|
|
32
33
|
tests/test_excel_reader.py
|
|
33
34
|
tests/test_importer.py
|
|
34
|
-
tests/test_validator.py
|
|
35
|
+
tests/test_validator.py
|
|
36
|
+
tests/test_vehicle_details.py
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from transdesk.importer.shared.data_normalizer import DataNormalizer
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.mark.parametrize(
|
|
7
|
+
"value,expected",
|
|
8
|
+
[
|
|
9
|
+
("N/A", False),
|
|
10
|
+
("n/a", False),
|
|
11
|
+
("", False),
|
|
12
|
+
(None, False),
|
|
13
|
+
("SIM", True),
|
|
14
|
+
("sim", True),
|
|
15
|
+
("S", True),
|
|
16
|
+
("1", True),
|
|
17
|
+
("true", True),
|
|
18
|
+
("NÃO", False),
|
|
19
|
+
("NAO", False),
|
|
20
|
+
("N", False),
|
|
21
|
+
("0", False),
|
|
22
|
+
("false", False),
|
|
23
|
+
],
|
|
24
|
+
)
|
|
25
|
+
def test_to_bool(value, expected):
|
|
26
|
+
assert DataNormalizer.to_bool(value) is expected
|
{transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/tests/test_importer.py
RENAMED
|
@@ -35,6 +35,7 @@ def test_policy_structure(apolices_bytes):
|
|
|
35
35
|
assert broker.principal_name == "TRANSDESK UNIDADE CURITIBA"
|
|
36
36
|
|
|
37
37
|
assert policy.data.lead.customer_info.principal_name == "ALM TRANSPORTES E LOGISTICA LTDA"
|
|
38
|
+
assert policy.data.lead.customer_info.cnpj
|
|
38
39
|
|
|
39
40
|
item = policy.data.items[0]
|
|
40
41
|
assert item.item_number == 1
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from transdesk.importer.transformers.canonical.apolice_builder import ApoliceBuilder
|
|
4
|
+
from transdesk.importer.transformers.internal.internal_mapper import InternalMapper
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _base_row(**overrides):
|
|
8
|
+
row = {
|
|
9
|
+
"COD_AGRUPADOR_APOLICE": "99",
|
|
10
|
+
"DIA_VCTO": "10",
|
|
11
|
+
"ID_UNITY": "1",
|
|
12
|
+
"SUB_ESTIPULANTE_CNPJ": "12345678000199",
|
|
13
|
+
"SUB_ESTIPULANTE_FANTASIA": "CORRETORA TESTE",
|
|
14
|
+
"SUB_ESTIPULANTE_RAZAO_SOCIAL": "CORRETORA TESTE LTDA",
|
|
15
|
+
"PESSOA": "J",
|
|
16
|
+
"CNPJ_CPF_CLIENTE": "98765432000188",
|
|
17
|
+
"NOME_FANTASIA_CLIENTE": "CLIENTE TESTE",
|
|
18
|
+
"RAZAO_SOCIAL_CLIENTE": "CLIENTE TESTE LTDA",
|
|
19
|
+
"PLACA_VEICULO": "ABC1D23",
|
|
20
|
+
"CHASSI": "9BWZZZ377VT004251",
|
|
21
|
+
"CODE_FIPE": "005541-7",
|
|
22
|
+
"NME_BENEFICIO": "Casco PT (VEÍCULO)",
|
|
23
|
+
"VALOR_MENSALIDADE": "100.00",
|
|
24
|
+
"IMPORTANCIA_SEGURADA": "50000",
|
|
25
|
+
"EMPRESA_RASTREAMENTO": "TRACKERLOG",
|
|
26
|
+
"CATEGORIA": "3",
|
|
27
|
+
"MODELO": "FH 540",
|
|
28
|
+
"RENAVAM": "12345678901",
|
|
29
|
+
"MARCA": "VOLVO",
|
|
30
|
+
"COR": "BRANCO",
|
|
31
|
+
"ANO": "2022",
|
|
32
|
+
"EIXOS": "3",
|
|
33
|
+
"CAMBIO": "AUTOMATICO",
|
|
34
|
+
"ALIENADO": "SIM",
|
|
35
|
+
}
|
|
36
|
+
row.update(overrides)
|
|
37
|
+
return row
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _build_item(rows):
|
|
41
|
+
df = pd.DataFrame(rows)
|
|
42
|
+
canonical = ApoliceBuilder().build(df)[0]
|
|
43
|
+
return InternalMapper().map(canonical).data.items[0]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_vehicle_details_mapped_from_spreadsheet_columns():
|
|
47
|
+
item = _build_item([_base_row()])
|
|
48
|
+
|
|
49
|
+
assert item.item_type == "veiculo"
|
|
50
|
+
assert item.brand_name == "VOLVO"
|
|
51
|
+
assert item.model_description == "FH 540"
|
|
52
|
+
assert item.brand == "VOLVO"
|
|
53
|
+
assert item.model == "FH 540"
|
|
54
|
+
assert item.vehicle_classification == 3
|
|
55
|
+
assert item.renavam == "12345678901"
|
|
56
|
+
assert item.color == "BRANCO"
|
|
57
|
+
assert item.model_year == 2022
|
|
58
|
+
assert item.manufacture_year == 2022
|
|
59
|
+
assert item.axles == 3
|
|
60
|
+
assert item.transmission == "AUTOMATICO"
|
|
61
|
+
assert item.is_alienated is True
|
|
62
|
+
assert item.has_vehicle_tracker is False
|
|
63
|
+
assert item.vehicle_tracker_company is None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_vehicle_tracker_company_only_when_has_tracker():
|
|
67
|
+
with_tracker = _build_item([
|
|
68
|
+
_base_row(NME_BENEFICIO="Casco PT (VEÍCULO)", VALOR_MENSALIDADE="100.00"),
|
|
69
|
+
_base_row(
|
|
70
|
+
NME_BENEFICIO="Rastreador p/ Veículo",
|
|
71
|
+
VALOR_MENSALIDADE="15.00",
|
|
72
|
+
EMPRESA_RASTREAMENTO="TRACKERLOG",
|
|
73
|
+
),
|
|
74
|
+
])
|
|
75
|
+
without_tracker = _build_item([_base_row()])
|
|
76
|
+
|
|
77
|
+
assert with_tracker.has_vehicle_tracker is True
|
|
78
|
+
assert with_tracker.vehicle_tracker_company == "TRACKERLOG"
|
|
79
|
+
|
|
80
|
+
assert without_tracker.has_vehicle_tracker is False
|
|
81
|
+
assert without_tracker.vehicle_tracker_company is None
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_alienado_na_is_false():
|
|
85
|
+
item = _build_item([_base_row(ALIENADO="N/A")])
|
|
86
|
+
|
|
87
|
+
assert item.is_alienated is False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{transdesk_importer_python_sdk-1.0.0 → transdesk_importer_python_sdk-1.1.0}/tests/test_validator.py
RENAMED
|
File without changes
|