|
|
|
|
@@ -231,41 +231,39 @@ export class CompaniesService {
|
|
|
|
|
label: string;
|
|
|
|
|
get: (company: Company) => unknown;
|
|
|
|
|
}[] = [
|
|
|
|
|
{
|
|
|
|
|
key: "tinNumber",
|
|
|
|
|
label: "Company TIN",
|
|
|
|
|
get: (c) => (c.tin && !c.tin.startsWith("D") ? c.tin : null),
|
|
|
|
|
},
|
|
|
|
|
{ key: "companyEmail", label: "Company email", get: (c) => c.email },
|
|
|
|
|
{ key: "companyPhone", label: "Company phone", get: (c) => c.phone },
|
|
|
|
|
{ key: "companyAddress", label: "Company address", get: (c) => c.address },
|
|
|
|
|
{ key: "fanNumber", label: "FAN number", get: (c) => c.fanNumber },
|
|
|
|
|
{
|
|
|
|
|
key: "contactPersonName",
|
|
|
|
|
label: "Contact person name",
|
|
|
|
|
get: (c) => c.attributes?.contactPersonName,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: "contactPersonPhone",
|
|
|
|
|
label: "Contact person phone",
|
|
|
|
|
get: (c) => c.attributes?.contactPersonPhone,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: "generalManagerName",
|
|
|
|
|
label: "General manager name",
|
|
|
|
|
get: (c) => c.attributes?.generalManagerName,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: "generalManagerEmail",
|
|
|
|
|
label: "General manager email",
|
|
|
|
|
get: (c) => c.attributes?.generalManagerEmail,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: "generalManagerPhone",
|
|
|
|
|
label: "General manager phone",
|
|
|
|
|
get: (c) => c.attributes?.generalManagerPhone,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
{
|
|
|
|
|
key: "tinNumber",
|
|
|
|
|
label: "Company TIN",
|
|
|
|
|
get: (c) => (c.tin && !c.tin.startsWith("D") ? c.tin : null),
|
|
|
|
|
},
|
|
|
|
|
{ key: "companyAddress", label: "Company address", get: (c) => c.address },
|
|
|
|
|
{ key: "fanNumber", label: "FAN number", get: (c) => c.fanNumber },
|
|
|
|
|
{
|
|
|
|
|
key: "contactPersonName",
|
|
|
|
|
label: "Contact person name",
|
|
|
|
|
get: (c) => c.attributes?.contactPersonName,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: "contactPersonPhone",
|
|
|
|
|
label: "Contact person phone",
|
|
|
|
|
get: (c) => c.attributes?.contactPersonPhone,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: "generalManagerName",
|
|
|
|
|
label: "General manager name",
|
|
|
|
|
get: (c) => c.attributes?.generalManagerName,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: "generalManagerEmail",
|
|
|
|
|
label: "General manager email",
|
|
|
|
|
get: (c) => c.attributes?.generalManagerEmail,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: "generalManagerPhone",
|
|
|
|
|
label: "General manager phone",
|
|
|
|
|
get: (c) => c.attributes?.generalManagerPhone,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
/** The nationality-based document setting code for a company. */
|
|
|
|
|
private documentSettingCodeFor(
|
|
|
|
|
@@ -314,8 +312,6 @@ export class CompaniesService {
|
|
|
|
|
fanNumber: dto.fanNumber ?? null,
|
|
|
|
|
country: dto.companyLocation ?? "Ethiopia",
|
|
|
|
|
address: dto.companyAddress ?? null,
|
|
|
|
|
phone: normalizeE164(dto.companyPhone) ?? null,
|
|
|
|
|
email: dto.companyEmail ?? null,
|
|
|
|
|
attributes: dto.attributes ?? null,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@@ -492,7 +488,8 @@ export class CompaniesService {
|
|
|
|
|
async findCompanyById(id: string): Promise<Company> {
|
|
|
|
|
const company = await this.companiesRepo.findById(id);
|
|
|
|
|
if (!company) throw new NotFoundException(`Company ${id} not found`);
|
|
|
|
|
company.companyProfiles = await this.companyProfilesRepo.findByCompanyId(id);
|
|
|
|
|
company.companyProfiles =
|
|
|
|
|
await this.companyProfilesRepo.findByCompanyId(id);
|
|
|
|
|
// External profiles carry the onboarding flag the backoffice gates
|
|
|
|
|
// approval decisions on (see ResponseCompanyDto.onboardingCompleted).
|
|
|
|
|
company.profiles = await this.profilesRepo.findByCompanyId(id);
|
|
|
|
|
@@ -515,9 +512,7 @@ export class CompaniesService {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (profile.status !== ProfileStatus.Active) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
"Selected company profile is not active",
|
|
|
|
|
);
|
|
|
|
|
throw new BadRequestException("Selected company profile is not active");
|
|
|
|
|
}
|
|
|
|
|
return profile;
|
|
|
|
|
}
|
|
|
|
|
@@ -760,11 +755,6 @@ export class CompaniesService {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const keys: string[] = [];
|
|
|
|
|
if (attrs.ownerFaydaSub) {
|
|
|
|
|
// The Company-column mirrors of the owner's verified contact details.
|
|
|
|
|
if (held("ownerEmail")) keys.push("companyEmail");
|
|
|
|
|
if (held("ownerPhone")) keys.push("companyPhone");
|
|
|
|
|
}
|
|
|
|
|
for (const subject of IDENTITY_SUBJECTS) {
|
|
|
|
|
if (!attrs[`${IDENTITY_PREFIX[subject]}FaydaSub`]) continue;
|
|
|
|
|
keys.push(...IDENTITY_OWNED_FIELDS[subject].filter(held));
|
|
|
|
|
@@ -789,9 +779,6 @@ export class CompaniesService {
|
|
|
|
|
if (dto.nationality !== undefined)
|
|
|
|
|
companyUpdates.nationality = dto.nationality;
|
|
|
|
|
if (dto.companyName !== undefined) companyUpdates.name = dto.companyName;
|
|
|
|
|
if (dto.companyEmail !== undefined) companyUpdates.email = dto.companyEmail;
|
|
|
|
|
if (dto.companyPhone !== undefined)
|
|
|
|
|
companyUpdates.phone = normalizeE164(dto.companyPhone);
|
|
|
|
|
if (dto.companyLocation !== undefined)
|
|
|
|
|
companyUpdates.country = dto.companyLocation;
|
|
|
|
|
if (dto.companyAddress !== undefined)
|
|
|
|
|
@@ -809,7 +796,9 @@ export class CompaniesService {
|
|
|
|
|
if (dto.contactPersonPhone !== undefined)
|
|
|
|
|
attrUpdates.contactPersonPhone = normalizeE164(dto.contactPersonPhone);
|
|
|
|
|
if (dto.contactVerifiedPhone !== undefined)
|
|
|
|
|
attrUpdates.contactVerifiedPhone = normalizeE164(dto.contactVerifiedPhone);
|
|
|
|
|
attrUpdates.contactVerifiedPhone = normalizeE164(
|
|
|
|
|
dto.contactVerifiedPhone,
|
|
|
|
|
);
|
|
|
|
|
if (dto.generalManagerName !== undefined)
|
|
|
|
|
attrUpdates.generalManagerName = dto.generalManagerName;
|
|
|
|
|
if (dto.generalManagerEmail !== undefined)
|
|
|
|
|
@@ -820,7 +809,8 @@ export class CompaniesService {
|
|
|
|
|
if (dto.poaPhone !== undefined)
|
|
|
|
|
attrUpdates.poaPhone = normalizeE164(dto.poaPhone);
|
|
|
|
|
if (dto.poaEmail !== undefined) attrUpdates.poaEmail = dto.poaEmail;
|
|
|
|
|
if (dto.poaLocation !== undefined) attrUpdates.poaLocation = dto.poaLocation;
|
|
|
|
|
if (dto.poaLocation !== undefined)
|
|
|
|
|
attrUpdates.poaLocation = dto.poaLocation;
|
|
|
|
|
if (dto.poaAddress !== undefined) attrUpdates.poaAddress = dto.poaAddress;
|
|
|
|
|
|
|
|
|
|
if (dto.licenceNumber !== undefined)
|
|
|
|
|
@@ -857,12 +847,6 @@ export class CompaniesService {
|
|
|
|
|
Object.assign(attrUpdates, dto.faydaIdentity);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// companyEmail/companyPhone are the Company-column mirrors of the owner's
|
|
|
|
|
// verified contact details (the portal derives and submits them, it never
|
|
|
|
|
// lets the customer type them once verified) — lock them the same way
|
|
|
|
|
// ownerEmail/ownerPhone themselves are locked below, once there is a
|
|
|
|
|
// verified owner to lock them to.
|
|
|
|
|
//
|
|
|
|
|
// Keyed on the verified VALUE, not on `ownerFaydaSub`: Fayda's email and
|
|
|
|
|
// phone claims are optional, so a verification can prove the person while
|
|
|
|
|
// supplying neither (see completeIdentityVerification's conditional
|
|
|
|
|
@@ -872,9 +856,8 @@ export class CompaniesService {
|
|
|
|
|
// forever, and re-verifying could never clear it because Fayda still has
|
|
|
|
|
// nothing to return.
|
|
|
|
|
if (attrUpdates.ownerFaydaSub) {
|
|
|
|
|
if (attrUpdates.ownerEmail && dto.companyEmail !== undefined)
|
|
|
|
|
companyUpdates.email = attrUpdates.ownerEmail;
|
|
|
|
|
if (attrUpdates.ownerPhone && dto.companyPhone !== undefined)
|
|
|
|
|
if (attrUpdates.ownerEmail) companyUpdates.email = attrUpdates.ownerEmail;
|
|
|
|
|
if (attrUpdates.ownerPhone)
|
|
|
|
|
companyUpdates.phone = normalizeE164(String(attrUpdates.ownerPhone));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1086,9 +1069,7 @@ export class CompaniesService {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** List a company's change requests, newest first (backoffice review). */
|
|
|
|
|
async listChangeRequests(
|
|
|
|
|
companyId: string,
|
|
|
|
|
): Promise<CompanyChangeRequest[]> {
|
|
|
|
|
async listChangeRequests(companyId: string): Promise<CompanyChangeRequest[]> {
|
|
|
|
|
await this.findCompanyById(companyId);
|
|
|
|
|
return this.changeRequestRepo.findByCompanyId(companyId);
|
|
|
|
|
}
|
|
|
|
|
@@ -1150,8 +1131,7 @@ export class CompaniesService {
|
|
|
|
|
reviewerId?: string,
|
|
|
|
|
): Promise<CompanyChangeRequest> {
|
|
|
|
|
const request = await this.changeRequestRepo.findById(id);
|
|
|
|
|
if (!request)
|
|
|
|
|
throw new NotFoundException(`Change request ${id} not found`);
|
|
|
|
|
if (!request) throw new NotFoundException(`Change request ${id} not found`);
|
|
|
|
|
if (request.status !== ChangeRequestStatus.Pending) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
`Change request ${id} is already ${request.status}`,
|
|
|
|
|
@@ -1164,7 +1144,10 @@ export class CompaniesService {
|
|
|
|
|
|
|
|
|
|
const snapshot = (request.snapshot ?? {}) as Partial<UpdateProfileDto>;
|
|
|
|
|
await this.assertTinAvailable(company, snapshot.tin);
|
|
|
|
|
const companyUpdates = this.mapProfileDtoToCompanyUpdates(company, snapshot);
|
|
|
|
|
const companyUpdates = this.mapProfileDtoToCompanyUpdates(
|
|
|
|
|
company,
|
|
|
|
|
snapshot,
|
|
|
|
|
);
|
|
|
|
|
await this.companiesRepo.update(company.id, companyUpdates);
|
|
|
|
|
await this.applyLicenseChanges(request);
|
|
|
|
|
await this.applyDocumentChanges(request);
|
|
|
|
|
@@ -1294,7 +1277,12 @@ export class CompaniesService {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (documentChanges.length > 0) {
|
|
|
|
|
await this.recordCompanyRevision(company, {}, submittedBy, documentChanges);
|
|
|
|
|
await this.recordCompanyRevision(
|
|
|
|
|
company,
|
|
|
|
|
{},
|
|
|
|
|
submittedBy,
|
|
|
|
|
documentChanges,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return uploaded;
|
|
|
|
|
}
|
|
|
|
|
@@ -1414,7 +1402,11 @@ export class CompaniesService {
|
|
|
|
|
status: ChangeRequestStatus.Pending,
|
|
|
|
|
});
|
|
|
|
|
if (company) {
|
|
|
|
|
this.companyNotifier.changeRequestSubmitted(company, existing.id, false);
|
|
|
|
|
this.companyNotifier.changeRequestSubmitted(
|
|
|
|
|
company,
|
|
|
|
|
existing.id,
|
|
|
|
|
false,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const history = await this.changeRequestRepo.findByCompanyId(companyId);
|
|
|
|
|
@@ -1446,8 +1438,7 @@ export class CompaniesService {
|
|
|
|
|
reviewerId?: string,
|
|
|
|
|
): Promise<CompanyChangeRequest> {
|
|
|
|
|
const request = await this.changeRequestRepo.findById(id);
|
|
|
|
|
if (!request)
|
|
|
|
|
throw new NotFoundException(`Change request ${id} not found`);
|
|
|
|
|
if (!request) throw new NotFoundException(`Change request ${id} not found`);
|
|
|
|
|
if (request.status !== ChangeRequestStatus.Pending) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
`Change request ${id} is already ${request.status}`,
|
|
|
|
|
@@ -1486,8 +1477,7 @@ export class CompaniesService {
|
|
|
|
|
reviewerId?: string,
|
|
|
|
|
): Promise<CompanyChangeRequest> {
|
|
|
|
|
const request = await this.changeRequestRepo.findById(id);
|
|
|
|
|
if (!request)
|
|
|
|
|
throw new NotFoundException(`Change request ${id} not found`);
|
|
|
|
|
if (!request) throw new NotFoundException(`Change request ${id} not found`);
|
|
|
|
|
if (request.status !== ChangeRequestStatus.Pending) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
`Change request ${id} is already ${request.status}`,
|
|
|
|
|
@@ -1569,10 +1559,7 @@ export class CompaniesService {
|
|
|
|
|
const reactivating =
|
|
|
|
|
status === ProfileStatus.Active &&
|
|
|
|
|
existing.status === ProfileStatus.Suspended;
|
|
|
|
|
if (
|
|
|
|
|
(status === ProfileStatus.Suspended || reactivating) &&
|
|
|
|
|
!note?.trim()
|
|
|
|
|
) {
|
|
|
|
|
if ((status === ProfileStatus.Suspended || reactivating) && !note?.trim()) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
status === ProfileStatus.Suspended
|
|
|
|
|
? "A message explaining the suspension is required — the customer will see it."
|
|
|
|
|
@@ -1594,7 +1581,9 @@ export class CompaniesService {
|
|
|
|
|
existing.status === ProfileStatus.Pending ||
|
|
|
|
|
existing.status === ProfileStatus.Rejected;
|
|
|
|
|
if (awaitingReview) {
|
|
|
|
|
const owners = await this.profilesRepo.findByCompanyId(existing.companyId);
|
|
|
|
|
const owners = await this.profilesRepo.findByCompanyId(
|
|
|
|
|
existing.companyId,
|
|
|
|
|
);
|
|
|
|
|
if (owners.length > 0 && !owners.some((o) => o.onboardingCompleted)) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
"This customer hasn't finished onboarding yet. Their roles can be reviewed once they submit their application.",
|
|
|
|
|
@@ -1652,11 +1641,17 @@ export class CompaniesService {
|
|
|
|
|
const names = pending.map((f) => f.name).join(", ");
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
`This role has ${pending.length} document(s) awaiting customer correction (${names}). ` +
|
|
|
|
|
`Approve it once the customer has re-uploaded them, or withdraw the change request first.`,
|
|
|
|
|
`Approve it once the customer has re-uploaded them, or withdraw the change request first.`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.applyProfileStatus(manager, existing, status, note, reviewerId);
|
|
|
|
|
return this.applyProfileStatus(
|
|
|
|
|
manager,
|
|
|
|
|
existing,
|
|
|
|
|
status,
|
|
|
|
|
note,
|
|
|
|
|
reviewerId,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -1992,7 +1987,9 @@ export class CompaniesService {
|
|
|
|
|
.map((f) => ({ key: f.key, label: f.label }));
|
|
|
|
|
|
|
|
|
|
// 2. Nationality-based company documents + which are already uploaded.
|
|
|
|
|
const documentSettingCode = this.documentSettingCodeFor(company.nationality);
|
|
|
|
|
const documentSettingCode = this.documentSettingCodeFor(
|
|
|
|
|
company.nationality,
|
|
|
|
|
);
|
|
|
|
|
const [setting, uploadedFiles] = await Promise.all([
|
|
|
|
|
this.fileUploadSettingsService
|
|
|
|
|
.getByCode(documentSettingCode)
|
|
|
|
|
@@ -2074,7 +2071,9 @@ export class CompaniesService {
|
|
|
|
|
? [`Upload the ${POA_DELEGATION_LABEL} for your Power of Attorney`]
|
|
|
|
|
: []),
|
|
|
|
|
...(flaggedDelegation
|
|
|
|
|
? [`Re-upload your ${POA_DELEGATION_LABEL} — EDR asked for a correction`]
|
|
|
|
|
? [
|
|
|
|
|
`Re-upload your ${POA_DELEGATION_LABEL} — EDR asked for a correction`,
|
|
|
|
|
]
|
|
|
|
|
: []),
|
|
|
|
|
...(identity.faydaRequired && !identity.owner.verified
|
|
|
|
|
? ["Verify the company owner's identity with Fayda"]
|
|
|
|
|
@@ -2088,10 +2087,10 @@ export class CompaniesService {
|
|
|
|
|
// 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.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"]
|
|
|
|
|
@@ -2137,7 +2136,10 @@ export class CompaniesService {
|
|
|
|
|
return new OnboardingRequirementsResponseDto({
|
|
|
|
|
documentSettingCode,
|
|
|
|
|
nationality: company.nationality ?? CompanyNationality.Ethiopian,
|
|
|
|
|
companyInfo: { complete: missingInfo.length === 0, missingFields: missingInfo },
|
|
|
|
|
companyInfo: {
|
|
|
|
|
complete: missingInfo.length === 0,
|
|
|
|
|
missingFields: missingInfo,
|
|
|
|
|
},
|
|
|
|
|
documents,
|
|
|
|
|
licenseProfiles,
|
|
|
|
|
poa: {
|
|
|
|
|
@@ -2179,7 +2181,7 @@ export class CompaniesService {
|
|
|
|
|
if (!requirements.isComplete) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
requirements.outstanding[0] ??
|
|
|
|
|
"Your onboarding is incomplete. Please complete all required steps before submitting.",
|
|
|
|
|
"Your onboarding is incomplete. Please complete all required steps before submitting.",
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -2188,7 +2190,10 @@ export class CompaniesService {
|
|
|
|
|
const profiles = await this.companyProfilesRepo.findByCompanyId(companyId);
|
|
|
|
|
for (const cp of profiles) {
|
|
|
|
|
if (cp.status !== ProfileStatus.Pending) {
|
|
|
|
|
await this.companyProfilesRepo.updateStatus(cp.id, ProfileStatus.Pending);
|
|
|
|
|
await this.companyProfilesRepo.updateStatus(
|
|
|
|
|
cp.id,
|
|
|
|
|
ProfileStatus.Pending,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -2214,12 +2219,12 @@ export class CompaniesService {
|
|
|
|
|
case CompanyStatus.Suspended:
|
|
|
|
|
throw new ForbiddenException(
|
|
|
|
|
`Your company account is suspended — you can't create ${action} right now. ` +
|
|
|
|
|
`Please contact EDR support for details.`,
|
|
|
|
|
`Please contact EDR support for details.`,
|
|
|
|
|
);
|
|
|
|
|
case CompanyStatus.Blacklisted:
|
|
|
|
|
throw new ForbiddenException(
|
|
|
|
|
`Your company account is blacklisted — you can't create ${action}. ` +
|
|
|
|
|
`Please contact EDR support.`,
|
|
|
|
|
`Please contact EDR support.`,
|
|
|
|
|
);
|
|
|
|
|
default:
|
|
|
|
|
throw new ForbiddenException(
|
|
|
|
|
@@ -2248,8 +2253,7 @@ export class CompaniesService {
|
|
|
|
|
switch (profile.status) {
|
|
|
|
|
case ProfileStatus.Suspended:
|
|
|
|
|
throw new ForbiddenException(
|
|
|
|
|
`Your ${role} role is suspended${
|
|
|
|
|
profile.reviewNote ? ` — ${profile.reviewNote}` : ""
|
|
|
|
|
`Your ${role} role is suspended${profile.reviewNote ? ` — ${profile.reviewNote}` : ""
|
|
|
|
|
}. Your other roles are unaffected. Please contact EDR support to resolve this.`,
|
|
|
|
|
);
|
|
|
|
|
case ProfileStatus.Blacklisted:
|
|
|
|
|
@@ -2258,8 +2262,7 @@ export class CompaniesService {
|
|
|
|
|
);
|
|
|
|
|
case ProfileStatus.Rejected:
|
|
|
|
|
throw new ForbiddenException(
|
|
|
|
|
`Your ${role} role was rejected${
|
|
|
|
|
profile.reviewNote ? ` — ${profile.reviewNote}` : ""
|
|
|
|
|
`Your ${role} role was rejected${profile.reviewNote ? ` — ${profile.reviewNote}` : ""
|
|
|
|
|
}. Amend and resubmit it from your settings page.`,
|
|
|
|
|
);
|
|
|
|
|
default:
|
|
|
|
|
@@ -2518,9 +2521,7 @@ export class CompaniesService {
|
|
|
|
|
LICENSE_RESOURCE,
|
|
|
|
|
);
|
|
|
|
|
return records
|
|
|
|
|
.filter(
|
|
|
|
|
(r) => r.code === LICENSE_CODE || r.code === LICENSE_PENDING_CODE,
|
|
|
|
|
)
|
|
|
|
|
.filter((r) => r.code === LICENSE_CODE || r.code === LICENSE_PENDING_CODE)
|
|
|
|
|
.map((r) => ({
|
|
|
|
|
id: r.id,
|
|
|
|
|
name: r.name,
|
|
|
|
|
@@ -2687,7 +2688,7 @@ export class CompaniesService {
|
|
|
|
|
if (missing.length > 0) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
`A freight forwarder acts on other companies' behalf, so a Power of Attorney is required. ` +
|
|
|
|
|
`Add the ${missing.map((f) => f.label.toLowerCase()).join(", ")} first.`,
|
|
|
|
|
`Add the ${missing.map((f) => f.label.toLowerCase()).join(", ")} first.`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -2699,13 +2700,13 @@ export class CompaniesService {
|
|
|
|
|
if (!onFile) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
`Upload the ${POA_DELEGATION_LABEL} for the Power of Attorney` +
|
|
|
|
|
(opts.requirePoa ? " — it is required for freight forwarders." : "."),
|
|
|
|
|
(opts.requirePoa ? " — it is required for freight forwarders." : "."),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
if (flagged) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
`The ${POA_DELEGATION_LABEL} on file needs to be corrected. ` +
|
|
|
|
|
`Re-upload it before continuing.`,
|
|
|
|
|
`Re-upload it before continuing.`,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -2769,7 +2770,8 @@ export class CompaniesService {
|
|
|
|
|
// of this check entirely.
|
|
|
|
|
if (dto.subject === "owner" || dto.subject === "poa") {
|
|
|
|
|
const other: IdentitySubject = dto.subject === "poa" ? "owner" : "poa";
|
|
|
|
|
const otherSub = company.attributes?.[`${IDENTITY_PREFIX[other]}FaydaSub`];
|
|
|
|
|
const otherSub =
|
|
|
|
|
company.attributes?.[`${IDENTITY_PREFIX[other]}FaydaSub`];
|
|
|
|
|
if (otherSub && otherSub === result.sub) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
|
`This identity is already registered as the company's ${IDENTITY_LABEL[other]}. The Power of Attorney must be a different person from the owner.`,
|
|
|
|
|
@@ -2976,8 +2978,8 @@ export class CompaniesService {
|
|
|
|
|
const snapshot = {
|
|
|
|
|
...(existing?.snapshot ?? {}),
|
|
|
|
|
faydaIdentity: {
|
|
|
|
|
...(((existing?.snapshot ?? {}) as Record<string, any>)
|
|
|
|
|
.faydaIdentity ?? {}),
|
|
|
|
|
...(((existing?.snapshot ?? {}) as Record<string, any>).faydaIdentity ??
|
|
|
|
|
{}),
|
|
|
|
|
...identity,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
@@ -3389,7 +3391,10 @@ export class CompaniesService {
|
|
|
|
|
"We couldn't find a business license for this TIN with eTrade. Please double-check the number and try again.",
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return this.etradeService.extractRegistrationData(businessInfo, companyInfo);
|
|
|
|
|
return this.etradeService.extractRegistrationData(
|
|
|
|
|
businessInfo,
|
|
|
|
|
companyInfo,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async fetchETradeData(tin: string, excludeCompanyId?: string) {
|
|
|
|
|
@@ -3421,7 +3426,9 @@ export class CompaniesService {
|
|
|
|
|
|
|
|
|
|
const tin = dto.tin ?? company.tin;
|
|
|
|
|
const registration = await this.resolveEtradeRegistration(tin);
|
|
|
|
|
const fresh: Partial<Record<(typeof ETRADE_SOURCED_FIELDS)[number], string>> = {
|
|
|
|
|
const fresh: Partial<
|
|
|
|
|
Record<(typeof ETRADE_SOURCED_FIELDS)[number], string>
|
|
|
|
|
> = {
|
|
|
|
|
companyName: registration.companyName,
|
|
|
|
|
licenceNumber: registration.licenceNumber,
|
|
|
|
|
statusDescription: registration.statusDescription,
|
|
|
|
|
|