diff --git a/src/schemas/22-1990n/schema.js b/src/schemas/22-1990n/schema.js new file mode 100644 index 00000000..135712b4 --- /dev/null +++ b/src/schemas/22-1990n/schema.js @@ -0,0 +1,475 @@ +const buildDefinitionReference = referenceId => ({ $ref: `#/definitions/${referenceId}` }); + +const schema = { + $schema: 'http://json-schema.org/draft-04/schema#', + type: 'object', + additionalProperties: false, + + definitions: { + fullName: { + type: 'object', + properties: { + first: { + type: 'string', + minLength: 1, + maxLength: 30, + }, + middle: { + type: ['string', 'null'], + maxLength: 1, + pattern: '^[A-Za-z]$', + }, + last: { + type: 'string', + minLength: 1, + maxLength: 30, + }, + }, + required: ['first', 'last'], + }, + + ssn: { + type: 'string', + pattern: '^\\d{9}$', + minLength: 9, + maxLength: 9, + description: 'Social Security Number — 9 digits, no hyphens', + }, + + date: { + type: 'string', + format: 'date', + description: 'ISO 8601 date string YYYY-MM-DD', + }, + + phone: { + type: 'string', + pattern: '^\\d{10}$', + minLength: 10, + maxLength: 10, + description: '10-digit phone number, no formatting characters', + }, + + email: { + type: 'string', + format: 'email', + maxLength: 255, + }, + + usAddress: { + type: 'object', + properties: { + street: { + type: 'string', + minLength: 1, + maxLength: 50, + }, + street2: { + type: ['string', 'null'], + maxLength: 50, + }, + city: { + type: 'string', + minLength: 1, + maxLength: 30, + }, + state: { + type: 'string', + maxLength: 5, + description: '2-letter state/territory code or APO/FPO/DPO designator', + }, + postalCode: { + type: 'string', + pattern: '^\\d{5}(-\\d{4})?$', + maxLength: 10, + }, + country: { + type: 'string', + enum: ['USA'], + description: 'Fixed to USA for domestic addresses', + }, + }, + required: ['street', 'city', 'state', 'postalCode'], + }, + + uploadedDocument: { + type: 'object', + description: 'Metadata for a document uploaded via /v0/document_uploads', + properties: { + name: { + type: 'string', + maxLength: 255, + description: 'Original filename of the uploaded document', + }, + size: { + type: 'integer', + minimum: 1, + description: 'File size in bytes', + }, + confirmationCode: { + type: 'string', + description: 'UUID returned by /v0/document_uploads after successful upload', + }, + }, + required: ['name', 'size', 'confirmationCode'], + }, + + servicePeriod: { + type: 'object', + description: 'A single period of military service (Item 10)', + properties: { + dateEnteredService: { + type: 'string', + format: 'date', + description: 'Item 10A — Date entered service; must be on or after 2003-10-01 for NCS eligibility', + }, + dateSeparated: { + type: ['string', 'null'], + format: 'date', + description: 'Item 10B — Date separated; leave null if currently serving', + }, + serviceComponent: { + type: 'string', + maxLength: 20, + description: 'Item 10C — Branch/component of service (e.g., USN, USAF, USAR, ARNG)', + }, + serviceStatus: { + type: 'string', + maxLength: 50, + description: 'Item 10D — Service status (e.g., Active duty, drilling reservist, IRR)', + }, + }, + required: ['dateEnteredService', 'serviceComponent', 'serviceStatus'], + }, + + bankAccount: { + type: 'object', + description: 'Direct deposit bank account information (Item 7)', + properties: { + accountType: { + type: 'string', + enum: ['checking', 'savings'], + description: 'Type of bank account', + }, + routingNumber: { + type: 'string', + pattern: '^\\d{9}$', + minLength: 9, + maxLength: 9, + description: 'ABA routing/transit number — 9 digits', + }, + accountNumber: { + type: 'string', + pattern: '^\\d{4,17}$', + minLength: 4, + maxLength: 17, + description: 'Bank account number — 4 to 17 digits', + }, + }, + required: ['accountType', 'routingNumber', 'accountNumber'], + }, + }, + + properties: { + + // ── Part I: Applicant Information ────────────────────────────────────────── + + applicantInformation: { + type: 'object', + description: 'Part I — Applicant identification fields (Items 1–6B)', + additionalProperties: false, + properties: { + veteranSocialSecurityNumber: buildDefinitionReference('ssn'), + + gender: { + type: 'string', + enum: ['F', 'M'], + description: 'Item 2 — Sex of applicant (F = Female, M = Male; verbatim from March 2023 PDF)', + }, + + veteranDateOfBirth: buildDefinitionReference('date'), + + veteranFullName: buildDefinitionReference('fullName'), + + veteranAddress: buildDefinitionReference('usAddress'), + + homePhone: { + $ref: '#/definitions/phone', + description: 'Item 6A — Home telephone number', + }, + + mobilePhone: { + type: ['string', 'null'], + pattern: '^\\d{10}$', + minLength: 10, + maxLength: 10, + description: 'Item 6A — Mobile telephone number (optional)', + }, + + email: { + type: ['string', 'null'], + format: 'email', + maxLength: 255, + description: 'Item 6B — Email address (optional)', + }, + }, + required: [ + 'veteranSocialSecurityNumber', + 'gender', + 'veteranDateOfBirth', + 'veteranFullName', + 'veteranAddress', + ], + }, + + // ── Part II: Type and Program of Education or Training ──────────────────── + + educationTraining: { + type: 'object', + description: 'Part II — Education or training type, school, objective, and benefit authorization (Items 8A–8C + multi-benefit auth)', + additionalProperties: false, + properties: { + + typeOfEducation: { + type: 'array', + description: 'Item 8A — Type(s) of education or training selected; at least one required', + minItems: 1, + uniqueItems: true, + items: { + type: 'string', + enum: [ + 'collegeOrOtherSchool', + 'apprenticeshipOrOJT', + 'vocationalFlightTraining', + 'correspondence', + 'nationalTestReimbursement', + 'licensingOrCertificationTest', + ], + }, + }, + + flightTrainingCourse: { + type: 'object', + description: 'Conditional — shown only when vocationalFlightTraining is selected in Item 8A', + additionalProperties: false, + properties: { + isAirlineTransportPilot: { + type: 'boolean', + description: 'Whether the flight training course is an Airline Transport Pilot (ATP) course; determines required medical certificate class', + }, + requirementsAcknowledged: { + type: 'boolean', + description: 'Applicant confirms they hold the required pilot certificate and medical certificate for their selected flight training type', + }, + }, + required: ['isAirlineTransportPilot', 'requirementsAcknowledged'], + }, + + schoolSelected: { + type: 'boolean', + description: 'Item 8B gate — indicates whether the applicant has selected a school or training establishment', + }, + + schoolInfo: { + type: 'object', + description: 'Item 8B — School or training establishment name and address (conditional on schoolSelected = true)', + additionalProperties: false, + properties: { + name: { + type: 'string', + minLength: 1, + maxLength: 100, + description: 'Name of school or training establishment', + }, + address: { + type: 'object', + additionalProperties: false, + properties: { + street: { + type: 'string', + minLength: 1, + maxLength: 50, + }, + city: { + type: 'string', + minLength: 1, + maxLength: 30, + }, + state: { + type: 'string', + maxLength: 5, + description: 'State code used for RPO routing (Eastern vs. Western)', + }, + postalCode: { + type: 'string', + pattern: '^\\d{5}(-\\d{4})?$', + maxLength: 10, + }, + }, + required: ['street', 'city', 'state', 'postalCode'], + }, + }, + required: ['name', 'address'], + }, + + educationalObjective: { + type: ['string', 'null'], + maxLength: 500, + description: 'Item 8C — Educational or career objective (optional; e.g., Bachelor of Arts in Accounting, welding certificate, police officer)', + }, + + highestRateAuthorization: { + type: 'boolean', + description: 'Part II multi-benefit authorization — if eligible for more than one benefit, applicant authorizes VA to pay the benefit with the highest monthly rate', + }, + }, + required: ['typeOfEducation', 'schoolSelected', 'highestRateAuthorization'], + }, + + // ── Part III: Service Information ───────────────────────────────────────── + + serviceInformation: { + type: 'object', + description: 'Part III — Active duty status, terminal leave, service periods, and supporting document uploads (Items 9A–9B, 10)', + additionalProperties: false, + properties: { + + activeDuty: { + type: 'boolean', + description: 'Item 9A — Is the applicant currently on active duty?', + }, + + terminalLeave: { + type: 'boolean', + description: 'Item 9B — Is the applicant currently on terminal leave just before discharge?', + }, + + servicePeriods: { + type: 'array', + description: 'Item 10 — Military service history; one or more service periods', + minItems: 1, + items: buildDefinitionReference('servicePeriod'), + }, + + ddForm2863: { + type: 'array', + description: 'Uploaded DD Form 2863 (NCS Election of Options) — required for processing', + minItems: 1, + maxItems: 3, + items: buildDefinitionReference('uploadedDocument'), + }, + + ddForm214: { + type: 'array', + description: 'Uploaded DD Form 214 Member 4 copy — required (or deferred when applicant is on terminal leave)', + minItems: 0, + maxItems: 3, + items: buildDefinitionReference('uploadedDocument'), + }, + }, + required: ['activeDuty', 'terminalLeave', 'servicePeriods'], + }, + + // ── Part IV: Concurrent Benefits ────────────────────────────────────────── + + concurrentBenefits: { + type: 'object', + description: 'Part IV — Concurrent benefit questions (Items 11A–11C)', + additionalProperties: false, + properties: { + + seniorRotcScholarship: { + type: 'boolean', + description: 'Item 11A — Is the applicant participating in a Senior ROTC scholarship program under Section 2107, Title 10, U.S. Code?', + }, + + federalTuitionAssistance: { + type: ['boolean', 'null'], + description: 'Item 11B — Is the applicant receiving or anticipating money (including Federal Tuition Assistance) from the Armed Forces or Public Health Service? Conditional — only collected when activeDuty = true.', + }, + + civilianGovtEmployee: { + type: ['boolean', 'null'], + description: 'Gate for Item 11C — Is the applicant a civilian employee of the U.S. Government? Determines whether Item 11C is shown.', + }, + + govtEmployeeFunding: { + type: ['boolean', 'null'], + description: 'Item 11C — Does the applicant expect to receive funds from their government agency or department for the same courses? Conditional on civilianGovtEmployee = true.', + }, + + govtEmployeeFundingSource: { + type: ['string', 'null'], + maxLength: 200, + description: 'Item 11C source — Description of the source of government civilian funding; conditional on govtEmployeeFunding = true', + }, + }, + required: ['seniorRotcScholarship'], + }, + + // ── Item 7: Direct Deposit ───────────────────────────────────────────────── + // Note: Appears as Part II on the paper form but is collected as its own + // chapter in the digital flow (Screens 16–17). + + directDeposit: { + type: 'object', + description: 'Item 7 — Direct deposit enrollment and bank account information', + additionalProperties: false, + properties: { + + noDirectDeposit: { + type: 'boolean', + description: 'Applicant has elected not to enroll in direct deposit; when true, bankAccount and bankDocument are not required', + }, + + bankAccount: buildDefinitionReference('bankAccount'), + + bankDocument: { + type: 'array', + description: 'Uploaded voided personal check or deposit slip to verify bank account information; required when bankAccount is provided', + minItems: 0, + maxItems: 3, + items: buildDefinitionReference('uploadedDocument'), + }, + }, + }, + + // ── Part V: Certification and Signature ─────────────────────────────────── + + certification: { + type: 'object', + description: 'Part V — Digital certification attestation, ESO consultation, date signed (Item 12A equivalent)', + additionalProperties: false, + properties: { + + privacyAgreementAccepted: { + type: 'boolean', + description: 'Applicant certifies all statements in the application are true and correct to the best of their knowledge and belief (verbatim Part V attestation)', + enum: [true], + }, + + esoConsultationCertified: { + type: ['boolean', 'null'], + description: 'Active duty applicants certify they have consulted with an Education Service Officer (ESO) regarding their education program; conditional on activeDuty = true', + }, + + dateSigned: { + type: 'string', + format: 'date', + description: 'Item 12B — Date application was signed; auto-populated from system date at submission time (ISO 8601 YYYY-MM-DD)', + }, + }, + required: ['privacyAgreementAccepted', 'dateSigned'], + }, + }, + + required: [ + 'applicantInformation', + 'educationTraining', + 'serviceInformation', + 'concurrentBenefits', + 'certification', + ], +}; + +export default schema; \ No newline at end of file