mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
fix: etrade inconsistencies
This commit is contained in:
@@ -928,7 +928,7 @@ export class CompaniesService {
|
||||
): Promise<ProfileResponseDto> {
|
||||
const { profile, company } = await this.getCompanyInfoByUserId(userId);
|
||||
|
||||
await this.assertEtradeFieldsAuthentic(company, dto);
|
||||
await this.applyEtradeSourcedFields(company, dto);
|
||||
|
||||
// Naming (or renaming) a Power of Attorney is one of the writes that can
|
||||
// leave the company with a representative and nothing evidencing them, so
|
||||
@@ -3216,22 +3216,24 @@ export class CompaniesService {
|
||||
/**
|
||||
* An eTrade-sourced field can only ever hold what a fresh eTrade lookup for
|
||||
* this TIN actually returns — the portal never lets the customer type these
|
||||
* once eTrade has supplied them, so a mismatch here means either stale
|
||||
* client state or a hand-crafted request, and either way the write is
|
||||
* refused rather than silently trusting it.
|
||||
* once eTrade has supplied them. Rather than trust the client's copy (stale
|
||||
* cache, hand-crafted request, or just a formatting mismatch) and reject it,
|
||||
* refetch eTrade ourselves and overwrite the touched fields with whatever it
|
||||
* says now — the client's submitted values for these keys only matter as a
|
||||
* "this field is part of the save" flag, never as data we persist.
|
||||
*/
|
||||
private async assertEtradeFieldsAuthentic(
|
||||
private async applyEtradeSourcedFields(
|
||||
company: Company,
|
||||
dto: UpdateProfileDto,
|
||||
): Promise<void> {
|
||||
const touched = ETRADE_SOURCED_FIELDS.some(
|
||||
(key) => dto[key] !== undefined,
|
||||
(key) => key !== "tin" && dto[key] !== undefined,
|
||||
);
|
||||
if (!touched) return;
|
||||
|
||||
const tin = dto.tin ?? company.tin;
|
||||
const registration = await this.resolveEtradeRegistration(tin);
|
||||
const expected: 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,
|
||||
@@ -3251,21 +3253,11 @@ export class CompaniesService {
|
||||
};
|
||||
|
||||
for (const key of ETRADE_SOURCED_FIELDS) {
|
||||
const submitted = dto[key];
|
||||
if (submitted === undefined) continue;
|
||||
const source = expected[key];
|
||||
// eTrade left this field blank — the onboarding/settings card falls back
|
||||
// to letting the customer type it directly, so nothing to check against.
|
||||
if (!source) continue;
|
||||
const same =
|
||||
key === "etradePhone"
|
||||
? normalizeE164(String(submitted)) === normalizeE164(source)
|
||||
: submitted === source;
|
||||
if (!same) {
|
||||
throw new BadRequestException(
|
||||
`${key} doesn't match eTrade's current record for this TIN. Re-verify with eTrade to pick up the latest details.`,
|
||||
);
|
||||
}
|
||||
if (key === "tin" || dto[key] === undefined) continue;
|
||||
const value = fresh[key];
|
||||
// eTrade left this field blank — fall back to whatever the client sent
|
||||
// (the onboarding/settings card lets the customer type it directly then).
|
||||
if (value) (dto as Record<string, unknown>)[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,9 +179,12 @@ export class UpdateProfileDto {
|
||||
@MaxLength(100)
|
||||
houseNo?: string;
|
||||
|
||||
// Not validated as a phone number: eTrade-sourced, so a fresh eTrade lookup
|
||||
// overwrites whatever the client sends here — see
|
||||
// CompaniesService.applyEtradeSourcedFields. Presence just flags "this save
|
||||
// touches an eTrade-owned field."
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MaxLength(20)
|
||||
@IsValidPhone()
|
||||
etradePhone?: string;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,9 @@ export class ETradeService {
|
||||
dateRegistered: businessInfo.DateRegistered,
|
||||
renewedFrom: businessInfo.RenewedFrom,
|
||||
renewalDate: businessInfo.RenewalDate,
|
||||
renewedTo: businessInfo.RenewedTo,
|
||||
// RenewedTo is ISO ("2018-07-07T00:00:00"); RenewedToDateString matches
|
||||
// RenewedFrom/RenewalDate's "M/D/YYYY" format — use that for consistency.
|
||||
renewedTo: businessInfo.RenewedToDateString,
|
||||
// eTrade returns uncoded uppercase text and sometimes a zone name in the
|
||||
// Region slot. Map it onto the canonical list; an unresolved value yields
|
||||
// "" so the form asks the user to pick rather than failing validation on
|
||||
|
||||
Reference in New Issue
Block a user