From abeb296157e8f6dfe858bd187ae0a2ba48311c94 Mon Sep 17 00:00:00 2001 From: Nathnael Date: Wed, 12 Aug 2026 08:39:54 +0000 Subject: [PATCH] fix: file upload seeder --- .../src/seed/file-upload-settings.seeder.ts | 79 ++++++++++++------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/apps/edr-freight-api/src/seed/file-upload-settings.seeder.ts b/apps/edr-freight-api/src/seed/file-upload-settings.seeder.ts index 4f314453a..dd8f9bd20 100644 --- a/apps/edr-freight-api/src/seed/file-upload-settings.seeder.ts +++ b/apps/edr-freight-api/src/seed/file-upload-settings.seeder.ts @@ -1,6 +1,7 @@ import { Injectable, Logger } from "@nestjs/common"; import { DataSource } from "typeorm"; +import { FileUploadField } from "../modules/file-upload-settings/entities/file-upload-field.entity"; import { FileUploadSetting } from "../modules/file-upload-settings/entities/file-upload-setting.entity"; import { poaDelegationField } from "../modules/file-upload-settings/poa-delegation.constants"; @@ -165,10 +166,13 @@ const FOREIGN_ONBOARDING_FIELDS: OnboardingField[] = [ * actually produce is a backoffice decision, edited in the file-settings editor. */ const COOPERATIVE_ONBOARDING_FIELDS: OnboardingField[] = [ + // First on purpose: only the first field of a new set is seeded, and this is + // the paper that distinguishes a co-operative from every other company. { - fileKey: "tin_certificate", - fileLabel: "TIN Certificate", - helpText: "Verified against the TIN registry during registration.", + fileKey: "cooperative_registration_certificate", + fileLabel: "Co-operative Union / Farm Registration Certificate", + helpText: + "Certificate issued by the co-operative promotion agency that registered the union or farm.", isRequired: true, isMultiple: false, maxFiles: 1, @@ -177,10 +181,9 @@ const COOPERATIVE_ONBOARDING_FIELDS: OnboardingField[] = [ displayOrder: 1, }, { - fileKey: "cooperative_registration_certificate", - fileLabel: "Co-operative Union / Farm Registration Certificate", - helpText: - "Certificate issued by the co-operative promotion agency that registered the union or farm.", + fileKey: "tin_certificate", + fileLabel: "TIN Certificate", + helpText: "Verified against the TIN registry during registration.", isRequired: true, isMultiple: false, maxFiles: 1, @@ -662,16 +665,15 @@ export class FileUploadSettingsSeeder { async run() { const settingRepository = this.dataSource.getRepository(FileUploadSetting); - // Seed only into an empty table: any existing rows (including - // soft-deleted ones, which would still conflict on the unique `code`) - // mean the data is admin-managed, so leave it untouched. - const existing = await settingRepository.count({ withDeleted: true }); - if (existing > 0) { - this.logger.log( - `file_upload_settings already has ${existing} rows — skipping seed`, - ); - return; - } + // Seed per CODE, not "only into an empty table". An existing row is + // admin-managed and never touched — including a soft-deleted one, which + // means the set was removed on purpose (and would still conflict on the + // unique `code`). What the table-wide check got wrong is the other half: a + // set added to this file after the first boot could never reach a database + // that already held the others, so it existed in code and nowhere else. + const existingCodes = new Set( + (await settingRepository.find({ withDeleted: true })).map((s) => s.code), + ); const allSettings: Array< OnboardingDocumentSetting & { description: string } @@ -701,20 +703,41 @@ export class FileUploadSettingsSeeder { })), ]; - // Insert setting rows only — no FileUploadField rows. Fields start empty - // and are configured from the backoffice file-settings editor; the field - // definitions above are kept as reference defaults. - await settingRepository.insert( - allSettings.map((documentSetting) => ({ - code: documentSetting.code, - label: documentSetting.label, - description: documentSetting.description, - entity: documentSetting.entity, - })), + const missing = allSettings.filter((s) => !existingCodes.has(s.code)); + if (missing.length === 0) { + this.logger.log("file upload settings up to date — nothing to seed"); + return; + } + + const inserted = await settingRepository.save( + missing.map((documentSetting) => + settingRepository.create({ + code: documentSetting.code, + label: documentSetting.label, + description: documentSetting.description, + entity: documentSetting.entity, + }), + ), ); + // A brand-new set gets exactly ONE field: its first reference default. The + // rest of the list above stays documentation — what a set actually asks for + // is a backoffice decision, edited in the file-settings editor. Seeding one + // means a set is never born empty (an empty set silently requires nothing), + // while leaving the admin a single row to extend rather than a list to prune. + const fieldRepository = this.dataSource.getRepository(FileUploadField); + const firstFields = inserted.flatMap((setting) => { + const reference = missing.find((s) => s.code === setting.code)?.fields[0]; + return reference + ? [fieldRepository.create({ ...reference, settingId: setting.id })] + : []; + }); + if (firstFields.length > 0) await fieldRepository.save(firstFields); + this.logger.log( - `Seeded ${allSettings.length} file upload settings with empty fields`, + `Seeded ${missing.length} file upload settings (${firstFields.length} with a default field): ${missing + .map((s) => s.code) + .join(", ")}`, ); } }