Spaces:
Sleeping
Sleeping
File size: 7,626 Bytes
55d9355 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | """Saved case-specific reasons and missing documentary evidence for EVO review."""
from copy import deepcopy
from document.mesa_content import content_for
from document.fundamentacao import global_foundation, POINTS, number
from decimal import Decimal, ROUND_HALF_UP
from models.construction_identity import construction_identity
def construction_key(row, index):
return str(row.get('id') or row.get('seq') or index+1)
def public_reason(obj, *keys):
for key in keys:
if obj.get(key + 'Interna') or obj.get(key + '_interna'):
continue
value = obj.get(key)
if isinstance(value, str) and value.strip():
return value.strip()
return ''
def saved_complements(source):
terrain = source.get('terreno') or {}
evaluation = terrain.get('avaliacao') or {}
ca = (evaluation.get('ajuste_territorial') or {}).get('configuracao', {}).get('campo_arbitrio') or {}
manual = terrain.get('fundamentacaoModelo') or {}
construction = source.get('construcao') or {}
global_state = source.get('fundamentacaoGlobal') or {}
result = {
'justificativa_vu': public_reason(ca, 'justificativa') or public_reason(terrain, 'justificativaVu', 'justificativaValorUnitario'),
'justificativa_area': public_reason(terrain, 'justificativaArea', 'areaAplicacaoJustificativa'),
'justificativa_fc': public_reason(global_state, 'justificativa', 'justificativaFc') or public_reason(source, 'justificativaFc', 'fatorComercializacaoJustificativa'),
'ajustes_nao_aplicados': public_reason(source, 'ajustesNaoAplicados'),
'criterio_adocao': public_reason(source, 'criterioAdocao'),
'fc_criterio': global_state.get('fatorComercializacao') or '',
'bdi': {construction_key(row, i): public_reason(row, 'bdiJustificativa', 'justificativaBdi') for i, row in enumerate(construction.get('construcoes') or [])},
'caracterizacao_grau': (manual.get('caracterizacao') or {}).get('grau',''),
'caracterizacao_justificativa': public_reason(manual.get('caracterizacao') or {}, 'justificativa'),
'identificacao_grau': (manual.get('identificacaoDados') or {}).get('grau',''),
'identificacao_justificativa': public_reason(manual.get('identificacaoDados') or {}, 'justificativa'),
'requisitos_grau_iii': manual.get('requisitosGrauIII') or {},
}
# Future EVO files may preserve these exact documentary fields as a package.
saved = source.get('complementosDocumentais') or {}
for key in result:
if saved.get(key): result[key] = saved[key]
return result
def apply_complements(source, values):
source = deepcopy(source)
terrain = source.setdefault('terreno', {})
state = terrain.setdefault('fundamentacaoModelo', {})
for prefix, key in [('caracterizacao','caracterizacao'),('identificacao','identificacaoDados')]:
selected = values.get(prefix+'_grau', '')
if selected and selected not in POINTS:
raise ValueError('Grau de fundamentação inválido.')
state[key] = {'grau':selected,'justificativa':values.get(prefix+'_justificativa','')}
allowed = {item['value'] for item in content_for(source)['criterios_fc']}
if values.get('fc_criterio') and values['fc_criterio'] not in allowed:
raise ValueError('Critério do fator de comercialização inválido.')
reqs = values.get('requisitos_grau_iii') or {}
allowed_reqs = {item['key'] for item in content_for(source)['requisitos_modelo']}
if set(reqs) - allowed_reqs or any(v not in ('','sim','nao',True,False) for v in reqs.values()):
raise ValueError('Requisitos do Grau III inválidos.')
state['requisitosGrauIII'] = reqs
source.setdefault('fundamentacaoGlobal', {})['fatorComercializacao'] = values.get('fc_criterio','')
source['complementosDocumentais'] = deepcopy(values)
return source
def review_for(source, complements):
terrain = source.get('terreno') or {}
area_model = terrain.get('areaModelo', (terrain.get('avaliacao') or {}).get('valor_area'))
area_apply = terrain.get('areaAplicacao', terrain.get('area'))
pending = []
for key, label, required in [
('justificativa_vu','Escolha do valor unitário / campo de arbítrio',True),
('justificativa_area','Diferença entre área do modelo e área de aplicação',number(area_model) != number(area_apply)),
('justificativa_fc','Escolha do fator de comercialização',True),
]:
if required and not complements.get(key): pending.append(label)
constructions = []
construction = source.get('construcao') or {}
for index, row in enumerate(construction.get('construcoes') or []):
key = construction_key(row,index)
name = row.get('nome') or f"Construção {row.get('seq') or index+1} — {row.get('tipoConstrucao') or 'tipo não informado'}"
constructions.append({'id':key,'nome':name,'bdi':row.get('bdi'),'grupo':construction_identity(source,row,index)})
if not complements.get('bdi',{}).get(key): pending.append(f'BDI: {name}')
foundation = global_foundation(source)
for label, item in [('Terreno',foundation['terrain']),('Construções',foundation['construction']),('Global',foundation)]:
if item['grade'] == 'Pendente': pending.append(f'Fundamentação {label.lower()}: há critérios não informados')
return {'pendencias':pending,'construcoes':constructions,'area_modelo':area_model,'area_aplicacao':area_apply,
'conferencias': check_saved_results(source),
'fundamentacao': {k:foundation[k]['grade'] for k in ['terrain','construction']} | {'global':foundation['grade']},
'criterios_fc':content_for(source)['criterios_fc'],'requisitos_modelo':content_for(source)['requisitos_modelo']}
def check_saved_results(source):
"""Report inconsistencies using saved precision; never repair source values."""
warnings = []
result = source.get('resultadoComposicao') or {}
def check(label, expected, actual):
actual = number(actual)
if expected is not None and actual is not None:
expected = expected.quantize(Decimal('.01'),rounding=ROUND_HALF_UP)
if abs(expected-actual) > Decimal('.02'):
warnings.append(f'{label}: os dados salvos não reproduzem o resultado registrado (conferência {expected}; registro {actual}). O resultado de origem foi preservado.')
terrain = source.get('terreno') or {}
vu = number(result.get('valorUnitarioTerreno',result.get('valorCa',terrain.get('valorUnitario'))))
area = number(terrain.get('areaAplicacao',terrain.get('area')))
check('Valor territorial',vu*area if vu is not None and area is not None else None,result.get('valorTerrenoPgv'))
construction_values = []
for i, item in enumerate((source.get('construcao') or {}).get('construcoes') or []):
a,c,bdi,kd = [number(item.get(key)) for key in ['areaCalculada','custoUnitario','bdi','kd']]
check(f"Construção {item.get('seq') or i+1}",a*c*(1+bdi/100)*kd if all(v is not None for v in (a,c,bdi,kd)) else None,item.get('vcalcConstSeq'))
construction_values.append(number(item.get('vcalcConstSeq')))
if construction_values and all(v is not None for v in construction_values):
check('Total das construções',sum(construction_values),result.get('valorConstrucao'))
base,fc = number(result.get('valorAposRedutoresImovel')),number(result.get('fatorFcAplicado'))
check('Aplicação final do Fc',base*fc if base is not None and fc is not None else None,result.get('valorTotalCalculado'))
return warnings
|