mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 08:58:21 +00:00
104 lines
3.8 KiB
TypeScript
104 lines
3.8 KiB
TypeScript
import { BaseEntity } from "@edr/api-common";
|
||
import { Column, Entity, Index } from "typeorm";
|
||
|
||
/**
|
||
* The ten canonical contract document templates. Import and export split by
|
||
* customs clearing (× freight type = 8); intercity does not, because it is a
|
||
* purely domestic Ethiopian movement that crosses no border and therefore has
|
||
* no customs leg at all (× freight type = 2).
|
||
*
|
||
* Contracts store DOMESTIC for intercity movements; the template layer labels
|
||
* those INTERCITY to match the commercial vocabulary used on the printed
|
||
* documents.
|
||
*
|
||
* The `_CUSTOMS` variant is issued when the contract has customs clearing
|
||
* enabled (the Service Provider clears in Djibouti/Ethiopia on the Client's
|
||
* behalf); `_NO_CUSTOMS` is the transport-only paper, where the Client handles
|
||
* its own declarations.
|
||
*/
|
||
export const CONTRACT_TEMPLATE_CODES = [
|
||
"IMPORT_BULK_CUSTOMS",
|
||
"IMPORT_BULK_NO_CUSTOMS",
|
||
"EXPORT_BULK_CUSTOMS",
|
||
"EXPORT_BULK_NO_CUSTOMS",
|
||
"INTERCITY_BULK",
|
||
"IMPORT_CONTAINER_CUSTOMS",
|
||
"IMPORT_CONTAINER_NO_CUSTOMS",
|
||
"EXPORT_CONTAINER_CUSTOMS",
|
||
"EXPORT_CONTAINER_NO_CUSTOMS",
|
||
"INTERCITY_CONTAINER",
|
||
] as const;
|
||
|
||
export type ContractTemplateCode = (typeof CONTRACT_TEMPLATE_CODES)[number];
|
||
|
||
/**
|
||
* One dynamic article on a contract template. `body` is plain multiline text:
|
||
* each non-empty line renders as a numbered clause; lines prefixed with "- "
|
||
* render as bullet points nested under the preceding clause. A single-line
|
||
* body renders as an unnumbered paragraph. Handlebars placeholders (e.g.
|
||
* {{client.companyName}}, {{contractDate}}, {{contractYear}}, {{reference}})
|
||
* are interpolated against the contract view model at render time.
|
||
*/
|
||
export interface ContractTemplateArticle {
|
||
id: string;
|
||
title: string;
|
||
body: string;
|
||
order: number;
|
||
}
|
||
|
||
/**
|
||
* Map a contract's stored direction/freight/customs triple onto a template
|
||
* code. `customsClearingEnabled` is treated as false when absent so an older
|
||
* contract row with a null flag still resolves to a real template rather than
|
||
* falling through to the generic layout.
|
||
*
|
||
* Intercity is domestic and has no customs leg, so it resolves to a single
|
||
* unsuffixed code regardless of the flag.
|
||
*/
|
||
export function contractTemplateCodeFor(
|
||
tradeDirection?: string | null,
|
||
freightType?: string | null,
|
||
customsClearingEnabled?: boolean | null,
|
||
): ContractTemplateCode {
|
||
const direction =
|
||
tradeDirection === "IMPORT"
|
||
? "IMPORT"
|
||
: tradeDirection === "EXPORT"
|
||
? "EXPORT"
|
||
: "INTERCITY";
|
||
const freight =
|
||
(freightType ?? "").toUpperCase().includes("BULK") ? "BULK" : "CONTAINER";
|
||
if (direction === "INTERCITY") {
|
||
return `INTERCITY_${freight}` as ContractTemplateCode;
|
||
}
|
||
const customs = customsClearingEnabled ? "CUSTOMS" : "NO_CUSTOMS";
|
||
return `${direction}_${freight}_${customs}` as ContractTemplateCode;
|
||
}
|
||
|
||
@Entity({ schema: "freight", name: "contract_templates" })
|
||
@Index(["code"], { unique: true })
|
||
export class ContractTemplate extends BaseEntity {
|
||
@Column({ name: "code", type: "varchar", length: 40, unique: true })
|
||
code!: ContractTemplateCode;
|
||
|
||
@Column({ name: "name", type: "varchar", length: 200 })
|
||
name!: string;
|
||
|
||
@Column({ name: "description", type: "text", nullable: true })
|
||
description?: string | null;
|
||
|
||
/** Cover-page service line, e.g. "Steel Billet Transportation and Customs Clearance Services". */
|
||
@Column({ name: "document_title", type: "varchar", length: 300 })
|
||
documentTitle!: string;
|
||
|
||
/** WHEREAS recitals rendered between the parties block and the articles. */
|
||
@Column({ name: "whereas_clauses", type: "jsonb", default: () => "'[]'" })
|
||
whereasClauses!: string[];
|
||
|
||
@Column({ name: "articles", type: "jsonb", default: () => "'[]'" })
|
||
articles!: ContractTemplateArticle[];
|
||
|
||
@Column({ name: "is_active", type: "boolean", default: true })
|
||
isActive!: boolean;
|
||
}
|