Russian Documents
INN, OGRN, BIK, SNILS, passport fields with checksum validation
Overview
Every B2B form in Russia requires INN, OGRN, BIK, bank account numbers. These fields have checksums — a simple mask is useless without validation. @letar/forms provides both headless validators and ready-to-use UI components.
For how the underlying masking works (token model, formatting modes, when a mask should be skipped) see the Input Mask Engine guide.
Full example
The complete sandbox example, read directly from the form-develop-app source at build time.
'use client'
import { Code, Heading, Text, VStack } from '@chakra-ui/react'
import { Form } from '@letar/forms'
import { useState } from 'react'
import { DemoPageLayout } from '../_components'
export default function DocumentsDemoPage() {
const [result, setResult] = useState<Record<string, unknown> | null>(null)
return (
<DemoPageLayout
title="Russian Documents"
description="Form.Document.* — ИНН, ОГРН, БИК, СНИЛС, Паспорт с масками и контрольными суммами"
>
{/* Реквизиты компании */}
<VStack gap={4} align="stretch">
<Heading size="lg">Реквизиты компании</Heading>
<Text color="fg.muted">Маски ввода + realtime валидация контрольных сумм.</Text>
<Form
initialValue={{
inn: '',
kpp: '',
ogrn: '',
bik: '',
account: '',
corrAccount: '',
}}
onSubmit={(data) => setResult(data)}
>
<Form.Document.INN name="inn" label="ИНН" required />
<Form.Document.KPP name="kpp" label="КПП" />
<Form.Document.OGRN name="ogrn" label="ОГРН" />
<Form.Document.BIK name="bik" label="БИК" />
<Form.Document.BankAccount name="account" label="Расчётный счёт" />
<Form.Document.CorrAccount name="corrAccount" label="Корр. счёт" />
<Form.Button.Submit>Сохранить</Form.Button.Submit>
</Form>
</VStack>
{/* Документы физлица */}
<VStack gap={4} align="stretch" mt={8}>
<Heading size="lg">Документы физлица</Heading>
<Form
initialValue={{
snils: '',
passport: '',
foreignPassport: '',
departmentCode: '',
birthCertificate: '',
}}
onSubmit={(data) => setResult(data)}
>
<Form.Document.SNILS name="snils" label="СНИЛС" />
<Form.Document.Passport name="passport" label="Паспорт" />
<Form.Document.ForeignPassport name="foreignPassport" label="Загранпаспорт" />
<Form.Document.DepartmentCode name="departmentCode" label="Код подразделения" />
<Form.Document.BirthCertificate name="birthCertificate" label="Свидетельство о рождении" />
<Form.Button.Submit>Сохранить</Form.Button.Submit>
</Form>
</VStack>
{result && (
<Code whiteSpace="pre" mt={4}>
{JSON.stringify(result, null, 2)}
</Code>
)}
</DemoPageLayout>
)
}shadcn-вариант этого примера ещё не готов.
Vue-пример для этого поля появится позже.
Angular-пример для этого поля появится позже.
Architecture: Two Layers
Layer 1: zRu Validators (headless)
Pure Zod schemas with checksum validation. Work on server and client, no UI dependency.
import { zRu } from '@letar/forms/validators/ru'
const CompanySchema = z.object({
inn: zRu.inn(), // 10 or 12 digits + checksum
kpp: zRu.kpp(), // 9 characters
ogrn: zRu.ogrn(), // 13 digits + checksum
bik: zRu.bik(), // 9 digits, starts with "04"
account: zRu.bankAccount(), // 20 digits
snils: zRu.snils(), // 11 digits + checksum
})
// INN variants
zRu.inn.legal() // legal entity only (10 digits)
zRu.inn.individual() // individual only (12 digits)Layer 2: Form.Document.* (UI)
Ready-to-use fields with input masks, icons, and real-time checksum validation.
<Form.Document.INN name="inn" label="INN" />
<Form.Document.KPP name="kpp" label="KPP" />
<Form.Document.OGRN name="ogrn" label="OGRN" />
<Form.Document.BIK name="bik" label="BIK" />
<Form.Document.BankAccount name="account" label="Bank Account" />
<Form.Document.CorrAccount name="corrAccount" label="Corr. Account" />
<Form.Document.SNILS name="snils" label="SNILS" />
<Form.Document.Passport name="passport" label="Passport" />Available Fields
| Component | Format | Checksum |
|---|---|---|
Form.Document.INN | 10 or 12 digits | Weighted sum mod 11 |
Form.Document.KPP | 9 characters | Format check (NNNNPPXXX) |
Form.Document.OGRN | 13 digits | First 12 mod 11 |
Form.Document.BIK | 9 digits | Starts with "04" |
Form.Document.BankAccount | 20 digits | Control key (with BIK) |
Form.Document.CorrAccount | 20 digits | Starts with "301" |
Form.Document.SNILS | XXX-XXX-XXX YY | Module 101 |
Form.Document.Passport | XX XX XXXXXX | Format check |
Custom Document Fields
Use createDocumentField to create your own document fields:
import { createDocumentField } from '@letar/forms'
import { LuFileText } from 'react-icons/lu'
const FieldCustomDoc = createDocumentField({
displayName: 'FieldCustomDoc',
mask: '999-999-999',
placeholder: '123-456-789',
icon: <LuFileText />,
validate: (value) => {
if (value.length < 9) return 'Too short'
return undefined
},
})Live Example
Try the interactive example on forms-example.letar.best.