fix: make the reference generate on apporaval

This commit is contained in:
Nathnael
2026-06-24 13:22:20 +00:00
parent 083a87ab9e
commit f7bbb03cc1
4 changed files with 61 additions and 18 deletions

View File

@@ -0,0 +1,31 @@
import { MigrationInterface, QueryRunner } from "typeorm";
/**
* Company-profile references are now minted only when a profile is approved
* (status → Active); pending profiles carry NULL. Drop the NOT NULL constraint
* on freight.company_profiles.reference. The existing unique index is kept —
* Postgres treats NULLs as distinct, so multiple pending (NULL) profiles don't
* collide.
*/
export class MakeCompanyProfileReferenceNullable1810000000002
implements MigrationInterface
{
name = "MakeCompanyProfileReferenceNullable1810000000002";
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "freight"."company_profiles" ALTER COLUMN "reference" DROP NOT NULL`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
// Reinstating NOT NULL requires every row to have a reference; any pending
// (NULL) profiles get a placeholder so the constraint can be re-applied.
await queryRunner.query(
`UPDATE "freight"."company_profiles" SET "reference" = 'PENDING-' || left(replace("id"::text, '-', ''), 12) WHERE "reference" IS NULL`,
);
await queryRunner.query(
`ALTER TABLE "freight"."company_profiles" ALTER COLUMN "reference" SET NOT NULL`,
);
}
}

View File

@@ -193,15 +193,13 @@ export class CompaniesService {
input.type, input.type,
); );
if (existing) continue; if (existing) continue;
const reference = await this.companyProfilesRepo.generateReference( // No reference yet — these profiles await backoffice approval, which
input.type, // is when the reference is minted (see setCompanyProfileStatus).
);
await this.companyProfilesRepo.create({ await this.companyProfilesRepo.create({
companyId: company.id, companyId: company.id,
type: input.type, type: input.type,
reference,
businessLicense: input.businessLicense ?? null, businessLicense: input.businessLicense ?? null,
status: ProfileStatus.Active, status: ProfileStatus.Pending,
}); });
} }
company.companyProfiles = await this.companyProfilesRepo.findByCompanyId( company.companyProfiles = await this.companyProfilesRepo.findByCompanyId(
@@ -310,12 +308,11 @@ export class CompaniesService {
type, type,
); );
if (existing) continue; if (existing) continue;
const reference = await this.companyProfilesRepo.generateReference(type); // No reference yet — minted on backoffice approval (setCompanyProfileStatus).
await this.companyProfilesRepo.create({ await this.companyProfilesRepo.create({
companyId, companyId,
type, type,
reference, status: ProfileStatus.Pending,
status: ProfileStatus.Active,
}); });
} }
} }
@@ -680,10 +677,20 @@ export class CompaniesService {
profileId: string, profileId: string,
status: ProfileStatus, status: ProfileStatus,
): Promise<CompanyProfile> { ): Promise<CompanyProfile> {
const updated = await this.companyProfilesRepo.updateStatus( const existing = await this.companyProfilesRepo.findById(profileId);
profileId, if (!existing)
status, throw new NotFoundException(`Company profile ${profileId} not found`);
);
// A reference number is only minted the first time a profile is approved
// (status → Active). Pending/unapproved profiles carry no reference.
const patch: Partial<CompanyProfile> = { status };
if (status === ProfileStatus.Active && !existing.reference) {
patch.reference = await this.companyProfilesRepo.generateReference(
existing.type,
);
}
const updated = await this.companyProfilesRepo.update(profileId, patch);
if (!updated) if (!updated)
throw new NotFoundException(`Company profile ${profileId} not found`); throw new NotFoundException(`Company profile ${profileId} not found`);
@@ -718,7 +725,7 @@ export class CompaniesService {
const existing = await this.companyProfilesRepo.findByType(companyId, type); const existing = await this.companyProfilesRepo.findByType(companyId, type);
if (existing) { if (existing) {
throw new ConflictException( throw new ConflictException(
`Company already has a ${type} profile (${existing.reference})`, `Company already has a ${type} profile (${existing.reference ?? "pending approval"})`,
); );
} }
@@ -931,7 +938,7 @@ export class CompaniesService {
const licenseProfiles = (company.companyProfiles ?? []).map((p) => ({ const licenseProfiles = (company.companyProfiles ?? []).map((p) => ({
profileId: p.id, profileId: p.id,
type: p.type, type: p.type,
reference: p.reference, reference: p.reference ?? "",
uploaded: (p.businessLicenseFiles?.length ?? 0) > 0, uploaded: (p.businessLicenseFiles?.length ?? 0) > 0,
})); }));
const missingLicenses = licenseProfiles.filter((p) => !p.uploaded); const missingLicenses = licenseProfiles.filter((p) => !p.uploaded);

View File

@@ -28,7 +28,7 @@ export class ResponseCompanyProfileDto {
this.id = profile.id; this.id = profile.id;
this.companyId = profile.companyId; this.companyId = profile.companyId;
this.type = profile.type; this.type = profile.type;
this.reference = profile.reference; this.reference = profile.reference ?? '';
this.status = profile.status; this.status = profile.status;
this.businessLicense = profile.businessLicense; this.businessLicense = profile.businessLicense;
this.licenseFiles = profile.businessLicenseFiles ?? []; this.licenseFiles = profile.businessLicenseFiles ?? [];

View File

@@ -40,14 +40,19 @@ export class CompanyProfile extends BaseEntity {
@Column({ name: "type", type: "varchar", length: 32, enum: ProfileType }) @Column({ name: "type", type: "varchar", length: 32, enum: ProfileType })
type!: ProfileType; type!: ProfileType;
/**
* Official profile reference (e.g. "EX-00001"). Minted only when the profile
* is approved (status → Active); pending/unapproved profiles carry NULL.
* The unique index tolerates this because Postgres treats NULLs as distinct.
* API responses surface it as "" when absent — see ResponseCompanyProfileDto.
*/
@Column({ @Column({
name: "reference", name: "reference",
type: "varchar", type: "varchar",
length: 20, length: 20,
nullable: false, nullable: true,
unique: true,
}) })
reference!: string; reference!: string | null;
@Column({ @Column({
name: "status", name: "status",