feat(companies): attach an eTrade business to each company profile

A TIN holds many business licences split by activity — export of coffee,
freight forwarding, import of vehicles — but the company picked one for
its whole record, so every operational role shared it. Each profile now
names the business it actually operates as.

Stored on `company_profiles.etrade_business` as a snapshot (licence
number, trade name, activity, renewal) rather than a bare licence number,
so the portal and backoffice can show it without an eTrade round-trip —
that API is slow, serves a broken TLS chain and is regularly down. Not
unique: one business may legitimately back several roles.

The licence number is a client input, so it is never stored as sent —
`ETradeService.findBusinessOption` looks it up under the company's own
TIN and persists eTrade's record, which makes another company's licence
simply unfindable.

Choosing one is required wherever the customer adds a role with a TIN
already on file. The onboarding wizard is the exception by necessity: it
picks roles on its first step, before a TIN exists, so there is nothing
to choose from yet. There it is enforced through
`getOnboardingRequirements` instead — an unattached role is reported
outstanding and blocks submission — and the picker sits on the documents
step beside that role's licence upload.

Lifted entirely for a co-operative or investment-licence company: eTrade
holds no record for its TIN, so the requirement would be unsatisfiable.
This commit is contained in:
Nathnael
2026-08-27 09:29:15 +00:00
parent 1f5e6296b1
commit 604710bd25
26 changed files with 922 additions and 55 deletions

View File

@@ -0,0 +1,36 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
/**
* Attaches an eTrade business licence to each operational profile.
*
* A TIN routinely holds a dozen or more licences, split by activity ("Export
* trade in coffee", "Freight Forwarders"), and until now the company picked one
* for the whole record — every role shared it. Each profile now names the
* business it actually operates as.
*
* Stored as a snapshot ({@link ETradeBusinessOption}: licenceNumber, tradeName,
* activity, renewedTo) rather than a bare licence number, so the portal and the
* backoffice can show which business is attached without an eTrade round-trip —
* eTrade is slow, serves a broken TLS chain, and is regularly down.
*
* Nullable: existing profiles have none until the customer attaches one, and a
* co-operative or investor-licence company has no eTrade record at all.
* Deliberately NOT unique — one business can back several profiles.
*/
export class CompanyProfileEtradeBusiness3760000000000 implements MigrationInterface {
name = 'CompanyProfileEtradeBusiness3760000000000';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.company_profiles
ADD COLUMN IF NOT EXISTS etrade_business jsonb
`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE freight.company_profiles
DROP COLUMN IF EXISTS etrade_business
`);
}
}