mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 08:48:11 +00:00
They hold a TIN but no business licence, so there is no eTrade record to look their registration up in. A checkbox on the first wizard step marks them, and everything that assumed a trade licence bends around it: - The company step replaces the eTrade lookup with typed registration details — name, region, zone, woreda, kebele, house number — required exactly because they are now on screen. applyEtradeSourcedFields skips the lookup rather than failing it, so what the customer sends is what is stored. - No freight-forwarder role. Forwarding is licensed work, so the option is not offered, and the API refuses it at start-onboarding and at every later role-add rather than letting approval fail on a document they cannot produce. - No per-role business-licence upload, client-side or in the completion gate. - Their own document set (company_onboarding_documents_cooperative) merges on top of the nationality one, admin-managed like every other set. Nationality wins a fileKey collision so no slot renders twice, and the DARS paper is not injected into it — the set it merges onto already carries one. - The owner is typed in full; with no eTrade manager on file the licence comparison reports "nothing to compare against", which backoffice now explains rather than leaving as a bare dash. Stored as an attributes flag, not a column: everything it changes is behavioural, and nothing queries or joins on it.
213 lines
6.0 KiB
TypeScript
213 lines
6.0 KiB
TypeScript
import { BaseEntity } from "@edr/api-common";
|
|
import { Column, Entity, Index, OneToMany } from "typeorm";
|
|
import { ExternalProfile } from "./external-profile.entity";
|
|
import { CompanyProfile } from "./company-profile.entity";
|
|
|
|
export enum CompanyType {
|
|
Customer = "customer",
|
|
FreightForwarder = "freight_forwarder",
|
|
DJFreightForwarder = "dj_freight_forwarder",
|
|
Transporter = "transporter",
|
|
}
|
|
|
|
/**
|
|
* Sector of the company — orthogonal to {@link CompanyType} (the trade role).
|
|
* Government bookings are billed to a single seeded `GOVERNMENT` company instead
|
|
* of carrying a null company + free-text institution.
|
|
*/
|
|
export enum CompanyKind {
|
|
Commercial = "commercial",
|
|
Government = "government",
|
|
}
|
|
|
|
export enum CompanyStatus {
|
|
Active = "active",
|
|
Pending = "pending",
|
|
Suspended = "suspended",
|
|
Blacklisted = "blacklisted",
|
|
}
|
|
|
|
export enum CompanyNationality {
|
|
Ethiopian = "ethiopian",
|
|
Foreign = "foreign",
|
|
}
|
|
|
|
/**
|
|
* `attributes` key marking a co-operative union or farm.
|
|
*
|
|
* Such a company has a TIN but no business licence, so there is no eTrade record to
|
|
* look its registration up in — the company name, registered address and the
|
|
* owner are all typed instead of fetched, and the eTrade authenticity check is
|
|
* skipped rather than failed. It is a flag rather than a column because
|
|
* everything it changes is behavioural (which lookup runs, which documents
|
|
* apply, which roles are offered); nothing queries or joins on it.
|
|
*/
|
|
export const COOPERATIVE_KEY = "cooperative";
|
|
|
|
/** Is this a co-operative union or farm (a TIN, but no business licence)? */
|
|
export function isCooperative(
|
|
company: Pick<Company, "attributes"> | null | undefined,
|
|
): boolean {
|
|
return company?.attributes?.[COOPERATIVE_KEY] === true;
|
|
}
|
|
|
|
@Entity({ schema: "freight", name: "companies" })
|
|
@Index(["tin"])
|
|
@Index(["type"])
|
|
@Index(["kind"])
|
|
export class Company extends BaseEntity {
|
|
@Column({ name: "name", type: "varchar", length: 200 })
|
|
name!: string;
|
|
|
|
@Column({ name: "type", type: "varchar", length: 32, enum: CompanyType })
|
|
type!: CompanyType;
|
|
|
|
/** Commercial customer vs. the seeded government entity. */
|
|
@Column({
|
|
name: "kind",
|
|
type: "varchar",
|
|
length: 20,
|
|
default: CompanyKind.Commercial,
|
|
enum: CompanyKind,
|
|
})
|
|
kind!: CompanyKind;
|
|
|
|
@Column({
|
|
name: "status",
|
|
type: "varchar",
|
|
length: 32,
|
|
default: CompanyStatus.Pending,
|
|
})
|
|
status!: CompanyStatus;
|
|
|
|
/** Set when the company is first promoted Pending → Active. Null for companies approved before this column existed. */
|
|
@Column({ name: "approved_at", type: "timestamptz", nullable: true })
|
|
approvedAt?: Date | null;
|
|
|
|
@Column({ name: "tin", type: "varchar", length: 10, unique: true })
|
|
tin!: string;
|
|
|
|
@Column({ name: "vat_number", type: "varchar", length: 50, nullable: true })
|
|
vatNumber?: string | null;
|
|
|
|
@Column({ name: "fan_number", type: "varchar", length: 16, nullable: true })
|
|
fanNumber?: string | null;
|
|
|
|
@Column({ name: "country", type: "varchar", length: 32, default: "Ethiopia" })
|
|
country!: string;
|
|
|
|
/** Whether the company is Ethiopian or Foreign — drives the required onboarding documents. */
|
|
@Column({
|
|
name: "nationality",
|
|
type: "varchar",
|
|
length: 32,
|
|
nullable: true,
|
|
enum: CompanyNationality,
|
|
})
|
|
nationality?: CompanyNationality | null;
|
|
|
|
@Column({ name: "address", type: "text", nullable: true })
|
|
address?: string | null;
|
|
|
|
@Column({ name: "phone", type: "varchar", length: 20, nullable: true })
|
|
phone?: string | null;
|
|
|
|
@Column({ name: "email", type: "varchar", length: 150, nullable: true })
|
|
email?: string | null;
|
|
|
|
@Column({
|
|
name: "contact_person_name",
|
|
type: "varchar",
|
|
length: 100,
|
|
nullable: true,
|
|
})
|
|
contactPersonName?: string | null;
|
|
|
|
@Column({
|
|
name: "contact_person_phone",
|
|
type: "varchar",
|
|
length: 20,
|
|
nullable: true,
|
|
})
|
|
contactPersonPhone?: string | null;
|
|
|
|
// The general manager used to live here as three columns. It named who to
|
|
// talk to, gated nothing, and nothing ever populated the columns — the write
|
|
// path put the values in `attributes`. Removed in RemoveGeneralManager; the
|
|
// company's people are now its owner (whoever the eTrade licence names) and
|
|
// its Power of Attorney, both in `attributes`.
|
|
|
|
@Column({ name: "website", type: "varchar", length: 200, nullable: true })
|
|
website?: string | null;
|
|
|
|
@Column({ name: "attributes", type: "jsonb", nullable: true })
|
|
attributes?: Record<string, any> | null;
|
|
|
|
@Column({
|
|
name: "licence_number",
|
|
type: "varchar",
|
|
length: 100,
|
|
nullable: true,
|
|
})
|
|
licenceNumber?: string | null;
|
|
|
|
@Column({ name: "status_description", type: "text", nullable: true })
|
|
statusDescription?: string | null;
|
|
|
|
@Column({
|
|
name: "date_registered",
|
|
type: "varchar",
|
|
length: 50,
|
|
nullable: true,
|
|
})
|
|
dateRegistered?: string | null;
|
|
|
|
@Column({
|
|
name: "renewed_from",
|
|
type: "varchar",
|
|
length: 50,
|
|
nullable: true,
|
|
})
|
|
renewedFrom?: string | null;
|
|
|
|
@Column({
|
|
name: "renewal_date",
|
|
type: "varchar",
|
|
length: 50,
|
|
nullable: true,
|
|
})
|
|
renewalDate?: string | null;
|
|
|
|
@Column({
|
|
name: "renewed_to",
|
|
type: "varchar",
|
|
length: 50,
|
|
nullable: true,
|
|
})
|
|
renewedTo?: string | null;
|
|
|
|
@Column({ name: "region", type: "varchar", length: 100, nullable: true })
|
|
region?: string | null;
|
|
|
|
@Column({ name: "zone", type: "varchar", length: 100, nullable: true })
|
|
zone?: string | null;
|
|
|
|
@Column({ name: "woreda", type: "varchar", length: 100, nullable: true })
|
|
woreda?: string | null;
|
|
|
|
@Column({ name: "kebele", type: "varchar", length: 100, nullable: true })
|
|
kebele?: string | null;
|
|
|
|
@Column({ name: "house_no", type: "varchar", length: 100, nullable: true })
|
|
houseNo?: string | null;
|
|
|
|
@Column({ name: "etrade_phone", type: "varchar", length: 20, nullable: true })
|
|
etradePhone?: string | null;
|
|
|
|
@OneToMany(() => ExternalProfile, (profile) => profile.company)
|
|
profiles?: ExternalProfile[];
|
|
|
|
@OneToMany(() => CompanyProfile, (profile) => profile.company)
|
|
companyProfiles?: CompanyProfile[];
|
|
}
|