make a contrat template

This commit is contained in:
Marshal
2026-07-09 21:13:07 +00:00
parent e4429592d3
commit 3443b79644
26 changed files with 3243 additions and 41 deletions

View File

@@ -0,0 +1,77 @@
import { BaseEntity } from "@edr/api-common";
import { Column, Entity, Index } from "typeorm";
/**
* The six canonical contract document templates, one per
* (trade direction × freight type) combination. Contracts store DOMESTIC for
* intercity movements; the template layer labels those INTERCITY to match the
* commercial vocabulary used on the printed documents.
*/
export const CONTRACT_TEMPLATE_CODES = [
"IMPORT_BULK",
"EXPORT_BULK",
"INTERCITY_BULK",
"IMPORT_CONTAINER",
"EXPORT_CONTAINER",
"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 pair onto a template code. */
export function contractTemplateCodeFor(
tradeDirection?: string | null,
freightType?: string | null,
): ContractTemplateCode {
const direction =
tradeDirection === "IMPORT"
? "IMPORT"
: tradeDirection === "EXPORT"
? "EXPORT"
: "INTERCITY";
const freight =
(freightType ?? "").toUpperCase().includes("BULK") ? "BULK" : "CONTAINER";
return `${direction}_${freight}` 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;
}