transdesk-importer-python-sdk 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- transdesk/importer/__init__.py +70 -0
- transdesk/importer/client.py +42 -0
- transdesk/importer/exporters/__init__.py +3 -0
- transdesk/importer/exporters/json_exporter.py +25 -0
- transdesk/importer/models/__init__.py +59 -0
- transdesk/importer/models/canonical.py +124 -0
- transdesk/importer/models/internal.py +182 -0
- transdesk/importer/readers/__init__.py +3 -0
- transdesk/importer/readers/excel_reader.py +77 -0
- transdesk/importer/shared/__init__.py +3 -0
- transdesk/importer/shared/data_normalizer.py +47 -0
- transdesk/importer/transformers/__init__.py +0 -0
- transdesk/importer/transformers/canonical/__init__.py +5 -0
- transdesk/importer/transformers/canonical/apolice_builder.py +272 -0
- transdesk/importer/transformers/canonical/cobertura_builder.py +39 -0
- transdesk/importer/transformers/canonical/item_classifier.py +22 -0
- transdesk/importer/transformers/internal/__init__.py +6 -0
- transdesk/importer/transformers/internal/coverage_mapper.py +138 -0
- transdesk/importer/transformers/internal/internal_mapper.py +176 -0
- transdesk/importer/transformers/internal/rcf_parser.py +47 -0
- transdesk/importer/transformers/internal/towing_parser.py +13 -0
- transdesk/importer/validators/__init__.py +3 -0
- transdesk/importer/validators/internal_policy_validator.py +75 -0
- transdesk_importer_python_sdk-1.0.0.dist-info/METADATA +228 -0
- transdesk_importer_python_sdk-1.0.0.dist-info/RECORD +28 -0
- transdesk_importer_python_sdk-1.0.0.dist-info/WHEEL +5 -0
- transdesk_importer_python_sdk-1.0.0.dist-info/licenses/LICENSE +21 -0
- transdesk_importer_python_sdk-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
|
|
3
|
+
from transdesk.importer.models.canonical import (
|
|
4
|
+
CanonicalItem,
|
|
5
|
+
CanonicalPolicy,
|
|
6
|
+
Cliente,
|
|
7
|
+
Complemento,
|
|
8
|
+
DadosBancarios,
|
|
9
|
+
Endereco,
|
|
10
|
+
ReboqueRisco,
|
|
11
|
+
Subestipulante,
|
|
12
|
+
UnidadeVenda,
|
|
13
|
+
VeiculoRisco,
|
|
14
|
+
VidaRisco,
|
|
15
|
+
)
|
|
16
|
+
from transdesk.importer.shared.data_normalizer import DataNormalizer
|
|
17
|
+
from transdesk.importer.transformers.canonical.item_classifier import ItemClassifier
|
|
18
|
+
from transdesk.importer.transformers.canonical.cobertura_builder import CoberturaBuilder
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ApoliceBuilder:
|
|
22
|
+
|
|
23
|
+
def build(self, df):
|
|
24
|
+
grouped = self.group_by_apolice(df)
|
|
25
|
+
|
|
26
|
+
return [
|
|
27
|
+
self.build_apolice(cod_apolice, rows)
|
|
28
|
+
for cod_apolice, rows in grouped.items()
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
def group_by_apolice(self, df):
|
|
32
|
+
grouped = defaultdict(list)
|
|
33
|
+
|
|
34
|
+
for _, row in df.iterrows():
|
|
35
|
+
grouped[str(row["COD_AGRUPADOR_APOLICE"])].append(row)
|
|
36
|
+
|
|
37
|
+
return grouped
|
|
38
|
+
|
|
39
|
+
def build_apolice(self, cod_apolice, rows):
|
|
40
|
+
first_row = rows[0]
|
|
41
|
+
return CanonicalPolicy(
|
|
42
|
+
cod_agrupador_apolice=int(cod_apolice),
|
|
43
|
+
dia_vencimento=DataNormalizer.to_int(first_row.get("DIA_VCTO")),
|
|
44
|
+
subestipulante=self.build_subestipulante(first_row),
|
|
45
|
+
cliente=self.build_cliente(first_row),
|
|
46
|
+
unidade_venda=self.build_unidade_venda(first_row),
|
|
47
|
+
itens=self.build_itens(rows),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------
|
|
51
|
+
# CLIENTE
|
|
52
|
+
# ---------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
def build_cliente(self, row):
|
|
55
|
+
return Cliente(
|
|
56
|
+
matricula=row.get("MATRICULA_CLIENTE"),
|
|
57
|
+
tipo_pessoa=row.get("PESSOA"),
|
|
58
|
+
documento=DataNormalizer.clean_doc(row.get("CNPJ_CPF_CLIENTE")),
|
|
59
|
+
nome_fantasia=row.get("NOME_FANTASIA_CLIENTE"),
|
|
60
|
+
razao_social=row.get("RAZAO_SOCIAL_CLIENTE"),
|
|
61
|
+
telefones=[
|
|
62
|
+
row.get("FONE1_CLIENTE"),
|
|
63
|
+
row.get("FONE2_CLIENTE"),
|
|
64
|
+
],
|
|
65
|
+
emails=[
|
|
66
|
+
row.get("EMAIL_CLIENTE"),
|
|
67
|
+
],
|
|
68
|
+
endereco=Endereco(
|
|
69
|
+
cep=row.get("CEP_CLIENTE"),
|
|
70
|
+
logradouro=row.get("ENDERECO_CLIENTE"),
|
|
71
|
+
numero=row.get("NUMERO_ENDERECO_CLIENTE"),
|
|
72
|
+
complemento=row.get("COMPLEMENTO_ENDERECO_CLIENTE"),
|
|
73
|
+
bairro=row.get("BAIRRO_ENDERECO_CLIENTE"),
|
|
74
|
+
cidade=row.get("CIDADE_ENDERECO_CLIENTE"),
|
|
75
|
+
uf=row.get("UF_ENDERECO_CLIENTE"),
|
|
76
|
+
),
|
|
77
|
+
dados_bancarios=DadosBancarios(
|
|
78
|
+
banco=row.get("BANCO"),
|
|
79
|
+
agencia=row.get("AGENCIA"),
|
|
80
|
+
conta=row.get("CONTA"),
|
|
81
|
+
chave_pix=row.get("CHAVE_PIX"),
|
|
82
|
+
titular=row.get("TITULAR"),
|
|
83
|
+
documento_titular=DataNormalizer.clean_doc(row.get("CNPJ_CPF_TITULAR")),
|
|
84
|
+
),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# ---------------------------------------------------------
|
|
88
|
+
# SUBESTIPULANTE
|
|
89
|
+
# ---------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
def build_subestipulante(self, row):
|
|
92
|
+
return Subestipulante(
|
|
93
|
+
id_unity=row.get("ID_UNITY"),
|
|
94
|
+
documento=DataNormalizer.clean_doc(row.get("SUB_ESTIPULANTE_CNPJ")),
|
|
95
|
+
nome_fantasia=row.get("SUB_ESTIPULANTE_FANTASIA"),
|
|
96
|
+
razao_social=row.get("SUB_ESTIPULANTE_RAZAO_SOCIAL"),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# ---------------------------------------------------------
|
|
100
|
+
# UNIDADE VENDA
|
|
101
|
+
# ---------------------------------------------------------
|
|
102
|
+
|
|
103
|
+
def build_unidade_venda(self, row):
|
|
104
|
+
return UnidadeVenda(
|
|
105
|
+
documento=DataNormalizer.clean_doc(row.get("CNPJ_CPF_UNIDADE_VENDA")),
|
|
106
|
+
nome_fantasia=row.get("NOME_FANTASIA_UNIDADE_VENDA"),
|
|
107
|
+
razao_social=row.get("RAZAO_SOCIAL_UNIDADE_VENDA"),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# ---------------------------------------------------------
|
|
111
|
+
# ITENS
|
|
112
|
+
# ---------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
def build_itens(self, rows):
|
|
115
|
+
grouped = defaultdict(list)
|
|
116
|
+
|
|
117
|
+
for row in rows:
|
|
118
|
+
key = self.build_item_key(row)
|
|
119
|
+
grouped[key].append(row)
|
|
120
|
+
|
|
121
|
+
return [
|
|
122
|
+
self.build_item(item_rows)
|
|
123
|
+
for item_rows in grouped.values()
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
def build_item_key(self, row):
|
|
127
|
+
if ItemClassifier.is_vida(row):
|
|
128
|
+
return (
|
|
129
|
+
f"VIDA|"
|
|
130
|
+
f"{row.get('CPF_SEGURADO_SEGURO_VIDA')}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if ItemClassifier.is_reboque(row):
|
|
134
|
+
return (
|
|
135
|
+
f"REBOQUE|"
|
|
136
|
+
f"{row.get('PLACA_VEICULO')}|"
|
|
137
|
+
f"{row.get('CHASSI')}|"
|
|
138
|
+
f"{row.get('MARCA_REBOQUE')}|"
|
|
139
|
+
f"{row.get('MODELO_REBOQUE')}"
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
f"VEICULO|"
|
|
144
|
+
f"{row.get('PLACA_VEICULO')}|"
|
|
145
|
+
f"{row.get('CHASSI')}"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
def build_item(self, rows):
|
|
149
|
+
row = rows[0]
|
|
150
|
+
|
|
151
|
+
if ItemClassifier.is_vida(row):
|
|
152
|
+
return self.build_vida(rows)
|
|
153
|
+
|
|
154
|
+
if ItemClassifier.is_reboque(row):
|
|
155
|
+
return self.build_reboque(rows)
|
|
156
|
+
|
|
157
|
+
return self.build_veiculo(rows)
|
|
158
|
+
|
|
159
|
+
# ---------------------------------------------------------
|
|
160
|
+
# VIDA
|
|
161
|
+
# ---------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
def build_vida(self, rows):
|
|
164
|
+
row = rows[0]
|
|
165
|
+
|
|
166
|
+
return CanonicalItem(
|
|
167
|
+
tipo_item="vida",
|
|
168
|
+
dados_risco=VidaRisco(
|
|
169
|
+
nome=row.get("NOME_SEGURADO_SEGURO_VIDA"),
|
|
170
|
+
cpf=DataNormalizer.clean_doc(row.get("CPF_SEGURADO_SEGURO_VIDA")),
|
|
171
|
+
rg=row.get("RG_SEGURADO_SEGURO_VIDA"),
|
|
172
|
+
data_nascimento=row.get("DATE_BIRTH_SEGURADO_SEGURO_VIDA"),
|
|
173
|
+
),
|
|
174
|
+
coberturas=CoberturaBuilder(rows).build(),
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
# ---------------------------------------------------------
|
|
178
|
+
# REBOQUE
|
|
179
|
+
# ---------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
def build_reboque(self, rows):
|
|
182
|
+
row = rows[0]
|
|
183
|
+
|
|
184
|
+
return CanonicalItem(
|
|
185
|
+
tipo_item="reboque",
|
|
186
|
+
dados_risco=ReboqueRisco(
|
|
187
|
+
placa=row.get("PLACA_VEICULO"),
|
|
188
|
+
chassi=row.get("CHASSI"),
|
|
189
|
+
marca=row.get("MARCA_REBOQUE"),
|
|
190
|
+
modelo=row.get("MODELO_REBOQUE"),
|
|
191
|
+
),
|
|
192
|
+
coberturas=CoberturaBuilder(rows).build(),
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# ---------------------------------------------------------
|
|
196
|
+
# VEICULO
|
|
197
|
+
# ---------------------------------------------------------
|
|
198
|
+
|
|
199
|
+
def build_veiculo(self, rows):
|
|
200
|
+
row = rows[0]
|
|
201
|
+
|
|
202
|
+
item = CanonicalItem(
|
|
203
|
+
tipo_item="veiculo",
|
|
204
|
+
dados_risco=VeiculoRisco(
|
|
205
|
+
placa=row.get("PLACA_VEICULO"),
|
|
206
|
+
chassi=row.get("CHASSI"),
|
|
207
|
+
codigo_fipe=row.get("CODE_FIPE"),
|
|
208
|
+
),
|
|
209
|
+
complementos=self.build_complementos(rows),
|
|
210
|
+
coberturas=CoberturaBuilder(rows).build(),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
self.merge_complement_coverages(item)
|
|
214
|
+
|
|
215
|
+
return item
|
|
216
|
+
|
|
217
|
+
# ---------------------------------------------------------
|
|
218
|
+
# COMPLEMENTOS
|
|
219
|
+
# ---------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
def build_complementos(self, rows):
|
|
222
|
+
complementos = {}
|
|
223
|
+
|
|
224
|
+
for row in rows:
|
|
225
|
+
descricao = row.get("COMPLEMENT_DESCRIPTION")
|
|
226
|
+
|
|
227
|
+
if not descricao:
|
|
228
|
+
continue
|
|
229
|
+
|
|
230
|
+
importancia = DataNormalizer.to_float(row.get("IMPORTANCIA_SEGURADA"))
|
|
231
|
+
|
|
232
|
+
key = (descricao, importancia)
|
|
233
|
+
|
|
234
|
+
if key not in complementos:
|
|
235
|
+
complementos[key] = Complemento(
|
|
236
|
+
descricao=descricao,
|
|
237
|
+
franquia=DataNormalizer.to_float(row.get("FRANQUIA")),
|
|
238
|
+
importancia_segurada=importancia,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
return list(complementos.values())
|
|
242
|
+
|
|
243
|
+
# ---------------------------------------------------------
|
|
244
|
+
# COMPLEMENTO -> VEICULO
|
|
245
|
+
# ---------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
def merge_complement_coverages(self, item):
|
|
248
|
+
casco_extra = 0
|
|
249
|
+
reparo_extra = 0
|
|
250
|
+
|
|
251
|
+
novas = []
|
|
252
|
+
|
|
253
|
+
for c in item.coberturas:
|
|
254
|
+
nome = c.nome
|
|
255
|
+
|
|
256
|
+
if nome == "Casco PT (COMPLEMENTO)":
|
|
257
|
+
casco_extra += (c.valor_mensalidade or 0)
|
|
258
|
+
continue
|
|
259
|
+
|
|
260
|
+
if nome == "Assistência a Reparos (COMPLEMENTO)":
|
|
261
|
+
reparo_extra += (c.valor_mensalidade or 0)
|
|
262
|
+
continue
|
|
263
|
+
|
|
264
|
+
novas.append(c)
|
|
265
|
+
|
|
266
|
+
for c in novas:
|
|
267
|
+
if c.nome == "Casco PT (VEÍCULO)":
|
|
268
|
+
c.valor_mensalidade = ((c.valor_mensalidade or 0) + casco_extra)
|
|
269
|
+
elif c.nome == "Assistência a Reparos (VEÍCULO)":
|
|
270
|
+
c.valor_mensalidade = ((c.valor_mensalidade or 0) + reparo_extra)
|
|
271
|
+
|
|
272
|
+
item.coberturas = novas
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from transdesk.importer.models.canonical import Cobertura
|
|
2
|
+
from transdesk.importer.shared.data_normalizer import DataNormalizer
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CoberturaBuilder:
|
|
6
|
+
|
|
7
|
+
def __init__(self, rows):
|
|
8
|
+
self.rows = rows
|
|
9
|
+
|
|
10
|
+
def build_key(self, row):
|
|
11
|
+
return "|".join([
|
|
12
|
+
row.get("NME_BENEFICIO", ""),
|
|
13
|
+
row.get("PRODUTO", ""),
|
|
14
|
+
row.get("NME_CATEGORIA", ""),
|
|
15
|
+
row.get("TIPO_CATEGORIA", ""),
|
|
16
|
+
])
|
|
17
|
+
|
|
18
|
+
def build(self):
|
|
19
|
+
coverages = {}
|
|
20
|
+
|
|
21
|
+
for row in self.rows:
|
|
22
|
+
key = self.build_key(row)
|
|
23
|
+
|
|
24
|
+
if key not in coverages:
|
|
25
|
+
coverages[key] = Cobertura(
|
|
26
|
+
nome=row.get("NME_BENEFICIO"),
|
|
27
|
+
tipo=row.get("TIPO"),
|
|
28
|
+
combo=row.get("COMBO"),
|
|
29
|
+
produto=row.get("PRODUTO"),
|
|
30
|
+
categoria=row.get("NME_CATEGORIA"),
|
|
31
|
+
tipo_categoria=row.get("TIPO_CATEGORIA"),
|
|
32
|
+
codigo_tabela=row.get("TABELA_PRECO"),
|
|
33
|
+
franquia=DataNormalizer.to_float(row.get("FRANQUIA")),
|
|
34
|
+
importancia_segurada=DataNormalizer.to_float(row.get("IMPORTANCIA_SEGURADA")),
|
|
35
|
+
valor_mensalidade=DataNormalizer.to_float(row.get("VALOR_MENSALIDADE")),
|
|
36
|
+
observacao=row.get("OBS"),
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return list(coverages.values())
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class ItemClassifier:
|
|
2
|
+
|
|
3
|
+
@staticmethod
|
|
4
|
+
def is_vida(row):
|
|
5
|
+
return bool(
|
|
6
|
+
row.get("NOME_SEGURADO_SEGURO_VIDA")
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
@staticmethod
|
|
10
|
+
def is_reboque(row):
|
|
11
|
+
return (
|
|
12
|
+
bool(row.get("PLACA_VEICULO"))
|
|
13
|
+
and bool(row.get("CHASSI"))
|
|
14
|
+
and bool(row.get("MARCA_REBOQUE"))
|
|
15
|
+
and bool(row.get("MODELO_REBOQUE"))
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def is_complemento(row):
|
|
20
|
+
return bool(
|
|
21
|
+
row.get("COMPLEMENT_DESCRIPTION")
|
|
22
|
+
)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from transdesk.importer.models.internal import InternalItem
|
|
2
|
+
from transdesk.importer.transformers.internal.rcf_parser import RCFParser
|
|
3
|
+
from transdesk.importer.transformers.internal.towing_parser import TowingParser
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CoverageMapper:
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.rcf_parser = RCFParser()
|
|
10
|
+
self.towing_parser = TowingParser()
|
|
11
|
+
|
|
12
|
+
def apply_all(self, item: InternalItem, coberturas):
|
|
13
|
+
for coverage in coberturas:
|
|
14
|
+
self.apply_coverage(item, coverage)
|
|
15
|
+
|
|
16
|
+
return item
|
|
17
|
+
|
|
18
|
+
def apply_coverage(self, item: InternalItem, coverage):
|
|
19
|
+
name = coverage.nome
|
|
20
|
+
|
|
21
|
+
if coverage.combo:
|
|
22
|
+
item.group_name = coverage.combo
|
|
23
|
+
|
|
24
|
+
if name in (
|
|
25
|
+
"Casco PT (VEÍCULO)",
|
|
26
|
+
"Casco PT (R/SR)",
|
|
27
|
+
):
|
|
28
|
+
self._apply_casco(item, coverage)
|
|
29
|
+
|
|
30
|
+
elif name == "Casco PT (COMPLEMENTO)":
|
|
31
|
+
self._apply_body(item, coverage)
|
|
32
|
+
|
|
33
|
+
elif name in (
|
|
34
|
+
"Assistência a Reparos (VEÍCULO)",
|
|
35
|
+
"Assistência a Reparos (R/SR)",
|
|
36
|
+
):
|
|
37
|
+
self._apply_repair(item, coverage)
|
|
38
|
+
|
|
39
|
+
elif name == "Assistência a Reparos (COMPLEMENTO)":
|
|
40
|
+
self._apply_body(item, coverage)
|
|
41
|
+
|
|
42
|
+
elif name == "RCF":
|
|
43
|
+
self._apply_rcf(item, coverage)
|
|
44
|
+
|
|
45
|
+
elif name == "APP":
|
|
46
|
+
self._apply_app(item, coverage)
|
|
47
|
+
|
|
48
|
+
elif name in (
|
|
49
|
+
"Assistência 24 Horas",
|
|
50
|
+
"Assistência 24 horas - AVULSO",
|
|
51
|
+
):
|
|
52
|
+
self._apply_towing(item, coverage)
|
|
53
|
+
|
|
54
|
+
elif name == "Assistência a Vidros":
|
|
55
|
+
self._apply_glass(item, coverage)
|
|
56
|
+
|
|
57
|
+
elif name in (
|
|
58
|
+
"Rastreador p/ Veículo",
|
|
59
|
+
"Rastreador p/ Reboque",
|
|
60
|
+
"SUPORTE EMERGENCIAL MONITORADO",
|
|
61
|
+
):
|
|
62
|
+
self._apply_tracker(item, coverage)
|
|
63
|
+
|
|
64
|
+
elif name == "Assistência Jurídica":
|
|
65
|
+
self._apply_legal_assistance(item, coverage)
|
|
66
|
+
|
|
67
|
+
def _apply_casco(self, item, coverage):
|
|
68
|
+
item.has_casco_cir = True
|
|
69
|
+
item.hull_value += (coverage.importancia_segurada or 0)
|
|
70
|
+
item.hull_premium += (coverage.valor_mensalidade or 0)
|
|
71
|
+
|
|
72
|
+
def _apply_repair(self, item, coverage):
|
|
73
|
+
item.has_repair_assistance = True
|
|
74
|
+
item.repair_assistance_value += (coverage.importancia_segurada or 0)
|
|
75
|
+
item.repair_assistance_premium += (coverage.valor_mensalidade or 0)
|
|
76
|
+
|
|
77
|
+
def _apply_body(self, item, coverage):
|
|
78
|
+
item.body_value += (coverage.importancia_segurada or 0)
|
|
79
|
+
item.body_premium += (coverage.valor_mensalidade or 0)
|
|
80
|
+
|
|
81
|
+
def _apply_app(self, item, coverage):
|
|
82
|
+
item.has_app = True
|
|
83
|
+
item.personal_accident_coverage_value = (coverage.importancia_segurada or 0)
|
|
84
|
+
item.personal_accident_coverage_premium = (coverage.valor_mensalidade or 0)
|
|
85
|
+
|
|
86
|
+
def _apply_legal_assistance(self, item, coverage):
|
|
87
|
+
item.has_legal_assistance = True
|
|
88
|
+
item.legal_assistance_value = 1
|
|
89
|
+
item.legal_assistance_premium += (coverage.valor_mensalidade or 0)
|
|
90
|
+
|
|
91
|
+
def _apply_tracker(self, item, coverage):
|
|
92
|
+
item.has_vehicle_tracker = True
|
|
93
|
+
item.vehicle_tracker_premium += (coverage.valor_mensalidade or 0)
|
|
94
|
+
|
|
95
|
+
def _apply_glass(self, item, coverage):
|
|
96
|
+
item.glass_coverage_type = "completo"
|
|
97
|
+
item.glass_premium += (coverage.valor_mensalidade or 0)
|
|
98
|
+
|
|
99
|
+
def _apply_rcf(self, item, coverage):
|
|
100
|
+
item.has_rcf = True
|
|
101
|
+
|
|
102
|
+
parsed = self.rcf_parser.parse(coverage.produto)
|
|
103
|
+
|
|
104
|
+
premiums = self._allocate_premium(
|
|
105
|
+
coverage.valor_mensalidade or 0,
|
|
106
|
+
{
|
|
107
|
+
"property_damage": parsed["property_damage_value"],
|
|
108
|
+
"bodily_injury": parsed["bodily_injury_value"],
|
|
109
|
+
"moral_damage": parsed["moral_damage_value"],
|
|
110
|
+
},
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
item.property_damage_value += (parsed["property_damage_value"])
|
|
114
|
+
item.property_damage_premium += (premiums["property_damage"])
|
|
115
|
+
|
|
116
|
+
item.bodily_injury_value += (parsed["bodily_injury_value"])
|
|
117
|
+
item.bodily_injury_premium += (premiums["bodily_injury"])
|
|
118
|
+
|
|
119
|
+
item.moral_damage_value += (parsed["moral_damage_value"])
|
|
120
|
+
item.moral_damage_premium += (premiums["moral_damage"])
|
|
121
|
+
|
|
122
|
+
def _apply_towing(self, item, coverage):
|
|
123
|
+
km = self.towing_parser.parse(coverage.produto)
|
|
124
|
+
|
|
125
|
+
item.towing_km += km
|
|
126
|
+
item.towing_premium += (coverage.valor_mensalidade or 0)
|
|
127
|
+
|
|
128
|
+
def _allocate_premium(self, total_premium, values):
|
|
129
|
+
total_premium = total_premium or 0
|
|
130
|
+
total = sum(values.values())
|
|
131
|
+
|
|
132
|
+
if total == 0:
|
|
133
|
+
return {key: 0 for key in values}
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
key: (total_premium * value / total)
|
|
137
|
+
for key, value in values.items()
|
|
138
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from transdesk.importer.models.internal import (
|
|
2
|
+
Address,
|
|
3
|
+
Broker,
|
|
4
|
+
CustomerInfo,
|
|
5
|
+
InternalItem,
|
|
6
|
+
InternalPolicy,
|
|
7
|
+
Lead,
|
|
8
|
+
Mobile,
|
|
9
|
+
PolicyData,
|
|
10
|
+
ResellerData,
|
|
11
|
+
Telephone,
|
|
12
|
+
)
|
|
13
|
+
from transdesk.importer.transformers.internal.coverage_mapper import CoverageMapper
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class InternalMapper:
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self.coverage_mapper = CoverageMapper()
|
|
20
|
+
|
|
21
|
+
def map(self, policy) -> InternalPolicy:
|
|
22
|
+
return InternalPolicy(
|
|
23
|
+
reference_id=str(policy.cod_agrupador_apolice),
|
|
24
|
+
payment_due_date=policy.dia_vencimento,
|
|
25
|
+
data=PolicyData(
|
|
26
|
+
reseller=ResellerData(broker=self.build_broker(policy)),
|
|
27
|
+
lead=self.build_lead(policy),
|
|
28
|
+
items=self.build_items(policy),
|
|
29
|
+
),
|
|
30
|
+
original_data=policy,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def build_broker(self, policy) -> Broker:
|
|
34
|
+
broker = policy.subestipulante
|
|
35
|
+
|
|
36
|
+
return Broker(
|
|
37
|
+
internal_code=broker.id_unity,
|
|
38
|
+
document_number=broker.documento,
|
|
39
|
+
principal_name=broker.nome_fantasia,
|
|
40
|
+
secondary_name=broker.razao_social,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def build_lead(self, policy) -> Lead:
|
|
44
|
+
customer = policy.cliente
|
|
45
|
+
|
|
46
|
+
email = next((e for e in customer.emails if e), None)
|
|
47
|
+
|
|
48
|
+
return Lead(
|
|
49
|
+
customer_info=CustomerInfo(
|
|
50
|
+
cnpj=customer.documento,
|
|
51
|
+
principal_name=(customer.razao_social or customer.nome_fantasia),
|
|
52
|
+
secondary_name="",
|
|
53
|
+
email_address=email,
|
|
54
|
+
),
|
|
55
|
+
telphone_info=self._build_phone(customer),
|
|
56
|
+
celphone_info=self._build_mobile(customer),
|
|
57
|
+
address_info=self._build_address(customer),
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def build_items(self, policy):
|
|
61
|
+
return [
|
|
62
|
+
self.map_item(item, index)
|
|
63
|
+
for index, item in enumerate(policy.itens, start=1)
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
def _build_phone(self, customer):
|
|
67
|
+
for phone in customer.telefones:
|
|
68
|
+
if not phone:
|
|
69
|
+
continue
|
|
70
|
+
|
|
71
|
+
digits = "".join(filter(str.isdigit, phone))
|
|
72
|
+
|
|
73
|
+
if len(digits) < 10:
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
numero = digits[2:]
|
|
77
|
+
|
|
78
|
+
if not numero.startswith("9"):
|
|
79
|
+
return Telephone(
|
|
80
|
+
telphone_type_code="03",
|
|
81
|
+
ddd=digits[:2],
|
|
82
|
+
number=numero,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return None
|
|
86
|
+
|
|
87
|
+
def _build_mobile(self, customer):
|
|
88
|
+
for phone in customer.telefones:
|
|
89
|
+
if not phone:
|
|
90
|
+
continue
|
|
91
|
+
|
|
92
|
+
digits = "".join(filter(str.isdigit, phone))
|
|
93
|
+
|
|
94
|
+
if len(digits) < 10:
|
|
95
|
+
continue
|
|
96
|
+
|
|
97
|
+
numero = digits[2:]
|
|
98
|
+
|
|
99
|
+
if numero.startswith("9"):
|
|
100
|
+
return Mobile(
|
|
101
|
+
telphone_type_code="02",
|
|
102
|
+
ddd=digits[:2],
|
|
103
|
+
number=numero,
|
|
104
|
+
allow_sms=True,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
def _build_address(self, customer):
|
|
110
|
+
address = customer.endereco
|
|
111
|
+
|
|
112
|
+
return Address(
|
|
113
|
+
type_code="02",
|
|
114
|
+
cep=address.cep,
|
|
115
|
+
name=address.logradouro,
|
|
116
|
+
number=address.numero,
|
|
117
|
+
complement=address.complemento,
|
|
118
|
+
district=address.bairro,
|
|
119
|
+
city=address.cidade,
|
|
120
|
+
acronym_federal_unit=address.uf,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def map_item(self, item, item_number) -> InternalItem:
|
|
124
|
+
internal_item = InternalItem(
|
|
125
|
+
item_number=item_number,
|
|
126
|
+
item_type=item.tipo_item,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
self.coverage_mapper.apply_all(internal_item, item.coberturas)
|
|
130
|
+
|
|
131
|
+
if item.tipo_item == "vida":
|
|
132
|
+
self._map_life(internal_item, item)
|
|
133
|
+
else:
|
|
134
|
+
self._map_vehicle(internal_item, item)
|
|
135
|
+
|
|
136
|
+
return internal_item
|
|
137
|
+
|
|
138
|
+
def _map_life(self, internal_item, item):
|
|
139
|
+
risco = item.dados_risco
|
|
140
|
+
|
|
141
|
+
internal_item.name = risco.nome
|
|
142
|
+
internal_item.cpf = risco.cpf
|
|
143
|
+
internal_item.rg = risco.rg
|
|
144
|
+
internal_item.birth_date = risco.data_nascimento
|
|
145
|
+
|
|
146
|
+
def _map_vehicle(self, internal_item, item):
|
|
147
|
+
risco = item.dados_risco
|
|
148
|
+
|
|
149
|
+
internal_item.license_plate = getattr(risco, "placa", None)
|
|
150
|
+
internal_item.chassi_number = getattr(risco, "chassi", None)
|
|
151
|
+
internal_item.fipe_code = getattr(risco, "codigo_fipe", None)
|
|
152
|
+
internal_item.body_description = self._build_body_description(item)
|
|
153
|
+
|
|
154
|
+
if item.tipo_item == "reboque":
|
|
155
|
+
internal_item.brand = getattr(risco, "marca", None)
|
|
156
|
+
internal_item.model = getattr(risco, "modelo", None)
|
|
157
|
+
|
|
158
|
+
def _build_body_description(self, item):
|
|
159
|
+
parts = []
|
|
160
|
+
|
|
161
|
+
for comp in (item.complementos or []):
|
|
162
|
+
amount = comp.importancia_segurada or 0
|
|
163
|
+
parts.append(
|
|
164
|
+
f"{comp.descricao} "
|
|
165
|
+
f"(R$ {self._format_currency(amount)})"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return " | ".join(parts)
|
|
169
|
+
|
|
170
|
+
def _format_currency(self, value):
|
|
171
|
+
return (
|
|
172
|
+
f"{value:,.2f}"
|
|
173
|
+
.replace(",", "_")
|
|
174
|
+
.replace(".", ",")
|
|
175
|
+
.replace("_", ".")
|
|
176
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RCFParser:
|
|
5
|
+
|
|
6
|
+
def parse(self, produto):
|
|
7
|
+
produto = (produto or "").upper()
|
|
8
|
+
|
|
9
|
+
dma_dc = self.extract_dma_dc(produto)
|
|
10
|
+
dmo = self.extract_dmo(produto)
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
"property_damage_value": dma_dc,
|
|
14
|
+
"bodily_injury_value": dma_dc,
|
|
15
|
+
"moral_damage_value": dmo,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def extract_dma_dc(self, text):
|
|
19
|
+
match = re.search(r"DMA E DC\s+([\d,]+)\s*(MILHÃO|MIL)?", text)
|
|
20
|
+
|
|
21
|
+
if not match:
|
|
22
|
+
return 0
|
|
23
|
+
|
|
24
|
+
amount = float(match.group(1).replace(",", "."))
|
|
25
|
+
|
|
26
|
+
unit = match.group(2)
|
|
27
|
+
|
|
28
|
+
if unit == "MIL":
|
|
29
|
+
return int(amount * 1000)
|
|
30
|
+
|
|
31
|
+
if unit == "MILHÃO":
|
|
32
|
+
return int(amount * 1_000_000)
|
|
33
|
+
|
|
34
|
+
return int(amount)
|
|
35
|
+
|
|
36
|
+
def extract_dmo(self, text):
|
|
37
|
+
match = re.search(r"DMO\s+([\d,]+)\s*(MIL)?", text)
|
|
38
|
+
|
|
39
|
+
if not match:
|
|
40
|
+
return 0
|
|
41
|
+
|
|
42
|
+
amount = float(match.group(1).replace(",", "."))
|
|
43
|
+
|
|
44
|
+
if match.group(2) == "MIL":
|
|
45
|
+
return int(amount * 1000)
|
|
46
|
+
|
|
47
|
+
return int(amount)
|