diff --git a/src/modules/OpportunityWorkplan/Entities/Delivery.php b/src/modules/OpportunityWorkplan/Entities/Delivery.php index 2bab303734..5bf63c23a0 100644 --- a/src/modules/OpportunityWorkplan/Entities/Delivery.php +++ b/src/modules/OpportunityWorkplan/Entities/Delivery.php @@ -114,23 +114,308 @@ public function jsonSerialize(): array ]; } + /** + * Verifica se um campo de metadata é obrigatório + * + * Lógica: + * 1. Se campo não está habilitado (inform) → não é obrigatório + * 2. Se campo está habilitado mas não tem require → é opcional + * 3. Se campo está habilitado E require = true → é obrigatório + * 4. Se campo já tem valor preenchido → considera satisfeito (retorna true) + * + * @param string $metadata_key Nome do campo de metadata + * @return bool Se o campo é obrigatório + */ public function isMetadataRequired(string $metadata_key):bool { + // Mapeamento completo: campo → [inform, require] $metadata_map = [ - 'accessibilityMeasures' => 'workplan_monitoringInformAccessibilityMeasures', - 'executedRevenue' => 'workplan_monitoringReportExecutedRevenue', - 'priorityAudience' => 'workplan_monitoringInformThePriorityAudience', - 'availabilityType' => 'workplan_monitoringInformTheFormOfAvailability', - 'numberOfParticipants' => 'workplan_registrationReportTheNumberOfParticipants', - 'participantProfile' => 'workplan_monitoringProvideTheProfileOfParticipants', + // MONITORAMENTO - Campos originais + 'accessibilityMeasures' => [ + 'inform' => 'workplan_monitoringInformAccessibilityMeasures', + 'require' => 'workplan_monitoringRequireAccessibilityMeasures' + ], + 'executedRevenue' => [ + 'inform' => 'workplan_monitoringReportExecutedRevenue', + 'require' => 'workplan_monitoringRequireExecutedRevenue' + ], + 'priorityAudience' => [ + 'inform' => 'workplan_monitoringInformThePriorityAudience', + 'require' => 'workplan_monitoringRequirePriorityAudience' + ], + 'availabilityType' => [ + 'inform' => 'workplan_monitoringInformTheFormOfAvailability', + 'require' => 'workplan_monitoringRequireAvailabilityType' + ], + 'numberOfParticipants' => [ + 'inform' => 'workplan_monitoringInformNumberOfParticipants', + 'require' => 'workplan_monitoringRequireNumberOfParticipants' + ], + 'participantProfile' => [ + 'inform' => 'workplan_monitoringProvideTheProfileOfParticipants', + 'require' => 'workplan_monitoringRequireParticipantProfile' + ], + + // PLANEJAMENTO - Campos originais + 'segmentDelivery' => [ + 'inform' => 'workplan_registrationInformCulturalArtisticSegment', + 'require' => 'workplan_deliveryRequireSegment' + ], + 'expectedNumberPeople' => [ + 'inform' => 'workplan_registrationReportTheNumberOfParticipants', + 'require' => 'workplan_deliveryRequireExpectedNumberPeople' + ], + + // PLANEJAMENTO - Novos campos + 'artChainLink' => [ + 'inform' => 'workplan_deliveryInformArtChainLink', + 'require' => 'workplan_deliveryRequireArtChainLink' + ], + 'totalBudget' => [ + 'inform' => 'workplan_deliveryInformTotalBudget', + 'require' => 'workplan_deliveryRequireTotalBudget' + ], + 'numberOfCities' => [ + 'inform' => 'workplan_deliveryInformNumberOfCities', + 'require' => 'workplan_deliveryRequireNumberOfCities' + ], + 'numberOfNeighborhoods' => [ + 'inform' => 'workplan_deliveryInformNumberOfNeighborhoods', + 'require' => 'workplan_deliveryRequireNumberOfNeighborhoods' + ], + 'mediationActions' => [ + 'inform' => 'workplan_deliveryInformMediationActions', + 'require' => 'workplan_deliveryRequireMediationActions' + ], + 'paidStaffByRole' => [ + 'inform' => 'workplan_deliveryInformPaidStaffByRole', + 'require' => 'workplan_deliveryRequirePaidStaffByRole' + ], + 'teamCompositionGender' => [ + 'inform' => 'workplan_deliveryInformTeamComposition', + 'require' => 'workplan_deliveryRequireTeamCompositionGender' + ], + 'teamCompositionRace' => [ + 'inform' => 'workplan_deliveryInformTeamComposition', + 'require' => 'workplan_deliveryRequireTeamCompositionRace' + ], + 'revenueType' => [ + 'inform' => 'workplan_deliveryInformRevenueType', + 'require' => 'workplan_deliveryRequireRevenueType' + ], + 'commercialUnits' => [ + 'inform' => 'workplan_deliveryInformCommercialUnits', + 'require' => 'workplan_deliveryRequireCommercialUnits' + ], + 'unitPrice' => [ + 'inform' => 'workplan_deliveryInformCommercialUnits', + 'require' => 'workplan_deliveryRequireUnitPrice' + ], + + // Campos detail (condicionais - só obrigatórios se gate = true) + 'communityCoauthorsDetail' => [ + 'inform' => 'workplan_deliveryInformCommunityCoauthors', + 'require' => 'workplan_deliveryRequireCommunityCoauthorsDetail', + 'gate' => 'hasCommunityCoauthors' + ], + 'transInclusionActions' => [ + 'inform' => 'workplan_deliveryInformTransInclusion', + 'require' => 'workplan_deliveryRequireTransInclusionActions', + 'gate' => 'hasTransInclusionStrategy' + ], + 'expectedAccessibilityMeasures' => [ + 'inform' => 'workplan_deliveryInformAccessibilityPlan', + 'require' => 'workplan_deliveryRequireExpectedAccessibilityMeasures', + 'gate' => 'hasAccessibilityPlan' + ], + 'environmentalPracticesDescription' => [ + 'inform' => 'workplan_deliveryInformEnvironmentalPractices', + 'require' => 'workplan_deliveryRequireEnvironmentalPracticesDescription', + 'gate' => 'hasEnvironmentalPractices' + ], + 'innovationTypes' => [ + 'inform' => 'workplan_deliveryInformInnovation', + 'require' => 'workplan_deliveryRequireInnovationTypes', + 'gate' => 'hasInnovationAction' + ], + + // Campos independentes + 'communicationChannels' => [ + 'inform' => 'workplan_deliveryInformCommunicationChannels', + 'require' => 'workplan_deliveryRequireCommunicationChannels' + ], + 'documentationTypes' => [ + 'inform' => 'workplan_deliveryInformDocumentationTypes', + 'require' => 'workplan_deliveryRequireDocumentationTypes' + ], + + // MONITORAMENTO - Novos campos executados + 'executedNumberOfCities' => [ + 'inform' => 'workplan_monitoringInformNumberOfCities', + 'require' => 'workplan_monitoringRequireNumberOfCities' + ], + 'executedNumberOfNeighborhoods' => [ + 'inform' => 'workplan_monitoringInformNumberOfNeighborhoods', + 'require' => 'workplan_monitoringRequireNumberOfNeighborhoods' + ], + 'executedMediationActions' => [ + 'inform' => 'workplan_monitoringInformMediationActions', + 'require' => 'workplan_monitoringRequireMediationActions' + ], + 'executedCommercialUnits' => [ + 'inform' => 'workplan_monitoringInformCommercialUnits', + 'require' => 'workplan_monitoringRequireCommercialUnits' + ], + 'executedUnitPrice' => [ + 'inform' => 'workplan_monitoringInformCommercialUnits', + 'require' => 'workplan_monitoringRequireUnitPrice' + ], + 'executedPaidStaffByRole' => [ + 'inform' => 'workplan_monitoringInformPaidStaffByRole', + 'require' => 'workplan_monitoringRequirePaidStaffByRole' + ], + 'executedTeamCompositionGender' => [ + 'inform' => 'workplan_monitoringInformTeamComposition', + 'require' => 'workplan_monitoringRequireTeamCompositionGender' + ], + 'executedTeamCompositionRace' => [ + 'inform' => 'workplan_monitoringInformTeamComposition', + 'require' => 'workplan_monitoringRequireTeamCompositionRace' + ], + 'executedArtChainLink' => [ + 'inform' => 'workplan_monitoringInformArtChainLink', + 'require' => 'workplan_monitoringRequireArtChainLink' + ], + 'executedCommunicationChannels' => [ + 'inform' => 'workplan_monitoringInformCommunicationChannels', + 'require' => 'workplan_monitoringRequireCommunicationChannels' + ], + 'executedRevenueType' => [ + 'inform' => 'workplan_monitoringInformRevenueType', + 'require' => 'workplan_monitoringRequireRevenueType' + ], + 'executedSegmentDelivery' => [ + 'inform' => 'workplan_monitoringInformSegmentDelivery', + 'require' => 'workplan_monitoringRequireSegmentDelivery' + ], + 'executedCommunityCoauthorsDetail' => [ + 'inform' => 'workplan_monitoringInformCommunityCoauthors', + 'require' => 'workplan_monitoringRequireCommunityCoauthorsDetail', + 'gate' => 'executedHasCommunityCoauthors' + ], + 'executedTransInclusionActions' => [ + 'inform' => 'workplan_monitoringInformTransInclusion', + 'require' => 'workplan_monitoringRequireTransInclusionActions', + 'gate' => 'executedHasTransInclusionStrategy' + ], + 'executedExpectedAccessibilityMeasures' => [ + 'inform' => 'workplan_monitoringInformAccessibilityPlan', + 'require' => 'workplan_monitoringRequireExpectedAccessibilityMeasures', + 'gate' => 'executedHasAccessibilityPlan' + ], + 'executedEnvironmentalPracticesDescription' => [ + 'inform' => 'workplan_monitoringInformEnvironmentalPractices', + 'require' => 'workplan_monitoringRequireEnvironmentalPracticesDescription', + 'gate' => 'executedHasEnvironmentalPractices' + ], + 'executedInnovationTypes' => [ + 'inform' => 'workplan_monitoringInformInnovation', + 'require' => 'workplan_monitoringRequireInnovationTypes', + 'gate' => 'executedHasInnovationAction' + ], + 'executedDocumentationTypes' => [ + 'inform' => 'workplan_monitoringInformDocumentationTypes', + 'require' => 'workplan_monitoringRequireDocumentationTypes' + ], ]; + // Campo não está no mapa → não é obrigatório + if (!isset($metadata_map[$metadata_key])) { + return false; + } + + $config = $metadata_map[$metadata_key]; + $opportunity = $this->goal->workplan->registration->opportunity->firstPhase; + + // Se campo não está habilitado → não é obrigatório + if (!($opportunity->{$config['inform']} ?? false)) { + return false; + } + + // Se campo tem gate, verifica se gate está ativo + if (isset($config['gate'])) { + $gate_value = $this->{$config['gate']} ?? null; + // Se gate não for 'true' (string), o detail não é obrigatório + if ($gate_value !== 'true') { + return false; + } + } + + // Se é um campo gate (require = null), sempre é obrigatório quando habilitado + if ($config['require'] === null) { + return true; + } + + // Se campo já tem valor preenchido, considera satisfeito if ($this->$metadata_key) { return true; } - - $opportunity = $this->goal->workplan->registration->opportunity->firstPhase; - $opportunity_metadata = $metadata_map[$metadata_key]; - return $opportunity->$opportunity_metadata ?? false; + // Verifica configuração de obrigatoriedade + return $opportunity->{$config['require']} ?? false; + } + + /** + * Valida campo JSON do tipo array de objetos + * Ex: paidStaffByRole = [{role, quantity, totalValue}, ...] + * + * @param string $field Nome do campo + * @return bool Se o campo tem conteúdo válido + */ + protected function validateJsonArrayField(string $field): bool { + $value = $this->$field; + if (!$value) return false; + + $decoded = is_string($value) ? json_decode($value, true) : $value; + return is_array($decoded) && count($decoded) > 0; + } + + /** + * Valida campo JSON do tipo objeto + * Ex: teamCompositionGender = {cisMale: 5, cisFemale: 8, ...} + * + * @param string $field Nome do campo + * @return bool Se o campo tem conteúdo válido + */ + protected function validateJsonObjectField(string $field): bool { + $value = $this->$field; + if (!$value) return false; + + $decoded = is_string($value) ? json_decode($value, true) : $value; + return is_array($decoded) && count($decoded) > 0; + } + + /** + * Valida campo multiselect (array) + * + * @param string $field Nome do campo + * @return bool Se o campo tem ao menos uma opção selecionada + */ + protected function validateMultiselectField(string $field): bool { + $value = $this->$field; + if (!$value) return false; + + $array = is_string($value) ? json_decode($value, true) : $value; + return is_array($array) && count($array) > 0; + } + + /** + * Valida campo select (não pode ser null ou vazio) + * + * @param string $field Nome do campo + * @return bool Se o campo tem valor selecionado + */ + protected function validateSelectField(string $field): bool { + $value = $this->$field; + return !is_null($value) && $value !== ''; } -} \ No newline at end of file +} diff --git a/src/modules/OpportunityWorkplan/Module.php b/src/modules/OpportunityWorkplan/Module.php index a96aa05ba5..08cb52d631 100644 --- a/src/modules/OpportunityWorkplan/Module.php +++ b/src/modules/OpportunityWorkplan/Module.php @@ -51,168 +51,937 @@ function _init(){ $errors['projectDuration'] = [i::__('Plano de metas - Duração do projeto (meses) obrigatório.')]; } - if (!$workplan?->culturalArtisticSegment) { - $errors['culturalArtisticSegment'] = [i::__('Plano de metas - Segmento artistico-cultural obrigatório.')]; + // Validação condicional de segmento artístico-cultural (Workplan) + if ($registration->opportunity->workplan_dataProjectInformCulturalArtisticSegment) { + $requireSegment = $registration->opportunity->workplan_dataProjectRequireCulturalArtisticSegment ?? false; + if ($requireSegment && !$workplan?->culturalArtisticSegment) { + $errors['culturalArtisticSegment'] = [i::__('Plano de metas - Segmento artístico-cultural obrigatório.')]; + } } - + if ($workplan?->goals->isEmpty()) { $errors['goal'] = [i::__('Meta do plano de metas obrigatório.')]; } - if ($registration->opportunity->workplan_deliveryReportTheDeliveriesLinkedToTheGoals) { - if (is_iterable($workplan?->goals)) { - foreach ($workplan?->goals as $goal) { - if ($goal?->deliveries->isEmpty()) { - $errors['delivery'] = [i::__('Entrega da meta do plano de metas obrigatório.')]; - } - } - } - } + // Validação de campos de goal com obrigatoriedade configurável + if ($workplan && is_iterable($workplan->goals)) { + foreach ($workplan->goals as $goal) { + // Validar título da meta + if ($registration->opportunity->workplan_goalInformTitle) { + $requireTitle = $registration->opportunity->workplan_goalRequireTitle ?? false; + if ($requireTitle && !$goal->title) { + $errors['goal'][] = i::__('Título da meta obrigatório'); + } + } + + // Validar descrição da meta + if ($registration->opportunity->workplan_goalInformDescription) { + $requireDescription = $registration->opportunity->workplan_goalRequireDescription ?? false; + if ($requireDescription && !$goal->description) { + $errors['goal'][] = i::__('Descrição da meta obrigatória'); + } + } + + // Validar mês inicial e final da meta — sempre obrigatórios + if (!$goal->monthInitial) { + $errors['goal'][] = i::__('Mês inicial da meta obrigatório.'); + } + if (!$goal->monthEnd) { + $errors['goal'][] = i::__('Mês final da meta obrigatório.'); + } + + // Validar etapa do fazer cultural — obrigatório quando a seção está visível + if ($registration->opportunity->workplan_metaInformTheStageOfCulturalMaking && !$goal->culturalMakingStage) { + $errors['goal'][] = i::__('Etapa do fazer cultural obrigatória.'); + } + } + } + + if ($registration->opportunity->workplan_deliveryReportTheDeliveriesLinkedToTheGoals) { + if (is_iterable($workplan?->goals)) { + foreach ($workplan?->goals as $goal) { + if ($goal?->deliveries->isEmpty()) { + $errors['delivery'][] = i::__('Entrega da meta do plano de metas obrigatório.'); + } + } + } + } + + // Validação de campos de delivery com obrigatoriedade configurável + if ($workplan && is_iterable($workplan->goals)) { + foreach ($workplan->goals as $goal) { + if (is_iterable($goal->deliveries)) { + foreach ($goal->deliveries as $delivery) { + // Campos core da entrega — sempre obrigatórios + if (!$delivery->name) { + $errors['delivery'][] = i::__("Campo 'Nome' obrigatório em uma das entregas."); + } + if (!$delivery->description) { + $errors['delivery'][] = i::__("Campo 'Descrição' obrigatório na entrega '{$delivery->name}'."); + } + if (!$delivery->typeDelivery) { + $errors['delivery'][] = i::__("Campo 'Tipo de entrega' obrigatório na entrega '{$delivery->name}'."); + } + + // Validar período de realização da entrega + if ($registration->opportunity->workplan_deliveryInformDeliveryPeriod && + $registration->opportunity->workplan_deliveryRequireDeliveryPeriod) { + if (!$delivery->monthInitial) { + $errors['delivery'][] = i::__("Campo 'Mês inicial' obrigatório na entrega '{$delivery->name}'."); + } + if (!$delivery->monthEnd) { + $errors['delivery'][] = i::__("Campo 'Mês final' obrigatório na entrega '{$delivery->name}'."); + } + } + + // Validar campos de receita (sub-campos obrigatórios quando receita = sim) + if ($registration->opportunity->workplan_registrationReportExpectedRenevue && + $delivery->generaterRevenue == 'true') { + if (!$delivery->renevueQtd) { + $errors['delivery'][] = i::__("Campo 'Previsão Quantidade' obrigatório na entrega '{$delivery->name}'."); + } + if (!$delivery->unitValueForecast) { + $errors['delivery'][] = i::__("Campo 'Previsão de valor unitário' obrigatório na entrega '{$delivery->name}'."); + } + } + + // Campos simples de planejamento (integer, currency, text) + $simple_fields = [ + 'artChainLink', 'totalBudget', 'numberOfCities', + 'numberOfNeighborhoods', 'mediationActions', + 'commercialUnits', 'unitPrice', 'segmentDelivery', + 'expectedNumberPeople', + ]; + + foreach ($simple_fields as $field) { + if ($delivery->isMetadataRequired($field) && !$delivery->$field) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + // Sub-campos condicionais: só obrigatórios quando o campo gate é 'true' + if ($delivery->hasCommunityCoauthors === 'true' && $delivery->isMetadataRequired('communityCoauthorsDetail') && !$delivery->communityCoauthorsDetail) { + $errors['delivery'][] = i::__("Campo 'Detalhamento do envolvimento de comunidades' obrigatório na entrega '{$delivery->name}'"); + } + if ($delivery->hasTransInclusionStrategy === 'true' && $delivery->isMetadataRequired('transInclusionActions') && !$delivery->transInclusionActions) { + $errors['delivery'][] = i::__("Campo 'Ações de inclusão Trans e Travestis' obrigatório na entrega '{$delivery->name}'"); + } + if ($delivery->hasEnvironmentalPractices === 'true' && $delivery->isMetadataRequired('environmentalPracticesDescription') && !$delivery->environmentalPracticesDescription) { + $errors['delivery'][] = i::__("Campo 'Descrição de práticas socioambientais' obrigatório na entrega '{$delivery->name}'"); + } + + // Campos JSON array de planejamento (paidStaffByRole) + $json_array_fields = ['paidStaffByRole']; + foreach ($json_array_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateJsonArrayField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + // Campos JSON object de planejamento (teamComposition*) + $json_object_fields = [ + 'teamCompositionGender', 'teamCompositionRace', + ]; + foreach ($json_object_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateJsonObjectField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + // Campos multiselect de planejamento + $multiselect_fields = [ + 'revenueType', 'communicationChannels', 'documentationTypes', + ]; + foreach ($multiselect_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateMultiselectField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + // Sub-campos multiselect condicionais ao gate + if ($delivery->hasAccessibilityPlan === 'true' && $delivery->isMetadataRequired('expectedAccessibilityMeasures') && !self::validateMultiselectField($delivery, 'expectedAccessibilityMeasures')) { + $errors['delivery'][] = i::__("Campo 'Medidas de acessibilidade previstas' obrigatório na entrega '{$delivery->name}'"); + } + if ($delivery->hasInnovationAction === 'true' && $delivery->isMetadataRequired('innovationTypes') && !self::validateMultiselectField($delivery, 'innovationTypes')) { + $errors['delivery'][] = i::__("Campo 'Tipos de experimentação/inovação' obrigatório na entrega '{$delivery->name}'"); + } + + // Campos gate: obrigatórios (sim/não) quando o bloco Inform está ativo + // Não têm flag Require separado — o proponente deve sempre responder ao campo gate + if ($registration->opportunity->workplan_deliveryInformCommunityCoauthors && !$delivery->hasCommunityCoauthors) { + $errors['delivery'][] = i::__("Campo 'Envolvimento de comunidades como coautores' obrigatório na entrega '{$delivery->name}'"); + } + if ($registration->opportunity->workplan_deliveryInformTransInclusion && !$delivery->hasTransInclusionStrategy) { + $errors['delivery'][] = i::__("Campo 'Estratégia de inclusão Trans/Travestis' obrigatório na entrega '{$delivery->name}'"); + } + if ($registration->opportunity->workplan_deliveryInformAccessibilityPlan && !$delivery->hasAccessibilityPlan) { + $errors['delivery'][] = i::__("Campo 'Plano de acessibilidade' obrigatório na entrega '{$delivery->name}'"); + } + if ($registration->opportunity->workplan_deliveryInformEnvironmentalPractices && !$delivery->hasEnvironmentalPractices) { + $errors['delivery'][] = i::__("Campo 'Práticas socioambientais' obrigatório na entrega '{$delivery->name}'"); + } + if ($registration->opportunity->workplan_deliveryInformPressStrategy && $registration->opportunity->workplan_deliveryRequireHasPressStrategy && !$delivery->hasPressStrategy) { + $errors['delivery'][] = i::__("Campo 'Estratégias de comunicação' obrigatório na entrega '{$delivery->name}'"); + } + if ($registration->opportunity->workplan_deliveryInformInnovation && !$delivery->hasInnovationAction) { + $errors['delivery'][] = i::__("Campo 'Previsão de ação de experimentação/inovação' obrigatório na entrega '{$delivery->name}'"); + } + + $monitoring_simple_fields = [ + 'availabilityType', + 'participantProfile', + 'numberOfParticipants', + 'executedRevenue', + 'executedNumberOfCities', + 'executedNumberOfNeighborhoods', + 'executedMediationActions', + 'executedCommercialUnits', + 'executedUnitPrice', + 'executedArtChainLink', + 'executedSegmentDelivery', + 'executedCommunityCoauthorsDetail', + 'executedTransInclusionActions', + 'executedEnvironmentalPracticesDescription', + ]; + foreach ($monitoring_simple_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateSelectField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + $monitoring_json_array_fields = ['executedPaidStaffByRole']; + foreach ($monitoring_json_array_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateJsonArrayField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + $monitoring_json_object_fields = [ + 'executedTeamCompositionGender', + 'executedTeamCompositionRace', + ]; + foreach ($monitoring_json_object_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateJsonObjectField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + $monitoring_multiselect_fields = [ + 'accessibilityMeasures', + 'priorityAudience', + 'executedCommunicationChannels', + 'executedRevenueType', + 'executedExpectedAccessibilityMeasures', + 'executedInnovationTypes', + 'executedDocumentationTypes', + ]; + foreach ($monitoring_multiselect_fields as $field) { + if ($delivery->isMetadataRequired($field) && !self::validateMultiselectField($delivery, $field)) { + $label = self::getFieldLabel($field); + $errors['delivery'][] = i::__("Campo '{$label}' obrigatório na entrega '{$delivery->name}'"); + } + } + + } + } + } + } + + $errorsResult = [...$errors]; + } + }); + + $app->hook("template(registration.registrationPrint.section):end", function(){ + $this->part('registration-details-workplan-print'); + }); + + $app->hook('mapas.printJsObject:before', function() { + $this->jsObject['EntitiesDescription']['workplan'] = Workplan::getPropertiesMetadata(); + $this->jsObject['EntitiesDescription']['workplan']['goal'] = Goal::getPropertiesMetadata(); + $this->jsObject['EntitiesDescription']['workplan']['goal']['delivery'] = Delivery::getPropertiesMetadata(); + }); + }); + } + + function register() + { + $app = App::i(); + + $app->registerController('workplan', ControllersWorkplan::class); + $app->registerController('delivery', ControllersDelivery::class); + + $this->registerOpportunityMetadata('workplanLabelDefault', [ + 'label' => i::__('Plano de metas label'), + 'default_value' => 'Plano de metas' + ]); + + $this->registerOpportunityMetadata('goalLabelDefault', [ + 'label' => i::__('Meta label'), + 'default_value' => 'Metas' + ]); + + $this->registerOpportunityMetadata('deliveryLabelDefault', [ + 'label' => i::__('Entrega label'), + 'default_value' => 'Entregas ' + ]); + + // metadados opportunity + $this->registerOpportunityMetadata('enableWorkplan', [ + 'label' => i::__('Habilitar plano de metas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + + $this->registerOpportunityMetadata('workplan_dataProjectInformCulturalArtisticSegment', [ + 'label' => i::__('Informar segmento artístico-cultural'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_dataProjectlimitMaximumDurationOfProjects', [ + 'label' => i::__('Limitar duração máxima dos projetos'), + 'type' => 'boolean', + 'default_value' => false + ]); + + + $this->registerOpportunityMetadata('workplan_dataProjectmaximumDurationInMonths', [ + 'label' => i::__('Duração máxima em meses'), + 'type' => 'integer', + 'default' => 1 + ]); + + + $this->registerOpportunityMetadata('workplan_metaInformTheStageOfCulturalMaking', [ + 'label' => i::__('Informar a etapa do fazer cultural'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_metaLimitNumberOfGoals', [ + 'label' => i::__('Limitar número de metas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + + $this->registerOpportunityMetadata('workplan_metaMaximumNumberOfGoals', [ + 'label' => i::__('Número máximo de metas'), + 'type' => 'integer', + 'default' => 1 + ]); + + $this->registerOpportunityMetadata('workplan_goalInformTitle', [ + 'label' => i::__('Informar título da meta'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_goalRequireTitle', [ + 'label' => i::__('Título da meta é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_goalInformDescription', [ + 'label' => i::__('Informar descrição da meta'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_goalRequireDescription', [ + 'label' => i::__('Descrição da meta é obrigatória'), + 'type' => 'boolean', + 'default_value' => false + ]); + + + $this->registerOpportunityMetadata('workplan_deliveryReportTheDeliveriesLinkedToTheGoals', [ + 'label' => i::__('Informar as entregas vinculadas à meta'), + 'type' => 'boolean', + 'default_value' => false + ]); + + + $this->registerOpportunityMetadata('workplan_deliveryLimitNumberOfDeliveries', [ + 'label' => i::__('Limitar número de entregas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + + $this->registerOpportunityMetadata('workplan_deliveryMaximumNumberOfDeliveries', [ + 'label' => i::__('Número máximo de entregas'), + 'type' => 'integer', + 'default' => 1 + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformDeliveryPeriod', [ + 'label' => i::__('Informar período de realização da atividade'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireDeliveryPeriod', [ + 'label' => i::__('Período de realização obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_registrationReportTheNumberOfParticipants', [ + 'label' => i::__('Informar a quantidade estimada de público'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_registrationInformCulturalArtisticSegment', [ + 'label' => i::__('Informar segmento artístico-cultural'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_registrationReportExpectedRenevue', [ + 'label' => i::__('Informar receita prevista'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformTheFormOfAvailability', [ + 'label' => i::__('Informar forma de disponibilização'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformAccessibilityMeasures', [ + 'label' => i::__('Informar as medidas de acessibilidade'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformThePriorityAudience', [ + 'label' => i::__('Informar os territórios prioritários'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringProvideTheProfileOfParticipants', [ + 'label' => i::__('Informar o perfil do público'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformNumberOfParticipants', [ + 'label' => i::__('Informar número de participantes executado'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringReportExecutedRevenue', [ + 'label' => i::__('Informar receita executada'), + 'type' => 'boolean', + 'default_value' => false + ]); + + // ============================================ + // CONFIGURAÇÕES PARA NOVOS CAMPOS DE ENTREGA + // ============================================ + + $this->registerOpportunityMetadata('workplan_deliveryInformArtChainLink', [ + 'label' => i::__('Informar principal elo das artes acionado'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformTotalBudget', [ + 'label' => i::__('Informar orçamento total da atividade'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformNumberOfCities', [ + 'label' => i::__('Informar número de municípios'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformNumberOfNeighborhoods', [ + 'label' => i::__('Informar número de bairros'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformMediationActions', [ + 'label' => i::__('Informar número de ações de mediação/formação de público'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformPaidStaffByRole', [ + 'label' => i::__('Informar pessoas remuneradas por função'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformTeamComposition', [ + 'label' => i::__('Informar composição da equipe (gênero e raça/cor)'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformRevenueType', [ + 'label' => i::__('Informar tipo de receita previsto'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformCommercialUnits', [ + 'label' => i::__('Informar unidades para comercialização'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformCommunityCoauthors', [ + 'label' => i::__('Informar envolvimento de comunidades como coautores'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformTransInclusion', [ + 'label' => i::__('Informar estratégias de inclusão Trans e Travestis'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformAccessibilityPlan', [ + 'label' => i::__('Informar medidas de acessibilidade previstas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformEnvironmentalPractices', [ + 'label' => i::__('Informar práticas socioambientais'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformPressStrategy', [ + 'label' => i::__('Informar estratégia de relacionamento com imprensa'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireHasPressStrategy', [ + 'label' => i::__('Estratégias de comunicação são obrigatórias'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformCommunicationChannels', [ + 'label' => i::__('Informar canais de comunicação'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformInnovation', [ + 'label' => i::__('Informar ações de experimentação/inovação'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryInformDocumentationTypes', [ + 'label' => i::__('Informar tipo de documentação'), + 'type' => 'boolean', + 'default_value' => false + ]); + + // ============================================ + // CONFIGURAÇÕES PARA MONITORAMENTO (CAMPOS EXECUTADOS) + // ============================================ + + $this->registerOpportunityMetadata('workplan_monitoringInformNumberOfCities', [ + 'label' => i::__('Informar número de municípios executados'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformNumberOfNeighborhoods', [ + 'label' => i::__('Informar número de bairros executados'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformMediationActions', [ + 'label' => i::__('Informar ações de mediação executadas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformCommercialUnits', [ + 'label' => i::__('Informar unidades comercializadas executadas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformPaidStaffByRole', [ + 'label' => i::__('Informar pessoas remuneradas executadas por função'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformTeamComposition', [ + 'label' => i::__('Informar composição da equipe executada (gênero e raça/cor)'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformRevenueType', [ + 'label' => i::__('Informar tipo de receita executada'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformCommunityCoauthors', [ + 'label' => i::__('Informar envolvimento executado de comunidades/coletivos como coautores/coexecutores'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformTransInclusion', [ + 'label' => i::__('Informar estratégias executadas de inclusão Trans e Travestis'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformAccessibilityPlan', [ + 'label' => i::__('Informar plano de acessibilidade executado'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformEnvironmentalPractices', [ + 'label' => i::__('Informar práticas socioambientais executadas'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformPressStrategy', [ + 'label' => i::__('Informar estratégia executada de relacionamento com imprensa'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformInnovation', [ + 'label' => i::__('Informar ações executadas de experimentação/inovação'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformDocumentationTypes', [ + 'label' => i::__('Informar tipos de documentação produzida (executado)'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformSegmentDelivery', [ + 'label' => i::__('Informar segmento artístico-cultural executado da entrega'), + 'type' => 'boolean', + 'default_value' => false + ]); + + // ============================================ + // METADADOS DE OBRIGATORIEDADE (REQUIRE) + // ============================================ + + // WORKPLAN - Segmento artístico-cultural + $this->registerOpportunityMetadata('workplan_dataProjectRequireCulturalArtisticSegment', [ + 'label' => i::__('Segmento artístico-cultural é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + // DELIVERY - PLANEJAMENTO - Campos originais + $this->registerOpportunityMetadata('workplan_deliveryRequireSegment', [ + 'label' => i::__('Segmento artístico-cultural (entrega) é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireExpectedNumberPeople', [ + 'label' => i::__('Quantidade estimada de público é obrigatória'), + 'type' => 'boolean', + 'default_value' => false + ]); + + // DELIVERY - PLANEJAMENTO - Novos campos + $this->registerOpportunityMetadata('workplan_deliveryRequireArtChainLink', [ + 'label' => i::__('Principal elo das artes é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireTotalBudget', [ + 'label' => i::__('Orçamento total é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireNumberOfCities', [ + 'label' => i::__('Número de municípios é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireNumberOfNeighborhoods', [ + 'label' => i::__('Número de bairros é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireMediationActions', [ + 'label' => i::__('Número de ações de mediação é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequirePaidStaffByRole', [ + 'label' => i::__('Pessoas remuneradas por função é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireTeamCompositionGender', [ + 'label' => i::__('Composição da equipe por gênero é obrigatória'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireTeamCompositionRace', [ + 'label' => i::__('Composição da equipe por raça/cor é obrigatória'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireRevenueType', [ + 'label' => i::__('Tipo de receita previsto é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireCommercialUnits', [ + 'label' => i::__('Unidades para comercialização é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireUnitPrice', [ + 'label' => i::__('Valor unitário previsto é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireCommunityCoauthorsDetail', [ + 'label' => i::__('Detalhamento de coautoria com comunidades é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireTransInclusionActions', [ + 'label' => i::__('Ações de inclusão Trans/Travestis são obrigatórias'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireExpectedAccessibilityMeasures', [ + 'label' => i::__('Medidas de acessibilidade previstas são obrigatórias'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireEnvironmentalPracticesDescription', [ + 'label' => i::__('Descrição de práticas socioambientais é obrigatória'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireCommunicationChannels', [ + 'label' => i::__('Canais de comunicação são obrigatórios'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_deliveryRequireInnovationTypes', [ + 'label' => i::__('Tipos de experimentação/inovação são obrigatórios'), + 'type' => 'boolean', + 'default_value' => false + ]); - $errorsResult = [...$errors]; - } - }); + $this->registerOpportunityMetadata('workplan_deliveryRequireDocumentationTypes', [ + 'label' => i::__('Tipos de documentação são obrigatórios'), + 'type' => 'boolean', + 'default_value' => false + ]); - $app->hook("template(registration.registrationPrint.section):end", function(){ - $this->part('registration-details-workplan-print'); - }); - - $app->hook('mapas.printJsObject:before', function() { - $this->jsObject['EntitiesDescription']['workplan'] = Workplan::getPropertiesMetadata(); - $this->jsObject['EntitiesDescription']['workplan']['goal'] = Goal::getPropertiesMetadata(); - $this->jsObject['EntitiesDescription']['workplan']['goal']['delivery'] = Delivery::getPropertiesMetadata(); - }); - }); - } + // MONITORAMENTO - Campos originais + $this->registerOpportunityMetadata('workplan_monitoringRequireAvailabilityType', [ + 'label' => i::__('Forma de disponibilização é obrigatória'), + 'type' => 'boolean', + 'default_value' => false + ]); - function register() - { - $app = App::i(); + $this->registerOpportunityMetadata('workplan_monitoringRequireAccessibilityMeasures', [ + 'label' => i::__('Medidas de acessibilidade executadas são obrigatórias'), + 'type' => 'boolean', + 'default_value' => false + ]); - $app->registerController('workplan', ControllersWorkplan::class); - $app->registerController('delivery', ControllersDelivery::class); - - $this->registerOpportunityMetadata('workplanLabelDefault', [ - 'label' => i::__('Plano de metas label'), - 'default_value' => 'Plano de metas' + $this->registerOpportunityMetadata('workplan_monitoringRequireParticipantProfile', [ + 'label' => i::__('Perfil do público é obrigatório'), + 'type' => 'boolean', + 'default_value' => false ]); - $this->registerOpportunityMetadata('goalLabelDefault', [ - 'label' => i::__('Meta label'), - 'default_value' => 'Metas' + $this->registerOpportunityMetadata('workplan_monitoringRequirePriorityAudience', [ + 'label' => i::__('Territórios prioritários são obrigatórios'), + 'type' => 'boolean', + 'default_value' => false ]); - $this->registerOpportunityMetadata('deliveryLabelDefault', [ - 'label' => i::__('Entrega label'), - 'default_value' => 'Entregas ' + $this->registerOpportunityMetadata('workplan_monitoringRequireExecutedRevenue', [ + 'label' => i::__('Receita executada é obrigatória'), + 'type' => 'boolean', + 'default_value' => false ]); - // metadados opportunity - $this->registerOpportunityMetadata('enableWorkplan', [ - 'label' => i::__('Habilitar plano de metas'), + // MONITORAMENTO - Novos campos executados + $this->registerOpportunityMetadata('workplan_monitoringRequireNumberOfCities', [ + 'label' => i::__('Número de municípios executados é obrigatório'), 'type' => 'boolean', 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_dataProjectlimitMaximumDurationOfProjects', [ - 'label' => i::__('Limitar duração máxima dos projetos'), + $this->registerOpportunityMetadata('workplan_monitoringRequireNumberOfNeighborhoods', [ + 'label' => i::__('Número de bairros executados é obrigatório'), 'type' => 'boolean', 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_dataProjectmaximumDurationInMonths', [ - 'label' => i::__('Duração máxima em meses'), - 'type' => 'integer', - 'default' => 1 + $this->registerOpportunityMetadata('workplan_monitoringRequireMediationActions', [ + 'label' => i::__('Ações de mediação executadas são obrigatórias'), + 'type' => 'boolean', + 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_metaInformTheStageOfCulturalMaking', [ - 'label' => i::__('Informar a etapa do fazer cultural'), + $this->registerOpportunityMetadata('workplan_monitoringRequireNumberOfParticipants', [ + 'label' => i::__('Número de participantes executado é obrigatório'), 'type' => 'boolean', 'default_value' => false - ]); - - $this->registerOpportunityMetadata('workplan_metaLimitNumberOfGoals', [ - 'label' => i::__('Limitar número de metas'), + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireCommercialUnits', [ + 'label' => i::__('Unidades comercializadas executadas são obrigatórias'), 'type' => 'boolean', 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_metaMaximumNumberOfGoals', [ - 'label' => i::__('Número máximo de metas'), - 'type' => 'integer', - 'default' => 1 + $this->registerOpportunityMetadata('workplan_monitoringRequireUnitPrice', [ + 'label' => i::__('Valor unitário executado é obrigatório'), + 'type' => 'boolean', + 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_deliveryReportTheDeliveriesLinkedToTheGoals', [ - 'label' => i::__('Informar as entregas vinculadas à meta'), + $this->registerOpportunityMetadata('workplan_monitoringRequirePaidStaffByRole', [ + 'label' => i::__('Pessoas remuneradas executadas por função é obrigatório'), 'type' => 'boolean', 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_deliveryLimitNumberOfDeliveries', [ - 'label' => i::__('Limitar número de entregas'), + $this->registerOpportunityMetadata('workplan_monitoringRequireTeamCompositionGender', [ + 'label' => i::__('Composição da equipe executada por gênero é obrigatória'), 'type' => 'boolean', 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_deliveryMaximumNumberOfDeliveries', [ - 'label' => i::__('Número máximo de entregas'), - 'type' => 'integer', - 'default' => 1 + $this->registerOpportunityMetadata('workplan_monitoringRequireTeamCompositionRace', [ + 'label' => i::__('Composição da equipe executada por raça/cor é obrigatória'), + 'type' => 'boolean', + 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_registrationReportTheNumberOfParticipants', [ - 'label' => i::__('Informar a quantidade estimada de público'), + + $this->registerOpportunityMetadata('workplan_monitoringRequireRevenueType', [ + 'label' => i::__('Tipo de receita executada é obrigatório'), 'type' => 'boolean', 'default_value' => false ]); - $this->registerOpportunityMetadata('workplan_registrationInformCulturalArtisticSegment', [ - 'label' => i::__('Informar segmento artístico-cultural'), + $this->registerOpportunityMetadata('workplan_monitoringRequireCommunityCoauthorsDetail', [ + 'label' => i::__('Detalhamento de coautoria/coexecução executada é obrigatório'), 'type' => 'boolean', 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_registrationReportExpectedRenevue', [ - 'label' => i::__('Informar receita prevista'), + + $this->registerOpportunityMetadata('workplan_monitoringRequireTransInclusionActions', [ + 'label' => i::__('Ações executadas de inclusão Trans e Travestis são obrigatórias'), 'type' => 'boolean', 'default_value' => false ]); - $this->registerOpportunityMetadata('workplan_monitoringInformTheFormOfAvailability', [ - 'label' => i::__('Informar forma de disponibilização'), + $this->registerOpportunityMetadata('workplan_monitoringRequireExpectedAccessibilityMeasures', [ + 'label' => i::__('Medidas de acessibilidade executadas são obrigatórias'), 'type' => 'boolean', 'default_value' => false ]); - $this->registerOpportunityMetadata('workplan_monitoringInformAccessibilityMeasures', [ - 'label' => i::__('Informar as medidas de acessibilidade'), + $this->registerOpportunityMetadata('workplan_monitoringRequireEnvironmentalPracticesDescription', [ + 'label' => i::__('Práticas socioambientais executadas são obrigatórias'), 'type' => 'boolean', 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_monitoringInformThePriorityAudience', [ - 'label' => i::__('Informar os territórios prioritários'), + + $this->registerOpportunityMetadata('workplan_monitoringRequireInnovationTypes', [ + 'label' => i::__('Tipos de experimentação/inovação executados são obrigatórios'), 'type' => 'boolean', 'default_value' => false ]); - - $this->registerOpportunityMetadata('workplan_monitoringProvideTheProfileOfParticipants', [ - 'label' => i::__('Informar o perfil do público'), + + $this->registerOpportunityMetadata('workplan_monitoringRequireDocumentationTypes', [ + 'label' => i::__('Tipos de documentação produzida são obrigatórios'), 'type' => 'boolean', 'default_value' => false ]); - $this->registerOpportunityMetadata('workplan_monitoringReportExecutedRevenue', [ - 'label' => i::__('Informar receita executada'), + $this->registerOpportunityMetadata('workplan_monitoringRequireSegmentDelivery', [ + 'label' => i::__('Segmento artístico-cultural executado é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformArtChainLink', [ + 'label' => i::__('Informar principal elo das artes acionado (executado)'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireArtChainLink', [ + 'label' => i::__('Principal elo das artes (executado) é obrigatório'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringInformCommunicationChannels', [ + 'label' => i::__('Informar canais de comunicação utilizados (executado)'), + 'type' => 'boolean', + 'default_value' => false + ]); + + $this->registerOpportunityMetadata('workplan_monitoringRequireCommunicationChannels', [ + 'label' => i::__('Canais de comunicação utilizados são obrigatórios'), 'type' => 'boolean', 'default_value' => false ]); @@ -523,5 +1292,539 @@ function register() $totalValueForecast = new Metadata('totalValueForecast', ['label' => \MapasCulturais\i::__('Previsão de valor total')]); $app->registerMetadata($totalValueForecast, Delivery::class); + + // ============================================ + // NOVOS CAMPOS DE PLANEJAMENTO DA ENTREGA + // ============================================ + + // Principal elo das artes acionado + $artChainLink = new Metadata('artChainLink', [ + 'label' => \MapasCulturais\i::__('Principal elo das artes acionado'), + 'type' => 'select', + 'options' => array( + \MapasCulturais\i::__('Acesso'), + \MapasCulturais\i::__('Criação'), + \MapasCulturais\i::__('Produção'), + \MapasCulturais\i::__('Difusão'), + \MapasCulturais\i::__('Circulação'), + \MapasCulturais\i::__('Internacionalização'), + \MapasCulturais\i::__('Formação'), + \MapasCulturais\i::__('Fruição'), + \MapasCulturais\i::__('Memória/Preservação'), + \MapasCulturais\i::__('Pesquisa'), + \MapasCulturais\i::__('Reflexão'), + \MapasCulturais\i::__('Gestão Cultural'), + ), + ]); + $app->registerMetadata($artChainLink, Delivery::class); + + // Orçamento total da atividade + $totalBudget = new Metadata('totalBudget', [ + 'label' => \MapasCulturais\i::__('Qual o orçamento total da atividade?'), + 'type' => 'currency' + ]); + $app->registerMetadata($totalBudget, Delivery::class); + + // Em quantos municípios + $numberOfCities = new Metadata('numberOfCities', [ + 'label' => \MapasCulturais\i::__('Em quantos municípios a atividade vai ser realizada?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => \MapasCulturais\i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($numberOfCities, Delivery::class); + + // Em quantos bairros + $numberOfNeighborhoods = new Metadata('numberOfNeighborhoods', [ + 'label' => \MapasCulturais\i::__('Em quantos bairros a atividade vai ser realizada?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => \MapasCulturais\i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($numberOfNeighborhoods, Delivery::class); + + // Quantas ações de mediação/formação de público + $mediationActions = new Metadata('mediationActions', [ + 'label' => \MapasCulturais\i::__('Quantas ações de mediação/formação de público estão previstas?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => \MapasCulturais\i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($mediationActions, Delivery::class); + + // Pessoas remuneradas por função (estrutura JSON) + $paidStaffByRole = new Metadata('paidStaffByRole', [ + 'label' => \MapasCulturais\i::__('Quantas pessoas serão remuneradas, por função?'), + 'type' => 'json', + 'options' => [ + // Direção e Gestão + \MapasCulturais\i::__('Diretor Artístico'), + \MapasCulturais\i::__('Diretor de Arte'), + \MapasCulturais\i::__('Diretor Musical'), + \MapasCulturais\i::__('Produtor Cultural'), + \MapasCulturais\i::__('Produtor Musical'), + \MapasCulturais\i::__('Produtor Audiovisual'), + \MapasCulturais\i::__('Gestor Cultural'), + \MapasCulturais\i::__('Curador'), + \MapasCulturais\i::__('Assistente de produção'), + \MapasCulturais\i::__('Assistente de direção'), + + // Artes Cênicas + \MapasCulturais\i::__('Ator/Atriz'), + \MapasCulturais\i::__('Bailarino'), + \MapasCulturais\i::__('Dançarino'), + \MapasCulturais\i::__('Coreógrafo'), + \MapasCulturais\i::__('Dramaturgo'), + \MapasCulturais\i::__('Iluminador'), + \MapasCulturais\i::__('Cenotécnico'), + \MapasCulturais\i::__('Figurinista'), + \MapasCulturais\i::__('Maquiador'), + \MapasCulturais\i::__('Contra-regra'), + \MapasCulturais\i::__('Assistente de palco'), + + // Música + \MapasCulturais\i::__('Músico/Musicista'), + \MapasCulturais\i::__('Cantor'), + \MapasCulturais\i::__('Compositor'), + \MapasCulturais\i::__('Arranjador'), + \MapasCulturais\i::__('Maestro'), + \MapasCulturais\i::__('Instrumentista'), + \MapasCulturais\i::__('DJ'), + + // Artes Visuais + \MapasCulturais\i::__('Artista Visual'), + \MapasCulturais\i::__('Pintor'), + \MapasCulturais\i::__('Escultor'), + \MapasCulturais\i::__('Fotógrafo'), + \MapasCulturais\i::__('Designer Gráfico'), + \MapasCulturais\i::__('Ilustrador'), + \MapasCulturais\i::__('Grafiteiro'), + \MapasCulturais\i::__('Muralista'), + + // Audiovisual + \MapasCulturais\i::__('Roteirista'), + \MapasCulturais\i::__('Operador de Câmera'), + \MapasCulturais\i::__('Editor de Vídeo'), + \MapasCulturais\i::__('Operador de Som'), + \MapasCulturais\i::__('Técnico de Iluminação'), + \MapasCulturais\i::__('Finalizador'), + + // Literatura e Comunicação + \MapasCulturais\i::__('Escritor'), + \MapasCulturais\i::__('Poeta'), + \MapasCulturais\i::__('Contador de Histórias'), + \MapasCulturais\i::__('Jornalista'), + \MapasCulturais\i::__('Redator'), + \MapasCulturais\i::__('Editor de Livros'), + \MapasCulturais\i::__('Revisor'), + \MapasCulturais\i::__('Tradutor'), + + // Educação e Mediação + \MapasCulturais\i::__('Educador Cultural'), + \MapasCulturais\i::__('Mediador'), + \MapasCulturais\i::__('Oficineiro'), + \MapasCulturais\i::__('Professor'), + \MapasCulturais\i::__('Instrutor'), + + // Técnicos e Suporte + \MapasCulturais\i::__('Técnico de Som'), + \MapasCulturais\i::__('Técnico de Iluminação'), + \MapasCulturais\i::__('Montador de Palco'), + \MapasCulturais\i::__('Maquinista'), + \MapasCulturais\i::__('Eletricista'), + \MapasCulturais\i::__('Engenheiro de Som'), + + // Cultura Popular + \MapasCulturais\i::__('Mestre de Cultura Popular'), + \MapasCulturais\i::__('Brincante'), + \MapasCulturais\i::__('Artesão'), + \MapasCulturais\i::__('Capoeirista'), + + // Digital + \MapasCulturais\i::__('Desenvolvedor de Software'), + \MapasCulturais\i::__('Web Designer'), + \MapasCulturais\i::__('Designer de Som'), + \MapasCulturais\i::__('Gestor de Redes Sociais'), + + // Administração + \MapasCulturais\i::__('Coordenador'), + \MapasCulturais\i::__('Secretário'), + \MapasCulturais\i::__('Assistente Administrativo'), + \MapasCulturais\i::__('Contador'), + + // Outros + \MapasCulturais\i::__('Pesquisador'), + \MapasCulturais\i::__('Consultor Cultural'), + \MapasCulturais\i::__('Assessor de Imprensa'), + \MapasCulturais\i::__('Outra'), + ], + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + } + ]); + $app->registerMetadata($paidStaffByRole, Delivery::class); + + // Composição da equipe por gênero + $teamCompositionGender = new Metadata('teamCompositionGender', [ + 'label' => \MapasCulturais\i::__('Composição prevista da equipe por gênero'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + } + ]); + $app->registerMetadata($teamCompositionGender, Delivery::class); + + // Composição da equipe por raça/cor + $teamCompositionRace = new Metadata('teamCompositionRace', [ + 'label' => \MapasCulturais\i::__('Composição prevista da equipe por raça/cor'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + } + ]); + $app->registerMetadata($teamCompositionRace, Delivery::class); + + // Tipo de receita previsto + $revenueType = new Metadata('revenueType', [ + 'label' => \MapasCulturais\i::__('Qual o tipo de receita previsto?'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Venda de ingressos'), + \MapasCulturais\i::__('Venda de produtos'), + \MapasCulturais\i::__('Patrocínio privado'), + \MapasCulturais\i::__('Apoio cultural'), + \MapasCulturais\i::__('Doações'), + \MapasCulturais\i::__('Cachê'), + \MapasCulturais\i::__('Prestação de serviços'), + \MapasCulturais\i::__('Direitos autorais'), + \MapasCulturais\i::__('Licenciamento'), + \MapasCulturais\i::__('Não haverá receita'), + \MapasCulturais\i::__('Outros'), + ), + ]); + $app->registerMetadata($revenueType, Delivery::class); + + // Quantidade de unidades para comercialização + $commercialUnits = new Metadata('commercialUnits', [ + 'label' => \MapasCulturais\i::__('Quantidade de unidades previstas para comercialização'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => \MapasCulturais\i::__('Deve ser um número maior ou igual a zero') + ] + ]); + $app->registerMetadata($commercialUnits, Delivery::class); + + // Valor unitário previsto + $unitPrice = new Metadata('unitPrice', [ + 'label' => \MapasCulturais\i::__('Valor unitário previsto (R$)'), + 'type' => 'currency' + ]); + $app->registerMetadata($unitPrice, Delivery::class); + + // Envolvimento de comunidades como coautores + $hasCommunityCoauthors = new Metadata('hasCommunityCoauthors', [ + 'label' => \MapasCulturais\i::__('A atividade prevê envolvimento de comunidades/coletivos como coautores/coexecutores?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasCommunityCoauthors, Delivery::class); + + // Estratégias Trans e Travestis (boolean) + $hasTransInclusionStrategy = new Metadata('hasTransInclusionStrategy', [ + 'label' => \MapasCulturais\i::__('A atividade prevê estratégias voltadas à promoção do acesso de pessoas Trans e Travestis?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasTransInclusionStrategy, Delivery::class); + + // Quais ações Trans e Travestis (condicional) + $transInclusionActions = new Metadata('transInclusionActions', [ + 'label' => \MapasCulturais\i::__('Quais ações foram previstas para incorporar estratégias voltadas à promoção do acesso de pessoas Trans e Travestis?'), + 'type' => 'text' + ]); + $app->registerMetadata($transInclusionActions, Delivery::class); + + // Medidas de acessibilidade (boolean) + $hasAccessibilityPlan = new Metadata('hasAccessibilityPlan', [ + 'label' => \MapasCulturais\i::__('A atividade prevê medidas de acessibilidade?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasAccessibilityPlan, Delivery::class); + + // Quais medidas de acessibilidade previstas (condicional) + $expectedAccessibilityMeasures = new Metadata('expectedAccessibilityMeasures', [ + 'label' => \MapasCulturais\i::__('Quais medidas de acessibilidade estão previstas na atividade?'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Rotas acessíveis, com espaço de manobra para cadeira de rodas'), + \MapasCulturais\i::__('Palco acessível'), + \MapasCulturais\i::__('Camarim acessível'), + \MapasCulturais\i::__('Piso tátil'), + \MapasCulturais\i::__('Rampas'), + \MapasCulturais\i::__("Elevadores adequados para PCD's"), + \MapasCulturais\i::__('Corrimãos e guarda-corpos'), + \MapasCulturais\i::__("Banheiros adaptados para PCD's"), + \MapasCulturais\i::__('Área de alimentação preferencial identificada'), + \MapasCulturais\i::__("Vagas de estacionamento para PCD's reservadas"), + \MapasCulturais\i::__("Assentos para pessoas obesas, pessoas com mobilidade reduzida, PCD's e pessoas idosas reservadas"), + \MapasCulturais\i::__('Filas preferenciais identificadas'), + \MapasCulturais\i::__('Iluminação adequada'), + \MapasCulturais\i::__('Livro e/ou similares em braile'), + \MapasCulturais\i::__('Audiolivro'), + \MapasCulturais\i::__('Uso Língua Brasileira de Sinais - Libras'), + \MapasCulturais\i::__('Sistema Braille em materiais impressos'), + \MapasCulturais\i::__('Sistema de sinalização ou comunicação tátil'), + \MapasCulturais\i::__('Audiodescrição'), + \MapasCulturais\i::__('Legendas para surdos e ensurdecidos'), + \MapasCulturais\i::__('Linguagem simples'), + \MapasCulturais\i::__('Textos adaptados para software de leitor de tela'), + \MapasCulturais\i::__('Capacitação em acessibilidade para equipes atuantes nos projetos culturais'), + \MapasCulturais\i::__('Contratação de profissionais especializados em acessibilidade cultural'), + \MapasCulturais\i::__('Contratação de profissionais com deficiência'), + \MapasCulturais\i::__('Formação e sensibilização de agentes culturais sobre acessibilidade'), + \MapasCulturais\i::__('Formação e sensibilização de públicos da cadeia produtiva cultural sobre acessibilidade'), + \MapasCulturais\i::__("Envolvimento de PCD's na concepção do projeto"), + \MapasCulturais\i::__('Outras'), + ), + ]); + $app->registerMetadata($expectedAccessibilityMeasures, Delivery::class); + + // Práticas socioambientais (boolean) + $hasEnvironmentalPractices = new Metadata('hasEnvironmentalPractices', [ + 'label' => \MapasCulturais\i::__('A atividade prevê medidas ou práticas socioambientais?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasEnvironmentalPractices, Delivery::class); + + // Quais práticas socioambientais (condicional) + $environmentalPracticesDescription = new Metadata('environmentalPracticesDescription', [ + 'label' => \MapasCulturais\i::__('Quais medidas e práticas socioambientais estão previstas na atividade?'), + 'type' => 'text' + ]); + $app->registerMetadata($environmentalPracticesDescription, Delivery::class); + + // Estratégia de relacionamento com imprensa + $hasPressStrategy = new Metadata('hasPressStrategy', [ + 'label' => \MapasCulturais\i::__('A atividade contará com uma estratégia de relacionamento com a imprensa?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasPressStrategy, Delivery::class); + + // Canais de comunicação + $communicationChannels = new Metadata('communicationChannels', [ + 'label' => \MapasCulturais\i::__('Quais canais de comunicação estão previstos para difusão da atividade?'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Instagram'), + \MapasCulturais\i::__('Facebook'), + \MapasCulturais\i::__('TikTok'), + \MapasCulturais\i::__('YouTube'), + \MapasCulturais\i::__('X/Twitter'), + \MapasCulturais\i::__('WhatsApp (listas/grupos)'), + \MapasCulturais\i::__('Telegram (canais/grupos)'), + \MapasCulturais\i::__('Site/página oficial do projeto'), + \MapasCulturais\i::__('E-mail marketing/newsletter'), + \MapasCulturais\i::__('Plataformas de eventos/inscrição (ex.: Sympla/Shotgun/Eventbrite)'), + \MapasCulturais\i::__('Portais, blogs e influenciadores/as locais'), + \MapasCulturais\i::__('Rádio comunitária'), + \MapasCulturais\i::__('Rádio comercial'), + \MapasCulturais\i::__('TV local'), + \MapasCulturais\i::__('Mídia impressa'), + \MapasCulturais\i::__('Cartazes e materiais impressos'), + \MapasCulturais\i::__('Carro de som'), + \MapasCulturais\i::__('Outros'), + ), + ]); + $app->registerMetadata($communicationChannels, Delivery::class); + + // Experimentação/inovação (boolean) + $hasInnovationAction = new Metadata('hasInnovationAction', [ + 'label' => \MapasCulturais\i::__('A atividade prevê ao menos uma ação de experimentação/inovação?'), + 'type' => 'select', + 'options' => array( + 'true' => \MapasCulturais\i::__('Sim'), + 'false' => \MapasCulturais\i::__('Não'), + ), + ]); + $app->registerMetadata($hasInnovationAction, Delivery::class); + + // Tipos de experimentação/inovação (condicional) + $innovationTypes = new Metadata('innovationTypes', [ + 'label' => \MapasCulturais\i::__('Quais tipos de experimentação/inovação previstos na atividade?'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Uso de novas tecnologias (AR, VR, IA, etc.)'), + \MapasCulturais\i::__('Novas linguagens artísticas'), + \MapasCulturais\i::__('Fusão de linguagens'), + \MapasCulturais\i::__('Metodologias participativas inovadoras'), + \MapasCulturais\i::__('Novos modelos de gestão cultural'), + \MapasCulturais\i::__('Economia criativa e novos modelos de negócio'), + \MapasCulturais\i::__('Sustentabilidade e práticas ambientais inovadoras'), + \MapasCulturais\i::__('Inclusão e acessibilidade de forma inovadora'), + \MapasCulturais\i::__('Experimentação em espaços não convencionais'), + \MapasCulturais\i::__('Coprodução/cocriação com públicos'), + \MapasCulturais\i::__('Outros'), + ), + ]); + $app->registerMetadata($innovationTypes, Delivery::class); + + // Tipo de documentação + $documentationTypes = new Metadata('documentationTypes', [ + 'label' => \MapasCulturais\i::__('Tipo de documentação que será produzida'), + 'type' => 'multiselect', + 'options' => array( + \MapasCulturais\i::__('Fotografia'), + \MapasCulturais\i::__('Vídeo'), + \MapasCulturais\i::__('Áudio'), + \MapasCulturais\i::__('Relatório textual'), + \MapasCulturais\i::__('Caderno de processo'), + \MapasCulturais\i::__('Publicação impressa'), + \MapasCulturais\i::__('Publicação digital'), + \MapasCulturais\i::__('Website/Plataforma online'), + \MapasCulturais\i::__('Redes sociais'), + \MapasCulturais\i::__('Depoimentos'), + \MapasCulturais\i::__('Registros de processo'), + \MapasCulturais\i::__('Acervo digitalizado'), + \MapasCulturais\i::__('Não haverá documentação específica'), + \MapasCulturais\i::__('Outros'), + ), + ]); + $app->registerMetadata($documentationTypes, Delivery::class); + } + + /** + * Valida campo JSON do tipo array + */ + public static function validateJsonArrayField($delivery, string $field): bool { + $value = $delivery->$field; + if (!$value) return false; + + $decoded = is_string($value) ? json_decode($value, true) : $value; + return is_array($decoded) && count($decoded) > 0; + } + + /** + * Valida campo JSON do tipo objeto + */ + public static function validateJsonObjectField($delivery, string $field): bool { + $value = $delivery->$field; + if (!$value) return false; + + $decoded = is_string($value) ? json_decode($value, true) : $value; + return is_array($decoded) && count($decoded) > 0; + } + + /** + * Valida campo multiselect (array) + */ + public static function validateMultiselectField($delivery, string $field): bool { + $value = $delivery->$field; + if (!$value) return false; + + $array = is_string($value) ? json_decode($value, true) : $value; + return is_array($array) && count($array) > 0; + } + + /** + * Valida campo select + */ + public static function validateSelectField($delivery, string $field): bool { + $value = $delivery->$field; + return !is_null($value) && $value !== ''; + } + + /** + * Retorna label amigável para campo + */ + public static function getFieldLabel(string $field): string { + $labels = [ + 'artChainLink' => 'Principal elo das artes', + 'totalBudget' => 'Orçamento total', + 'numberOfCities' => 'Número de municípios', + 'numberOfNeighborhoods' => 'Número de bairros', + 'mediationActions' => 'Ações de mediação', + 'paidStaffByRole' => 'Pessoas remuneradas por função', + 'teamCompositionGender' => 'Composição da equipe por gênero', + 'teamCompositionRace' => 'Composição da equipe por raça/cor', + 'revenueType' => 'Tipo de receita', + 'commercialUnits' => 'Unidades para comercialização', + 'unitPrice' => 'Valor unitário', + 'communityCoauthorsDetail' => 'Detalhamento de coautoria', + 'transInclusionActions' => 'Ações de inclusão Trans/Travestis', + 'expectedAccessibilityMeasures' => 'Medidas de acessibilidade', + 'environmentalPracticesDescription' => 'Práticas socioambientais', + 'communicationChannels' => 'Canais de comunicação', + 'innovationTypes' => 'Tipos de experimentação/inovação', + 'documentationTypes' => 'Tipos de documentação', + 'hasCommunityCoauthors' => 'Envolvimento de comunidades', + 'hasTransInclusionStrategy' => 'Estratégia de inclusão Trans/Travestis', + 'hasAccessibilityPlan' => 'Medidas de acessibilidade', + 'hasEnvironmentalPractices' => 'Práticas socioambientais', + 'hasPressStrategy' => 'Estratégia de imprensa', + 'hasInnovationAction' => 'Experimentação/inovação', + 'segmentDelivery' => 'Segmento artístico-cultural', + 'expectedNumberPeople' => 'Número previsto de pessoas', + // Monitoramento + 'executedNumberOfCities' => 'Municípios executados', + 'executedNumberOfNeighborhoods' => 'Bairros executados', + 'executedMediationActions' => 'Ações de mediação executadas', + 'executedCommercialUnits' => 'Unidades comercializadas', + 'executedUnitPrice' => 'Valor unitário executado', + 'executedPaidStaffByRole' => 'Pessoas remuneradas executadas', + 'executedTeamCompositionGender' => 'Composição executada por gênero', + 'executedTeamCompositionRace' => 'Composição executada por raça/cor', + 'availabilityType' => 'Forma de disponibilização', + 'accessibilityMeasures' => 'Medidas de acessibilidade', + 'numberOfParticipants' => 'Número de participantes', + 'participantProfile' => 'Perfil do público', + 'priorityAudience' => 'Territórios prioritários', + 'executedRevenue' => 'Receita executada', + 'executedRevenueType' => 'Tipo de receita executada', + 'executedSegmentDelivery' => 'Segmento artístico-cultural executado', + 'executedHasCommunityCoauthors' => 'Envolvimento executado de comunidades', + 'executedCommunityCoauthorsDetail' => 'Detalhamento executado de coautoria', + 'executedHasTransInclusionStrategy' => 'Estratégia executada de inclusão Trans/Travestis', + 'executedTransInclusionActions' => 'Ações executadas de inclusão Trans/Travestis', + 'executedHasAccessibilityPlan' => 'Plano de acessibilidade executado', + 'executedExpectedAccessibilityMeasures' => 'Medidas de acessibilidade executadas', + 'executedHasEnvironmentalPractices' => 'Práticas socioambientais executadas', + 'executedEnvironmentalPracticesDescription' => 'Descrição de práticas socioambientais executadas', + 'executedHasPressStrategy' => 'Estratégia executada de relacionamento com imprensa', + 'executedHasInnovationAction' => 'Ação executada de experimentação/inovação', + 'executedInnovationTypes' => 'Tipos de experimentação/inovação executados', + 'executedDocumentationTypes' => 'Tipos de documentação produzida', + ]; + + return $labels[$field] ?? $field; } -} \ No newline at end of file +} diff --git a/src/modules/OpportunityWorkplan/Services/WorkplanService.php b/src/modules/OpportunityWorkplan/Services/WorkplanService.php index 1d74162eec..68be21a4e3 100644 --- a/src/modules/OpportunityWorkplan/Services/WorkplanService.php +++ b/src/modules/OpportunityWorkplan/Services/WorkplanService.php @@ -63,6 +63,32 @@ public function save($registration, $workplan, $data) $delivery->renevueQtd = $d['renevueQtd'] ?? null; $delivery->unitValueForecast = $d['unitValueForecast'] ?? null; $delivery->totalValueForecast = $d['totalValueForecast'] ?? null; + + // Novos campos de planejamento + $delivery->artChainLink = $d['artChainLink'] ?? null; + $delivery->totalBudget = $d['totalBudget'] ?? null; + $delivery->numberOfCities = $d['numberOfCities'] ?? null; + $delivery->numberOfNeighborhoods = $d['numberOfNeighborhoods'] ?? null; + $delivery->mediationActions = $d['mediationActions'] ?? null; + $delivery->paidStaffByRole = $d['paidStaffByRole'] ?? null; + $delivery->teamCompositionGender = $d['teamCompositionGender'] ?? null; + $delivery->teamCompositionRace = $d['teamCompositionRace'] ?? null; + $delivery->revenueType = $d['revenueType'] ?? null; + $delivery->commercialUnits = $d['commercialUnits'] ?? null; + $delivery->unitPrice = $d['unitPrice'] ?? null; + $delivery->hasCommunityCoauthors = $d['hasCommunityCoauthors'] ?? null; + $delivery->hasTransInclusionStrategy = $d['hasTransInclusionStrategy'] ?? null; + $delivery->transInclusionActions = $d['transInclusionActions'] ?? null; + $delivery->hasAccessibilityPlan = $d['hasAccessibilityPlan'] ?? null; + $delivery->expectedAccessibilityMeasures = $d['expectedAccessibilityMeasures'] ?? null; + $delivery->hasEnvironmentalPractices = $d['hasEnvironmentalPractices'] ?? null; + $delivery->environmentalPracticesDescription = $d['environmentalPracticesDescription'] ?? null; + $delivery->hasPressStrategy = $d['hasPressStrategy'] ?? null; + $delivery->communicationChannels = $d['communicationChannels'] ?? null; + $delivery->hasInnovationAction = $d['hasInnovationAction'] ?? null; + $delivery->innovationTypes = $d['innovationTypes'] ?? null; + $delivery->documentationTypes = $d['documentationTypes'] ?? null; + $delivery->goal = $goal; $delivery->save(true); } @@ -71,6 +97,12 @@ public function save($registration, $workplan, $data) $workplan = $app->repo(Workplan::class)->find($workplan->id); + // Força reload do banco para garantir que novas goals/deliveries + // (cujas collections ArrayCollection em memória estão vazias) sejam + // retornadas corretamente na serialização — corrige bug de entrega + // desaparecendo após salvar a meta. + $app->em->refresh($workplan); + return $workplan; } } \ No newline at end of file diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js index 2942db51e4..a648974e13 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/script.js @@ -51,6 +51,154 @@ app.component('opportunity-enable-workplan', { this.disabledDeliveries(); } }, + // Watchers para resetar campos "require" quando "inform" é desmarcado + // Workplan + 'entity.workplan_dataProjectInformCulturalArtisticSegment'(_new) { + if (!_new) this.entity.workplan_dataProjectRequireCulturalArtisticSegment = false; + }, + // Goal + 'entity.workplan_goalInformTitle'(_new) { + if (!_new) this.entity.workplan_goalRequireTitle = false; + }, + 'entity.workplan_goalInformDescription'(_new) { + if (!_new) this.entity.workplan_goalRequireDescription = false; + }, + // Delivery - Planejamento (campos originais) + 'entity.workplan_registrationReportTheNumberOfParticipants'(_new) { + if (!_new) this.entity.workplan_deliveryRequireExpectedNumberPeople = false; + }, + 'entity.workplan_registrationInformCulturalArtisticSegment'(_new) { + if (!_new) this.entity.workplan_deliveryRequireSegment = false; + }, + // Delivery - Planejamento (novos campos) + 'entity.workplan_deliveryInformArtChainLink'(_new) { + if (!_new) this.entity.workplan_deliveryRequireArtChainLink = false; + }, + 'entity.workplan_deliveryInformTotalBudget'(_new) { + if (!_new) this.entity.workplan_deliveryRequireTotalBudget = false; + }, + 'entity.workplan_deliveryInformNumberOfCities'(_new) { + if (!_new) this.entity.workplan_deliveryRequireNumberOfCities = false; + }, + 'entity.workplan_deliveryInformNumberOfNeighborhoods'(_new) { + if (!_new) this.entity.workplan_deliveryRequireNumberOfNeighborhoods = false; + }, + 'entity.workplan_deliveryInformMediationActions'(_new) { + if (!_new) this.entity.workplan_deliveryRequireMediationActions = false; + }, + 'entity.workplan_deliveryInformPaidStaffByRole'(_new) { + if (!_new) this.entity.workplan_deliveryRequirePaidStaffByRole = false; + }, + 'entity.workplan_deliveryInformTeamComposition'(_new) { + if (!_new) { + this.entity.workplan_deliveryRequireTeamCompositionGender = false; + this.entity.workplan_deliveryRequireTeamCompositionRace = false; + } + }, + 'entity.workplan_deliveryInformRevenueType'(_new) { + if (!_new) this.entity.workplan_deliveryRequireRevenueType = false; + }, + 'entity.workplan_deliveryInformCommercialUnits'(_new) { + if (!_new) { + this.entity.workplan_deliveryRequireCommercialUnits = false; + this.entity.workplan_deliveryRequireUnitPrice = false; + } + }, + 'entity.workplan_deliveryInformCommunityCoauthors'(_new) { + if (!_new) this.entity.workplan_deliveryRequireCommunityCoauthorsDetail = false; + }, + 'entity.workplan_deliveryInformTransInclusion'(_new) { + if (!_new) this.entity.workplan_deliveryRequireTransInclusionActions = false; + }, + 'entity.workplan_deliveryInformAccessibilityPlan'(_new) { + if (!_new) this.entity.workplan_deliveryRequireExpectedAccessibilityMeasures = false; + }, + 'entity.workplan_deliveryInformEnvironmentalPractices'(_new) { + if (!_new) this.entity.workplan_deliveryRequireEnvironmentalPracticesDescription = false; + }, + 'entity.workplan_deliveryInformCommunicationChannels'(_new) { + if (!_new) this.entity.workplan_deliveryRequireCommunicationChannels = false; + }, + 'entity.workplan_deliveryInformInnovation'(_new) { + if (!_new) this.entity.workplan_deliveryRequireInnovationTypes = false; + }, + 'entity.workplan_deliveryInformDocumentationTypes'(_new) { + if (!_new) this.entity.workplan_deliveryRequireDocumentationTypes = false; + }, + // Monitoramento - Campos originais + 'entity.workplan_monitoringInformTheFormOfAvailability'(_new) { + if (!_new) this.entity.workplan_monitoringRequireAvailabilityType = false; + }, + 'entity.workplan_monitoringInformAccessibilityMeasures'(_new) { + if (!_new) this.entity.workplan_monitoringRequireAccessibilityMeasures = false; + }, + 'entity.workplan_monitoringProvideTheProfileOfParticipants'(_new) { + if (!_new) this.entity.workplan_monitoringRequireParticipantProfile = false; + }, + 'entity.workplan_monitoringInformNumberOfParticipants'(_new) { + if (!_new) this.entity.workplan_monitoringRequireNumberOfParticipants = false; + }, + 'entity.workplan_monitoringInformThePriorityAudience'(_new) { + if (!_new) this.entity.workplan_monitoringRequirePriorityAudience = false; + }, + 'entity.workplan_monitoringReportExecutedRevenue'(_new) { + if (!_new) this.entity.workplan_monitoringRequireExecutedRevenue = false; + }, + // Monitoramento - Novos campos + 'entity.workplan_monitoringInformNumberOfCities'(_new) { + if (!_new) this.entity.workplan_monitoringRequireNumberOfCities = false; + }, + 'entity.workplan_monitoringInformNumberOfNeighborhoods'(_new) { + if (!_new) this.entity.workplan_monitoringRequireNumberOfNeighborhoods = false; + }, + 'entity.workplan_monitoringInformMediationActions'(_new) { + if (!_new) this.entity.workplan_monitoringRequireMediationActions = false; + }, + 'entity.workplan_monitoringInformCommercialUnits'(_new) { + if (!_new) { + this.entity.workplan_monitoringRequireCommercialUnits = false; + this.entity.workplan_monitoringRequireUnitPrice = false; + } + }, + 'entity.workplan_monitoringInformPaidStaffByRole'(_new) { + if (!_new) this.entity.workplan_monitoringRequirePaidStaffByRole = false; + }, + 'entity.workplan_monitoringInformTeamComposition'(_new) { + if (!_new) { + this.entity.workplan_monitoringRequireTeamCompositionGender = false; + this.entity.workplan_monitoringRequireTeamCompositionRace = false; + } + }, + 'entity.workplan_monitoringInformArtChainLink'(_new) { + if (!_new) this.entity.workplan_monitoringRequireArtChainLink = false; + }, + 'entity.workplan_monitoringInformCommunicationChannels'(_new) { + if (!_new) this.entity.workplan_monitoringRequireCommunicationChannels = false; + }, + 'entity.workplan_monitoringInformRevenueType'(_new) { + if (!_new) this.entity.workplan_monitoringRequireRevenueType = false; + }, + 'entity.workplan_monitoringInformCommunityCoauthors'(_new) { + if (!_new) this.entity.workplan_monitoringRequireCommunityCoauthorsDetail = false; + }, + 'entity.workplan_monitoringInformTransInclusion'(_new) { + if (!_new) this.entity.workplan_monitoringRequireTransInclusionActions = false; + }, + 'entity.workplan_monitoringInformAccessibilityPlan'(_new) { + if (!_new) this.entity.workplan_monitoringRequireExpectedAccessibilityMeasures = false; + }, + 'entity.workplan_monitoringInformEnvironmentalPractices'(_new) { + if (!_new) this.entity.workplan_monitoringRequireEnvironmentalPracticesDescription = false; + }, + 'entity.workplan_monitoringInformInnovation'(_new) { + if (!_new) this.entity.workplan_monitoringRequireInnovationTypes = false; + }, + 'entity.workplan_monitoringInformDocumentationTypes'(_new) { + if (!_new) this.entity.workplan_monitoringRequireDocumentationTypes = false; + }, + 'entity.workplan_monitoringInformSegmentDelivery'(_new) { + if (!_new) this.entity.workplan_monitoringRequireSegmentDelivery = false; + }, }, computed: { getWorkplanLabelDefault() { @@ -80,29 +228,125 @@ app.component('opportunity-enable-workplan', { this.entity.save(3000); }, disabledWorkPlan() { + // Duração do projeto this.entity.workplan_dataProjectlimitMaximumDurationOfProjects = false; this.entity.workplan_dataProjectmaximumDurationInMonths = 0; + this.entity.workplan_dataProjectInformCulturalArtisticSegment = false; + this.entity.workplan_dataProjectRequireCulturalArtisticSegment = false; + // Metas this.entity.workplan_metaInformTheStageOfCulturalMaking = false; this.entity.workplan_metaLimitNumberOfGoals = false; this.entity.workplan_metaMaximumNumberOfGoals = 0; + this.entity.workplan_goalInformTitle = false; + this.entity.workplan_goalRequireTitle = false; + this.entity.workplan_goalInformDescription = false; + this.entity.workplan_goalRequireDescription = false; + // Entregas this.entity.workplan_deliveryReportTheDeliveriesLinkedToTheGoals = false; this.disabledDeliveries(); }, disabledDeliveries() { + // Limites de entregas this.entity.workplan_deliveryLimitNumberOfDeliveries = false; this.entity.workplan_deliveryMaximumNumberOfDeliveries = 0; + + // Inscrição - Campos originais this.entity.workplan_registrationReportTheNumberOfParticipants = false; + this.entity.workplan_deliveryRequireExpectedNumberPeople = false; this.entity.workplan_registrationReportExpectedRenevue = false; this.entity.workplan_registrationInformCulturalArtisticSegment = false; + this.entity.workplan_deliveryRequireSegment = false; + + // Inscrição - Novos campos + this.entity.workplan_deliveryInformArtChainLink = false; + this.entity.workplan_deliveryRequireArtChainLink = false; + this.entity.workplan_deliveryInformTotalBudget = false; + this.entity.workplan_deliveryRequireTotalBudget = false; + this.entity.workplan_deliveryInformNumberOfCities = false; + this.entity.workplan_deliveryRequireNumberOfCities = false; + this.entity.workplan_deliveryInformNumberOfNeighborhoods = false; + this.entity.workplan_deliveryRequireNumberOfNeighborhoods = false; + this.entity.workplan_deliveryInformMediationActions = false; + this.entity.workplan_deliveryRequireMediationActions = false; + this.entity.workplan_deliveryInformPaidStaffByRole = false; + this.entity.workplan_deliveryRequirePaidStaffByRole = false; + this.entity.workplan_deliveryInformTeamComposition = false; + this.entity.workplan_deliveryRequireTeamCompositionGender = false; + this.entity.workplan_deliveryRequireTeamCompositionRace = false; + this.entity.workplan_deliveryInformRevenueType = false; + this.entity.workplan_deliveryRequireRevenueType = false; + this.entity.workplan_deliveryInformCommercialUnits = false; + this.entity.workplan_deliveryRequireCommercialUnits = false; + this.entity.workplan_deliveryRequireUnitPrice = false; + this.entity.workplan_deliveryInformCommunityCoauthors = false; + this.entity.workplan_deliveryRequireCommunityCoauthorsDetail = false; + this.entity.workplan_deliveryInformTransInclusion = false; + this.entity.workplan_deliveryRequireTransInclusionActions = false; + this.entity.workplan_deliveryInformAccessibilityPlan = false; + this.entity.workplan_deliveryRequireExpectedAccessibilityMeasures = false; + this.entity.workplan_deliveryInformEnvironmentalPractices = false; + this.entity.workplan_deliveryRequireEnvironmentalPracticesDescription = false; + this.entity.workplan_deliveryInformPressStrategy = false; + this.entity.workplan_deliveryInformCommunicationChannels = false; + this.entity.workplan_deliveryRequireCommunicationChannels = false; + this.entity.workplan_deliveryInformInnovation = false; + this.entity.workplan_deliveryRequireInnovationTypes = false; + this.entity.workplan_deliveryInformDocumentationTypes = false; + this.entity.workplan_deliveryRequireDocumentationTypes = false; + // Monitoramento - Campos originais this.entity.workplan_monitoringInformTheFormOfAvailability = false; + this.entity.workplan_monitoringRequireAvailabilityType = false; this.entity.workplan_monitoringInformAccessibilityMeasures = false; + this.entity.workplan_monitoringRequireAccessibilityMeasures = false; this.entity.workplan_monitoringProvideTheProfileOfParticipants = false; + this.entity.workplan_monitoringRequireParticipantProfile = false; + this.entity.workplan_monitoringInformNumberOfParticipants = false; + this.entity.workplan_monitoringRequireNumberOfParticipants = false; this.entity.workplan_monitoringInformThePriorityAudience = false; + this.entity.workplan_monitoringRequirePriorityAudience = false; this.entity.workplan_monitoringReportExecutedRevenue = false; + this.entity.workplan_monitoringRequireExecutedRevenue = false; + + // Monitoramento - Novos campos + this.entity.workplan_monitoringInformNumberOfCities = false; + this.entity.workplan_monitoringRequireNumberOfCities = false; + this.entity.workplan_monitoringInformNumberOfNeighborhoods = false; + this.entity.workplan_monitoringRequireNumberOfNeighborhoods = false; + this.entity.workplan_monitoringInformMediationActions = false; + this.entity.workplan_monitoringRequireMediationActions = false; + this.entity.workplan_monitoringInformCommercialUnits = false; + this.entity.workplan_monitoringRequireCommercialUnits = false; + this.entity.workplan_monitoringRequireUnitPrice = false; + this.entity.workplan_monitoringInformPaidStaffByRole = false; + this.entity.workplan_monitoringRequirePaidStaffByRole = false; + this.entity.workplan_monitoringInformTeamComposition = false; + this.entity.workplan_monitoringRequireTeamCompositionGender = false; + this.entity.workplan_monitoringRequireTeamCompositionRace = false; + this.entity.workplan_monitoringInformArtChainLink = false; + this.entity.workplan_monitoringRequireArtChainLink = false; + this.entity.workplan_monitoringInformCommunicationChannels = false; + this.entity.workplan_monitoringRequireCommunicationChannels = false; + this.entity.workplan_monitoringInformRevenueType = false; + this.entity.workplan_monitoringRequireRevenueType = false; + this.entity.workplan_monitoringInformCommunityCoauthors = false; + this.entity.workplan_monitoringRequireCommunityCoauthorsDetail = false; + this.entity.workplan_monitoringInformTransInclusion = false; + this.entity.workplan_monitoringRequireTransInclusionActions = false; + this.entity.workplan_monitoringInformAccessibilityPlan = false; + this.entity.workplan_monitoringRequireExpectedAccessibilityMeasures = false; + this.entity.workplan_monitoringInformEnvironmentalPractices = false; + this.entity.workplan_monitoringRequireEnvironmentalPracticesDescription = false; + this.entity.workplan_monitoringInformPressStrategy = false; + this.entity.workplan_monitoringInformInnovation = false; + this.entity.workplan_monitoringRequireInnovationTypes = false; + this.entity.workplan_monitoringInformDocumentationTypes = false; + this.entity.workplan_monitoringRequireDocumentationTypes = false; + this.entity.workplan_monitoringInformSegmentDelivery = false; + this.entity.workplan_monitoringRequireSegmentDelivery = false; }, pluralParaSingular(texto) { const palavras = texto.split(' '); @@ -134,4 +378,4 @@ app.component('opportunity-enable-workplan', { return palavrasNoSingular.join(' '); } }, -}) \ No newline at end of file +}) diff --git a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php index 8b05a74074..ca410d9503 100644 --- a/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php +++ b/src/modules/OpportunityWorkplan/components/opportunity-enable-workplan/template.php @@ -66,6 +66,15 @@

+
+ + +
+
-
{{ getDeliveryLabelDefault }} {{ index_ + 1 }}
-
- - -
-
- - -
+ +
+

{{ getDeliveryLabelDefault }}, execução e receita

+

-
- - +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+
+ + +
+
+ + +
+
+
+ +
+ + +
+
+
+ +
-
- - +
+ + +
+ +
+ +
-
- - +
+ +
+
+ + +
+ + +
+ +
+ +
+ +
+ +
+
+ +
+ + + +
+ +
+
+ + +
+
+ + +
+
+
+ + +
+

+

+ +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ {{ calculateGenderTotal(delivery.teamCompositionGender) }} +
+
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ {{ calculateRaceTotal(delivery.teamCompositionRace) }} +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+

+

+ +
+ + +
+ +
+ + + +
+
+
+ + +
+
+ + + +
+
+ +
+ + + +
+
@@ -248,4 +564,4 @@
- \ No newline at end of file + diff --git a/src/modules/ProjectMonitoring/Module.php b/src/modules/ProjectMonitoring/Module.php index 2379526807..a56930d889 100644 --- a/src/modules/ProjectMonitoring/Module.php +++ b/src/modules/ProjectMonitoring/Module.php @@ -358,6 +358,455 @@ public function register() { ]); $app->registerMetadata($evidenceLinks, Delivery::class); + // ============================================ + // NOVOS CAMPOS DE MONITORAMENTO (EXECUTADOS) + // ============================================ + + // Municípios executados + $executedNumberOfCities = new Metadata('executedNumberOfCities', [ + 'label' => i::__('Em quantos municípios a atividade foi realizada?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedNumberOfCities')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedNumberOfCities, Delivery::class); + + // Bairros executados + $executedNumberOfNeighborhoods = new Metadata('executedNumberOfNeighborhoods', [ + 'label' => i::__('Em quantos bairros a atividade foi realizada?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedNumberOfNeighborhoods')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedNumberOfNeighborhoods, Delivery::class); + + // Ações de mediação executadas + $executedMediationActions = new Metadata('executedMediationActions', [ + 'label' => i::__('Quantas ações de mediação/formação de público foram realizadas?'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedMediationActions')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedMediationActions, Delivery::class); + + // Unidades comercializadas (executado) + $executedCommercialUnits = new Metadata('executedCommercialUnits', [ + 'label' => i::__('Quantidade de unidades efetivamente comercializadas'), + 'type' => 'integer', + 'validations' => [ + 'v::intVal()->min(0)' => i::__('Deve ser um número maior ou igual a zero') + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedCommercialUnits')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedCommercialUnits, Delivery::class); + + // Valor unitário executado + $executedUnitPrice = new Metadata('executedUnitPrice', [ + 'label' => i::__('Valor unitário praticado (R$)'), + 'type' => 'currency', + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedUnitPrice')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedUnitPrice, Delivery::class); + + // Pessoas remuneradas por função (executado) + $executedPaidStaffByRole = new Metadata('executedPaidStaffByRole', [ + 'label' => i::__('Quantas pessoas foram remuneradas, por função?'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + }, + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedPaidStaffByRole')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedPaidStaffByRole, Delivery::class); + + // Composição da equipe por gênero (executado) + $executedTeamCompositionGender = new Metadata('executedTeamCompositionGender', [ + 'label' => i::__('Composição efetiva da equipe por gênero'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + }, + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedTeamCompositionGender')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedTeamCompositionGender, Delivery::class); + + // Composição da equipe por raça/cor (executado) + $executedTeamCompositionRace = new Metadata('executedTeamCompositionRace', [ + 'label' => i::__('Composição efetiva da equipe por raça/cor'), + 'type' => 'json', + 'serialize' => function ($val) { + return json_encode($val); + }, + 'unserialize' => function($val) { + return json_decode((string) $val, true); + }, + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedTeamCompositionRace')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedTeamCompositionRace, Delivery::class); + + $executedArtChainLink = new Metadata('executedArtChainLink', [ + 'label' => i::__('Principal elo das artes acionado (executado)'), + 'type' => 'select', + 'options' => [ + i::__('Acesso'), + i::__('Criação'), + i::__('Produção'), + i::__('Difusão'), + i::__('Circulação'), + i::__('Internacionalização'), + i::__('Formação'), + i::__('Fruição'), + i::__('Memória/Preservação'), + i::__('Pesquisa'), + i::__('Reflexão'), + i::__('Gestão Cultural'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedArtChainLink')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedArtChainLink, Delivery::class); + + $executedCommunicationChannels = new Metadata('executedCommunicationChannels', [ + 'label' => i::__('Canais de comunicação utilizados (executado)'), + 'type' => 'multiselect', + 'options' => [ + i::__('Instagram'), + i::__('Facebook'), + i::__('TikTok'), + i::__('YouTube'), + i::__('X/Twitter'), + i::__('WhatsApp (listas/grupos)'), + i::__('Telegram (canais/grupos)'), + i::__('Site/página oficial do projeto'), + i::__('E-mail marketing/newsletter'), + i::__('Plataformas de eventos/inscrição (ex.: Sympla/Shotgun/Eventbrite)'), + i::__('Portais, blogs e influenciadores/as locais'), + i::__('Rádio comunitária'), + i::__('Rádio comercial'), + i::__('TV local'), + i::__('Mídia impressa'), + i::__('Cartazes e materiais impressos'), + i::__('Carro de som'), + i::__('Outros'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedCommunicationChannels')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedCommunicationChannels, Delivery::class); + + $executedRevenueType = new Metadata('executedRevenueType', [ + 'label' => i::__('Qual o tipo de receita executada?'), + 'type' => 'multiselect', + 'options' => [ + i::__('Venda de ingressos'), + i::__('Venda de produtos'), + i::__('Patrocínio privado'), + i::__('Apoio cultural'), + i::__('Doações'), + i::__('Cachê'), + i::__('Prestação de serviços'), + i::__('Direitos autorais'), + i::__('Licenciamento'), + i::__('Não haverá receita'), + i::__('Outros'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedRevenueType')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedRevenueType, Delivery::class); + + $executedSegmentDelivery = new Metadata('executedSegmentDelivery', [ + 'label' => i::__('Segmento artístico-cultural executado da entrega'), + 'type' => 'select', + 'options' => [ + i::__('Artes Visuais'), + i::__('Artesanato'), + i::__('Audiovisual e Mídias Interativas'), + i::__('Circo'), + i::__('Culturas Tradicionais e Populares'), + i::__('Culturas dos Povos Originários'), + i::__('Dança'), + i::__('Design e Serviços Criativos'), + i::__('Economia, Produção e Áreas Técnicas da Cultura'), + i::__('Festas Populares'), + i::__('Humanidades'), + i::__('Livro, Leitura e Literatura'), + i::__('Música'), + i::__('Patrimônio Cultural Imaterial'), + i::__('Patrimônio Cultural Material'), + i::__('Performance'), + i::__('Produção e Áreas Técnicas da Cultura'), + i::__('Teatro'), + i::__('Transversalidades') + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedSegmentDelivery')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedSegmentDelivery, Delivery::class); + + $executedHasCommunityCoauthors = new Metadata('executedHasCommunityCoauthors', [ + 'label' => i::__('A atividade executada contou com envolvimento de comunidades/coletivos como coautores/coexecutores?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasCommunityCoauthors, Delivery::class); + + $executedCommunityCoauthorsDetail = new Metadata('executedCommunityCoauthorsDetail', [ + 'label' => i::__('Descreva o envolvimento executado das comunidades/coletivos como coautores/coexecutores'), + 'type' => 'text', + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedCommunityCoauthorsDetail')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedCommunityCoauthorsDetail, Delivery::class); + + $executedHasTransInclusionStrategy = new Metadata('executedHasTransInclusionStrategy', [ + 'label' => i::__('A atividade executada contou com estratégias voltadas à promoção do acesso de pessoas Trans e Travestis?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasTransInclusionStrategy, Delivery::class); + + $executedTransInclusionActions = new Metadata('executedTransInclusionActions', [ + 'label' => i::__('Quais ações executadas promoveram o acesso de pessoas Trans e Travestis?'), + 'type' => 'text', + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedTransInclusionActions')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedTransInclusionActions, Delivery::class); + + $executedHasAccessibilityPlan = new Metadata('executedHasAccessibilityPlan', [ + 'label' => i::__('A atividade executada contou com medidas de acessibilidade?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasAccessibilityPlan, Delivery::class); + + $executedExpectedAccessibilityMeasures = new Metadata('executedExpectedAccessibilityMeasures', [ + 'label' => i::__('Quais medidas de acessibilidade foram executadas na atividade?'), + 'type' => 'multiselect', + 'options' => [ + i::__('Rotas acessíveis, com espaço de manobra para cadeira de rodas'), + i::__('Palco acessível'), + i::__('Camarim acessível'), + i::__('Piso tátil'), + i::__('Rampas'), + i::__("Elevadores adequados para PCD's"), + i::__('Corrimãos e guarda-corpos'), + i::__("Banheiros adaptados para PCD's"), + i::__('Área de alimentação preferencial identificada'), + i::__("Vagas de estacionamento para PCD's reservadas"), + i::__("Assentos para pessoas obesas, pessoas com mobilidade reduzida, PCD's e pessoas idosas reservadas"), + i::__('Filas preferenciais identificadas'), + i::__('Iluminação adequada'), + i::__('Livro e/ou similares em braile'), + i::__('Audiolivro'), + i::__('Uso Língua Brasileira de Sinais - Libras'), + i::__('Sistema Braille em materiais impressos'), + i::__('Sistema de sinalização ou comunicação tátil'), + i::__('Audiodescrição'), + i::__('Legendas para surdos e ensurdecidos'), + i::__('Linguagem simples'), + i::__('Textos adaptados para software de leitor de tela'), + i::__('Capacitação em acessibilidade para equipes atuantes nos projetos culturais'), + i::__('Contratação de profissionais especializados em acessibilidade cultural'), + i::__('Contratação de profissionais com deficiência'), + i::__('Formação e sensibilização de agentes culturais sobre acessibilidade'), + i::__('Formação e sensibilização de públicos da cadeia produtiva cultural sobre acessibilidade'), + i::__("Envolvimento de PCD's na concepção do projeto"), + i::__('Outras'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedExpectedAccessibilityMeasures')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedExpectedAccessibilityMeasures, Delivery::class); + + $executedHasEnvironmentalPractices = new Metadata('executedHasEnvironmentalPractices', [ + 'label' => i::__('A atividade executada contou com medidas ou práticas socioambientais?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasEnvironmentalPractices, Delivery::class); + + $executedEnvironmentalPracticesDescription = new Metadata('executedEnvironmentalPracticesDescription', [ + 'label' => i::__('Quais medidas e práticas socioambientais foram executadas na atividade?'), + 'type' => 'text', + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedEnvironmentalPracticesDescription')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedEnvironmentalPracticesDescription, Delivery::class); + + $executedHasPressStrategy = new Metadata('executedHasPressStrategy', [ + 'label' => i::__('A atividade executada contou com estratégia de relacionamento com a imprensa?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasPressStrategy, Delivery::class); + + $executedHasInnovationAction = new Metadata('executedHasInnovationAction', [ + 'label' => i::__('A atividade executada contou com ação de experimentação/inovação?'), + 'type' => 'select', + 'options' => [ + 'true' => i::__('Sim'), + 'false' => i::__('Não'), + ], + ]); + $app->registerMetadata($executedHasInnovationAction, Delivery::class); + + $executedInnovationTypes = new Metadata('executedInnovationTypes', [ + 'label' => i::__('Quais tipos de experimentação/inovação foram executados?'), + 'type' => 'multiselect', + 'options' => [ + i::__('Uso de novas tecnologias (AR, VR, IA, etc.)'), + i::__('Novas linguagens artísticas'), + i::__('Fusão de linguagens'), + i::__('Metodologias participativas inovadoras'), + i::__('Novos modelos de gestão cultural'), + i::__('Economia criativa e novos modelos de negócio'), + i::__('Sustentabilidade e práticas ambientais inovadoras'), + i::__('Inclusão e acessibilidade de forma inovadora'), + i::__('Experimentação em espaços não convencionais'), + i::__('Coprodução/cocriação com públicos'), + i::__('Outros'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedInnovationTypes')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedInnovationTypes, Delivery::class); + + $executedDocumentationTypes = new Metadata('executedDocumentationTypes', [ + 'label' => i::__('Tipo de documentação produzida (executado)'), + 'type' => 'multiselect', + 'options' => [ + i::__('Fotografia'), + i::__('Vídeo'), + i::__('Áudio'), + i::__('Relatório textual'), + i::__('Caderno de processo'), + i::__('Publicação impressa'), + i::__('Publicação digital'), + i::__('Website/Plataforma online'), + i::__('Redes sociais'), + i::__('Depoimentos'), + i::__('Registros de processo'), + i::__('Acervo digitalizado'), + i::__('Não haverá documentação específica'), + i::__('Outros'), + ], + 'should_validate' => function($entity) { + if($entity->isMetadataRequired('executedDocumentationTypes')) { + return i::__('Campo obrigatório'); + } + return false; + } + ]); + $app->registerMetadata($executedDocumentationTypes, Delivery::class); + + // Medidas de acessibilidade executadas (já existe accessibilityMeasures) + // Este campo já existe e será usado para os dados executados + // Metadados para Registration (Inscrição) $this->registerRegistrationMetadata('workplanSnapshot', [ 'label' => i::__('Snapshot do plano de trabalho'), @@ -420,6 +869,32 @@ public function register() { $delivery->numberOfParticipants = $data['numberOfParticipants']; $delivery->participantProfile = $data['participantProfile']; $delivery->priorityAudience = $data['priorityAudience']; + + // Novos campos executados + $delivery->executedNumberOfCities = $data['executedNumberOfCities'] ?? null; + $delivery->executedNumberOfNeighborhoods = $data['executedNumberOfNeighborhoods'] ?? null; + $delivery->executedMediationActions = $data['executedMediationActions'] ?? null; + $delivery->executedCommercialUnits = $data['executedCommercialUnits'] ?? null; + $delivery->executedUnitPrice = $data['executedUnitPrice'] ?? null; + $delivery->executedPaidStaffByRole = $data['executedPaidStaffByRole'] ?? null; + $delivery->executedTeamCompositionGender = $data['executedTeamCompositionGender'] ?? null; + $delivery->executedTeamCompositionRace = $data['executedTeamCompositionRace'] ?? null; + $delivery->executedArtChainLink = $data['executedArtChainLink'] ?? null; + $delivery->executedCommunicationChannels = $data['executedCommunicationChannels'] ?? null; + $delivery->executedRevenueType = $data['executedRevenueType'] ?? null; + $delivery->executedSegmentDelivery = $data['executedSegmentDelivery'] ?? null; + $delivery->executedHasCommunityCoauthors = $data['executedHasCommunityCoauthors'] ?? null; + $delivery->executedCommunityCoauthorsDetail = $data['executedCommunityCoauthorsDetail'] ?? null; + $delivery->executedHasTransInclusionStrategy = $data['executedHasTransInclusionStrategy'] ?? null; + $delivery->executedTransInclusionActions = $data['executedTransInclusionActions'] ?? null; + $delivery->executedHasAccessibilityPlan = $data['executedHasAccessibilityPlan'] ?? null; + $delivery->executedExpectedAccessibilityMeasures = $data['executedExpectedAccessibilityMeasures'] ?? null; + $delivery->executedHasEnvironmentalPractices = $data['executedHasEnvironmentalPractices'] ?? null; + $delivery->executedEnvironmentalPracticesDescription = $data['executedEnvironmentalPracticesDescription'] ?? null; + $delivery->executedHasPressStrategy = $data['executedHasPressStrategy'] ?? null; + $delivery->executedHasInnovationAction = $data['executedHasInnovationAction'] ?? null; + $delivery->executedInnovationTypes = $data['executedInnovationTypes'] ?? null; + $delivery->executedDocumentationTypes = $data['executedDocumentationTypes'] ?? null; } $app->hook('entity(Registration).save:finish', function() use ($goals, $deliveries, $first_phase, $app) { @@ -486,7 +961,33 @@ public function register() { 'numberOfParticipants' => $delivery->numberOfParticipants, 'participantProfile' => $delivery->participantProfile, 'priorityAudience' => $delivery->priorityAudience, - 'status' => $delivery->status + 'status' => $delivery->status, + + // Novos campos executados + 'executedNumberOfCities' => $delivery->executedNumberOfCities, + 'executedNumberOfNeighborhoods' => $delivery->executedNumberOfNeighborhoods, + 'executedMediationActions' => $delivery->executedMediationActions, + 'executedCommercialUnits' => $delivery->executedCommercialUnits, + 'executedUnitPrice' => $delivery->executedUnitPrice, + 'executedPaidStaffByRole' => $delivery->executedPaidStaffByRole, + 'executedTeamCompositionGender' => $delivery->executedTeamCompositionGender, + 'executedTeamCompositionRace' => $delivery->executedTeamCompositionRace, + 'executedArtChainLink' => $delivery->executedArtChainLink, + 'executedCommunicationChannels' => $delivery->executedCommunicationChannels, + 'executedRevenueType' => $delivery->executedRevenueType, + 'executedSegmentDelivery' => $delivery->executedSegmentDelivery, + 'executedHasCommunityCoauthors' => $delivery->executedHasCommunityCoauthors, + 'executedCommunityCoauthorsDetail' => $delivery->executedCommunityCoauthorsDetail, + 'executedHasTransInclusionStrategy' => $delivery->executedHasTransInclusionStrategy, + 'executedTransInclusionActions' => $delivery->executedTransInclusionActions, + 'executedHasAccessibilityPlan' => $delivery->executedHasAccessibilityPlan, + 'executedExpectedAccessibilityMeasures' => $delivery->executedExpectedAccessibilityMeasures, + 'executedHasEnvironmentalPractices' => $delivery->executedHasEnvironmentalPractices, + 'executedEnvironmentalPracticesDescription' => $delivery->executedEnvironmentalPracticesDescription, + 'executedHasPressStrategy' => $delivery->executedHasPressStrategy, + 'executedHasInnovationAction' => $delivery->executedHasInnovationAction, + 'executedInnovationTypes' => $delivery->executedInnovationTypes, + 'executedDocumentationTypes' => $delivery->executedDocumentationTypes, ]; } diff --git a/src/themes/BaseV2/assets-src/sass/2.components/_opportunity-enable-workplan.scss b/src/themes/BaseV2/assets-src/sass/2.components/_opportunity-enable-workplan.scss index fb7f71e12b..f0345b1c34 100644 --- a/src/themes/BaseV2/assets-src/sass/2.components/_opportunity-enable-workplan.scss +++ b/src/themes/BaseV2/assets-src/sass/2.components/_opportunity-enable-workplan.scss @@ -1,5 +1,16 @@ @use '../0.settings/mixins' as *; +@keyframes fadeIn { + from { + opacity: 0; + transform: translateX(-4px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + .opportunity-enable-workplan { display: flex; flex-direction: column; @@ -39,7 +50,7 @@ margin-top: size(16); padding: size(20); width: 100%; - } + } .field__limits { max-width: 6.25rem; @@ -48,8 +59,8 @@ } .disabled-workplan { - display:flex; - align-items:right; + display: flex; + align-items: center; justify-content: flex-end; } @@ -60,4 +71,64 @@ .mt { margin-top: size(8); } + + .field__checkbox--sub { + margin-left: 16px; + font-size: 0.9em; + color: #666; + font-weight: normal; + white-space: nowrap; + animation: fadeIn 0.2s ease-in; + + input[type="checkbox"] { + margin-right: 8px; + } + } + + .field__group { + display: flex; + flex-direction: row; + align-items: center; + flex-wrap: wrap; + margin-bottom: 12px; + gap: 0; + + .field__checkbox { + flex-shrink: 0; + } + + .field__checkbox--sub:last-child { + margin-bottom: 0; + } + } + + .config-section-header { + font-size: 1.1rem; + font-weight: 600; + color: var(--mc-primary-500, #4a90e2); + margin-top: 24px; + margin-bottom: 8px; + padding-bottom: 6px; + border-bottom: 2px solid var(--mc-primary-500, #4a90e2); + + &:first-child { + margin-top: 0; + } + + @media (max-width: 768px) { + font-size: 1rem; + } + } + + .config-section-description { + font-size: 0.85rem; + color: var(--mc-gray-700, #666); + margin-bottom: 16px; + font-style: italic; + line-height: 1.4; + + @media (max-width: 768px) { + font-size: 0.8rem; + } + } } \ No newline at end of file diff --git a/src/themes/BaseV2/assets-src/sass/2.components/_registration-workplan.scss b/src/themes/BaseV2/assets-src/sass/2.components/_registration-workplan.scss index 6590b4e49d..d39dac98b7 100644 --- a/src/themes/BaseV2/assets-src/sass/2.components/_registration-workplan.scss +++ b/src/themes/BaseV2/assets-src/sass/2.components/_registration-workplan.scss @@ -31,8 +31,6 @@ &__goals-period { margin-top: size(12); - border: 1px solid #ccc; - padding: size(12); p { font-weight: bold; } @@ -88,4 +86,85 @@ padding: .5rem; z-index: 2; } -} \ No newline at end of file + + .form-section { + padding: size(16) 0; + + &__title { + font-size: 1.2rem; + font-weight: bold; + color: var(--mc-primary-500); + margin-bottom: size(8); + border-bottom: 2px solid var(--mc-primary-500); + padding-bottom: size(4); + + @media (max-width: 768px) { + font-size: 1rem; + } + } + + &__description { + font-size: 0.9rem; + color: var(--mc-gray-700, #666); + margin-bottom: size(16); + font-style: italic; + line-height: 1.5; + + @media (max-width: 768px) { + font-size: 0.85rem; + } + } + + &__divider { + border: 0; + border-top: 1px dashed var(--mc-gray-300, #ccc); + margin: size(24) 0; + } + } +} + +// Paid Staff by Role - Lista de funções remuneradas +.paid-staff-list { + margin-top: size(12); +} + +.paid-staff-item { + background-color: #ffffff; + border: 1px solid #ddd; + border-radius: 4px; + padding: size(16); + margin-bottom: size(12); + + &__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: size(12); + padding-bottom: size(8); + border-bottom: 1px solid #eee; + } + + &__title { + color: var(--mc-primary-500); + font-size: 0.95rem; + margin: 0; + } + + &__fields { + .field { + margin-bottom: size(8); + + label { + display: block; + font-weight: 500; + margin-bottom: size(4); + font-size: 0.9rem; + color: #666; + } + } + } +} + +.paid-staff-add { + margin-top: size(12); +} diff --git a/tests/src/OpportunityWorkplanRequireFieldsTest.php b/tests/src/OpportunityWorkplanRequireFieldsTest.php new file mode 100644 index 0000000000..0aaa39b6da --- /dev/null +++ b/tests/src/OpportunityWorkplanRequireFieldsTest.php @@ -0,0 +1,311 @@ +app; + $user = $this->userDirector->createUser(); + $this->login($user); + + // Criar oportunidade com workplan habilitado + $opportunity = $this->createOpportunityWithWorkplan([ + 'workplan_deliveryInformNumberOfCities' => true, + 'workplan_deliveryRequireNumberOfCities' => false, // NÃO obrigatório + ]); + + // Criar registration com workplan/goal/delivery + $registration = $this->createRegistrationWithWorkplan($opportunity, $user, [ + 'delivery' => [ + 'numberOfCities' => null // Vazio + ] + ]); + + $workplan = $app->repo(Workplan::class)->findOneBy(['registration' => $registration->id]); + $delivery = $workplan->goals[0]->deliveries[0]; + + // Verificar que campo NÃO é obrigatório + $isRequired = $delivery->isMetadataRequired('numberOfCities'); + $this->assertFalse($isRequired, 'Campo deveria ser opcional quando require=false'); + } + + /** + * Testa campo habilitado E obrigatório rejeita vazio + */ + public function testDeliveryFieldRequiredRejectsEmpty() + { + $app = $this->app; + $user = $this->userDirector->createUser(); + $this->login($user); + + $opportunity = $this->createOpportunityWithWorkplan([ + 'workplan_deliveryInformNumberOfCities' => true, + 'workplan_deliveryRequireNumberOfCities' => true, // Obrigatório + ]); + + $registration = $this->createRegistrationWithWorkplan($opportunity, $user, [ + 'delivery' => [ + 'numberOfCities' => null // Vazio + ] + ]); + + $workplan = $app->repo(Workplan::class)->findOneBy(['registration' => $registration->id]); + $delivery = $workplan->goals[0]->deliveries[0]; + + // Verificar que campo É obrigatório + $isRequired = $delivery->isMetadataRequired('numberOfCities'); + $this->assertTrue($isRequired, 'Campo deveria ser obrigatório quando require=true'); + } + + /** + * Testa campo JSON array vazio não passa quando obrigatório + */ + public function testJsonArrayFieldEmpty() + { + $app = $this->app; + $user = $this->userDirector->createUser(); + $this->login($user); + + $opportunity = $this->createOpportunityWithWorkplan([ + 'workplan_deliveryInformPaidStaffByRole' => true, + 'workplan_deliveryRequirePaidStaffByRole' => true, + ]); + + $registration = $this->createRegistrationWithWorkplan($opportunity, $user, [ + 'delivery' => [ + 'paidStaffByRole' => '[]' // Array vazio + ] + ]); + + $workplan = $app->repo(Workplan::class)->findOneBy(['registration' => $registration->id]); + $delivery = $workplan->goals[0]->deliveries[0]; + + // Validar que array vazio não passa + $value = $delivery->paidStaffByRole; + $decoded = json_decode($value, true); + $isValid = is_array($decoded) && count($decoded) > 0; + + $this->assertFalse($isValid, 'Array JSON vazio deveria falhar validação'); + } + + /** + * Testa campo gate+detail - detail só obrigatório se gate = true + */ + public function testGateDetailLogic() + { + $app = $this->app; + $user = $this->userDirector->createUser(); + $this->login($user); + + $opportunity = $this->createOpportunityWithWorkplan([ + 'workplan_deliveryInformTransInclusion' => true, + 'workplan_deliveryRequireTransInclusionActions' => true, + ]); + + // Cenário 1: gate = false → detail NÃO obrigatório + $registration1 = $this->createRegistrationWithWorkplan($opportunity, $user, [ + 'delivery' => [ + 'hasTransInclusionStrategy' => 'false', // Gate = false + 'transInclusionActions' => null + ] + ]); + + $workplan1 = $app->repo(Workplan::class)->findOneBy(['registration' => $registration1->id]); + $delivery1 = $workplan1->goals[0]->deliveries[0]; + + $isRequired1 = $delivery1->isMetadataRequired('transInclusionActions'); + $this->assertFalse($isRequired1, 'Detail NÃO deveria ser obrigatório quando gate=false'); + + // Cenário 2: gate = true → detail obrigatório + $registration2 = $this->createRegistrationWithWorkplan($opportunity, $user, [ + 'delivery' => [ + 'hasTransInclusionStrategy' => 'true', // Gate = true + 'transInclusionActions' => null + ] + ]); + + $workplan2 = $app->repo(Workplan::class)->findOneBy(['registration' => $registration2->id]); + $delivery2 = $workplan2->goals[0]->deliveries[0]; + + $isRequired2 = $delivery2->isMetadataRequired('transInclusionActions'); + $this->assertTrue($isRequired2, 'Detail deveria ser obrigatório quando gate=true'); + } + + /** + * Testa Goal - título opcional + */ + public function testGoalTitleOptional() + { + $app = $this->app; + $user = $this->userDirector->createUser(); + $this->login($user); + + $opportunity = $this->createOpportunityWithWorkplan([ + 'workplan_goalInformTitle' => true, + 'workplan_goalRequireTitle' => false, // Opcional + ]); + + $registration = $this->createRegistrationWithWorkplan($opportunity, $user, [ + 'goal' => [ + 'title' => null // Vazio + ] + ]); + + // Validação manual (hook não roda em teste unitário isolado) + $goalInformTitle = $opportunity->workplan_goalInformTitle; + $goalRequireTitle = $opportunity->workplan_goalRequireTitle ?? false; + $goalTitle = null; + + $hasError = ($goalInformTitle && $goalRequireTitle && !$goalTitle); + + $this->assertFalse($hasError, 'Título opcional não deveria gerar erro'); + } + + /** + * Testa Goal - título obrigatório + */ + public function testGoalTitleRequired() + { + $app = $this->app; + $user = $this->userDirector->createUser(); + $this->login($user); + + $opportunity = $this->createOpportunityWithWorkplan([ + 'workplan_goalInformTitle' => true, + 'workplan_goalRequireTitle' => true, // Obrigatório + ]); + + $registration = $this->createRegistrationWithWorkplan($opportunity, $user, [ + 'goal' => [ + 'title' => null // Vazio + ] + ]); + + // Validação manual + $goalInformTitle = $opportunity->workplan_goalInformTitle; + $goalRequireTitle = $opportunity->workplan_goalRequireTitle ?? false; + $goalTitle = null; + + $hasError = ($goalInformTitle && $goalRequireTitle && !$goalTitle); + + $this->assertTrue($hasError, 'Título obrigatório vazio deveria gerar erro'); + } + + // ===================================== + // MÉTODOS AUXILIARES + // ===================================== + + /** + * Cria oportunidade com workplan habilitado e metadata customizados + */ + private function createOpportunityWithWorkplan(array $metadata = []) + { + $app = $this->app; + $user = $this->userDirector->createUser(); + $agent = $user->profile; + + // Criar projeto pai + $project = new \MapasCulturais\Entities\Project; + $project->name = 'Projeto Teste Workplan'; + $project->shortDescription = 'Teste'; + $project->owner = $agent; + $project->save(true); + + // Criar oportunidade + $opportunity = new \MapasCulturais\Entities\Opportunity; + $opportunity->name = 'Oportunidade Teste Workplan'; + $opportunity->shortDescription = 'Teste'; + $opportunity->owner = $agent; + $opportunity->ownerEntity = $project; + $opportunity->registrationFrom = new \DateTime('now'); + $opportunity->registrationTo = new \DateTime('+30 days'); + + // Habilitar workplan + $opportunity->enableWorkplan = true; + $opportunity->workplan_deliveryReportTheDeliveriesLinkedToTheGoals = true; + + // Aplicar metadata customizados + foreach ($metadata as $key => $value) { + $opportunity->$key = $value; + } + + $opportunity->save(true); + + return $opportunity; + } + + /** + * Cria registration com workplan/goal/delivery + */ + private function createRegistrationWithWorkplan($opportunity, $user, array $data = []) + { + $app = $this->app; + + // Criar registration + $registration = new \MapasCulturais\Entities\Registration; + $registration->opportunity = $opportunity; + $registration->owner = $user->profile; + $registration->save(true); + + // Criar workplan + $workplan = new Workplan; + $workplan->registration = $registration; + $workplan->owner = $user->profile; + $workplan->projectDuration = 12; + $workplan->save(true); + + // Criar goal + $goal = new Goal; + $goal->workplan = $workplan; + $goal->owner = $user->profile; + $goal->monthInitial = 1; + $goal->monthEnd = 12; + $goal->title = $data['goal']['title'] ?? 'Meta Teste'; + $goal->description = $data['goal']['description'] ?? 'Descrição teste'; + $goal->save(true); + + // Criar delivery + $delivery = new Delivery; + $delivery->goal = $goal; + $delivery->owner = $user->profile; + $delivery->name = $data['delivery']['name'] ?? 'Entrega Teste'; + $delivery->description = $data['delivery']['description'] ?? 'Descrição teste'; + $delivery->typeDelivery = 'Outro'; + + // Aplicar metadata customizados do delivery + if (isset($data['delivery'])) { + foreach ($data['delivery'] as $key => $value) { + if (!in_array($key, ['name', 'description', 'typeDelivery'])) { + $delivery->$key = $value; + } + } + } + + $delivery->save(true); + + return $registration; + } +}