Skip to content

Commit d6cb92b

Browse files
authored
Merge pull request mapasculturais#3601 from vitfera/feature/opportunity-execution-phase
implementação da fase de execução
2 parents 44c194b + ef3c364 commit d6cb92b

21 files changed

Lines changed: 1597 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
77

88
## [UNRELEASED]
99
### Novas Funcionalidades
10+
- Módulo de **fase de execução** que permite ao gestor configurar uma fase de acompanhamento para os agentes contemplados após a publicação do resultado. Durante esta fase, o agente contemplado pode abrir múltiplos **pedidos de alteração** no projeto aprovado — troca de data, substituição de item de orçamento, mudança de local, entre outros. Cada pedido é avaliado individualmente por uma comissão configurada pelo gestor (mesmo modelo de avaliação simplificada já existente nas fases de seleção). Os pedidos ficam como registro histórico das alterações aprovadas durante a vigência do projeto e não interferem no fluxo das fases seguintes de prestação de informações.
1011
- Implementa configuração que permide definir a imagem de avatar de qualquer entidade como obrigatória
1112
- Adiciona botão para duplicar campos do formulário de inscrição, criando a cópia logo abaixo do campo original
1213
- Adiciona botão para duplicar anexos do formulário de inscrição, incluindo a cópia do arquivo modelo e inserindo o novo anexo logo abaixo do original
1314

1415
### Melhorias
15-
- Adiciona um campo de busca para encontrar colunas por palavra-chave na listagem por tabela nas entidades.
16+
- Adiciona um campo de busca para encontrar colunas por palavra-chave na listagem por tabela nas entidades.
1617
- Implementa a funcionalidade que permite ao saasSuperAdmin ordenar globalmente as colunas das tabelas que utilizam o entity-table, por meio de drag and drop.
1718
- Suprime campo de RG do cadastro do agente e dos campos @ para prevalecer o uso do CIN (Carteira de Identidade Nacional)
1819
- Ordena opções de tipos de campos da lista de campos @ em ordem alfabética
1920
- Ajusta exportação da planilha para organizar as colunas segundo a ordem definida pelo superSaasAdmin
2021
- Melhora a exibição do botão minha conta no header para exibir o nome do perfil do agente responsável logado
22+
- Implementa visualização das datas de recurso no step vertical de fases
2123
- Adiciona configuração para exibir ou ocultar o detalhamento da avaliação anterior na fase de recurso
2224
- Exibe o detalhamento da avaliação da fase anterior para avaliadores da fase de recurso
2325
- Adiciona configuração para exigir foto de perfil do agente coletivo vinculado na inscrição para proponentes do tipo coletivo e pessoa jurídica
@@ -73,11 +75,11 @@ e este projeto adere ao [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
7375
- Evita reprocessar os mesmos campos do formulário de inscrição várias vezes ao montar a página
7476

7577
## [7.7.42] - 2026-05-20
76-
### Correções
78+
### Correções
7779
- Melhora performace do sistema de criaçao de cache de permissão
7880

7981
## [7.7.41] - 2026-05-19
80-
### Correções
82+
### Correções
8183
- Corrige erro que impedia o botão exibir detalhamento de aparecer na tela do avaliador do recurso
8284

8385
## [7.7.40] - 2026-05-15

src/core/Controllers/Registration.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -484,7 +484,15 @@ function GET_view(){
484484
'number' => $entity->number
485485
]);
486486

487-
$app->redirect($parent_registration->singleUrl);
487+
// Fallback para fases sem vínculo por number (ex: fase de execução):
488+
// redireciona para a inscrição linkada via previousPhaseRegistrationId.
489+
if (!$parent_registration && $entity->previousPhaseRegistrationId) {
490+
$parent_registration = $app->repo('Registration')->find($entity->previousPhaseRegistrationId);
491+
}
492+
493+
if ($parent_registration) {
494+
$app->redirect($parent_registration->singleUrl);
495+
}
488496
}
489497
parent::GET_single();
490498
}

src/modules/Entities/components/entity-field-datepicker/script.js

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,15 @@ app.component('entity-field-datepicker', {
127127
},
128128

129129
dateFormat() {
130-
let mcdate = this.entity[this.prop];
130+
let mcdate = this.normalizeMcDate(this.entity[this.prop]);
131131
if (mcdate == null || mcdate == '') {
132132
return '';
133133
}
134134
return mcdate ? mcdate.date('2-digit year') : '';
135135
},
136136

137137
timeFormat() {
138-
let mcdate = this.entity[this.prop];
138+
let mcdate = this.normalizeMcDate(this.entity[this.prop]);
139139
return mcdate ? mcdate?.time('full') : '';
140140
},
141141

@@ -256,12 +256,38 @@ app.component('entity-field-datepicker', {
256256
this.$emit('change', datetime);
257257
},
258258

259+
normalizeMcDate(value) {
260+
if (!value) {
261+
return null;
262+
}
263+
264+
if (value instanceof McDate) {
265+
return value;
266+
}
267+
268+
if (value instanceof Date || typeof value === 'string') {
269+
return new McDate(value);
270+
}
271+
272+
if (value?._date) {
273+
return new McDate(value._date);
274+
}
275+
276+
if (value?.date) {
277+
return new McDate(value.date);
278+
}
279+
280+
return null;
281+
},
282+
259283
initializeModels() {
284+
const mcdate = this.normalizeMcDate(this.entity[this.prop]);
285+
this.entity[this.prop] = mcdate;
260286

261-
this.model = this.entity[this.prop]?._date;
262-
this.modelDate = this.entity[this.prop]?._date;
263-
if (this.entity[this.prop]?.time('full')) {
264-
let time = this.entity[this.prop]?.time('full').split(':');
287+
this.model = mcdate?._date;
288+
this.modelDate = mcdate?._date;
289+
if (typeof mcdate?.time === 'function' && mcdate.time('full')) {
290+
let time = mcdate.time('full').split(':');
265291
this.modelTime = {
266292
hours: time[0],
267293
minutes: time[1],
@@ -271,8 +297,8 @@ app.component('entity-field-datepicker', {
271297
this.modelTime = '';
272298
}
273299

274-
this.timeInput = this.entity[this.prop]?.time('full');
275-
this.dateInput = this.entity[this.prop]?.date('2-digit year');
300+
this.timeInput = typeof mcdate?.time === 'function' ? mcdate.time('full') : '';
301+
this.dateInput = typeof mcdate?.date === 'function' ? mcdate.date('2-digit year') : '';
276302
},
277303
}
278-
});
304+
});

src/modules/Opportunities/components/opportunity-phase-config-status/script.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,27 @@ app.component('opportunity-phase-config-status', {
1919
}
2020
},
2121

22+
watch: {
23+
'phase.id': {
24+
immediate: true,
25+
handler() {
26+
this.refreshStatuses();
27+
}
28+
},
29+
30+
'phase.statusLabels': {
31+
deep: true,
32+
handler() {
33+
this.refreshStatuses();
34+
}
35+
}
36+
},
37+
2238
methods: {
39+
refreshStatuses() {
40+
this.statuses = this.defaultStatuses();
41+
},
42+
2343
updateStatus(status) {
2444
const key = String(status.key);
2545
if (status.enabled) {
@@ -100,8 +120,4 @@ app.component('opportunity-phase-config-status', {
100120
});
101121
}
102122
},
103-
104-
mounted() {
105-
this.statuses = this.defaultStatuses();
106-
},
107123
});

src/modules/Opportunities/components/opportunity-phases-config/script.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ app.component('opportunity-phases-config', {
5353
lastPhaseIndex() {
5454
return this.phases.findLastIndex((phase) => phase.isLastPhase);
5555
},
56+
57+
hasExecutionPhase() {
58+
return this.phases.some(p => p.isExecutionPhase);
59+
},
5660
},
5761

5862
methods: {
@@ -65,13 +69,17 @@ app.component('opportunity-phases-config', {
6569
this.phases.splice(this.phases.length, 0, collectionPhase, evaluationPhase);
6670
},
6771

72+
addExecutionPhases ({ collectionPhase, evaluationPhase }) {
73+
this.phases.splice(this.lastPhaseIndex + 1, 0, collectionPhase, evaluationPhase);
74+
},
75+
6876
showPublishTimestamp(phase) {
6977
const previousPhase = this.getPreviousPhase(phase);
7078
const nextPhase = this.getNextPhase(phase);
7179

7280
if (phase.isLastPhase) {
7381
return true;
74-
} else if (phase.__objectType == 'opportunity' && nextPhase.__objectType != 'evaluationmethodconfiguration' && phase.publishTimestamp) {
82+
} else if (phase.__objectType == 'opportunity' && nextPhase?.__objectType != 'evaluationmethodconfiguration' && phase.publishTimestamp) {
7583
return true;
7684
} else if (phase.__objectType == 'evaluationmethodconfiguration' && previousPhase.__objectType == 'opportunity' && previousPhase.publishTimestamp) {
7785
return true;

src/modules/Opportunities/components/opportunity-phases-config/template.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
mc-stepper-vertical
1212
opportunity-create-data-collect-phase
1313
opportunity-create-evaluation-phase
14+
opportunity-create-execution-phase
1415
opportunity-create-reporting-phase
1516
opportunity-phase-config-data-collection
1617
opportunity-phase-config-evaluation
@@ -28,6 +29,7 @@
2829
<?= i::__('Tipo') ?>:
2930
<template v-if="item.__objectType == 'opportunity' && !item.isLastPhase">
3031
<span v-if="item.isReportingPhase" class="type"><?= i::__('Prestação de informações') ?></span>
32+
<span v-else-if="item.isExecutionPhase" class="type"><?= i::__('Fase de Execução') ?></span>
3133
<span v-else class="type"><?= i::__('Coleta de dados') ?></span>
3234
</template>
3335
<span v-else-if="item.__objectType == 'evaluationmethodconfiguration'" class="type">{{item.type.name}}</span>
@@ -130,6 +132,14 @@
130132

131133
<template v-else-if="index === phases.length - 1">
132134
<div class="add-phase grid-12">
135+
<div class="col-12" v-if="!hasExecutionPhase">
136+
<mc-alert v-if="!firstPhase?.isContinuousFlow && !lastPhase?.publishTimestamp" type="warning">
137+
<p><small class="required"><?= i::__("A data e hora da 'Publicação final' precisa estar preenchida para adicionar a fase de execução.") ?></small></p>
138+
</mc-alert>
139+
140+
<opportunity-create-execution-phase v-if="!firstPhase?.isContinuousFlow && lastPhase?.publishTimestamp" :opportunity="entity" @create="addExecutionPhases"></opportunity-create-execution-phase>
141+
</div>
142+
133143
<div class="col-12" v-if="!finalReportingPhase">
134144
<mc-alert v-if="!firstPhase?.isContinuousFlow && !lastPhase?.publishTimestamp" type="warning">
135145
<p><small class="required"><?= i::__("A data e hora da 'Publicação final' precisa estar preenchida para adicionar novas fases de prestação de informações.") ?></small></p>

src/modules/Opportunities/components/opportunity-phases-timeline/script.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ app.component('opportunity-phases-timeline', {
7878
},
7979

8080
isActive(item, registration) {
81+
if (item.isExecutionPhase) {
82+
return item.registrationFrom?.isPast() && item.registrationTo?.isFuture();
83+
}
84+
85+
if (item.__objectType == 'evaluationmethodconfiguration') {
86+
return item.evaluationFrom?.isPast() && item.evaluationTo?.isFuture();
87+
}
88+
8189
if (!registration) {
8290
return false;
8391
}
@@ -110,6 +118,14 @@ app.component('opportunity-phases-timeline', {
110118
},
111119

112120
itHappened(item, registration) {
121+
if (item.isExecutionPhase) {
122+
return item.registrationTo?.isPast();
123+
}
124+
125+
if (item.__objectType == 'evaluationmethodconfiguration') {
126+
return item.evaluationTo?.isPast();
127+
}
128+
113129
if (!registration) {
114130
return false;
115131
}

src/modules/Opportunities/components/opportunity-phases-timeline/template.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444

4545
<registration-status v-if="shouldShowResults(item, registration)" :registration="registration" :phase="item"></registration-status>
4646

47-
<div v-if="isDataCollectionPhase(item) && isActive(item, registration) && registration.status == 0">
47+
<div v-if="isDataCollectionPhase(item) && !item.isExecutionPhase && isActive(item, registration) && registration.status == 0">
4848
<mc-link :entity="registration" route="edit" class="button button--primary"><?= i::__('Preencher formulário') ?></mc-link>
4949
</div>
5050
<?php $this->applyComponentHook('registration', 'end'); ?>
@@ -55,4 +55,4 @@
5555
</template>
5656
</template>
5757
<?php $this->applyComponentHook('item', 'after'); ?>
58-
</section>
58+
</section>

src/modules/Opportunities/views/registration/single.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,8 @@
344344
<?php endif; ?>
345345
<?php $phase = $phase->nextPhase; ?>
346346
<?php endwhile ?>
347+
348+
<?php $this->applyTemplateHook('registration-ficha-tab', 'end', [$entity]) ?>
347349
</div>
348350
</mc-tab>
349351

0 commit comments

Comments
 (0)