Skip to content

Commit

Permalink
Krp 1225/create business identification (#206)
Browse files Browse the repository at this point in the history
* extend request with business

* extend types

* update beneficial owner with business request type

* extract person ident logic

* create business ident

* remove business check

* add test

* add retrieve endpoint
  • Loading branch information
lera authored Nov 11, 2024
1 parent 752ae33 commit 95c9b46
Show file tree
Hide file tree
Showing 9 changed files with 452 additions and 105 deletions.
14 changes: 13 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,10 +888,22 @@ router.post(

router.post(
"/businesses/:business_id/beneficial_owners",
middlewares.withPerson,
middlewares.withBusiness,
safeRequestHandler(businessesAPI.createBeneficialOwner)
);

router.post(
"/businesses/:business_id/identifications",
middlewares.withBusiness,
safeRequestHandler(businessesAPI.createBusinessIdentification)
);

router.get(
"/businesses/:business_id/identifications/:identification_id",
middlewares.withBusiness,
safeRequestHandler(businessesAPI.retrieveBusinessIdentification)
);

// COMMERCIAL REGISTRATIONS

router.get(
Expand Down
40 changes: 38 additions & 2 deletions src/helpers/middlewares.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,46 @@
import * as express from "express";
import HttpStatusCodes from "http-status";
import { getPerson } from "../db";
import { MockPerson } from "./types";
import { getPerson, getBusiness } from "../db";
import { MockPerson, MockBusiness } from "./types";
import generateID from "./id";

export type RequestWithPerson = express.Request & { person?: MockPerson };
export type RequestWithBusiness = express.Request & { business?: MockBusiness };

export const withBusiness = async (
req: RequestWithBusiness,
res: express.Response,
next: express.NextFunction
) => {
const businessId =
req.params.business_id ||
req.params.businessId ||
(req.body || {}).business_id;
if (!businessId) {
next();
return;
}

const business = await getBusiness(businessId);

if (!business) {
res.status(HttpStatusCodes.NOT_FOUND).send({
errors: [
{
id: generateID(),
status: 404,
code: "model_not_found",
title: "Model Not Found",
detail: `Couldn't find 'Solaris::Business' for id '${businessId}'.`,
},
],
});
return;
}

req.business = business;
next();
};

export const withPerson = async (
req: RequestWithPerson,
Expand Down
73 changes: 73 additions & 0 deletions src/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,77 @@ export type MockCreatePerson = {
billing_account?: BillingAccount;
};

export enum BusinessIdentificationStatus {
CREATED = "created",
PENDING = "pending",
SUCCESSFUL = "successful",
FAILED = "failed",
EXPIRED = "expired",
}

export enum LegalIdentificationStatus {
CREATED = "created",
INFORMATION_REQUIRED = "information_required",
BLOCKED_INTERNALY = "blocked_internally",
SUCCESSFUL = "successful",
FAILED = "failed",
EXPIRED = "expired",
}

export type LegalRepresentativeIdentification = {
id: string;
reference: string;
url: string;
status: IdentificationStatus;
completed_at: string;
method: string;
language: string;
};

export type LegalRepresentativeIdentificationResponse = {
person_id: string;
identifications: LegalRepresentativeIdentification[];
};

export enum BusinessDocumentType {
PROOF_OF_ADDRESS = "PROOF_OF_ADDRESS",
FOUNDATION_DOCUMENT = "FOUNDATION_DOCUMENT",
SHAREHOLDERS_LIST = "SHAREHOLDERS_LIST",
REGISTER_EXTRACT = "REGISTER_EXTRACT",
VAT_CERTIFICATE = "VAT_CERTIFICATE",
}

export const COMPLIANCE_QUESTIONS = "COMPLIANCE_QUESTIONS";

export type BusinessIdentification = {
id: string;
business_id: string;
method: string;
reference: string;
status: BusinessIdentificationStatus;
completed_at?: string;
legal_identification_status: LegalIdentificationStatus;
legal_identification_reason?: string;
legal_identification_missing_information_details?: string;
legal_representatives: LegalRepresentativeIdentificationResponse[];
legal_identification_missing_information: string[];
};

export enum LegalRepresentativeType {
PERSON = "Person",
BUSINESS = "Business",
}

export type LegalRepresentative = {
id: string;
legal_representative_id: string;
legal_representative_type: LegalRepresentativeType;
valid_until: string;
power_of_attorney_confirmed_at: string;
type_of_representation: string;
business_id: string;
};

export type MockBusiness = {
id: string;
name: string;
Expand Down Expand Up @@ -337,6 +408,8 @@ export type MockBusiness = {
createdAt: string;
beneficialOwners?: BeneficialOwner[];
accountOpeningRequests?: AccountOpeningRequest[];
identifications?: BusinessIdentification[];
legalRepresentatives?: LegalRepresentative[];
};

export type MockCreateBusiness = {
Expand Down
46 changes: 14 additions & 32 deletions src/routes/business/beneficialOwner.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,38 @@
import type { Request, Response } from "express";
import type { Response } from "express";

import generateID from "../../helpers/id";
import { getBusiness, saveBusiness } from "../../db";
import { saveBusiness } from "../../db";
import uuid from "node-uuid";
import { BeneficialOwner } from "../../helpers/types";
import { RequestWithBusiness } from "../../helpers/middlewares";

export const createBeneficialOwner = async (req: Request, res: Response) => {
const { business_id: businessId } = req.params;
export const createBeneficialOwner = async (
req: RequestWithBusiness,
res: Response
) => {
const { business } = req;

try {
const business = await getBusiness(businessId);

const beneficialOwner: BeneficialOwner = {
id: generateID(),
beneficial_owner_id: generateID(),
person_id: req.body.person_id,
voting_share: req.body.voting_share,
business_id: businessId,
business_id: business.id,
fictitious: req.body.fictitious,
relationship_to_business: req.body.relationship_to_business,
valid_until: null,
};

if (!business.beneficialOwners) {
business.beneficialOwners = [{ ...beneficialOwner }];
} else {
business.beneficialOwners.push({ ...beneficialOwner });
}
const beneficialOwners = business.beneficialOwners || [];

beneficialOwners.push({ ...beneficialOwner });
business.beneficialOwners = beneficialOwners;

await saveBusiness(business);

return res.status(201).send(beneficialOwner);
} catch (err) {
if (
err.message ===
`Business which has businessId: ${businessId} was not found in redis`
) {
const resp = {
errors: [
{
id: uuid.v4(),
status: 404,
code: "model_not_found",
title: "Model Not Found",
detail: `Couldn't find 'Solaris::Business' for id '${businessId}'.`,
},
],
};

return res.status(404).send(resp);
}

} catch (error) {
return res.status(500).send({
errors: [
{
Expand Down
95 changes: 95 additions & 0 deletions src/routes/business/identification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { Response } from "express";

import { RequestWithBusiness } from "../../helpers/middlewares";
import { saveBusiness, getPerson, savePerson } from "../../db";
import generateID from "../../helpers/id";
import {
BusinessIdentification,
LegalIdentificationStatus,
BusinessIdentificationStatus,
LegalRepresentative,
LegalRepresentativeIdentificationResponse,
} from "../../helpers/types";
import {
createIdentification,
generatePendingIdentitfication,
} from "../identifications";

const mapLegalRepresentative = async (
legalRepresentative: LegalRepresentative
): Promise<LegalRepresentativeIdentificationResponse> => {
let person = await getPerson(legalRepresentative.legal_representative_id);
const identification = await createIdentification(person);
await generatePendingIdentitfication(person, identification.id);
person = await getPerson(person.id);

return {
identifications: Object.values(person.identifications),
person_id: person.id,
};
};

export const createBusinessIdentification = async (
req: RequestWithBusiness,
res: Response
) => {
const { business } = req;

const { legalRepresentatives } = business;

const identification: BusinessIdentification = {
id: generateID(),
method: "idnow",
business_id: business.id,
reference: "ABC",
completed_at: null,
status: BusinessIdentificationStatus.CREATED,
legal_identification_missing_information_details: null,
legal_identification_missing_information: [],
legal_identification_status: LegalIdentificationStatus.CREATED,
legal_identification_reason: null,
legal_representatives: await Promise.all(
legalRepresentatives.map(mapLegalRepresentative)
),
};

if (!business.identifications) {
business.identifications = [identification];
} else {
business.identifications.push(identification);
}

await saveBusiness(business);

return res.status(200).send(identification);
};

export const retrieveBusinessIdentification = async (
req: RequestWithBusiness,
res: Response
) => {
const { business } = req;
const { identification_id: identificationId } = req.params;

const identification = business.identifications.find(
(ident) => ident.id === identificationId
);

if (!identification) {
const resp = {
errors: [
{
id: generateID(),
status: 404,
code: "model_not_found",
title: "Model Not Found",
detail: `Couldn't find 'Solaris::BusinessIdentification' for id '${identificationId}'.`,
},
],
};

return res.status(404).send(resp);
}

return res.status(200).send(identification);
};
1 change: 1 addition & 0 deletions src/routes/business/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./businesses";
export * from "./documents";
export * from "./beneficialOwner";
export * from "./identification";
Loading

0 comments on commit 95c9b46

Please sign in to comment.