Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

08 — Form Specifications (Bulk Module)

The only "form" in this module is the CSV itself plus the import confirm decision. This file specifies the CSV column contract field by field (from the adapter) and the complete error-string catalogue (exact strings from source). Every string below is quoted from code — the client must display these verbatim.


1. CSV column contract (import — students entity)

Source: students-import.adapter.ts:17-26 (columns array) + :38-144 (validation/resolution). Header names are exact, case-sensitive, no normalization — the server parses with columns:true (bulk-import.service.ts:26-30) which keys rows by the literal header.

1.1 Field-by-field

#ColumnRequiredFormat / matchingAdapter source
1firstNameyesany non-empty string; → CreateUserDto.firstName (students-import.adapter.ts:66-70; create-user.dto.ts:5-8):18
2lastNameyesany non-empty string; → CreateUserDto.lastName (create-user.dto.ts:15-17):19
3emailyesmust match /^[^\s@]+@[^\s@]+\.[^\s@]+$/ (students-import.adapter.ts:11,46-47); must not already be registered (:55-56 via UsersService.findByEmail):20
4admissionNumberyesany non-empty string; must not already exist (:49-54 via StudentRepository.findByAdmissionNumber):21
5gradeyesmatched against existing Grade by name OR code (:118-121, $or):22
6sectionyesmatched against existing Section by name (:122-125):23
7academicYearyesmatched against existing AcademicYear by name (:113-117):24
8rollNumbernoblank → omitted (`

1.2 Reference resolution rule (critical)

The adapter never creates academicYear/grade/section/class — they must exist in the tenant before import. Class lookup is derived: grade + section (+ academicYear)classRepo.findOne (:131-142). "Not found" rows are rejected, not auto-created (03 J2).

1.3 Value handling

  • Cell values are trimmed by the parser (trim:true, bulk-import.service.ts:29) and again in the adapter (:41-44); required check is !value after trim → empty string fails.
  • Row keys (headers) are not trimmed/normalized — a " firstName " header fails every lookup (06 §2.2; 14 §2).
  • Extra/unknown columns are ignored (parser keeps them in the row object but the adapter only reads its columns keys).

1.4 Template copy (client-generated (forward-looking))

RowContent
headerfirstName,lastName,email,admissionNumber,grade,section,academicYear,rollNumber
exampleAmara,Osei,amara.o@school.example,A-2026-001,Grade 7,B,2026-2027,01

Downloadable template is client-generated — there is no server template endpoint (04 §6).

2. Error-string catalogue (exact)

2.1 Request-level (file rejected, no rows touched)

TriggerCodeExact stringSource
multipart field file missing400 VALIDATION_ERRORCSV file is required (multipart field "file").bulk.controller.ts:44-45
CSV unparsable400 VALIDATION_ERRORMalformed CSV: could not parse file.bulk-import.service.ts:32
zero data rows400 VALIDATION_ERRORCSV must include a header row and data.bulk-import.service.ts:35
unknown entity404 RESOURCE_NOT_FOUNDNo import adapter for entity "X". (X = path param)bulk-import.service.ts:19

2.2 Row-level (per ImportRowError.errors[])

GroupExact stringSource
required missingMissing required column "X". (X = column name)students-import.adapter.ts:43
formatInvalid email format.:47
duplicateAdmission number "X" already exists.:54
duplicateEmail "X" already registered.:56
referenceAcademic year "X" not found.:117
referenceGrade "X" not found.:121
referenceSection "X" not found.:125
referenceNo class found for grade "X" section "Y".:140
create-timearbitrary message from the thrown Error (err.message)bulk-import.service.ts:57-62

2.3 Client-side (pre-upload, mirroring server strings)

ConditionCopyMirrors
unparsable fileThis file could not be read as CSV.bulk-import.service.ts:32
header only / emptyCSV must include a header row and data.:35
missing required headerMissing required column "X".students-import.adapter.ts:43
whitespace-damaged headerHeader " firstName" has leading/trailing space — the server will not match it.:26-30 (headers unnormalized)
non-UTF-8 encodingFile is not UTF-8 — re-encode and retry.bulk.controller.ts:47

3. Export "form" (SS5)

No input form: entity select → download. Columns produced (students-import.adapter.ts:92-97): admissionNumber, rollNumber, status, admissionDate (ISO string; '' when unset); rows sorted by admissionNumber asc (:90-91). Export is a roster snapshot, not an import template (04 §6).