mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 09:00:57 +00:00
fix: onboarding validation
This commit is contained in:
@@ -2057,6 +2057,11 @@ export class CompaniesService {
|
||||
// replace it before the application counts as complete.
|
||||
const flaggedDelegation = delegationDue && delegation.flagged;
|
||||
|
||||
// Mirrors `poaProven` in buildCompanyIdentityState — see the note there.
|
||||
const poaProven = identity.faydaRequired
|
||||
? identity.poa.verified
|
||||
: identity.poa.verified || Boolean(identity.poa.name?.trim());
|
||||
|
||||
const outstanding = [
|
||||
...missingInfo.map((f) => `Add your ${f.label.toLowerCase()}`),
|
||||
...missingDocs.map((d) => `Upload your ${d.fileLabel}`),
|
||||
@@ -2074,8 +2079,19 @@ export class CompaniesService {
|
||||
...(identity.faydaRequired && !identity.owner.verified
|
||||
? ["Verify the company owner's identity with Fayda"]
|
||||
: []),
|
||||
...((poaRequired || poaProvided) && !identity.poa.verified
|
||||
? ["Verify your Power of Attorney's identity with Fayda"]
|
||||
// Nationality-aware, exactly like `poaProven` in
|
||||
// buildCompanyIdentityState and the check in `assertIdentityVerified`:
|
||||
// Fayda is an Ethiopian national ID, so a foreign company's typed
|
||||
// representative has to count. Demanding a verification here regardless
|
||||
// made this list disagree with the rule actually enforced, and left a
|
||||
// foreign freight forwarder unable to submit — asked for a Fayda
|
||||
// verification its representative may have no way to obtain.
|
||||
...((poaRequired || poaProvided) && !poaProven
|
||||
? [
|
||||
identity.faydaRequired
|
||||
? "Verify your Power of Attorney's identity with Fayda"
|
||||
: "Name your Power of Attorney, or verify them with Fayda",
|
||||
]
|
||||
: []),
|
||||
...(identity.passportRequired && !identity.owner.passportNumber
|
||||
? ["Add the company owner's passport number"]
|
||||
@@ -2089,7 +2105,10 @@ export class CompaniesService {
|
||||
const poaItemCount = delegationDue ? 1 : 0;
|
||||
// One item per identity credential the company has to prove: the owner
|
||||
// always (Fayda for Ethiopian, passport for foreign), plus the PoA once
|
||||
// there is one — that one is Fayda whatever the nationality.
|
||||
// there is one — Fayda for an Ethiopian company, a named representative
|
||||
// for a foreign one, same rule as `poaProven` above. Counting a foreign
|
||||
// company's typed PoA as unproven here left the progress bar permanently
|
||||
// short of 100% on an item it had already satisfied.
|
||||
const ownerCredentialDue =
|
||||
identity.faydaRequired || identity.passportRequired;
|
||||
const ownerCredentialProven = identity.faydaRequired
|
||||
@@ -2099,7 +2118,7 @@ export class CompaniesService {
|
||||
(ownerCredentialDue ? 1 : 0) + (delegationDue ? 1 : 0);
|
||||
const missingIdentityCount =
|
||||
(ownerCredentialDue && !ownerCredentialProven ? 1 : 0) +
|
||||
(delegationDue && !identity.poa.verified ? 1 : 0);
|
||||
(delegationDue && !poaProven ? 1 : 0);
|
||||
const total =
|
||||
requiredInfo.length +
|
||||
requiredDocCount +
|
||||
@@ -2767,7 +2786,13 @@ export class CompaniesService {
|
||||
// The verified payload owns the person's details from here on.
|
||||
...(result.fullName ? { [`${prefix}Name`]: result.fullName } : {}),
|
||||
...(result.email ? { [`${prefix}Email`]: result.email } : {}),
|
||||
...(result.phoneNumber ? { [`${prefix}Phone`]: result.phoneNumber } : {}),
|
||||
// Fayda returns whatever the national registry holds, which is routinely a
|
||||
// local number ("0911223344"). Every typed phone in this service is stored
|
||||
// E.164, and `@IsValidPhone()` rejects anything else — so a raw claim here
|
||||
// becomes a value the portal reads back and cannot resubmit.
|
||||
...(result.phoneNumber
|
||||
? { [`${prefix}Phone`]: normalizeE164(result.phoneNumber) }
|
||||
: {}),
|
||||
...(result.address ? { [`${prefix}Address`]: result.address } : {}),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { IsString, IsOptional, IsEmail, MaxLength, IsEnum, IsIn } from 'class-validator';
|
||||
import {
|
||||
IsString,
|
||||
IsOptional,
|
||||
IsEmail,
|
||||
MaxLength,
|
||||
IsEnum,
|
||||
IsIn,
|
||||
Matches,
|
||||
} from 'class-validator';
|
||||
import { ETHIOPIAN_REGIONS, type EthiopianRegion } from '@edr/types';
|
||||
import { CompanyNationality } from '../entities/company.entity';
|
||||
import { IsValidPhone } from '../../../common/validators/is-phone-number.validator';
|
||||
@@ -39,9 +47,13 @@ export class UpdateProfileDto {
|
||||
@IsTin({ message: 'TIN must be exactly 10 digits' })
|
||||
tin?: string;
|
||||
|
||||
// Ethiopian VAT registration numbers are 10 digits, the same shape as the
|
||||
// TIN. Both portal forms enforce that; without it here the API happily stored
|
||||
// whatever a stale client sent, and the two layers disagreed about what the
|
||||
// column may hold.
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(50)
|
||||
@Matches(/^\d{10}$/, { message: 'VAT number must be exactly 10 digits' })
|
||||
vatNumber?: string;
|
||||
|
||||
// `fanNumber` is deliberately absent: the FAN is the Fayda number of the
|
||||
|
||||
Reference in New Issue
Block a user