mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
feat(portal): offer the investment-licence path in the onboarding wizard
A foreign company can now say it operates on an investment licence, on the same step as its nationality and roles. The box only appears for a foreign company, and moving the nationality answer back to Ethiopian drops it — the API refuses both pairings. The company step's eTrade gate now reads `manualRegistration` (co-operative OR investment licence): the TIN lookup still runs, but finding nothing is an expected outcome rather than a blocker, and the registration section is typed instead. What stays keyed to `cooperative` alone is the per-role business licence — an investor holds one, a co-operative does not — so the licence cards and their validation are unchanged for investors. Also carries the client plumbing for the revert endpoint the settings card uses next.
This commit is contained in:
@@ -162,6 +162,13 @@ export default function OnboardingWizardDialog({
|
|||||||
const [cooperative, setCooperative] = useState<boolean>(
|
const [cooperative, setCooperative] = useState<boolean>(
|
||||||
company?.company?.attributes?.cooperative === true,
|
company?.company?.attributes?.cooperative === true,
|
||||||
);
|
);
|
||||||
|
// A foreign company operating on an Ethiopian Investment Commission licence.
|
||||||
|
// eTrade holds nothing for its TIN, so it types the registration exactly as a
|
||||||
|
// co-operative does — but it still holds a licence per role, so nothing about
|
||||||
|
// the licence step changes.
|
||||||
|
const [investorLicence, setInvestorLicence] = useState<boolean>(
|
||||||
|
company?.company?.attributes?.investorLicence === true,
|
||||||
|
);
|
||||||
// Ticking the box drops the selections the company can no longer hold, rather
|
// Ticking the box drops the selections the company can no longer hold, rather
|
||||||
// than letting Continue fail on ones the API refuses: a co-op cannot forward
|
// than letting Continue fail on ones the API refuses: a co-op cannot forward
|
||||||
// freight, and is registered in Ethiopia so it is never foreign.
|
// freight, and is registered in Ethiopia so it is never foreign.
|
||||||
@@ -171,8 +178,20 @@ export default function OnboardingWizardDialog({
|
|||||||
setRoles((prev) => prev.filter((r) => r !== "freight_forwarder"));
|
setRoles((prev) => prev.filter((r) => r !== "freight_forwarder"));
|
||||||
// Ethiopian is then the only answer left, so it is made rather than asked.
|
// Ethiopian is then the only answer left, so it is made rather than asked.
|
||||||
setNationality("ethiopian");
|
setNationality("ethiopian");
|
||||||
|
// Which also rules out the investment licence — that is a foreign
|
||||||
|
// company's, and the API refuses the pair.
|
||||||
|
setInvestorLicence(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
// The investment licence is a foreign company's document. Moving the answer
|
||||||
|
// back to Ethiopian drops it rather than sending a pair the API refuses.
|
||||||
|
const handleNationalityChange = useCallback(
|
||||||
|
(value: CompanyNationality | null) => {
|
||||||
|
setNationality(value);
|
||||||
|
if (value !== "foreign") setInvestorLicence(false);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
const [documentFiles, setDocumentFiles] = useState<
|
const [documentFiles, setDocumentFiles] = useState<
|
||||||
Record<string, File | File[] | null>
|
Record<string, File | File[] | null>
|
||||||
>({});
|
>({});
|
||||||
@@ -224,6 +243,7 @@ export default function OnboardingWizardDialog({
|
|||||||
roles: ProfileTypeValue[];
|
roles: ProfileTypeValue[];
|
||||||
nationality?: CompanyNationality;
|
nationality?: CompanyNationality;
|
||||||
cooperative?: boolean;
|
cooperative?: boolean;
|
||||||
|
investorLicence?: boolean;
|
||||||
}) => api.companies.startOnboarding.call(vars),
|
}) => api.companies.startOnboarding.call(vars),
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
// Nationality drives the server-resolved identity requirements (Fayda vs
|
// Nationality drives the server-resolved identity requirements (Fayda vs
|
||||||
@@ -307,6 +327,7 @@ export default function OnboardingWizardDialog({
|
|||||||
setRoles(existingProfiles.map((p) => p.type));
|
setRoles(existingProfiles.map((p) => p.type));
|
||||||
setNationality(savedNationality);
|
setNationality(savedNationality);
|
||||||
setCooperative(company?.company?.attributes?.cooperative === true);
|
setCooperative(company?.company?.attributes?.cooperative === true);
|
||||||
|
setInvestorLicence(company?.company?.attributes?.investorLicence === true);
|
||||||
// Resume into the form only when profiles exist; otherwise send the user to
|
// Resume into the form only when profiles exist; otherwise send the user to
|
||||||
// role selection so the missing operational profiles get created.
|
// role selection so the missing operational profiles get created.
|
||||||
setPhase(hasOperationalProfiles ? "form" : "nationality-role");
|
setPhase(hasOperationalProfiles ? "form" : "nationality-role");
|
||||||
@@ -322,8 +343,9 @@ export default function OnboardingWizardDialog({
|
|||||||
roles: roles as ProfileTypeValue[],
|
roles: roles as ProfileTypeValue[],
|
||||||
nationality: nationality ?? undefined,
|
nationality: nationality ?? undefined,
|
||||||
cooperative,
|
cooperative,
|
||||||
|
investorLicence,
|
||||||
});
|
});
|
||||||
}, [roles, nationality, cooperative, startMutation]);
|
}, [roles, nationality, cooperative, investorLicence, startMutation]);
|
||||||
|
|
||||||
// Back from the form's first step returns to nationality/role selection.
|
// Back from the form's first step returns to nationality/role selection.
|
||||||
// Safe to re-enter: startOnboarding is idempotent — it reuses the existing
|
// Safe to re-enter: startOnboarding is idempotent — it reuses the existing
|
||||||
@@ -479,6 +501,10 @@ export default function OnboardingWizardDialog({
|
|||||||
// startOnboarding has persisted it, and the form's whole company step
|
// startOnboarding has persisted it, and the form's whole company step
|
||||||
// branches on it.
|
// branches on it.
|
||||||
cooperative: requirementsQuery.data?.cooperative ?? cooperative,
|
cooperative: requirementsQuery.data?.cooperative ?? cooperative,
|
||||||
|
// Same rule, same reason: only a persisted flag changes what the company
|
||||||
|
// step asks for.
|
||||||
|
investorLicence:
|
||||||
|
requirementsQuery.data?.investorLicence ?? investorLicence,
|
||||||
// A freight forwarder cannot answer the power-of-attorney question — the
|
// A freight forwarder cannot answer the power-of-attorney question — the
|
||||||
// API forces "yes" — so the step offers no way to change it.
|
// API forces "yes" — so the step offers no way to change it.
|
||||||
declarationLocked: requirementsQuery.data?.poa?.locked ?? false,
|
declarationLocked: requirementsQuery.data?.poa?.locked ?? false,
|
||||||
@@ -546,7 +572,7 @@ export default function OnboardingWizardDialog({
|
|||||||
</Text>
|
</Text>
|
||||||
<NationalitySelect
|
<NationalitySelect
|
||||||
value={nationality}
|
value={nationality}
|
||||||
onChange={setNationality}
|
onChange={handleNationalityChange}
|
||||||
embedded
|
embedded
|
||||||
// A co-op is registered in Ethiopia by the co-operative
|
// A co-op is registered in Ethiopia by the co-operative
|
||||||
// promotion agency — foreign is not on offer rather than
|
// promotion agency — foreign is not on offer rather than
|
||||||
@@ -563,6 +589,22 @@ export default function OnboardingWizardDialog({
|
|||||||
label="We're a co-operative union or farm"
|
label="We're a co-operative union or farm"
|
||||||
description="For members with a TIN but no business licence. You'll type your registration details instead of us pulling them from eTrade, and upload your co-operative papers in place of a trade licence."
|
description="For members with a TIN but no business licence. You'll type your registration details instead of us pulling them from eTrade, and upload your co-operative papers in place of a trade licence."
|
||||||
/>
|
/>
|
||||||
|
{/* Only a foreign company is offered this: the licence is the
|
||||||
|
Investment Commission's, and it is the reason eTrade has
|
||||||
|
nothing to look up. Same consequence as the co-operative box —
|
||||||
|
typed registration instead of a lookup — but the per-role
|
||||||
|
business licence still applies, so the documents step is
|
||||||
|
unchanged. */}
|
||||||
|
{nationality === "foreign" && !cooperative && (
|
||||||
|
<Checkbox
|
||||||
|
checked={investorLicence}
|
||||||
|
onChange={(e) =>
|
||||||
|
setInvestorLicence(e.currentTarget.checked)
|
||||||
|
}
|
||||||
|
label="We operate on a foreign investment licence"
|
||||||
|
description="For investors registered with the Ethiopian Investment Commission rather than the trade registry. eTrade holds no record of your TIN, so you'll type your registration details instead — and our team reviews them by hand."
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Text fw={600} size="lg" c="edr-text">
|
<Text fw={600} size="lg" c="edr-text">
|
||||||
What does your company do?(multiple)
|
What does your company do?(multiple)
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export const URL_CONSTANTS = {
|
|||||||
ONBOARDING_STEP: "/api/companies/onboarding-step",
|
ONBOARDING_STEP: "/api/companies/onboarding-step",
|
||||||
ONBOARDING_COMPLETE: "/api/companies/onboarding/complete",
|
ONBOARDING_COMPLETE: "/api/companies/onboarding/complete",
|
||||||
ONBOARDING_REQUIREMENTS: "/api/companies/onboarding/requirements",
|
ONBOARDING_REQUIREMENTS: "/api/companies/onboarding/requirements",
|
||||||
|
ONBOARDING_REVERT_TO_ETRADE: "/api/companies/onboarding/revert-to-etrade",
|
||||||
DASHBOARD: "/api/companies/dashboard",
|
DASHBOARD: "/api/companies/dashboard",
|
||||||
FETCH_ETRADE_INFO: "/api/companies/fetch-etrade-info",
|
FETCH_ETRADE_INFO: "/api/companies/fetch-etrade-info",
|
||||||
DOCUMENTS: (id: string) => `/api/companies/${id}/documents`,
|
DOCUMENTS: (id: string) => `/api/companies/${id}/documents`,
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ export default function CompanyProfileForm({
|
|||||||
identity: rawIdentity,
|
identity: rawIdentity,
|
||||||
onIdentityChange,
|
onIdentityChange,
|
||||||
cooperative = false,
|
cooperative = false,
|
||||||
|
investorLicence = false,
|
||||||
declarationLocked = false,
|
declarationLocked = false,
|
||||||
}: {
|
}: {
|
||||||
documentSettingCode: string;
|
documentSettingCode: string;
|
||||||
@@ -118,6 +119,13 @@ export default function CompanyProfileForm({
|
|||||||
* nationality one (resolved by the caller into `documentSettingCode`).
|
* nationality one (resolved by the caller into `documentSettingCode`).
|
||||||
*/
|
*/
|
||||||
cooperative?: boolean;
|
cooperative?: boolean;
|
||||||
|
/**
|
||||||
|
* The company is a foreign investor on an Investment Commission licence. Like
|
||||||
|
* a co-operative, eTrade holds no record of it, so the registration is typed
|
||||||
|
* and the lookup gate does not apply — but it does hold a business licence
|
||||||
|
* per role, so the licence step is untouched.
|
||||||
|
*/
|
||||||
|
investorLicence?: boolean;
|
||||||
/**
|
/**
|
||||||
* The company operates as a freight forwarder, so the power-of-attorney
|
* The company operates as a freight forwarder, so the power-of-attorney
|
||||||
* answer is forced to "yes" and cannot be changed here.
|
* answer is forced to "yes" and cannot be changed here.
|
||||||
@@ -133,6 +141,12 @@ export default function CompanyProfileForm({
|
|||||||
[rawIdentity],
|
[rawIdentity],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// eTrade has nothing to say about this company, whichever of the two reasons
|
||||||
|
// applies — so the registration is typed here and the lookup cannot gate the
|
||||||
|
// step. Everything the two cases do NOT share (the per-role business licence)
|
||||||
|
// keeps reading `cooperative` on its own.
|
||||||
|
const manualRegistration = cooperative || investorLicence;
|
||||||
|
|
||||||
const [step, setStep] = useState<CompanyStep>(initialStep ?? "company");
|
const [step, setStep] = useState<CompanyStep>(initialStep ?? "company");
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
/**
|
/**
|
||||||
@@ -434,7 +448,7 @@ export default function CompanyProfileForm({
|
|||||||
// holds nothing — and wiping them because the customer went back to fix a
|
// holds nothing — and wiping them because the customer went back to fix a
|
||||||
// digit of their TIN would throw away an address they had just typed by
|
// digit of their TIN would throw away an address they had just typed by
|
||||||
// hand, over a lookup that never filled anything in the first place.
|
// hand, over a lookup that never filled anything in the first place.
|
||||||
if (cooperative && !etradeFilledRef.current) {
|
if (manualRegistration && !etradeFilledRef.current) {
|
||||||
setLiveEtradeOwner(null);
|
setLiveEtradeOwner(null);
|
||||||
setEtradeCleared(true);
|
setEtradeCleared(true);
|
||||||
return;
|
return;
|
||||||
@@ -730,7 +744,7 @@ export default function CompanyProfileForm({
|
|||||||
// A co-operative never runs the lookup, so there is nothing to be verified
|
// A co-operative never runs the lookup, so there is nothing to be verified
|
||||||
// against; its TIN is validated by the schema like any other typed field.
|
// against; its TIN is validated by the schema like any other typed field.
|
||||||
const tinVerified =
|
const tinVerified =
|
||||||
cooperative || tinStatus === "verified" || hasRegistrationDetails;
|
manualRegistration || tinStatus === "verified" || hasRegistrationDetails;
|
||||||
|
|
||||||
// Single source of truth for step sequence — navigation, labels and the
|
// Single source of truth for step sequence — navigation, labels and the
|
||||||
// progress bar all derive from this so adding/removing a step is one edit.
|
// progress bar all derive from this so adding/removing a step is one edit.
|
||||||
@@ -770,8 +784,8 @@ export default function CompanyProfileForm({
|
|||||||
Boolean(watch(passportField)?.trim()));
|
Boolean(watch(passportField)?.trim()));
|
||||||
|
|
||||||
const requiredKeys: (keyof FormData)[] = [];
|
const requiredKeys: (keyof FormData)[] = [];
|
||||||
if (step === "company" && cooperative) {
|
if (step === "company" && manualRegistration) {
|
||||||
// A co-operative has no eTrade record, so the fields every other company
|
// These companies have no eTrade record, so the fields every other company
|
||||||
// gets read-only from the licence are typed here — and are therefore
|
// gets read-only from the licence are typed here — and are therefore
|
||||||
// required here. House number stays optional: plenty of addresses have none.
|
// required here. House number stays optional: plenty of addresses have none.
|
||||||
requiredKeys.push("companyName", "region", "zone", "woreda", "kebele");
|
requiredKeys.push("companyName", "region", "zone", "woreda", "kebele");
|
||||||
@@ -887,8 +901,9 @@ export default function CompanyProfileForm({
|
|||||||
}
|
}
|
||||||
// The TIN must resolve to a real eTrade record before anything else on
|
// The TIN must resolve to a real eTrade record before anything else on
|
||||||
// this step is even worth validating — gates here rather than through zod.
|
// this step is even worth validating — gates here rather than through zod.
|
||||||
// A co-operative is exempt: it has no licence for eTrade to hold, so
|
// A co-operative and a foreign investor are exempt: eTrade holds no record
|
||||||
// `tinVerified` is true for it and only the duplicate-TIN check applies.
|
// for either, so `tinVerified` is true and only the duplicate-TIN check
|
||||||
|
// applies.
|
||||||
if (step === "company" && tinStatus === "taken") {
|
if (step === "company" && tinStatus === "taken") {
|
||||||
failCheck(
|
failCheck(
|
||||||
"This TIN is already registered to another company account.",
|
"This TIN is already registered to another company account.",
|
||||||
@@ -987,6 +1002,7 @@ export default function CompanyProfileForm({
|
|||||||
tinStatus={tinStatus}
|
tinStatus={tinStatus}
|
||||||
tinVerified={tinVerified}
|
tinVerified={tinVerified}
|
||||||
hasRegistrationDetails={hasRegistrationDetails}
|
hasRegistrationDetails={hasRegistrationDetails}
|
||||||
|
manualRegistration={manualRegistration}
|
||||||
cooperative={cooperative}
|
cooperative={cooperative}
|
||||||
onETradeDataLoaded={handleETradeDataLoaded}
|
onETradeDataLoaded={handleETradeDataLoaded}
|
||||||
onETradeStatusChange={setTinStatus}
|
onETradeStatusChange={setTinStatus}
|
||||||
@@ -1002,6 +1018,7 @@ export default function CompanyProfileForm({
|
|||||||
source={ownerSource}
|
source={ownerSource}
|
||||||
sourced={ownerSourced}
|
sourced={ownerSourced}
|
||||||
cooperative={cooperative}
|
cooperative={cooperative}
|
||||||
|
manualRegistration={manualRegistration}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -17,10 +17,13 @@ export interface CompanyInfoStepProps {
|
|||||||
/** Registration fields are already populated (a lookup passed, now or earlier). */
|
/** Registration fields are already populated (a lookup passed, now or earlier). */
|
||||||
hasRegistrationDetails: boolean;
|
hasRegistrationDetails: boolean;
|
||||||
/**
|
/**
|
||||||
* The company is a co-operative union or farm: it has a TIN but no business
|
* eTrade holds no record for this company's TIN, so the registration is typed
|
||||||
* licence, so eTrade holds no record to look up and the registration is typed
|
* here rather than fetched. True for a co-operative union or farm (no
|
||||||
* here instead.
|
* business licence) and for a foreign investor (licensed by the Investment
|
||||||
|
* Commission, not the trade registry).
|
||||||
*/
|
*/
|
||||||
|
manualRegistration?: boolean;
|
||||||
|
/** Which of the two it is — wording only; the behaviour is the same. */
|
||||||
cooperative?: boolean;
|
cooperative?: boolean;
|
||||||
onETradeDataLoaded: (data: CompanyRegistrationData) => void;
|
onETradeDataLoaded: (data: CompanyRegistrationData) => void;
|
||||||
onETradeStatusChange: (status: ETradeStatus) => void;
|
onETradeStatusChange: (status: ETradeStatus) => void;
|
||||||
@@ -32,6 +35,7 @@ export default function CompanyInfoStep({
|
|||||||
tinStatus,
|
tinStatus,
|
||||||
tinVerified,
|
tinVerified,
|
||||||
hasRegistrationDetails,
|
hasRegistrationDetails,
|
||||||
|
manualRegistration = false,
|
||||||
cooperative = false,
|
cooperative = false,
|
||||||
onETradeDataLoaded,
|
onETradeDataLoaded,
|
||||||
onETradeStatusChange,
|
onETradeStatusChange,
|
||||||
@@ -71,14 +75,16 @@ export default function CompanyInfoStep({
|
|||||||
index={2}
|
index={2}
|
||||||
title="Company TIN"
|
title="Company TIN"
|
||||||
subtitle={
|
subtitle={
|
||||||
cooperative
|
!manualRegistration
|
||||||
? "We'll check eTrade for your TIN. Co-operatives often aren't listed — if yours isn't, you'll fill the details in below."
|
? "We'll pull your registration straight from eTrade — nothing to type by hand once it's found."
|
||||||
: "We'll pull your registration straight from eTrade — nothing to type by hand once it's found."
|
: cooperative
|
||||||
|
? "We'll check eTrade for your TIN. Co-operatives often aren't listed — if yours isn't, you'll fill the details in below."
|
||||||
|
: "We'll check eTrade for your TIN. An investment licence usually isn't on it — if yours isn't, you'll fill the details in below."
|
||||||
}
|
}
|
||||||
status={
|
status={
|
||||||
tinStatus === "taken"
|
tinStatus === "taken"
|
||||||
? "blocked"
|
? "blocked"
|
||||||
: cooperative
|
: manualRegistration
|
||||||
? watch("tinNumber")?.trim() && !errors.tinNumber
|
? watch("tinNumber")?.trim() && !errors.tinNumber
|
||||||
? "done"
|
? "done"
|
||||||
: "todo"
|
: "todo"
|
||||||
@@ -96,26 +102,26 @@ export default function CompanyInfoStep({
|
|||||||
onReset={onETradeReset}
|
onReset={onETradeReset}
|
||||||
alreadyVerified={hasRegistrationDetails}
|
alreadyVerified={hasRegistrationDetails}
|
||||||
selectedLicenceNumber={watch("licenceNumber")}
|
selectedLicenceNumber={watch("licenceNumber")}
|
||||||
registrationOptional={cooperative}
|
registrationOptional={manualRegistration}
|
||||||
/>
|
/>
|
||||||
{!cooperative && tinVerified && (
|
{!manualRegistration && tinVerified && (
|
||||||
<ETradeCompanyCard tin={watch("tinNumber")} watch={watch} />
|
<ETradeCompanyCard tin={watch("tinNumber")} watch={watch} />
|
||||||
)}
|
)}
|
||||||
</StepSection>
|
</StepSection>
|
||||||
|
|
||||||
{/* A co-operative keeps its typed registration section either way. When
|
{/* A company eTrade cannot answer for keeps its typed registration
|
||||||
the lookup found something these arrive prefilled — still editable,
|
section either way. When the lookup did find something these arrive
|
||||||
because for a co-op they are the customer's own statement rather than
|
prefilled — still editable, because here they are the customer's own
|
||||||
the licence's, and the API takes them as given (`applyEtradeSourcedFields`
|
statement rather than the licence's, and the API takes them as given
|
||||||
skips co-operatives entirely). */}
|
(`applyEtradeSourcedFields` skips both cases entirely). */}
|
||||||
{cooperative && (
|
{manualRegistration && (
|
||||||
<StepSection
|
<StepSection
|
||||||
index={3}
|
index={3}
|
||||||
title="Registration details"
|
title="Registration details"
|
||||||
subtitle={
|
subtitle={
|
||||||
hasRegistrationDetails
|
hasRegistrationDetails
|
||||||
? "From eTrade. Correct anything that doesn't look right — for a co-operative these are yours to state."
|
? "From eTrade. Correct anything that doesn't look right — these are yours to state."
|
||||||
: "Everything we'd normally read off an eTrade licence. We need it from you instead."
|
: "Everything we'd normally read off an eTrade licence. We need it from you instead. Our team checks it against the papers you upload."
|
||||||
}
|
}
|
||||||
status={
|
status={
|
||||||
watch("companyName")?.trim() && watch("region")?.trim()
|
watch("companyName")?.trim() && watch("region")?.trim()
|
||||||
@@ -126,7 +132,11 @@ export default function CompanyInfoStep({
|
|||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<TextInput
|
<TextInput
|
||||||
label="Company Name"
|
label="Company Name"
|
||||||
placeholder="Registered name of the union or farm"
|
placeholder={
|
||||||
|
cooperative
|
||||||
|
? "Registered name of the union or farm"
|
||||||
|
: "Name on your investment licence"
|
||||||
|
}
|
||||||
error={errors.companyName?.message}
|
error={errors.companyName?.message}
|
||||||
{...register("companyName")}
|
{...register("companyName")}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ export interface OwnerStepProps {
|
|||||||
sourced: Record<OwnerField, string>;
|
sourced: Record<OwnerField, string>;
|
||||||
/** A co-operative union or farm: no licence, so no eTrade record to match. */
|
/** A co-operative union or farm: no licence, so no eTrade record to match. */
|
||||||
cooperative?: boolean;
|
cooperative?: boolean;
|
||||||
|
/** No eTrade record at all (co-operative or foreign investment licence). */
|
||||||
|
manualRegistration?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +58,7 @@ export default function OwnerStep({
|
|||||||
source,
|
source,
|
||||||
sourced,
|
sourced,
|
||||||
cooperative = false,
|
cooperative = false,
|
||||||
|
manualRegistration = false,
|
||||||
}: OwnerStepProps) {
|
}: OwnerStepProps) {
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -74,15 +77,17 @@ export default function OwnerStep({
|
|||||||
return (
|
return (
|
||||||
<Stack gap="md">
|
<Stack gap="md">
|
||||||
<Text size="sm" c="edr-muted">
|
<Text size="sm" c="edr-muted">
|
||||||
{cooperative && !etradeOwner
|
{manualRegistration && !etradeOwner
|
||||||
? "The person who runs the co-operative union or farm. eTrade held no record for your TIN, so we need all of these from you."
|
? cooperative
|
||||||
|
? "The person who runs the co-operative union or farm. eTrade held no record for your TIN, so we need all of these from you."
|
||||||
|
: "The person your investment licence names. eTrade held no record for your TIN, so we need all of these from you."
|
||||||
: "These are the details of the person registered on your eTrade licence. What eTrade and Fayda gave us is shown as they gave it; anything they left blank we need from you."}
|
: "These are the details of the person registered on your eTrade licence. What eTrade and Fayda gave us is shown as they gave it; anything they left blank we need from you."}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
{/* A co-operative is not told its licence listed no manager — it has no
|
{/* A company with no eTrade record is not told its licence listed no
|
||||||
licence. Its own "nothing came back" case is covered by the line
|
manager — eTrade never held one. That "nothing came back" case is
|
||||||
above. */}
|
covered by the line above. */}
|
||||||
{!cooperative && !etradeOwner && !ownerVerified && (
|
{!manualRegistration && !etradeOwner && !ownerVerified && (
|
||||||
<Alert color="blue" variant="light" icon={<Info size={18} />}>
|
<Alert color="blue" variant="light" icon={<Info size={18} />}>
|
||||||
Your eTrade licence didn't list a manager, so there's nothing for us
|
Your eTrade licence didn't list a manager, so there's nothing for us
|
||||||
to prefill. Enter the details of the person registered on it.
|
to prefill. Enter the details of the person registered on it.
|
||||||
|
|||||||
@@ -241,10 +241,18 @@ export const api = {
|
|||||||
nationality?: CompanyNationality;
|
nationality?: CompanyNationality;
|
||||||
/** No business licence: registration typed, no eTrade lookup, no forwarding. */
|
/** No business licence: registration typed, no eTrade lookup, no forwarding. */
|
||||||
cooperative?: boolean;
|
cooperative?: boolean;
|
||||||
|
/** Foreign investment licence: registration typed, no eTrade lookup. */
|
||||||
|
investorLicence?: boolean;
|
||||||
},
|
},
|
||||||
CompanyInfoResponse
|
CompanyInfoResponse
|
||||||
>("companies", "startOnboarding", companiesService.startOnboarding),
|
>("companies", "startOnboarding", companiesService.startOnboarding),
|
||||||
|
|
||||||
|
revertToRegularCompany: endpoint<void, CompanyInfoResponse>(
|
||||||
|
"companies",
|
||||||
|
"revertToRegularCompany",
|
||||||
|
companiesService.revertToRegularCompany,
|
||||||
|
),
|
||||||
|
|
||||||
setOnboardingStep: endpoint<{ step: string }, void>(
|
setOnboardingStep: endpoint<{ step: string }, void>(
|
||||||
"companies",
|
"companies",
|
||||||
"setOnboardingStep",
|
"setOnboardingStep",
|
||||||
|
|||||||
@@ -223,6 +223,8 @@ export interface OnboardingRequirements {
|
|||||||
nationality: string;
|
nationality: string;
|
||||||
/** No business licence: registration typed by hand, no eTrade lookup. */
|
/** No business licence: registration typed by hand, no eTrade lookup. */
|
||||||
cooperative: boolean;
|
cooperative: boolean;
|
||||||
|
/** Foreign investment licence: registration typed by hand, no eTrade record. */
|
||||||
|
investorLicence: boolean;
|
||||||
companyInfo: {
|
companyInfo: {
|
||||||
complete: boolean;
|
complete: boolean;
|
||||||
missingFields: { key: string; label: string }[];
|
missingFields: { key: string; label: string }[];
|
||||||
@@ -363,6 +365,7 @@ export const companiesService = {
|
|||||||
roles: ProfileTypeValue[];
|
roles: ProfileTypeValue[];
|
||||||
nationality?: CompanyNationality;
|
nationality?: CompanyNationality;
|
||||||
cooperative?: boolean;
|
cooperative?: boolean;
|
||||||
|
investorLicence?: boolean;
|
||||||
}): Promise<CompanyInfoResponse> => {
|
}): Promise<CompanyInfoResponse> => {
|
||||||
const response = await client.post<ApiResponse<CompanyInfoResponse>>(
|
const response = await client.post<ApiResponse<CompanyInfoResponse>>(
|
||||||
URL_CONSTANTS.COMPANIES_API.ONBOARDING_START,
|
URL_CONSTANTS.COMPANIES_API.ONBOARDING_START,
|
||||||
@@ -371,6 +374,18 @@ export const companiesService = {
|
|||||||
return unwrap(response.data);
|
return unwrap(response.data);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give up the foreign investment-licence route and go back through eTrade.
|
||||||
|
* The API clears the typed registration and reopens onboarding at the company
|
||||||
|
* step, so the caller must refresh the company info afterwards.
|
||||||
|
*/
|
||||||
|
revertToRegularCompany: async (): Promise<CompanyInfoResponse> => {
|
||||||
|
const response = await client.post<ApiResponse<CompanyInfoResponse>>(
|
||||||
|
URL_CONSTANTS.COMPANIES_API.ONBOARDING_REVERT_TO_ETRADE,
|
||||||
|
);
|
||||||
|
return unwrap(response.data);
|
||||||
|
},
|
||||||
|
|
||||||
setOnboardingStep: async (payload: { step: string }): Promise<void> => {
|
setOnboardingStep: async (payload: { step: string }): Promise<void> => {
|
||||||
await client.patch(URL_CONSTANTS.COMPANIES_API.ONBOARDING_STEP, payload);
|
await client.patch(URL_CONSTANTS.COMPANIES_API.ONBOARDING_STEP, payload);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export interface ProfileResponse {
|
|||||||
nationality: string | null;
|
nationality: string | null;
|
||||||
/** No business licence: the registration is typed, not fetched from eTrade. */
|
/** No business licence: the registration is typed, not fetched from eTrade. */
|
||||||
cooperative: boolean;
|
cooperative: boolean;
|
||||||
|
/** Foreign investor on an investment licence: same typed registration, no eTrade record. */
|
||||||
|
investorLicence: boolean;
|
||||||
companyProfiles: CompanyProfileResponse[];
|
companyProfiles: CompanyProfileResponse[];
|
||||||
companyLocation: string;
|
companyLocation: string;
|
||||||
companyAddress: string | null;
|
companyAddress: string | null;
|
||||||
|
|||||||
Reference in New Issue
Block a user