mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 05:55:02 +00:00
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.
172 lines
6.7 KiB
TypeScript
172 lines
6.7 KiB
TypeScript
/**
|
|
* Giving the investment-licence route back.
|
|
*
|
|
* A company that ticked the box by mistake, or that has since been registered
|
|
* with the trade registry, switches from Settings → Company. It is a
|
|
* re-application rather than a settings edit, and this spec is about proving
|
|
* that literally: the typed registration is cleared (nothing on file was ever
|
|
* checked against a licence), the company returns to pending, onboarding
|
|
* reopens on the company step — and only after a real eTrade lookup does the
|
|
* backoffice stop flagging it.
|
|
*
|
|
* Each test is one leg of a single journey, in order, retries off. The
|
|
* portal ↔ backoffice hops re-evaluate the spec bundle, so every leg resolves
|
|
* the company from the database rather than from module state.
|
|
*/
|
|
|
|
import {
|
|
approveFirstProfile,
|
|
companyByEmail,
|
|
completeInvestorOnboarding,
|
|
etradeTin,
|
|
expectProfileActive,
|
|
fillAria,
|
|
latestJourney,
|
|
openCustomer,
|
|
signupIdentity,
|
|
wizardClick,
|
|
SIGNUP_PASSWORD,
|
|
chooseRoleBusiness,
|
|
} from "./onboarding-utils";
|
|
|
|
const stamp = Date.now();
|
|
const who = signupIdentity("switchback", stamp);
|
|
|
|
describe("onboarding — switching back to eTrade registration", { retries: 0 }, () => {
|
|
it("onboards on an investment licence", () => {
|
|
completeInvestorOnboarding(who, stamp, {
|
|
companyName: `E2E Switchback Trading ${stamp}`,
|
|
});
|
|
|
|
companyByEmail(who.email).then((company) => {
|
|
expect((company.attributes ?? {})["investorLicence"]).to.eq(true);
|
|
expect(company.region, "typed address").to.eq("Addis Ababa");
|
|
});
|
|
});
|
|
|
|
it("is approved by the backoffice", () => {
|
|
cy.loginBackoffice("chief@edr.local");
|
|
latestJourney("switchback").then((company) => {
|
|
openCustomer(company.name);
|
|
cy.contains("Manual entry · investment licence").should("be.visible");
|
|
approveFirstProfile();
|
|
expectProfileActive(company.name);
|
|
});
|
|
});
|
|
|
|
it("switches back from settings, which clears the typed registration", () => {
|
|
latestJourney("switchback").then((company) => {
|
|
cy.loginPortal(company.email, SIGNUP_PASSWORD);
|
|
});
|
|
cy.visitPortal("/settings");
|
|
|
|
cy.contains("Registration source", { timeout: 20000 }).should("be.visible");
|
|
cy.contains("button", "Switch to eTrade registration").click();
|
|
|
|
// The confirmation has to state the cost outright — this is the screen
|
|
// that decides whether the customer knows they are re-applying.
|
|
cy.contains("Switch to eTrade registration?").should("be.visible");
|
|
cy.contains("The registration details you typed are cleared").should(
|
|
"be.visible",
|
|
);
|
|
cy.contains("Your company goes back to pending").should("be.visible");
|
|
cy.contains("button", "Switch and re-apply").click();
|
|
|
|
cy.contains("Registration source", { timeout: 20000 }).should("not.exist");
|
|
|
|
latestJourney("switchback").then((company) => {
|
|
expect((company.attributes ?? {})["investorLicence"]).to.be.undefined;
|
|
expect(company.status).to.eq("pending");
|
|
expect(company.onboarding_completed).to.eq(false);
|
|
// The wizard treats a populated registration as a lookup that already
|
|
// passed, so leaving any of it behind would walk the customer straight
|
|
// past the eTrade step this switch exists to reach.
|
|
expect(company.region, "typed address cleared").to.be.null;
|
|
expect(company.licence_number).to.be.null;
|
|
expect(company.etrade_phone).to.be.null;
|
|
expect(company.onboarding_step, "resume target").to.eq("company");
|
|
});
|
|
});
|
|
|
|
it("re-runs onboarding through eTrade and resubmits", () => {
|
|
latestJourney("switchback").then((company) => {
|
|
cy.loginPortal(company.email, SIGNUP_PASSWORD);
|
|
});
|
|
cy.visitPortal("/portal");
|
|
|
|
// Reopened on the company step — not on the furthest step reached before.
|
|
cy.contains("Confirm your VAT number", { timeout: 30000 }).should(
|
|
"be.visible",
|
|
);
|
|
// The typed registration section is gone: this is an ordinary company now.
|
|
cy.contains("Nothing on file at eTrade for this TIN").should("not.exist");
|
|
|
|
// Wait for the profile to rehydrate before typing. The company still holds
|
|
// the TIN it typed under the flag (the switch clears the registration, not
|
|
// the tax number), and RHF re-seeds the field when the refetch lands — so a
|
|
// TIN typed into an empty-looking field is silently replaced by the stored
|
|
// one, and the lookup then runs against the wrong number.
|
|
cy.get('.mantine-Modal-content [aria-label="VAT Number"]')
|
|
.should("not.have.value", "");
|
|
fillAria('[aria-label^="TIN Number"]', etradeTin(stamp));
|
|
cy.get('.mantine-Modal-content [aria-label^="TIN Number"]').should(
|
|
"have.value",
|
|
etradeTin(stamp),
|
|
);
|
|
cy.contains("Verified with eTrade", { timeout: 20000 }).should("be.visible");
|
|
cy.wait(500);
|
|
wizardClick("Continue");
|
|
|
|
// Owner, representation and contact are already satisfied server-side —
|
|
// the switch keeps everything except the registration — so each step only
|
|
// needs advancing.
|
|
cy.contains("Company Owner", { timeout: 20000 }).should("be.visible");
|
|
wizardClick("Continue");
|
|
cy.contains("The owner acts for the company", { timeout: 20000 }).should(
|
|
"be.visible",
|
|
);
|
|
wizardClick("Continue");
|
|
cy.contains("Contact Person", { timeout: 20000 }).should("be.visible");
|
|
wizardClick("Continue");
|
|
|
|
// Documents and the licence were uploaded before the switch and survive
|
|
// it, so the step is already satisfied.
|
|
cy.contains("Upload Importer Business license file(s)", {
|
|
timeout: 20000,
|
|
}).should("be.visible");
|
|
chooseRoleBusiness();
|
|
wizardClick("Submit for review");
|
|
cy.contains("You're all set", { timeout: 30000 }).should("be.visible");
|
|
|
|
latestJourney("switchback").then((company) => {
|
|
expect(company.onboarding_completed).to.eq(true);
|
|
expect((company.attributes ?? {})["investorLicence"]).to.be.undefined;
|
|
// eTrade answered this time, and its licence is on file.
|
|
expect(company.licence_number).to.eq("LIC-E2E-0001");
|
|
});
|
|
});
|
|
|
|
it("the backoffice no longer flags it", () => {
|
|
cy.loginBackoffice("chief@edr.local");
|
|
latestJourney("switchback").then((company) => {
|
|
openCustomer(company.name);
|
|
|
|
cy.contains("eTrade trade licence").should("be.visible");
|
|
cy.contains("Manual entry").should("not.exist");
|
|
cy.contains("Registration entered by hand").should("not.exist");
|
|
});
|
|
});
|
|
|
|
it("offers nothing to switch to a customer who came through eTrade", () => {
|
|
// The seeded demo customer onboarded the ordinary way, so the card must
|
|
// not be on its settings page at all.
|
|
cy.loginPortal();
|
|
cy.visitPortal("/settings");
|
|
|
|
cy.contains("Operational Services", { timeout: 20000 }).should("be.visible");
|
|
cy.contains("Registration source").should("not.exist");
|
|
});
|
|
});
|
|
|
|
export {};
|