@77sol-lab/form-schemas 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +182 -0
  2. package/package.json +61 -0
  3. package/src/domains/financing/enums/banks.ts +22 -0
  4. package/src/domains/financing/enums/index.ts +2 -0
  5. package/src/domains/financing/enums/occupation.ts +2 -0
  6. package/src/domains/financing/formalization/extras.ts +21 -0
  7. package/src/domains/financing/formalization/index.ts +18 -0
  8. package/src/domains/financing/formalization/refinements.ts +70 -0
  9. package/src/domains/financing/formalization/registry.ts +153 -0
  10. package/src/domains/financing/formalization/schemas/AlfaPFSchema.ts +46 -0
  11. package/src/domains/financing/formalization/schemas/AlfaPJSchema.ts +65 -0
  12. package/src/domains/financing/formalization/schemas/BancoDoBrasilPFSchema.ts +46 -0
  13. package/src/domains/financing/formalization/schemas/BancoDoBrasilPJSchema.ts +65 -0
  14. package/src/domains/financing/formalization/schemas/BtgPFSchema.ts +46 -0
  15. package/src/domains/financing/formalization/schemas/BtgPJSchema.ts +77 -0
  16. package/src/domains/financing/formalization/schemas/BvPFSchema.ts +59 -0
  17. package/src/domains/financing/formalization/schemas/BvPJSchema.ts +84 -0
  18. package/src/domains/financing/formalization/schemas/CaixaEconomicaFederalPFSchema.ts +48 -0
  19. package/src/domains/financing/formalization/schemas/CaixaEconomicaFederalPJSchema.ts +67 -0
  20. package/src/domains/financing/formalization/schemas/CashMePFSchema.ts +46 -0
  21. package/src/domains/financing/formalization/schemas/CashMePJSchema.ts +65 -0
  22. package/src/domains/financing/formalization/schemas/Credito77PFSchema.ts +46 -0
  23. package/src/domains/financing/formalization/schemas/Credito77PJSchema.ts +65 -0
  24. package/src/domains/financing/formalization/schemas/EosPFSchema.ts +46 -0
  25. package/src/domains/financing/formalization/schemas/EosPJSchema.ts +65 -0
  26. package/src/domains/financing/formalization/schemas/HdtEnergyPFSchema.ts +46 -0
  27. package/src/domains/financing/formalization/schemas/HdtEnergyPJSchema.ts +65 -0
  28. package/src/domains/financing/formalization/schemas/LosangoPFSchema.ts +46 -0
  29. package/src/domains/financing/formalization/schemas/LosangoPJSchema.ts +65 -0
  30. package/src/domains/financing/formalization/schemas/SafraPFSchema.ts +46 -0
  31. package/src/domains/financing/formalization/schemas/SafraPJSchema.ts +65 -0
  32. package/src/domains/financing/formalization/schemas/SantanderPFSchema.ts +46 -0
  33. package/src/domains/financing/formalization/schemas/SantanderPJSchema.ts +65 -0
  34. package/src/domains/financing/formalization/schemas/SolAgoraPFSchema.ts +46 -0
  35. package/src/domains/financing/formalization/schemas/SolAgoraPJSchema.ts +75 -0
  36. package/src/domains/financing/formalization/schemas/SolfacilPFSchema.ts +46 -0
  37. package/src/domains/financing/formalization/schemas/SolfacilPJSchema.ts +70 -0
  38. package/src/domains/financing/formalization/uiMeta.ts +122 -0
  39. package/src/domains/financing/index.ts +1 -0
  40. package/src/index.ts +1 -0
  41. package/src/shared/enums/document-type.ts +4 -0
  42. package/src/shared/enums/index.ts +3 -0
  43. package/src/shared/enums/nationality.ts +2 -0
  44. package/src/shared/enums/sex.ts +4 -0
  45. package/src/shared/fields/index.ts +1 -0
  46. package/src/shared/fields/primitives.ts +32 -0
  47. package/src/shared/regex/index.ts +75 -0
  48. package/src/shared/regex/patterns.ts +15 -0
  49. package/src/shared/regex/validators.spec.ts +162 -0
  50. package/src/shared/regex/validators.ts +96 -0
package/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # @77sol-lab/form-schemas
2
+
3
+ Repositório **central de schemas Zod da 77**: a **fonte única de verdade para validação** compartilhada entre frontend e backend. A regra é escrita **uma única vez** em [Zod](https://zod.dev) e importada nas duas pontas — o front valida o formulário (on blur/submit) e o back valida o payload (na borda da API) com o **mesmo schema**. Sem duplicar lógica, sem divergência, sem o clássico "passou no front e o back recusou".
4
+
5
+ A estrutura cresce **por domínio**: cada contexto de negócio mora em `src/domains/<domínio>/<escopo>/`, e a camada `src/shared/` reúne primitivos reutilizáveis (regex/validadores de formato, primitivos de campo Zod e enums comuns) — agnósticos a domínio. Crescer é **adicionar um domínio**, não inflar um arquivo gigante.
6
+
7
+ O **primeiro domínio** é o de **financiamento** (`financing/formalization`) e serve de exemplo concreto ao longo deste README. Ele define, por banco, uma ficha **PF** (pessoa física) e uma ficha **PJ** (pessoa jurídica): **cada banco são dois arquivos próprios em `schemas/`** — `<Banco>PFSchema.ts` e `<Banco>PJSchema.ts` — com o **shape declarado explicitamente** (`z.object({...}).strict()`). Você abre o arquivo e lê os campos direto, sem build dinâmico nem catálogo.
8
+
9
+ > Contexto do primeiro domínio: [USI-4045](https://linear.app/77sol/issue/USI-4045).
10
+
11
+ ## Instalação
12
+
13
+ Pacote público no **npm**. Instale junto com o `zod`:
14
+
15
+ ```bash
16
+ yarn add @77sol-lab/form-schemas zod
17
+ # ou
18
+ npm install @77sol-lab/form-schemas zod
19
+ ```
20
+
21
+ `zod` é `peerDependency` (fica a cargo do consumidor, evita versões duplicadas). O pacote é **source-only** (distribui `.ts`, sem build): inclua-o na sua transpilação — ex.: `transpilePackages: ['@77sol-lab/form-schemas']` no Next.js.
22
+
23
+ ## Import (subpath por banco)
24
+
25
+ Cada banco é **self-contained**: os arquivos `<Banco>PFSchema.ts`/`<Banco>PJSchema.ts`
26
+ ficam disponíveis individualmente via o **subpath export com wildcard**
27
+ `./financing/formalization/schemas/*`. Não há mais registry agregado nem mapas
28
+ centrais — você importa **diretamente o arquivo do banco**:
29
+
30
+ ```ts
31
+ import { bvPfSchema } from '@77sol-lab/form-schemas/financing/formalization/schemas/BvPFSchema'
32
+ import { bvPjSchema } from '@77sol-lab/form-schemas/financing/formalization/schemas/BvPJSchema'
33
+ import { santanderPjSchema } from '@77sol-lab/form-schemas/financing/formalization/schemas/SantanderPJSchema'
34
+ import { credito77PfSchema } from '@77sol-lab/form-schemas/financing/formalization/schemas/Credito77PFSchema'
35
+ ```
36
+
37
+ O nome do **arquivo** é PascalCase do slug (`bv`→`Bv`, `banco-do-brasil`→`BancoDoBrasil`,
38
+ `cash-me`→`CashMe`, `credito77`→`Credito77`, `hdt-energy`→`HdtEnergy`, `sol-agora`→`SolAgora`);
39
+ o nome do **export** continua camelCase (`bvPfSchema`, `bancoDoBrasilPjSchema`, …).
40
+
41
+ ## Uso
42
+
43
+ A validação roda igual no front (on blur/submit) e no back (no submit), a partir do
44
+ mesmo schema. Importe o arquivo do banco e use `.parse()`/`.safeParse()`:
45
+
46
+ ```ts
47
+ import { bvPfSchema } from '@77sol-lab/form-schemas/financing/formalization/schemas/BvPFSchema'
48
+ import { santanderPjSchema } from '@77sol-lab/form-schemas/financing/formalization/schemas/SantanderPJSchema'
49
+
50
+ const result = bvPfSchema.safeParse(payloadPf)
51
+ if (!result.success) {
52
+ console.log(result.error.issues) // → 400 no back / feedback no front
53
+ }
54
+
55
+ santanderPjSchema.parse(payloadPj) // empresa + avalista[]
56
+ ```
57
+
58
+ Cada arquivo também exporta o tipo inferido correspondente (`BvPfFicha`,
59
+ `SantanderPjFicha`, …). Como o shape é declarado explicitamente, `z.infer` produz
60
+ **tipos precisos por campo**.
61
+
62
+ > Como não há mais um mapa central, quem precisa **escolher o schema dinamicamente
63
+ > pelo slug** monta o próprio mapa local importando só os bancos que usa.
64
+
65
+ Bancos disponíveis (slug): `credito77`, `losango`, `santander`, `btg`, `bv`,
66
+ `banco-do-brasil`, `caixa-economica-federal`, `alfa`, `safra`, `cash_me_`,
67
+ `hdt_energy`, `solfacil`, `sol_agora`, `eos`.
68
+
69
+ ## Estrutura
70
+
71
+ ```
72
+ src/
73
+ shared/ # infra interna agnóstica a domínio (não exportada)
74
+ regex/ # patterns + validators (CPF/CNPJ/cpfCnpj, datas) + field schemas de formato
75
+ fields/ # primitivos de campo Zod (textField, numberField, integerField, fileUploadField…)
76
+ enums/ # enums reutilizáveis (tipo_documento, sexo, nacionalidade)
77
+ domains/
78
+ financing/
79
+ enums/ # bancos (BANK_SLUGS/BankEnum) + ocupações
80
+ formalization/
81
+ extras.ts # campos da formalização comuns (uploads, vínculo, seguro RD/RE)
82
+ refinements.ts # applyPfRefinements / applyPjRefinements (RN-011/012 + nacionalidade→RNE)
83
+ schemas/ # DOIS ARQUIVOS ACHATADOS POR BANCO, shape explícito — sem index/barril
84
+ BvPFSchema.ts # ficha PF do BV (bvPfSchema)
85
+ BvPJSchema.ts # ficha PJ do BV (bvPjSchema)
86
+ SantanderPFSchema.ts # … um par PF/PJ por banco
87
+ … # (14 bancos = 28 arquivos achatados)
88
+ index.ts # barrel do escopo (só BANK_SLUGS/BankEnum/tipos — NÃO os schemas)
89
+ index.ts
90
+ index.ts # API pública root
91
+ ```
92
+
93
+ Cada arquivo de banco é independente: `<Banco>PFSchema.ts` e `<Banco>PJSchema.ts`
94
+ importam os primitivos de `shared/`, os campos comuns de `extras.ts` e os refinements de
95
+ `refinements.ts`, e declaram o `z.object({...})` de PF e de PJ. Sem gerador, sem catálogo
96
+ de dados, sem builder e **sem registry agregado**: cada arquivo é exposto direto pelo
97
+ subpath export `./financing/formalization/schemas/*`.
98
+
99
+ ## Manutenção
100
+
101
+ A manutenção é **manual e local aos arquivos do banco** — edite o shape direto em
102
+ `schemas/<Banco>PFSchema.ts` (PF) ou `schemas/<Banco>PJSchema.ts` (PJ).
103
+
104
+ - **Mudar campos de um banco:** abra `schemas/<Banco>PFSchema.ts` ou `schemas/<Banco>PJSchema.ts`
105
+ e edite o `z.object({...})` (adicione/remova chaves, troque o schema de formato, marque `.optional()`).
106
+ - **Regra de formato/primitivo:** `shared/regex` (CPF/CNPJ/datas…) ou
107
+ `shared/fields/primitives` (`textField`, `numberField`, `integerField`).
108
+ - **Campos comuns da formalização** (uploads, vínculo, seguro): `extras.ts`.
109
+ - **Regra cross-field (RN):** `refinements.ts`.
110
+
111
+ ### Adicionar um banco
112
+
113
+ **Regra do pacote: cada banco são dois arquivos de schema próprios (achatados em `schemas/`).** O passo a passo
114
+ está na skill **`adicionar-banco-financing`** (`.claude/skills/`). Em resumo:
115
+
116
+ 1. Crie `schemas/<Banco>PFSchema.ts` e `schemas/<Banco>PJSchema.ts` (shape explícito).
117
+ 2. (Opcional) adicione o `slug` em `BANK_SLUGS` (`enums/banks.ts`) só se quiser o banco
118
+ listado no enum — os schemas não dependem mais dele.
119
+ 3. `yarn check:fix && yarn typecheck`.
120
+
121
+ > Teste de schema é dispensado por enquanto — não crie `.spec.ts` por banco.
122
+
123
+ > Não há registry para registrar: os arquivos ficam disponíveis automaticamente via o
124
+ > subpath export `./financing/formalization/schemas/*`, sem fiação central.
125
+
126
+ ## Regras de negócio materializadas
127
+
128
+ Aplicadas via `.superRefine(...)` em cada banco; os campos usam os nomes definidos no schema.
129
+
130
+ | RN | Onde |
131
+ | -- | -- |
132
+ | RN-011 (RG ≤ 10 anos) | `refinements.ts` (`type_doc='rg'` + `doc_issue_date`) · CNH "não vencida" → TODO (dado ausente) |
133
+ | RN-012 (CNPJ por ocupação) | `refinements.ts` (`nature_of_occupation` + `cnpj_proprietary`; presentes só onde o banco os declara, ex. BV) |
134
+ | RN-014 (Seguro RD/RE default ON) | `extras.ts` (`rd_re_insurance_toggle: z.boolean().default(true)`) |
135
+ | Nacionalidade → RNE | `refinements.ts` (`nationality` ≠ Brasil força `type_doc='rne'`) |
136
+ | Vínculo de terceiro | `refinements.ts` (`account_third_party` ≠ `nao_se_aplica` exige `bond_document_upload`) |
137
+ | `.strict()` | cada ficha rejeita qualquer campo fora do shape declarado do banco |
138
+
139
+ ### Campos `dropdown`/`autocomplete`
140
+
141
+ Campos de seleção são **string validada** (`textField`), exceto `type_doc`
142
+ (`DocumentTypeEnum`) e `sex` (`SexEnum`). Para restringir um deles, troque no
143
+ `<Banco>PFSchema.ts`/`<Banco>PJSchema.ts` do banco por um `z.enum([...])` — está tudo explícito no shape.
144
+
145
+ ## Distribuição (source-only)
146
+
147
+ Este repositório **armazena apenas os schemas em TypeScript** — **não faz build**. Os `exports` apontam direto para o source (`src/*.ts`); quem consome transpila.
148
+
149
+ ```jsonc
150
+ "exports": {
151
+ ".": "./src/index.ts",
152
+ "./financing/formalization": "./src/domains/financing/formalization/index.ts",
153
+ // wildcard: cada arquivo de banco é importável direto, sem registry central
154
+ "./financing/formalization/schemas/*": "./src/domains/financing/formalization/schemas/*.ts"
155
+ }
156
+ ```
157
+
158
+ - **Next.js / frontend:** adicione `@77sol-lab/form-schemas` em `transpilePackages`.
159
+ - **api-v3 / backend (Node + TS):** garanta que o pacote seja transpilado.
160
+ - **`zod`** é `peerDependency`.
161
+
162
+ ## Agnóstico a framework
163
+
164
+ Depende **apenas de `zod`** (peer) — zero React/Next/Vue/DOM. Entrega schemas Zod, não
165
+ componentes: valide com `.parse()`/`.safeParse()` e, para renderizar, derive os campos
166
+ do schema (`.shape`). Um guardrail do Biome (`noRestrictedImports`) barra imports de UI.
167
+
168
+ ## Scripts
169
+
170
+ ```bash
171
+ yarn build # typecheck + lint + test (portões de validação; não gera dist)
172
+ yarn typecheck # tsc --noEmit
173
+ yarn test # vitest
174
+ yarn lint # biome lint
175
+ yarn run check # biome check (lint + formatação)
176
+ yarn check:fix # biome check --write
177
+ yarn release:local # npm publish (publica o source no npm; roda build antes via prepublishOnly)
178
+ ```
179
+
180
+ ## Versionamento e publicação
181
+
182
+ Publicação automática no **npm** via `semantic-release` (ver `.releaserc.json`): a cada push na `main`, o CI roda typecheck/lint/test e publica a próxima versão derivada dos **Conventional Commits**, com changelog automático. Não edite a versão no `package.json` manualmente — o `semantic-release` a gerencia.
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@77sol-lab/form-schemas",
3
+ "version": "1.1.0",
4
+ "description": "Fonte central de schemas Zod da 77 (source-only). Financiamento (PF) é o primeiro domínio; consumido direto do código-fonte pelo backend e frontend.",
5
+ "license": "MIT",
6
+ "private": false,
7
+ "type": "module",
8
+ "main": "./src/index.ts",
9
+ "types": "./src/index.ts",
10
+ "exports": {
11
+ ".": "./src/index.ts",
12
+ "./financing/formalization": "./src/domains/financing/formalization/index.ts",
13
+ "./financing/formalization/schemas/*": "./src/domains/financing/formalization/schemas/*.ts"
14
+ },
15
+ "files": [
16
+ "src"
17
+ ],
18
+ "sideEffects": false,
19
+ "publishConfig": {
20
+ "access": "public",
21
+ "registry": "https://registry.npmjs.org"
22
+ },
23
+ "scripts": {
24
+ "build": "yarn typecheck && yarn lint && yarn test",
25
+ "release:local": "npm publish",
26
+ "prepublishOnly": "yarn build",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "lint": "biome lint",
31
+ "lint:fix": "biome lint --write",
32
+ "format": "biome format --write",
33
+ "format:check": "biome format",
34
+ "check": "biome check",
35
+ "check:fix": "biome check --write",
36
+ "docs:dev": "vitepress dev docs",
37
+ "docs:build": "vitepress build docs",
38
+ "docs:preview": "vitepress preview docs",
39
+ "prepare": "husky"
40
+ },
41
+ "peerDependencies": {
42
+ "zod": "^3.23.0"
43
+ },
44
+ "devDependencies": {
45
+ "@biomejs/biome": "2.4.15",
46
+ "@commitlint/cli": "^19.0.0",
47
+ "@commitlint/config-conventional": "^19.0.0",
48
+ "@semantic-release/changelog": "^6.0.3",
49
+ "@semantic-release/git": "^10.0.1",
50
+ "@types/node": "^20.11.0",
51
+ "husky": "^9.0.0",
52
+ "semantic-release": "^23.0.0",
53
+ "typescript": "^5.4.0",
54
+ "vitepress": "^1.6.3",
55
+ "vitest": "^1.5.0",
56
+ "zod": "^3.23.0"
57
+ },
58
+ "engines": {
59
+ "node": ">=20"
60
+ }
61
+ }
@@ -0,0 +1,22 @@
1
+ import { z } from 'zod'
2
+
3
+ export const BANK_SLUGS = [
4
+ 'credito77',
5
+ 'losango',
6
+ 'santander',
7
+ 'btg',
8
+ 'bv',
9
+ 'banco-do-brasil',
10
+ 'caixa-economica-federal',
11
+ 'alfa',
12
+ 'safra',
13
+ 'cash_me_',
14
+ 'hdt_energy',
15
+ 'solfacil',
16
+ 'sol_agora',
17
+ 'eos',
18
+ ] as const
19
+
20
+ export const BankEnum = z.enum(BANK_SLUGS)
21
+ export type Bank = z.infer<typeof BankEnum>
22
+ export type BankSlug = Bank
@@ -0,0 +1,2 @@
1
+ export * from './banks'
2
+ export * from './occupation'
@@ -0,0 +1,2 @@
1
+ // RN-012: ocupações que tornam "CNPJ do proprietário" obrigatório.
2
+ export const OCCUPATIONS_REQUIRING_CNPJ = ['empresario', 'empresario_ou_proprietario'] as const
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod'
2
+ import { fileUploadField, selectField } from '../../../shared/fields/primitives'
3
+
4
+ const THIRD_PARTY_OPTIONS = [
5
+ 'nao_se_aplica',
6
+ 'conjuge',
7
+ 'pai_mae',
8
+ 'filho_filha',
9
+ 'parente',
10
+ 'outro',
11
+ ] as const
12
+
13
+ export const formalizationExtras = {
14
+ energy_bill_upload: fileUploadField,
15
+ account_third_party: selectField(THIRD_PARTY_OPTIONS),
16
+ bond_document_upload: fileUploadField.optional(),
17
+ document_front_upload: fileUploadField,
18
+ document_back_upload: fileUploadField,
19
+ // RN-014: seguro RD/RE começa ON.
20
+ rd_re_insurance_toggle: z.boolean().default(true),
21
+ }
@@ -0,0 +1,18 @@
1
+ export { NATIONALITY_BRAZIL } from '../../../shared/enums'
2
+ export type { Bank, BankSlug } from '../enums'
3
+ export { BANK_SLUGS, BankEnum, OCCUPATIONS_REQUIRING_CNPJ } from '../enums'
4
+ export { applyPfRefinements, applyPjRefinements } from './refinements'
5
+ export type { PersonType } from './registry'
6
+ export {
7
+ getFormalizationBaseSchema,
8
+ getFormalizationSchema,
9
+ pfBaseSchemaRegistry,
10
+ pfSchemaRegistry,
11
+ pjBaseSchemaRegistry,
12
+ pjSchemaRegistry,
13
+ } from './registry'
14
+ export type {
15
+ FormalizationFieldKind,
16
+ FormalizationFieldMeta,
17
+ } from './uiMeta'
18
+ export { FORMALIZATION_FIELD_META } from './uiMeta'
@@ -0,0 +1,70 @@
1
+ import { z } from 'zod'
2
+ import { NATIONALITY_BRAZIL } from '../../../shared/enums'
3
+ import { yearsSinceDMY } from '../../../shared/regex'
4
+ import { OCCUPATIONS_REQUIRING_CNPJ } from '../enums'
5
+
6
+ const RG_MAX_EMISSION_YEARS = 10
7
+
8
+ function applyCommonRefinements(data: Record<string, unknown>, ctx: z.RefinementCtx): void {
9
+ // Documento de vínculo obrigatório se a conta está em nome de terceiro.
10
+ if (
11
+ 'account_third_party' in data &&
12
+ typeof data.account_third_party === 'string' &&
13
+ data.account_third_party !== 'nao_se_aplica' &&
14
+ !data.bond_document_upload
15
+ ) {
16
+ ctx.addIssue({
17
+ code: z.ZodIssueCode.custom,
18
+ path: ['bond_document_upload'],
19
+ message: 'Documento de vínculo é obrigatório quando a conta está em nome de terceiro',
20
+ })
21
+ }
22
+ }
23
+
24
+ export function applyPfRefinements(data: Record<string, unknown>, ctx: z.RefinementCtx): void {
25
+ applyCommonRefinements(data, ctx)
26
+
27
+ // RN-012: CNPJ do proprietário obrigatório se ocupação ∈ {empresário/proprietário}.
28
+ if (
29
+ 'nature_of_occupation' in data &&
30
+ typeof data.nature_of_occupation === 'string' &&
31
+ (OCCUPATIONS_REQUIRING_CNPJ as readonly string[]).includes(data.nature_of_occupation) &&
32
+ !data.cnpj_proprietary
33
+ ) {
34
+ ctx.addIssue({
35
+ code: z.ZodIssueCode.custom,
36
+ path: ['cnpj_proprietary'],
37
+ message: 'CNPJ do proprietário é obrigatório para esta ocupação',
38
+ })
39
+ }
40
+
41
+ // RN-011: RG aceito com no máximo 10 anos de emissão.
42
+ if (data.type_doc === 'rg' && typeof data.doc_issue_date === 'string') {
43
+ const years = yearsSinceDMY(data.doc_issue_date)
44
+ if (years !== null && years > RG_MAX_EMISSION_YEARS) {
45
+ ctx.addIssue({
46
+ code: z.ZodIssueCode.custom,
47
+ path: ['doc_issue_date'],
48
+ message: `RG deve ter no máximo ${RG_MAX_EMISSION_YEARS} anos de emissão`,
49
+ })
50
+ }
51
+ }
52
+
53
+ // Nacionalidade ≠ Brasil → tipo de documento deve ser RNE (estrangeiro).
54
+ if (
55
+ typeof data.nationality === 'string' &&
56
+ data.nationality.toLowerCase() !== NATIONALITY_BRAZIL &&
57
+ 'type_doc' in data &&
58
+ data.type_doc !== 'rne'
59
+ ) {
60
+ ctx.addIssue({
61
+ code: z.ZodIssueCode.custom,
62
+ path: ['type_doc'],
63
+ message: 'Para nacionalidade estrangeira, o documento deve ser Registro de Estrangeiro (RNE)',
64
+ })
65
+ }
66
+ }
67
+
68
+ export function applyPjRefinements(data: Record<string, unknown>, ctx: z.RefinementCtx): void {
69
+ applyCommonRefinements(data, ctx)
70
+ }
@@ -0,0 +1,153 @@
1
+ import type { AnyZodObject, ZodTypeAny } from 'zod'
2
+ import type { BankSlug } from '../enums'
3
+ import { applyPfRefinements } from './refinements'
4
+ import { alfaPfObject, alfaPfSchema } from './schemas/AlfaPFSchema'
5
+ import { alfaPjObject, alfaPjSchema } from './schemas/AlfaPJSchema'
6
+ import { bancoDoBrasilPfObject, bancoDoBrasilPfSchema } from './schemas/BancoDoBrasilPFSchema'
7
+ import { bancoDoBrasilPjObject, bancoDoBrasilPjSchema } from './schemas/BancoDoBrasilPJSchema'
8
+ import { btgPfObject, btgPfSchema } from './schemas/BtgPFSchema'
9
+ import { btgPjObject, btgPjSchema } from './schemas/BtgPJSchema'
10
+ import { bvPfObject, bvPfSchema } from './schemas/BvPFSchema'
11
+ import { bvPjObject, bvPjSchema } from './schemas/BvPJSchema'
12
+ import {
13
+ caixaEconomicaFederalPfObject,
14
+ caixaEconomicaFederalPfSchema,
15
+ } from './schemas/CaixaEconomicaFederalPFSchema'
16
+ import {
17
+ caixaEconomicaFederalPjObject,
18
+ caixaEconomicaFederalPjSchema,
19
+ } from './schemas/CaixaEconomicaFederalPJSchema'
20
+ import { cashMePfObject, cashMePfSchema } from './schemas/CashMePFSchema'
21
+ import { cashMePjObject, cashMePjSchema } from './schemas/CashMePJSchema'
22
+ import { credito77PfObject, credito77PfSchema } from './schemas/Credito77PFSchema'
23
+ import { credito77PjObject, credito77PjSchema } from './schemas/Credito77PJSchema'
24
+ import { eosPfObject, eosPfSchema } from './schemas/EosPFSchema'
25
+ import { eosPjObject, eosPjSchema } from './schemas/EosPJSchema'
26
+ import { hdtEnergyPfObject, hdtEnergyPfSchema } from './schemas/HdtEnergyPFSchema'
27
+ import { hdtEnergyPjObject, hdtEnergyPjSchema } from './schemas/HdtEnergyPJSchema'
28
+ import { losangoPfObject, losangoPfSchema } from './schemas/LosangoPFSchema'
29
+ import { losangoPjObject, losangoPjSchema } from './schemas/LosangoPJSchema'
30
+ import { safraPfObject, safraPfSchema } from './schemas/SafraPFSchema'
31
+ import { safraPjObject, safraPjSchema } from './schemas/SafraPJSchema'
32
+ import { santanderPfObject, santanderPfSchema } from './schemas/SantanderPFSchema'
33
+ import { santanderPjObject, santanderPjSchema } from './schemas/SantanderPJSchema'
34
+ import { solAgoraPfObject, solAgoraPfSchema } from './schemas/SolAgoraPFSchema'
35
+ import { solAgoraPjObject, solAgoraPjSchema } from './schemas/SolAgoraPJSchema'
36
+ import { solfacilPfObject, solfacilPfSchema } from './schemas/SolfacilPFSchema'
37
+ import { solfacilPjObject, solfacilPjSchema } from './schemas/SolfacilPJSchema'
38
+
39
+ export type PersonType = 'pf' | 'pj'
40
+
41
+ export const pfSchemaRegistry = {
42
+ credito77: credito77PfSchema,
43
+ losango: losangoPfSchema,
44
+ santander: santanderPfSchema,
45
+ btg: btgPfSchema,
46
+ bv: bvPfSchema,
47
+ 'banco-do-brasil': bancoDoBrasilPfSchema,
48
+ 'caixa-economica-federal': caixaEconomicaFederalPfSchema,
49
+ alfa: alfaPfSchema,
50
+ safra: safraPfSchema,
51
+ cash_me_: cashMePfSchema,
52
+ hdt_energy: hdtEnergyPfSchema,
53
+ solfacil: solfacilPfSchema,
54
+ sol_agora: solAgoraPfSchema,
55
+ eos: eosPfSchema,
56
+ } satisfies Record<BankSlug, ZodTypeAny>
57
+
58
+ export const pjSchemaRegistry = {
59
+ credito77: credito77PjSchema,
60
+ losango: losangoPjSchema,
61
+ santander: santanderPjSchema,
62
+ btg: btgPjSchema,
63
+ bv: bvPjSchema,
64
+ 'banco-do-brasil': bancoDoBrasilPjSchema,
65
+ 'caixa-economica-federal': caixaEconomicaFederalPjSchema,
66
+ alfa: alfaPjSchema,
67
+ safra: safraPjSchema,
68
+ cash_me_: cashMePjSchema,
69
+ hdt_energy: hdtEnergyPjSchema,
70
+ solfacil: solfacilPjSchema,
71
+ sol_agora: solAgoraPjSchema,
72
+ eos: eosPjSchema,
73
+ } satisfies Record<BankSlug, ZodTypeAny>
74
+
75
+ export const pfBaseSchemaRegistry = {
76
+ credito77: credito77PfObject,
77
+ losango: losangoPfObject,
78
+ santander: santanderPfObject,
79
+ btg: btgPfObject,
80
+ bv: bvPfObject,
81
+ 'banco-do-brasil': bancoDoBrasilPfObject,
82
+ 'caixa-economica-federal': caixaEconomicaFederalPfObject,
83
+ alfa: alfaPfObject,
84
+ safra: safraPfObject,
85
+ cash_me_: cashMePfObject,
86
+ hdt_energy: hdtEnergyPfObject,
87
+ solfacil: solfacilPfObject,
88
+ sol_agora: solAgoraPfObject,
89
+ eos: eosPfObject,
90
+ } satisfies Record<BankSlug, AnyZodObject>
91
+
92
+ export const pjBaseSchemaRegistry = {
93
+ credito77: credito77PjObject,
94
+ losango: losangoPjObject,
95
+ santander: santanderPjObject,
96
+ btg: btgPjObject,
97
+ bv: bvPjObject,
98
+ 'banco-do-brasil': bancoDoBrasilPjObject,
99
+ 'caixa-economica-federal': caixaEconomicaFederalPjObject,
100
+ alfa: alfaPjObject,
101
+ safra: safraPjObject,
102
+ cash_me_: cashMePjObject,
103
+ hdt_energy: hdtEnergyPjObject,
104
+ solfacil: solfacilPjObject,
105
+ sol_agora: solAgoraPjObject,
106
+ eos: eosPjObject,
107
+ } satisfies Record<BankSlug, AnyZodObject>
108
+
109
+ export interface FormalizationSchemaOptions {
110
+ prestamistaIncluso?: boolean
111
+ }
112
+
113
+ const CREDITO77_PRESTAMISTA_FIELDS = {
114
+ doc: true,
115
+ issuing_body: true,
116
+ doc_issue_date: true,
117
+ sex: true,
118
+ } as const
119
+
120
+ function isCredito77WithoutPrestamista(
121
+ bank: BankSlug,
122
+ person: PersonType,
123
+ options: FormalizationSchemaOptions,
124
+ ): boolean {
125
+ return bank === 'credito77' && person === 'pf' && !options.prestamistaIncluso
126
+ }
127
+
128
+ export function getFormalizationSchema(
129
+ bank: BankSlug,
130
+ person: PersonType,
131
+ options: FormalizationSchemaOptions = {},
132
+ ) {
133
+ if (isCredito77WithoutPrestamista(bank, person, options)) {
134
+ return credito77PfObject
135
+ .omit(CREDITO77_PRESTAMISTA_FIELDS)
136
+ .strict()
137
+ .superRefine(applyPfRefinements)
138
+ }
139
+
140
+ return person === 'pf' ? pfSchemaRegistry[bank] : pjSchemaRegistry[bank]
141
+ }
142
+
143
+ export function getFormalizationBaseSchema(
144
+ bank: BankSlug,
145
+ person: PersonType,
146
+ options: FormalizationSchemaOptions = {},
147
+ ) {
148
+ if (isCredito77WithoutPrestamista(bank, person, options)) {
149
+ return credito77PfObject.omit(CREDITO77_PRESTAMISTA_FIELDS)
150
+ }
151
+
152
+ return person === 'pf' ? pfBaseSchemaRegistry[bank] : pjBaseSchemaRegistry[bank]
153
+ }
@@ -0,0 +1,46 @@
1
+ import { z } from 'zod'
2
+ import { DocumentTypeEnum, SexEnum } from '../../../../shared/enums'
3
+ import { textField } from '../../../../shared/fields/primitives'
4
+ import {
5
+ birthDateSchema,
6
+ cepSchema,
7
+ cpfCnpjSchema,
8
+ cpfSchema,
9
+ currencySchema,
10
+ emailSchema,
11
+ issueDateSchema,
12
+ phoneBRSchema,
13
+ } from '../../../../shared/regex'
14
+ import { formalizationExtras } from '../extras'
15
+ import { applyPfRefinements } from '../refinements'
16
+
17
+ export const alfaPfObject = z.object({
18
+ ...formalizationExtras,
19
+ name: textField(),
20
+ cep: cepSchema,
21
+ cpf: cpfSchema,
22
+ address: textField(),
23
+ birth_date: birthDateSchema,
24
+ number: textField(),
25
+ sex: SexEnum,
26
+ complement: textField(15).optional(),
27
+ mother_name: textField(),
28
+ district: textField(),
29
+ energy_account_in_requester_name: textField(),
30
+ city: textField(),
31
+ energy_bill_owner_document: cpfCnpjSchema.optional(),
32
+ state: textField(2),
33
+ nationality: textField(),
34
+ cellphone: phoneBRSchema,
35
+ email: emailSchema,
36
+ monthly_income: currencySchema,
37
+ profession: textField(),
38
+ type_doc: DocumentTypeEnum,
39
+ doc: textField(15),
40
+ issuing_body: textField(6),
41
+ doc_issue_date: issueDateSchema,
42
+ })
43
+
44
+ export const alfaPfSchema = alfaPfObject.strict().superRefine(applyPfRefinements)
45
+
46
+ export type AlfaPfFicha = z.infer<typeof alfaPfSchema>
@@ -0,0 +1,65 @@
1
+ import { z } from 'zod'
2
+ import { SexEnum } from '../../../../shared/enums'
3
+ import { textField } from '../../../../shared/fields/primitives'
4
+ import {
5
+ birthDateSchema,
6
+ cepSchema,
7
+ cnpjSchema,
8
+ cpfSchema,
9
+ currencySchema,
10
+ emailSchema,
11
+ issueDateSchema,
12
+ phoneBRSchema,
13
+ } from '../../../../shared/regex'
14
+ import { formalizationExtras } from '../extras'
15
+ import { applyPjRefinements } from '../refinements'
16
+
17
+ export const alfaPjObject = z.object({
18
+ ...formalizationExtras,
19
+ financing_company: z
20
+ .object({
21
+ corporate_name: textField(),
22
+ cep: cepSchema,
23
+ address: textField(),
24
+ foundation_date: issueDateSchema,
25
+ number: textField(),
26
+ cnpj: cnpjSchema,
27
+ email: emailSchema,
28
+ complement: textField(15).optional(),
29
+ phone: phoneBRSchema,
30
+ district: textField(),
31
+ legal_nature: textField(),
32
+ city: textField(),
33
+ economic_activity_group: textField(),
34
+ state: textField(2),
35
+ economic_activity: textField(),
36
+ monthly_income: currencySchema,
37
+ })
38
+ .strict(),
39
+ financing_company_guarantor: z.array(
40
+ z
41
+ .object({
42
+ name: textField(),
43
+ cep: cepSchema,
44
+ cpf: cpfSchema,
45
+ address: textField(),
46
+ birth_date: birthDateSchema,
47
+ number: textField(),
48
+ complement: textField(15).optional(),
49
+ cellphone: phoneBRSchema,
50
+ email: emailSchema,
51
+ district: textField(),
52
+ sex: SexEnum,
53
+ city: textField(),
54
+ state: textField(2),
55
+ office: textField(),
56
+ mother_name: textField(),
57
+ nationality: textField(),
58
+ })
59
+ .strict(),
60
+ ),
61
+ })
62
+
63
+ export const alfaPjSchema = alfaPjObject.strict().superRefine(applyPjRefinements)
64
+
65
+ export type AlfaPjFicha = z.infer<typeof alfaPjSchema>