File size: 9,986 Bytes
55d9355
 
 
 
 
 
 
af24288
55d9355
 
 
 
 
 
 
 
 
 
 
 
 
af24288
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55d9355
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca18aa5
55d9355
ca18aa5
 
4e531da
55d9355
 
4e531da
55d9355
 
 
 
 
 
 
 
 
ca18aa5
 
 
 
 
 
 
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import copy
import unittest
from decimal import Decimal
from unittest.mock import patch

from docx import Document
from document.evo_adjustments import applied_reducers, visible_memory
from document.evolutivo import gerar_corpo_evolutivo, condition_description, construction_characteristics, construction_groups
from document.avaliacoes_importadas import calculation_sections
from document.numbering import NumeradorSecoes
from test_evolutivo_documento import fixture, resolved, all_text


def body(imported):
    doc=Document(); n=NumeradorSecoes(3); n.subsecao(); n.subsecao()
    with patch('document.evolutivo.matching_local',return_value=(None,'')):
        gerar_corpo_evolutivo(doc,imported,n)
    return doc


class AppliedAdjustmentsTests(unittest.TestCase):
    def test_area_compositions_share_one_table_with_construction_names(self):
        data=fixture((2021,2022))
        for year in data['anos']:
            first=year['construcao']['construcoes'][0]
            first.update(nome='Casa principal',areaTotal=90,areaCalculada=87.5,
                areasEquivalentes=[{'descricao':'área privativa','area':80,'coeficiente':1},
                                   {'descricao':'varandas','area':10,'coeficiente':.75}])
            second=copy.deepcopy(first)
            second.update(id='segundo',seq=2,nome='Edícula de apoio',areaTotal=50,areaCalculada=45,
                areasEquivalentes=[{'descricao':'área privativa','area':40,'coeficiente':1},
                                   {'descricao':'área de serviço','area':10,'coeficiente':.5}])
            year['construcao']['construcoes'].append(second)
        doc=Document()
        construction_characteristics(doc,construction_groups(resolved(data)['avaliacoes']))
        tables=[t for t in doc.tables if t.rows[0].cells[1].text=='Tipo de área']
        self.assertEqual(len(tables),1)
        rows=[[c.text for c in row.cells] for row in tables[0].rows]
        self.assertEqual(rows[1:], [
            ['Construção 1 — Casa principal','área privativa','80,00','1,0000','80,00'],
            ['Construção 1 — Casa principal','varandas','10,00','0,7500','7,50'],
            ['Construção 2 — Edícula de apoio','área privativa','40,00','1,0000','40,00'],
            ['Construção 2 — Edícula de apoio','área de serviço','10,00','0,5000','5,00']])
        self.assertEqual([p.text for p in doc.paragraphs if p.text.startswith('Composição das áreas')],['Composição das áreas'])

    def test_area_changes_preserve_exercise_scope_in_the_unified_table(self):
        data=fixture((2021,2022))
        data['anos'][1]['construcao']['construcoes'][0]['areasEquivalentes'][0]['coeficiente']=.8
        doc=Document()
        construction_characteristics(doc,construction_groups(resolved(data)['avaliacoes']))
        table=next(t for t in doc.tables if t.rows[0].cells[1].text=='Tipo de área')
        self.assertEqual(len(table.rows),3)
        self.assertIn('IPTU 2022',table.cell(1,0).text)
        self.assertIn('IPTU 2023',table.cell(2,0).text)
        self.assertEqual(table.cell(1,4).text,'75,00')
        self.assertEqual(table.cell(2,4).text,'80,00')

    def test_neutral_execution_overrides_selected_conditions_and_reference_rates(self):
        row=resolved(fixture((2021,)))['avaliacoes'][0]
        row['evolutivo']['resultadoComposicao']['redutor88I']=.7
        self.assertEqual(applied_reducers(row,True),[])
        self.assertNotIn('redutores_terreno',[s['codigo'] for s in visible_memory(row)])
        self.assertIn('redutores_imovel',[s['codigo'] for s in visible_memory(row)])

    def test_legacy_unknown_effect_remains_visible_without_inventing_rate(self):
        row={'evolutivo':{'resultadoComposicao':{'memoria':[{'codigo':'redutores_terreno','resultado':10}]}}}
        self.assertEqual(applied_reducers(row,True),[('redutorTerreno','Redução territorial registrada',None)])
        self.assertEqual(len(visible_memory(row)),1)

    def test_component_fallback_uses_effective_saved_memory(self):
        row={'evolutivo':{'resultadoComposicao':{'memoria':[{'codigo':'redutores_terreno','fator':.895,'componentes':[
            {'rotulo':'Art. 88, inciso I','reducao':.105},{'rotulo':'Topografia','reducao':0}]}]}}}
        self.assertEqual(applied_reducers(row,True),[('redutor88I','Art. 88, inciso I',Decimal('.105'))])

    def test_only_effective_reducers_and_years_appear_in_body_and_annex(self):
        data=fixture((2021,2022))
        for year in data['anos']:
            r=year['resultadoComposicao']; r['memoria'][4]['fator']=1
            r.update(redutor88I=0,redutor6061=0,redutorTopografia=0,redutorAgua=0,redutor88II=0,redutorImovel=0,redutorApan=0,limiteArt88Aplicado=False)
        r=data['anos'][0]['resultadoComposicao'];r['redutor88I']=.105;r['memoria'][0].update(fator=.895,componentes=[{'rotulo':'Art. 88, inciso I','reducao':.105},{'rotulo':'Topografia','reducao':0}])
        imported=resolved(data); before=copy.deepcopy(imported)
        doc=body(imported);text=all_text(doc)
        for absent in ['Topografia:','Inundação — aplicado','Arts. 60 e 61:','Limite do art. 88','Art. 88, inciso II','APAN considerada no campo','Componentes de referência da MESA:']:
            self.assertNotIn(absent,text)
        t=next(t for t in doc.tables if any(c.text=='Art. 88, inciso I — redução efetiva' for r in t.rows for c in r.cells))
        reduction=next(r for r in t.rows if r.cells[0].text=='Art. 88, inciso I — redução efetiva')
        self.assertEqual([c.text for c in reduction.cells[1:]],['10,50%','—'])
        ann=Document();calculation_sections(ann,imported)
        self.assertNotIn('Redução do imóvel',all_text(ann));self.assertEqual(imported,before)

    def test_inciso_i_excludes_areas_at_or_below_historical_threshold(self):
        row=resolved(fixture((2021,)))['avaliacoes'][0]
        row['evolutivo']['resultadoComposicao']['memoria'][0]['fator']=.895
        row['evolutivo']['resultadoComposicao']['redutor88I']=.105
        row['evolutivo']['catalogoAjustes']={'art_88_i':{'limite_minimo_area':.1,'condicoes':[
            {'codigo':'app','rotulo':'APP aplicada','redutor':.7},{'codigo':'outra','rotulo':'Área não participante','redutor':.85}]}}
        row['evolutivo']['terreno']['avaliacao']['ajuste_territorial']['configuracao']['art_88_i']={'condicoes':{'app':{'percentual_area':.15},'outra':{'percentual_area':.1}}}
        text=condition_description(row,True)
        self.assertIn('APP aplicada',text);self.assertIn('10,50%',text);self.assertNotIn('Área não participante',text)

    def test_6061_describes_only_maximum_selected_condition(self):
        row={'evolutivo':{'resultadoComposicao':{'redutor6061':.6},'catalogoAjustes':{'art_60_61':{'condicoes':[
            {'codigo':'vila','rotulo':'Vila menor','redutor':.3},{'codigo':'encravado','rotulo':'Encravado aplicado','redutor':.6}]}},
            'terreno':{'avaliacao':{'ajuste_territorial':{'configuracao':{'art_60_61':{'selecionados':['vila','encravado']}}}}}}}
        text=condition_description(row,True)
        self.assertIn('Encravado aplicado',text);self.assertNotIn('Vila menor',text)

    def test_applied_cap_is_documented_with_effective_not_gross_percentage(self):
        imported=resolved(fixture((2021,)));r=imported['avaliacoes'][0]['evolutivo']['resultadoComposicao']
        r.update(redutor88II=.2,redutor88IIBruto=.6,limiteArt88Aplicado=True,valorReferenciaArt88=1000,valorAposIncisoIArt88=187.5,valorAposArt88Bruto=75,valorAposArt88Aplicado=150)
        r['memoria'][4]['fator']=.8
        imported['avaliacoes'][0]['evolutivo']['catalogoAjustes']={'limite_reducao_art_88':.85}
        text=all_text(body(imported))
        self.assertIn('limite conjunto dos incisos I e II do art. 88, previsto em seu § 3º, foi efetivamente acionado',text)
        self.assertIn('20,00%',text);self.assertNotIn('60,00%',text);self.assertIn('R$ 150,00',text)
        self.assertIn('85,00%',text);self.assertIn('15,00%',text)

    def test_composite_cub_and_bdi_tables_deduplicate_across_constructions(self):
        data=fixture((2021,2022));
        for y in data['anos']:
            c=copy.deepcopy(y['construcao']['construcoes'][0]);c.update(id='segundo',seq=2,nome='Galpão B')
            y['construcao']['construcoes'].append(c)
        doc=body(resolved(data))
        weighted=[t for t in doc.tables if t.rows[0].cells[1].text=='Projeto CUB']
        self.assertEqual(len(weighted),1);self.assertEqual(len(weighted[0].rows),3)
        self.assertEqual(weighted[0].cell(1,1).text,'CUB composto')
        self.assertEqual([c.text for c in weighted[0].rows[0].cells][2:],['IPTU 2022','IPTU 2023'])
        calculations=[t for t in doc.tables if any(r.cells[0].text=='Valor da construção (R$)' for r in t.rows)]
        self.assertEqual(len(calculations),2)
        for t in calculations:
            for label in ['Projeto CUB','CUB (R$/m²)','BDI (%)','Fator BDI','Depreciação K','Kd']:
                self.assertTrue(any(r.cells[0].text==label for r in t.rows))

    def test_changes_in_characteristics_and_compositions_keep_applicability(self):
        data=fixture((2021,2022));data['anos'][1]['construcao']['construcoes'][0].update(areaCalculada=80,valorResidual=.2)
        data['anos'][1]['construcao']['construcoes'][0]['cubComposicaoCalculada'][0]['peso']=50
        doc=body(resolved(data))
        t=next(t for t in doc.tables if t.rows[0].cells[1].text=='Tipo / ano')
        self.assertEqual(len(t.rows),3)
        self.assertIn('IPTU 2023',t.rows[2].cells[0].text)
        t=next(t for t in doc.tables if t.rows[0].cells[1].text=='Projeto CUB')
        self.assertEqual(len(t.rows),2)
        from docx.opc.constants import RELATIONSHIP_TYPE as RT
        from docx.oxml import parse_xml
        text=' '.join(parse_xml(doc.part.part_related_by(RT.FOOTNOTES).blob).itertext())
        for expected in ['IPTU 2022','IPTU 2023','60,00%','50,00%']:
            self.assertIn(expected,text)