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.
Files changed (28) hide show
  1. transdesk/importer/__init__.py +70 -0
  2. transdesk/importer/client.py +42 -0
  3. transdesk/importer/exporters/__init__.py +3 -0
  4. transdesk/importer/exporters/json_exporter.py +25 -0
  5. transdesk/importer/models/__init__.py +59 -0
  6. transdesk/importer/models/canonical.py +124 -0
  7. transdesk/importer/models/internal.py +182 -0
  8. transdesk/importer/readers/__init__.py +3 -0
  9. transdesk/importer/readers/excel_reader.py +77 -0
  10. transdesk/importer/shared/__init__.py +3 -0
  11. transdesk/importer/shared/data_normalizer.py +47 -0
  12. transdesk/importer/transformers/__init__.py +0 -0
  13. transdesk/importer/transformers/canonical/__init__.py +5 -0
  14. transdesk/importer/transformers/canonical/apolice_builder.py +272 -0
  15. transdesk/importer/transformers/canonical/cobertura_builder.py +39 -0
  16. transdesk/importer/transformers/canonical/item_classifier.py +22 -0
  17. transdesk/importer/transformers/internal/__init__.py +6 -0
  18. transdesk/importer/transformers/internal/coverage_mapper.py +138 -0
  19. transdesk/importer/transformers/internal/internal_mapper.py +176 -0
  20. transdesk/importer/transformers/internal/rcf_parser.py +47 -0
  21. transdesk/importer/transformers/internal/towing_parser.py +13 -0
  22. transdesk/importer/validators/__init__.py +3 -0
  23. transdesk/importer/validators/internal_policy_validator.py +75 -0
  24. transdesk_importer_python_sdk-1.0.0.dist-info/METADATA +228 -0
  25. transdesk_importer_python_sdk-1.0.0.dist-info/RECORD +28 -0
  26. transdesk_importer_python_sdk-1.0.0.dist-info/WHEEL +5 -0
  27. transdesk_importer_python_sdk-1.0.0.dist-info/licenses/LICENSE +21 -0
  28. 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,6 @@
1
+ from .coverage_mapper import CoverageMapper
2
+ from .internal_mapper import InternalMapper
3
+ from .rcf_parser import RCFParser
4
+ from .towing_parser import TowingParser
5
+
6
+ __all__ = ["CoverageMapper", "InternalMapper", "RCFParser", "TowingParser"]
@@ -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)