Present financial institutions with professional investor-facing descriptions and products
tests / ci (push) Canceled after 0s
tests / ci (push) Canceled after 0s
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
## Comunicação pública — orientação expressa do proprietário,11/11/2026
|
||||||
|
|
||||||
|
Apresentar conteúdos públicos com linguagem profissional,clara e apelativa,centrada na entidade,oportunidade,produtos e serviços úteis ao investidor. Não usar descrições de importação,cadastro,fichas,ficheiros ou documentos geradores como apresentação comercial. Preservar as fontes,versões e rastreabilidade no cadastro/editor;ligações institucionais e datas úteis podem ser consultadas separadamente. Não inventar ofertas,condições,disponibilidade,cobertura ou garantias para preencher lacunas. Esta orientação aplica-se transversalmente aos conteúdos do portal.
|
||||||
|
|
||||||
## Simulação de viabilidade — clarificação expressa do proprietário,11/11/2026
|
## Simulação de viabilidade — clarificação expressa do proprietário,11/11/2026
|
||||||
|
|
||||||
O InvesteMoz simula impostos para avaliar a viabilidade do negócio; não actua como entidade que executa pagamentos. Apresentar PPC/PEC e distribuição por meses como estimativas fiscais e projecções de tesouraria,com fontes e pressupostos. Montantes já pagos,quando fornecidos,são dados documentais para o cálculo. Não confundir uma hipótese de caixa com pagamento efectuado,declaração submetida ou cumprimento fiscal certificado. Preservar os cálculos,históricos e dados aceites; não criar serviços de cobrança/execução de pagamentos como requisito de conclusão deste simulador.
|
O InvesteMoz simula impostos para avaliar a viabilidade do negócio; não actua como entidade que executa pagamentos. Apresentar PPC/PEC e distribuição por meses como estimativas fiscais e projecções de tesouraria,com fontes e pressupostos. Montantes já pagos,quando fornecidos,são dados documentais para o cálculo. Não confundir uma hipótese de caixa com pagamento efectuado,declaração submetida ou cumprimento fiscal certificado. Preservar os cálculos,históricos e dados aceites; não criar serviços de cobrança/execução de pagamentos como requisito de conclusão deste simulador.
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
## 11/11/2026 — apresentação pública profissional do directório financeiro em QA
|
||||||
|
|
||||||
|
**Pedido do proprietário:** descrições profissionais e apelativas,com descrição/produtos/serviços úteis ao investidor,sem texto de fichas,importação ou documento gerador;orientação transversal registada emAGENTS. O directório e o detalhe passam a uma apresentação editorial comum:reconhece exclusivamente o resumo genérico exacto da importação e usa a natureza publicada da entidade;descrições editoriais específicas são preservadas e limpas da fórmula genérica final. Produtos identificados são apresentados pelos nomes/instrumentos existentes;os sete catálogos específicos disponíveis não são extrapolados para outras entidades. Nas restantes,apresentam-se áreas de serviço conhecidas ou contacto para conhecer soluções,sem inventar produtos/condições.
|
||||||
|
|
||||||
|
Retirados da apresentação pública nomes de ficheiros,hashes,linhas de importação,códigos de sucessora e descrições de registo interno. Ligações institucionais e datas úteis continuam acessíveis;metadados e fontes permanecem intactos no cadastro/editor. Instituições históricas/fundidas continuam identificadas como tal. Instituições sem chamada não mostram uma secção vazia de candidatura. Textos da selecção guardada e chamadas públicas dePorque investir/Serviços ajustados à linguagem do investidor. Fluxos de interesse,dossier,edição,revisão/publicação e filtros preservados.
|
||||||
|
|
||||||
|
**QA:** nove verificações afectadas aprovadas,três novas incluindo270referências fornecidas,descrições específicas preservadas,produtos não inventados e renderização dos componentes públicos reais de lista/detalhe. Sem escrita em dados activos ou nova publicação do lote;é uma projecção editorial dos dados já publicados. Evidência `outputs/funding-public-editorial-20261111/verification.json` e quatroHTMLs;isto não constitui conferência visual no navegador. Tipos/lint/compilação aprovados;pacote Git com aplicação/reversão conferidas no fecho.
|
||||||
|
|
||||||
|
**Estado:** implementado em QA,não instalado;homologação mantém `8f295f05` e270instituições publicadas. Restam instalação/visual e enriquecimento de produtos específicos quando houver informação disponível. O saldo dos outros módulos e resíduos fiscais permanece inalterado. Não é necessário seleccionar ou publicar novamente as270instituições para esta alteração de apresentação.
|
||||||
|
|
||||||
## 11/11/2026 — rectificação do volume PEC instalada em8f295f05
|
## 11/11/2026 — rectificação do volume PEC instalada em8f295f05
|
||||||
|
|
||||||
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às11:03:24Africa/Johannesburg(09:03:24UTC): `8f295f05510c786afeb8ea1dfa692e08d90a0d30`. No simulador fiscal,L8,**Rectificar volume na simulação PEC** conserva o valor original e o corrigido,com escolha explícita/fonte e efeito na estimativa apresentados no relatório. A hipótese de tesouraria anterior exige reconciliação se o total mudar;nenhum comprovativo pago,dedução ou saldo oficial é duplicado. Não executa pagamentos nem submete declaração rectificativa.
|
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às11:03:24Africa/Johannesburg(09:03:24UTC): `8f295f05510c786afeb8ea1dfa692e08d90a0d30`. No simulador fiscal,L8,**Rectificar volume na simulação PEC** conserva o valor original e o corrigido,com escolha explícita/fonte e efeito na estimativa apresentados no relatório. A hipótese de tesouraria anterior exige reconciliação se o total mudar;nenhum comprovativo pago,dedução ou saldo oficial é duplicado. Não executa pagamentos nem submete declaração rectificativa.
|
||||||
|
|||||||
@@ -1,3 +1,13 @@
|
|||||||
|
## 11/11/2026 — apresentação pública profissional do directório financeiro em QA
|
||||||
|
|
||||||
|
**Pedido do proprietário:** descrições profissionais e apelativas,com descrição/produtos/serviços úteis ao investidor,sem texto de fichas,importação ou documento gerador;orientação transversal registada emAGENTS. O directório e o detalhe passam a uma apresentação editorial comum:reconhece exclusivamente o resumo genérico exacto da importação e usa a natureza publicada da entidade;descrições editoriais específicas são preservadas e limpas da fórmula genérica final. Produtos identificados são apresentados pelos nomes/instrumentos existentes;os sete catálogos específicos disponíveis não são extrapolados para outras entidades. Nas restantes,apresentam-se áreas de serviço conhecidas ou contacto para conhecer soluções,sem inventar produtos/condições.
|
||||||
|
|
||||||
|
Retirados da apresentação pública nomes de ficheiros,hashes,linhas de importação,códigos de sucessora e descrições de registo interno. Ligações institucionais e datas úteis continuam acessíveis;metadados e fontes permanecem intactos no cadastro/editor. Instituições históricas/fundidas continuam identificadas como tal. Instituições sem chamada não mostram uma secção vazia de candidatura. Textos da selecção guardada e chamadas públicas dePorque investir/Serviços ajustados à linguagem do investidor. Fluxos de interesse,dossier,edição,revisão/publicação e filtros preservados.
|
||||||
|
|
||||||
|
**QA:** nove verificações afectadas aprovadas,três novas incluindo270referências fornecidas,descrições específicas preservadas,produtos não inventados e renderização dos componentes públicos reais de lista/detalhe. Sem escrita em dados activos ou nova publicação do lote;é uma projecção editorial dos dados já publicados. Evidência `outputs/funding-public-editorial-20261111/verification.json` e quatroHTMLs;isto não constitui conferência visual no navegador. Tipos/lint/compilação aprovados;pacote Git com aplicação/reversão conferidas no fecho.
|
||||||
|
|
||||||
|
**Estado:** implementado em QA,não instalado;homologação mantém `8f295f05` e270instituições publicadas. Restam instalação/visual e enriquecimento de produtos específicos quando houver informação disponível. O saldo dos outros módulos e resíduos fiscais permanece inalterado. Não é necessário seleccionar ou publicar novamente as270instituições para esta alteração de apresentação.
|
||||||
|
|
||||||
## 11/11/2026 — rectificação do volume PEC instalada em8f295f05
|
## 11/11/2026 — rectificação do volume PEC instalada em8f295f05
|
||||||
|
|
||||||
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às11:03:24Africa/Johannesburg(09:03:24UTC): `8f295f05510c786afeb8ea1dfa692e08d90a0d30`. No simulador fiscal,L8,**Rectificar volume na simulação PEC** conserva o valor original e o corrigido,com escolha explícita/fonte e efeito na estimativa apresentados no relatório. A hipótese de tesouraria anterior exige reconciliação se o total mudar;nenhum comprovativo pago,dedução ou saldo oficial é duplicado. Não executa pagamentos nem submete declaração rectificativa.
|
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às11:03:24Africa/Johannesburg(09:03:24UTC): `8f295f05510c786afeb8ea1dfa692e08d90a0d30`. No simulador fiscal,L8,**Rectificar volume na simulação PEC** conserva o valor original e o corrigido,com escolha explícita/fonte e efeito na estimativa apresentados no relatório. A hipótese de tesouraria anterior exige reconciliação se o total mudar;nenhum comprovativo pago,dedução ou saldo oficial é duplicado. Não executa pagamentos nem submete declaração rectificativa.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,3 +1,13 @@
|
|||||||
|
## 11/11/2026 — apresentação pública profissional do directório financeiro em QA
|
||||||
|
|
||||||
|
**Pedido do proprietário:** descrições profissionais e apelativas,com descrição/produtos/serviços úteis ao investidor,sem texto de fichas,importação ou documento gerador;orientação transversal registada emAGENTS. O directório e o detalhe passam a uma apresentação editorial comum:reconhece exclusivamente o resumo genérico exacto da importação e usa a natureza publicada da entidade;descrições editoriais específicas são preservadas e limpas da fórmula genérica final. Produtos identificados são apresentados pelos nomes/instrumentos existentes;os sete catálogos específicos disponíveis não são extrapolados para outras entidades. Nas restantes,apresentam-se áreas de serviço conhecidas ou contacto para conhecer soluções,sem inventar produtos/condições.
|
||||||
|
|
||||||
|
Retirados da apresentação pública nomes de ficheiros,hashes,linhas de importação,códigos de sucessora e descrições de registo interno. Ligações institucionais e datas úteis continuam acessíveis;metadados e fontes permanecem intactos no cadastro/editor. Instituições históricas/fundidas continuam identificadas como tal. Instituições sem chamada não mostram uma secção vazia de candidatura. Textos da selecção guardada e chamadas públicas dePorque investir/Serviços ajustados à linguagem do investidor. Fluxos de interesse,dossier,edição,revisão/publicação e filtros preservados.
|
||||||
|
|
||||||
|
**QA:** nove verificações afectadas aprovadas,três novas incluindo270referências fornecidas,descrições específicas preservadas,produtos não inventados e renderização dos componentes públicos reais de lista/detalhe. Sem escrita em dados activos ou nova publicação do lote;é uma projecção editorial dos dados já publicados. Evidência `outputs/funding-public-editorial-20261111/verification.json` e quatroHTMLs;isto não constitui conferência visual no navegador. Tipos/lint/compilação aprovados;pacote Git com aplicação/reversão conferidas no fecho.
|
||||||
|
|
||||||
|
**Estado:** implementado em QA,não instalado;homologação mantém `8f295f05` e270instituições publicadas. Restam instalação/visual e enriquecimento de produtos específicos quando houver informação disponível. O saldo dos outros módulos e resíduos fiscais permanece inalterado. Não é necessário seleccionar ou publicar novamente as270instituições para esta alteração de apresentação.
|
||||||
|
|
||||||
## 11/11/2026 — rectificação do volume PEC instalada em8f295f05
|
## 11/11/2026 — rectificação do volume PEC instalada em8f295f05
|
||||||
|
|
||||||
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às11:03:24Africa/Johannesburg(09:03:24UTC): `8f295f05510c786afeb8ea1dfa692e08d90a0d30`. No simulador fiscal,L8,**Rectificar volume na simulação PEC** conserva o valor original e o corrigido,com escolha explícita/fonte e efeito na estimativa apresentados no relatório. A hipótese de tesouraria anterior exige reconciliação se o total mudar;nenhum comprovativo pago,dedução ou saldo oficial é duplicado. Não executa pagamentos nem submete declaração rectificativa.
|
Instalação expressamente autorizada,executada pelo processo Git existente e conferida às11:03:24Africa/Johannesburg(09:03:24UTC): `8f295f05510c786afeb8ea1dfa692e08d90a0d30`. No simulador fiscal,L8,**Rectificar volume na simulação PEC** conserva o valor original e o corrigido,com escolha explícita/fonte e efeito na estimativa apresentados no relatório. A hipótese de tesouraria anterior exige reconciliação se o total mudar;nenhum comprovativo pago,dedução ou saldo oficial é duplicado. Não executa pagamentos nem submete declaração rectificativa.
|
||||||
|
|||||||
@@ -69,45 +69,7 @@ export function ClassificationSummary({ value }: { value: Classification }) {
|
|||||||
.map((k) => financialTypes[k] ?? k)
|
.map((k) => financialTypes[k] ?? k)
|
||||||
.join(' · ') || 'Por classificar'}
|
.join(' · ') || 'Por classificar'}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
{['merged','historical','unknown'].includes(value.institution_status??'') && <p>{value.institution_status==='merged'?'Instituição integrada noutra entidade ou com actividade encerrada.':value.institution_status==='historical'?'Informação histórica.':'Situação actual por confirmar junto da instituição.'}</p>}
|
||||||
Situação segundo a fonte:{' '}
|
|
||||||
{value.institution_status === 'merged'
|
|
||||||
? 'Fundida/extinta segundo o documento; não apresentar como instituição activa'
|
|
||||||
: value.institution_status === 'historical'
|
|
||||||
? 'Registo histórico'
|
|
||||||
: value.institution_status === 'unknown'
|
|
||||||
? 'Situação actual por confirmar'
|
|
||||||
: 'Reportada no documento; não é confirmação independente de licença actual'}
|
|
||||||
{value.successor_key
|
|
||||||
? ` · Sucessora indicada: ${value.successor_key}`
|
|
||||||
: ''}
|
|
||||||
</p>
|
|
||||||
{value.register_reference && (
|
|
||||||
<p>
|
|
||||||
Referência de registo no documento:{' '}
|
|
||||||
{value.register_reference}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
<details>
|
|
||||||
<summary>
|
|
||||||
Rastrear o conteúdo até ao documento e à linha de origem
|
|
||||||
</summary>
|
|
||||||
<p>
|
|
||||||
{value.source_document_name} · SHA-256{' '}
|
|
||||||
{value.source_document_sha256}
|
|
||||||
</p>
|
|
||||||
{(value.source_rows ?? []).map((row) => (
|
|
||||||
<article key={row.id}>
|
|
||||||
<h3>{row.section}</h3>
|
|
||||||
<p>{row.id}</p>
|
|
||||||
<p>{row.text}</p>
|
|
||||||
</article>
|
|
||||||
))}
|
|
||||||
</details>
|
|
||||||
<p>
|
|
||||||
Categoria e presença física não confirmam cobertura de crédito,
|
|
||||||
oferta de seguro específica, garantia ou aceitação do processo.
|
|
||||||
</p>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,8 +127,8 @@ export default function FundingCriteriaAssessment({
|
|||||||
)}
|
)}
|
||||||
{criteria.length === 0 && (
|
{criteria.length === 0 && (
|
||||||
<p>
|
<p>
|
||||||
Esta ficha não contém critérios suficientes para conferir a
|
Consulte a entidade para conhecer as condições de acesso
|
||||||
correspondência.
|
aplicáveis ao seu investimento.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{awaitingType > 0 && (
|
{awaitingType > 0 && (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
evidenceLabels,
|
evidenceLabels,
|
||||||
type Classification,
|
type Classification,
|
||||||
} from '@/components/funding-classification';
|
} from '@/components/funding-classification';
|
||||||
|
import {fundingPublicPresentation} from '@/lib/funding-public-presentation';
|
||||||
import { card, input, button } from '@/pages/investment/shared';
|
import { card, input, button } from '@/pages/investment/shared';
|
||||||
|
|
||||||
export type FundingSource = {
|
export type FundingSource = {
|
||||||
@@ -551,40 +552,7 @@ export function InstitutionFields({
|
|||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
function Source({ value }: { value: FundingSource }) {
|
function Source({value}:{value:FundingSource}){return <>{value.source_url&&<details><summary>Informação institucional</summary><a className="underline" href={value.source_url} target="_blank" rel="noopener noreferrer">Consultar informação da entidade</a><p>Consulta em {value.checked_on}.</p></details>}{value.evidence_status==='historical'&&<p>Informação histórica.</p>}{value.evidence_status==='conflict'&&<p>Informação por esclarecer junto da instituição.</p>}</>;}
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p>
|
|
||||||
{value.source_url ? (
|
|
||||||
<a
|
|
||||||
className="underline"
|
|
||||||
href={value.source_url}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
{value.source_reference}
|
|
||||||
</a>
|
|
||||||
) : (
|
|
||||||
value.source_reference
|
|
||||||
)}{' '}
|
|
||||||
· consulta {value.checked_on}
|
|
||||||
</p>
|
|
||||||
{value.source_document_name && (
|
|
||||||
<p>
|
|
||||||
Documento: {value.source_document_name} · SHA-256{' '}
|
|
||||||
{value.source_document_sha256}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{value.evidence_status && (
|
|
||||||
<p>
|
|
||||||
{evidenceLabels[value.evidence_status] ??
|
|
||||||
value.evidence_status}{' '}
|
|
||||||
· {value.origin}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export function InstitutionView({ value }: { value: InstitutionDetails }) {
|
export function InstitutionView({ value }: { value: InstitutionDetails }) {
|
||||||
return (
|
return (
|
||||||
<section className={card}>
|
<section className={card}>
|
||||||
@@ -643,29 +611,28 @@ export function InstitutionView({ value }: { value: InstitutionDetails }) {
|
|||||||
: l.role === 'branch'
|
: l.role === 'branch'
|
||||||
? 'Balcão'
|
? 'Balcão'
|
||||||
: 'Ponto de contacto institucional'}
|
: 'Ponto de contacto institucional'}
|
||||||
: {l.label} · {l.address}
|
: {l.label && !/^(Morada|Localização).*(documento|fonte)/i.test(l.label) ? l.label+' · ' : ''}{l.address}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{l.territory_path?.map((n) => n.name).join(' / ') ||
|
{l.territory_path?.map((n) => n.name).join(' / ') ||
|
||||||
'Correspondência ao cadastro territorial por confirmar'}
|
'Localização detalhada a confirmar'}
|
||||||
</p>
|
</p>
|
||||||
{l.location_text && (
|
{l.location_text && (
|
||||||
<p>Local indicado: {l.location_text}</p>
|
<p>Local indicado: {l.location_text}</p>
|
||||||
)}
|
)}
|
||||||
{l.hours && (
|
{l.hours && (
|
||||||
<p>
|
<p>
|
||||||
Horário indicado na fonte (pode ter mudado):{' '}
|
Horário de atendimento:{' '}
|
||||||
{l.hours}
|
{l.hours}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<Source value={l} />
|
<Source value={l} />
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
<h3>Produtos e programas</h3>
|
<h3>Produtos e serviços</h3>
|
||||||
{value.products.length === 0 && (
|
{value.products.length === 0 && (
|
||||||
<p>
|
<p>
|
||||||
Produtos e condições por confirmar; a identificação da
|
{fundingPublicPresentation({...value,title:"",kind:"institution",summary:""}).services.join(" · ") || "Consulte as soluções disponíveis junto da instituição."}
|
||||||
instituição não confirma oferta disponível.
|
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{value.products.map((p) => (
|
{value.products.map((p) => (
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
type PublicFunding = {
|
||||||
|
title: string;
|
||||||
|
kind: string;
|
||||||
|
summary: string;
|
||||||
|
institution_types?: string[];
|
||||||
|
institution_services?: string[];
|
||||||
|
products?: {
|
||||||
|
title: string;
|
||||||
|
instrument?: string | null;
|
||||||
|
availability?: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
const descriptions: Record<string, string> = {
|
||||||
|
commercial_bank: 'Instituição de banca comercial.',
|
||||||
|
investment_bank: 'Instituição especializada em banca de investimento.',
|
||||||
|
bank: 'Instituição bancária.',
|
||||||
|
microbank: 'Instituição de microfinanças.',
|
||||||
|
cooperative: 'Cooperativa de crédito.',
|
||||||
|
savings: 'Organização de poupança e empréstimo.',
|
||||||
|
investment_manager:
|
||||||
|
'Sociedade especializada na gestão de fundos de investimento.',
|
||||||
|
investment_company: 'Sociedade de investimento.',
|
||||||
|
pension_manager: 'Entidade gestora de fundos de pensões.',
|
||||||
|
insurer: 'Instituição do sector segurador.',
|
||||||
|
reinsurer: 'Instituição especializada em resseguros.',
|
||||||
|
microinsurer: 'Instituição de microsseguros.',
|
||||||
|
broker: 'Entidade de corretagem de seguros.',
|
||||||
|
agent: 'Entidade de mediação de seguros.',
|
||||||
|
reinsurance_broker: 'Entidade de corretagem de resseguros.',
|
||||||
|
exchange_house: 'Instituição de serviços cambiais.',
|
||||||
|
electronic_money: 'Instituição de moeda electrónica.',
|
||||||
|
interbank: 'Entidade de serviços interbancários.',
|
||||||
|
supervisor: 'Entidade de supervisão do sistema financeiro.',
|
||||||
|
};
|
||||||
|
const services: Record<string, string> = {
|
||||||
|
equity: 'Capital e participação no investimento',
|
||||||
|
foreign_exchange: 'Câmbio',
|
||||||
|
guarantee: 'Garantias',
|
||||||
|
insurance: 'Seguros',
|
||||||
|
investment: 'Financiamento do investimento',
|
||||||
|
investment_management: 'Gestão de investimentos',
|
||||||
|
payments: 'Serviços de pagamento',
|
||||||
|
pension: 'Pensões',
|
||||||
|
working_capital: 'Financiamento de tesouraria',
|
||||||
|
};
|
||||||
|
export function fundingPublicPresentation(p: PublicFunding) {
|
||||||
|
const generic =
|
||||||
|
p.summary.trim() ===
|
||||||
|
'Instituição identificada no documento fornecido. Categoria, localização e contactos são informativos; produtos, disponibilidade e condições não documentados permanecem por confirmar.';
|
||||||
|
const description = generic
|
||||||
|
? (descriptions[p.institution_types?.[0] ?? ''] ??
|
||||||
|
'Entidade do sector financeiro.')
|
||||||
|
: p.summary
|
||||||
|
.replace(
|
||||||
|
/\s*Referência informativa, não concessão de financiamento\.?$/,
|
||||||
|
'',
|
||||||
|
)
|
||||||
|
.replace(/Público\/finalidade:/g, 'Soluções para')
|
||||||
|
.replace(/\.\./g, '.')
|
||||||
|
.trim();
|
||||||
|
const products = (p.products ?? [])
|
||||||
|
.filter((x) => x.availability !== 'closed')
|
||||||
|
.map((x) => ({ title: x.title, detail: x.instrument?.trim() ?? '' }));
|
||||||
|
return {
|
||||||
|
description,
|
||||||
|
products,
|
||||||
|
services: [
|
||||||
|
...new Set(
|
||||||
|
(p.institution_services ?? [])
|
||||||
|
.map((s) => services[s])
|
||||||
|
.filter(Boolean),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {fundingPublicPresentation} from '@/lib/funding-public-presentation';
|
||||||
import {
|
import {
|
||||||
financialCategories,
|
financialCategories,
|
||||||
financialTypes,
|
financialTypes,
|
||||||
@@ -64,11 +65,11 @@ export default function Index({
|
|||||||
</Link>
|
</Link>
|
||||||
)}
|
)}
|
||||||
<Link className="underline" href="/fundos/guardados">
|
<Link className="underline" href="/fundos/guardados">
|
||||||
Programas que guardei
|
A minha selecção
|
||||||
</Link>
|
</Link>
|
||||||
<p>
|
<p>
|
||||||
Consulte os programas e as condições documentadas. Cada chamada
|
Encontre bancos, seguradoras, fundos e outros parceiros financeiros.
|
||||||
tem as suas próprias datas e critérios.
|
Explore as soluções e os contactos adequados ao seu investimento.
|
||||||
</p>
|
</p>
|
||||||
<form
|
<form
|
||||||
className={`${card} flex flex-wrap gap-4`}
|
className={`${card} flex flex-wrap gap-4`}
|
||||||
@@ -273,7 +274,8 @@ export default function Index({
|
|||||||
{item.payload.title}
|
{item.payload.title}
|
||||||
</Link>
|
</Link>
|
||||||
</h2>
|
</h2>
|
||||||
<p>{item.payload.summary}</p>
|
<p>{fundingPublicPresentation(item.payload).description}</p>
|
||||||
|
{item.payload.kind==='institution' && <div><h3 className="font-semibold">Produtos e serviços</h3>{fundingPublicPresentation(item.payload).products.length ? <ul>{fundingPublicPresentation(item.payload).products.map(p=><li key={p.title}>{p.title}{p.detail ? ': '+p.detail : ''}</li>)}</ul> : <p>{fundingPublicPresentation(item.payload).services.join(' · ') || 'Consulte as soluções disponíveis junto da instituição.'}</p>}</div>}
|
||||||
<p>
|
<p>
|
||||||
{(item.payload.institution_categories ?? [])
|
{(item.payload.institution_categories ?? [])
|
||||||
.map((k) => financialCategories[k] ?? k)
|
.map((k) => financialCategories[k] ?? k)
|
||||||
@@ -283,16 +285,16 @@ export default function Index({
|
|||||||
.map((k) => financialTypes[k] ?? k)
|
.map((k) => financialTypes[k] ?? k)
|
||||||
.join(' · ')}
|
.join(' · ')}
|
||||||
</p>
|
</p>
|
||||||
<p>Gestor: {item.payload.manager_name}</p>
|
{item.payload.kind!=='institution' && <p>Entidade gestora: {item.payload.manager_name}</p>}
|
||||||
{Object.entries(item.filter_explanation ?? {}).map(
|
{Object.entries(item.filter_explanation ?? {}).map(
|
||||||
([key, message]) => (
|
([key, message]) => (
|
||||||
<p key={key}>{message}</p>
|
<p key={key}>{message}</p>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
{item.payload.windows.length === 0 ? (
|
{item.payload.kind==='institution' && item.payload.windows.length===0 ? null : item.payload.windows.length === 0 ? (
|
||||||
<p>
|
<p>
|
||||||
Não existe uma janela de candidatura confirmada
|
Não existe uma janela de candidatura confirmada
|
||||||
nesta ficha.
|
para este programa.
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<ul>
|
<ul>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export default function Saved({
|
|||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Frame title="Programas que guardei">
|
<Frame title="A minha selecção">
|
||||||
<Link className="underline" href="/fundos">
|
<Link className="underline" href="/fundos">
|
||||||
Consultar fundos e programas
|
Consultar fundos e programas
|
||||||
</Link>
|
</Link>
|
||||||
@@ -39,7 +39,7 @@ export default function Saved({
|
|||||||
className="underline"
|
className="underline"
|
||||||
href={`/fundos/${item.id}`}
|
href={`/fundos/${item.id}`}
|
||||||
>
|
>
|
||||||
Consultar ficha publicada
|
Consultar detalhes
|
||||||
</Link>
|
</Link>
|
||||||
{item.current_version !== item.saved_version && (
|
{item.current_version !== item.saved_version && (
|
||||||
<p>
|
<p>
|
||||||
@@ -51,7 +51,7 @@ export default function Saved({
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<p>
|
<p>
|
||||||
A ficha já não está disponível publicamente. A sua
|
Esta informação já não está disponível para consulta pública. A sua
|
||||||
referência guardada foi conservada.
|
referência guardada foi conservada.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {fundingPublicPresentation} from '@/lib/funding-public-presentation';
|
||||||
import {
|
import {
|
||||||
InstitutionView,
|
InstitutionView,
|
||||||
type InstitutionDetails,
|
type InstitutionDetails,
|
||||||
@@ -32,7 +33,7 @@ export default function Show({
|
|||||||
return (
|
return (
|
||||||
<Frame title={payload.title}>
|
<Frame title={payload.title}>
|
||||||
<Link className="underline" href="/fundos/guardados">
|
<Link className="underline" href="/fundos/guardados">
|
||||||
Programas que guardei
|
A minha selecção
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
className="underline"
|
className="underline"
|
||||||
@@ -40,7 +41,7 @@ export default function Show({
|
|||||||
>
|
>
|
||||||
Voltar aos fundos e programas
|
Voltar aos fundos e programas
|
||||||
</Link>
|
</Link>
|
||||||
<p>{payload.summary}</p>
|
<section><h2 className="text-xl font-semibold">Descrição</h2><p>{fundingPublicPresentation(payload).description}</p></section>
|
||||||
{payload.kind === 'institution' && (
|
{payload.kind === 'institution' && (
|
||||||
<InstitutionView value={payload as InstitutionDetails} />
|
<InstitutionView value={payload as InstitutionDetails} />
|
||||||
)}
|
)}
|
||||||
@@ -55,8 +56,7 @@ export default function Show({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<p>
|
<p>
|
||||||
Gestor: {payload.manager_name}. Versão publicada:{' '}
|
Entidade responsável: {payload.manager_name}.
|
||||||
{record.version}.
|
|
||||||
</p>
|
</p>
|
||||||
{(payload.territory_paths ?? []).map((path, index) => (
|
{(payload.territory_paths ?? []).map((path, index) => (
|
||||||
<p key={index}>
|
<p key={index}>
|
||||||
@@ -82,30 +82,14 @@ export default function Show({
|
|||||||
: 'Cobertura por confirmar'}
|
: 'Cobertura por confirmar'}
|
||||||
{payload.coverage_text ? ` — ${payload.coverage_text}` : ''}
|
{payload.coverage_text ? ` — ${payload.coverage_text}` : ''}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
{payload.source_url && <details><summary>Informação institucional</summary><a className="underline" href={payload.source_url} target="_blank" rel="noopener noreferrer">Consultar informação da entidade</a><p>Actualização consultada em {payload.source_checked_on}.</p></details>}
|
||||||
<a
|
{(payload.kind!=='institution'||payload.windows.length>0) && ( <section className={card}>
|
||||||
className="underline"
|
|
||||||
href={payload.source_url || undefined}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Consultar fonte: {payload.source_reference}
|
|
||||||
</a>
|
|
||||||
. Consultada em {payload.source_checked_on}.{' '}
|
|
||||||
{payload.source_document_name && (
|
|
||||||
<span>
|
|
||||||
Documento: {payload.source_document_name} · SHA-256{' '}
|
|
||||||
{payload.source_document_sha256}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<section className={card}>
|
|
||||||
<h2 className="text-xl font-semibold">
|
<h2 className="text-xl font-semibold">
|
||||||
Janelas de candidatura
|
Janelas de candidatura
|
||||||
</h2>
|
</h2>
|
||||||
{payload.windows.length === 0 && (
|
{payload.windows.length === 0 && (
|
||||||
<p>
|
<p>
|
||||||
Não há uma janela de candidatura confirmada nesta ficha.
|
Não há uma janela de candidatura confirmada para este programa.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{payload.windows.map((item) => (
|
{payload.windows.map((item) => (
|
||||||
@@ -150,11 +134,11 @@ export default function Show({
|
|||||||
)}
|
)}
|
||||||
</article>
|
</article>
|
||||||
))}
|
))}
|
||||||
</section>
|
</section>)}
|
||||||
<FundingCriteriaAssessment
|
{(payload.kind!=='institution'||payload.criteria.length>0||payload.windows.some(window=>window.criteria.length>0)) && ( <FundingCriteriaAssessment
|
||||||
key={record.id + ':' + record.version_id}
|
key={record.id + ':' + record.version_id}
|
||||||
record={record}
|
record={record}
|
||||||
/>
|
/>)}
|
||||||
{payload.documents.length > 0 && (
|
{payload.documents.length > 0 && (
|
||||||
<section className={card}>
|
<section className={card}>
|
||||||
<h2>Documentos associados</h2>
|
<h2>Documentos associados</h2>
|
||||||
@@ -179,7 +163,7 @@ export default function Show({
|
|||||||
<section className={card}>
|
<section className={card}>
|
||||||
<h2>Guardar interesse</h2>
|
<h2>Guardar interesse</h2>
|
||||||
<p>
|
<p>
|
||||||
Guarda uma referência privada ao programa. Não envia uma
|
Guarde esta opção para retomar a preparação do investimento. Não envia uma
|
||||||
candidatura nem contacta a entidade.
|
candidatura nem contacta a entidade.
|
||||||
</p>
|
</p>
|
||||||
{saved ? (
|
{saved ? (
|
||||||
@@ -210,7 +194,7 @@ export default function Show({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Guardar programa na minha área privada
|
Guardar na minha selecção
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<Link className="underline" href="/login">
|
<Link className="underline" href="/login">
|
||||||
|
|||||||
@@ -1272,7 +1272,7 @@ export default function Services(p: Props) {
|
|||||||
</p>
|
</p>
|
||||||
))}
|
))}
|
||||||
<Link href="/investidor" className="underline">
|
<Link href="/investidor" className="underline">
|
||||||
Abrir fichas e manifestações de interesse da
|
Consultar oportunidades e manifestações de interesse da
|
||||||
carteira
|
carteira
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ export default function WhyInvest() {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'Território e ligações',
|
'Território e ligações',
|
||||||
'Compare localizações, corredores e parques com o mercado que pretende servir. Confira acessos, logística, infra-estruturas e as condições documentadas de cada ficha.',
|
'Compare localizações, corredores e parques com o mercado que pretende servir. Confira acessos, logística, infra-estruturas e as condições de cada localização.',
|
||||||
'/oportunidades?view=map',
|
'/oportunidades?view=map',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@@ -163,7 +163,7 @@ export default function WhyInvest() {
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'Localizações e enquadramentos',
|
'Localizações e enquadramentos',
|
||||||
'Explore o mapa e as fichas de corredores, clusters, zonas, parques e projectos. Cada referência conserva a natureza e o fundamento que lhe estão associados.',
|
'Explore o mapa e os detalhes de corredores, clusters, zonas, parques e projectos. Cada referência conserva a natureza e o fundamento que lhe estão associados.',
|
||||||
'/oportunidades?view=map',
|
'/oportunidades?view=map',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import * as presentation from '../../resources/js/lib/funding-public-presentation.ts';
|
||||||
import test from "node:test";
|
import test from "node:test";
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
@@ -26,6 +27,7 @@ function load(file, mocks = {}) {
|
|||||||
(name) =>
|
(name) =>
|
||||||
name in mocks
|
name in mocks
|
||||||
? mocks[name]
|
? mocks[name]
|
||||||
|
: name === "@/lib/funding-public-presentation" ? presentation
|
||||||
: name === "@/components/funding-classification"
|
: name === "@/components/funding-classification"
|
||||||
? load("components/funding-classification.tsx", mocks)
|
? load("components/funding-classification.tsx", mocks)
|
||||||
: name === "@/pages/investment/shared"
|
: name === "@/pages/investment/shared"
|
||||||
@@ -87,7 +89,8 @@ test("actual institution view keeps physical address separate from product cover
|
|||||||
assert.match(html, /Condições: Por confirmar/);
|
assert.match(html, /Condições: Por confirmar/);
|
||||||
assert.match(html, /Disponibilidade: Por confirmar/);
|
assert.match(html, /Disponibilidade: Por confirmar/);
|
||||||
assert.match(html, /https:\/\/example.invalid\/contact/);
|
assert.match(html, /https:\/\/example.invalid\/contact/);
|
||||||
assert.match(html, /SYNTHETIC QA source/);
|
assert.match(html, /https:\/\/example\.invalid\/source/);
|
||||||
|
assert.match(html, /Consultar informação da entidade/);
|
||||||
assert.match(html, /não é elegibilidade confirmada/);
|
assert.match(html, /não é elegibilidade confirmada/);
|
||||||
});
|
});
|
||||||
test("adding a physical location does not fill product financing coverage", () => {
|
test("adding a physical location does not fill product financing coverage", () => {
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import fs from 'node:fs';
|
||||||
|
import { compileFunction } from 'node:vm';
|
||||||
|
import { createRequire } from 'node:module';
|
||||||
|
import React from 'react';
|
||||||
|
import { renderToStaticMarkup } from 'react-dom/server';
|
||||||
|
import ts from 'typescript';
|
||||||
|
import * as presentation from '../../resources/js/lib/funding-public-presentation.ts';
|
||||||
|
const require = createRequire(import.meta.url),
|
||||||
|
records = Object.values(
|
||||||
|
JSON.parse(
|
||||||
|
fs.readFileSync(
|
||||||
|
new URL(
|
||||||
|
'../../database/data/funding-institutions-nov2026.json',
|
||||||
|
import.meta.url,
|
||||||
|
),
|
||||||
|
'utf8',
|
||||||
|
),
|
||||||
|
).references,
|
||||||
|
).map((x) => x.draft);
|
||||||
|
function load(file) {
|
||||||
|
const module = { exports: {} };
|
||||||
|
const output = ts.transpileModule(
|
||||||
|
fs.readFileSync(
|
||||||
|
new URL('../../resources/js/' + file, import.meta.url),
|
||||||
|
'utf8',
|
||||||
|
),
|
||||||
|
{
|
||||||
|
compilerOptions: {
|
||||||
|
module: ts.ModuleKind.CommonJS,
|
||||||
|
target: ts.ScriptTarget.ES2022,
|
||||||
|
jsx: ts.JsxEmit.ReactJSX,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
).outputText;
|
||||||
|
compileFunction(output, ['require', 'module', 'exports'])(
|
||||||
|
(name) =>
|
||||||
|
name === '@/lib/funding-public-presentation'
|
||||||
|
? presentation
|
||||||
|
: name === '@/pages/investment/shared'
|
||||||
|
? {
|
||||||
|
Frame: ({ title, children }) =>
|
||||||
|
React.createElement(
|
||||||
|
'main',
|
||||||
|
null,
|
||||||
|
React.createElement('h1', null, title),
|
||||||
|
children,
|
||||||
|
),
|
||||||
|
card: 'card',
|
||||||
|
button: 'button',
|
||||||
|
input: 'input',
|
||||||
|
}
|
||||||
|
: name === '@inertiajs/react'
|
||||||
|
? {
|
||||||
|
Link: ({ children, href }) =>
|
||||||
|
React.createElement('a', { href }, children),
|
||||||
|
router: {},
|
||||||
|
useForm: (data) => ({ data, setData() {}, get() {} }),
|
||||||
|
}
|
||||||
|
: name === './shared'
|
||||||
|
? { windowLabels: {} }
|
||||||
|
: name === '@/lib/portal-api' ? {portalRequest:()=>{throw Error('Read-only rendering must not send requests');}} : name === '@/components/funding-classification'
|
||||||
|
? load('components/funding-classification.tsx')
|
||||||
|
: name === '@/components/funding-institution'
|
||||||
|
? load('components/funding-institution.tsx')
|
||||||
|
: name === '@/components/funding-dossier-choice'
|
||||||
|
? { FundingDossierChoice: () => null }
|
||||||
|
: name ===
|
||||||
|
'@/components/funding-criteria-assessment'
|
||||||
|
? load('components/funding-criteria-assessment.tsx')
|
||||||
|
: require(name),
|
||||||
|
module,
|
||||||
|
module.exports,
|
||||||
|
);
|
||||||
|
return module.exports;
|
||||||
|
}
|
||||||
|
test('all270 imported institutions get readable descriptions and evidenced services without changing the published payload', () => {
|
||||||
|
assert.equal(records.length, 270);
|
||||||
|
for (const p of records) {
|
||||||
|
const original = JSON.stringify(p),
|
||||||
|
m = presentation.fundingPublicPresentation(p);
|
||||||
|
assert.ok(m.description);
|
||||||
|
assert.doesNotMatch(
|
||||||
|
m.description,
|
||||||
|
/documento fornecido|ficha|SHA-256|Público\/finalidade/,
|
||||||
|
);
|
||||||
|
assert.equal(JSON.stringify(p), original);
|
||||||
|
assert.equal(
|
||||||
|
m.products.length,
|
||||||
|
(p.products ?? []).filter((x) => x.availability !== 'closed')
|
||||||
|
.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
test('deliberate editorial descriptions survive and missing products are never fabricated', () => {
|
||||||
|
const m = presentation.fundingPublicPresentation({
|
||||||
|
title: 'Example',
|
||||||
|
kind: 'institution',
|
||||||
|
summary: 'Apoio especializado ao investimento rural.',
|
||||||
|
products: [],
|
||||||
|
institution_types: ['insurer'],
|
||||||
|
});
|
||||||
|
assert.equal(m.description, 'Apoio especializado ao investimento rural.');
|
||||||
|
assert.deepEqual(m.products, []);
|
||||||
|
assert.deepEqual(m.services, []);
|
||||||
|
});
|
||||||
|
test('actual public cards and detail lead with description/products and keep import metadata out of the visitor text', () => {
|
||||||
|
const Index = load('pages/funding/index.tsx').default,
|
||||||
|
Show = load('pages/funding/show.tsx').default;
|
||||||
|
for (const raw of [
|
||||||
|
records[0],
|
||||||
|
records.find((p) => p.institution_types.includes('investment_manager')),
|
||||||
|
]) {
|
||||||
|
const p = { ...raw, windows: [], documents: [] },
|
||||||
|
item = {
|
||||||
|
id: 'synthetic',
|
||||||
|
version_id: 'synthetic-version',
|
||||||
|
version: 1,
|
||||||
|
payload: p,
|
||||||
|
};
|
||||||
|
const index = renderToStaticMarkup(
|
||||||
|
React.createElement(Index, {
|
||||||
|
items: { data: [item], links: [] },
|
||||||
|
filters: {},
|
||||||
|
kinds: {},
|
||||||
|
options: { territories: [], sectors: [] },
|
||||||
|
management: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
const detail = renderToStaticMarkup(
|
||||||
|
React.createElement(Show, {
|
||||||
|
record: item,
|
||||||
|
authenticated: false,
|
||||||
|
saved: null,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
for (const html of [index, detail]) {
|
||||||
|
assert.doesNotMatch(
|
||||||
|
html,
|
||||||
|
/ficha|documento fornecido|SHA-256|linha de origem|Instituições financeiras em Moçambique\.docx/i,
|
||||||
|
);
|
||||||
|
assert.match(html, /Produtos e serviços/);
|
||||||
|
assert.match(
|
||||||
|
html,
|
||||||
|
new RegExp(raw.title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const out=new URL('../../outputs/funding-public-editorial-20261111/',import.meta.url);fs.mkdirSync(out,{recursive:true});const name=p.products.length?'bank-with-products':'investment-manager';fs.writeFileSync(new URL(name+'-index.html',out),index);fs.writeFileSync(new URL(name+'-detail.html',out),detail);fs.writeFileSync(new URL('verification.json',out),JSON.stringify({publishedSourceRecords:270,descriptionsPreserved:true,productsNotFabricated:true,actualPublicComponents:true,metadataRemovedFromPresentation:true,privateEditorialFieldsUnchanged:true,activeDataWrites:0,visualBrowserReview:false},null,2));
|
||||||
|
assert.match(detail, /Descrição/);
|
||||||
|
if (p.products.length) assert.match(detail, /Soluções PME/);
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user