Merge branch 'alpha' into feat/telebirr-integration

This commit is contained in:
Abubeker Yasin
2026-06-10 13:22:47 +03:00
869 changed files with 97176 additions and 5351 deletions

View File

@@ -22,6 +22,7 @@ export enum ProviderMethod {
EBIRR = "EBIRR",
WAAFI = "WAAFI",
CARD = "CARD",
DMONEY = "DMONEY",
}
export type PaymentPlatform = "web" | "mobile";
@@ -48,6 +49,9 @@ export interface ProviderInitiationInput {
* MWALLET_ACCOUNT) require the payer's phone number up front to pre-fill the hosted page.
*/
payerAccount?: string;
/** Optional caller-supplied redirect targets for redirect/HPP-style providers. */
returnUrl?: string;
redirectUrl?: string;
}
export interface ProviderInitiationResult {

View File

@@ -0,0 +1,72 @@
import type { BaseEntity } from "../common";
export interface IDropdownOptionMeta {
icon?: string;
color?: string;
badge?: string;
}
export interface IDropdownOption extends BaseEntity {
settingId: string;
/** Stored value (machine-readable identifier). */
value: string;
/** Display label shown to end users. */
label: string;
/** Optional helper text. */
note?: string | null;
disabled: boolean;
order: number;
meta?: IDropdownOptionMeta | null;
}
export interface IDropdownSettingMeta {
icon?: string;
color?: string;
searchable?: boolean;
clearable?: boolean;
permissions?: string[];
version?: string;
}
export interface IDropdownSetting extends BaseEntity {
/** Stable code referenced from forms (snake_case). */
code: string;
/** Display label for admins. */
label: string;
description?: string | null;
multiple: boolean;
meta?: IDropdownSettingMeta | null;
/**
* Options that belong to this dropdown. Named `children` to match the shape
* historically consumed by the freight portal.
*/
children: IDropdownOption[];
}
/* ------------------------------------------------------------------ *
* Wire DTOs (shared between API and frontend)
* ------------------------------------------------------------------ */
export interface CreateDropdownOptionDto {
value: string;
label: string;
note?: string;
disabled?: boolean;
order?: number;
meta?: IDropdownOptionMeta;
}
export type UpdateDropdownOptionDto = Partial<CreateDropdownOptionDto>;
export interface CreateDropdownSettingDto {
code: string;
label: string;
description?: string;
multiple?: boolean;
meta?: IDropdownSettingMeta;
children?: CreateDropdownOptionDto[];
}
export type UpdateDropdownSettingDto = Partial<
Omit<CreateDropdownSettingDto, "children">
>;

View File

@@ -0,0 +1,89 @@
import type { BaseEntity } from "../common";
export type FileUploadEntity =
| "customer"
| "booking"
| "consignment"
| "shipment"
| "invoice"
| "train"
| "other";
export interface IFileUploadField extends BaseEntity {
settingId: string;
/** Stable identifier used by the API / object storage. */
fileKey: string;
/** Human-readable label rendered above the input. */
fileLabel: string;
helpText?: string | null;
isRequired: boolean;
isMultiple: boolean;
/** Upper bound on file count when isMultiple is true. */
maxFiles: number;
allowedExtensions: string[];
maxSizeMb: number;
order: number;
}
export interface IFileUploadSetting extends BaseEntity {
/** Stable code referenced from forms (snake_case). */
code: string;
/** Display label for admins. */
label: string;
description?: string | null;
entity: FileUploadEntity;
fields: IFileUploadField[];
}
/* ------------------------------------------------------------------ *
* Wire DTOs (shared between API and frontend)
* ------------------------------------------------------------------ */
export interface CreateFileUploadFieldDto {
fileKey: string;
fileLabel: string;
helpText?: string;
isRequired: boolean;
isMultiple: boolean;
maxFiles: number;
allowedExtensions: string[];
maxSizeMb: number;
order?: number;
}
export type UpdateFileUploadFieldDto = Partial<CreateFileUploadFieldDto>;
export interface CreateFileUploadSettingDto {
code: string;
label: string;
description?: string;
entity: FileUploadEntity;
fields?: CreateFileUploadFieldDto[];
}
export type UpdateFileUploadSettingDto = Partial<
Omit<CreateFileUploadSettingDto, "fields">
>;
/* ------------------------------------------------------------------ *
* Helpers — encode the required × multiple matrix.
*
* required | multiple | min | max
* ---------|----------|------|--------------
* no | no | 0 | 1
* yes | no | 1 | 1
* no | yes | 0 | field.maxFiles
* yes | yes | 1 | field.maxFiles
* ------------------------------------------------------------------ */
export function getMinFiles(
field: Pick<IFileUploadField, "isRequired">,
): number {
return field.isRequired ? 1 : 0;
}
export function getEffectiveMaxFiles(
field: Pick<IFileUploadField, "isMultiple" | "maxFiles">,
): number {
return field.isMultiple ? Math.max(1, field.maxFiles) : 1;
}

View File

@@ -1,11 +1,57 @@
import type { BaseEntity } from "../common";
export * from "./file_upload_settings";
export * from "./dropdown_settings";
export enum TradeDirection {
IMPORT = 'IMPORT',
EXPORT = 'EXPORT',
BOTH = 'BOTH',
}
export enum PriorityType {
USD_PAYER = 'USD_PAYER',
RAIL_AND_FORWARDING = 'RAIL_AND_FORWARDING',
GOVERNMENT_ACCOUNT = 'GOVERNMENT_ACCOUNT',
HIGH_VOLUME_SHIPMENT = 'HIGH_VOLUME_SHIPMENT',
}
export enum ExceededAction {
WARNING_ONLY = 'WARNING_ONLY',
HARD_BLOCK = 'HARD_BLOCK',
}
export enum CalculationMethod {
PER_TON = 'PER_TON',
FLAT_FEE = 'FLAT_FEE',
PERCENTAGE = 'PERCENTAGE',
}
export enum FreightType {
Container = 'CONTAINER',
Bulk = 'BULK',
}
export enum BookingStatus {
Draft = "DRAFT",
Confirmed = "CONFIRMED",
Submitted = "SUBMITTED",
ChangesRequested = "CHANGES_REQUESTED",
PendingApproval = "PENDING_APPROVAL",
ApprovedPendingSignature = "APPROVED_PENDING_SIGNATURE",
Approved = "APPROVED",
ContractReady = "CONTRACT_READY",
SignedCustomer = "SIGNED_CUSTOMER",
FullyExecuted = "FULLY_EXECUTED",
PnrGenerated = "PNR_GENERATED",
PaymentVerificationInProgress = "PAYMENT_VERIFICATION_IN_PROGRESS",
Paid = "PAID",
InTransit = "IN_TRANSIT",
Completed = "COMPLETED",
Delivered = "DELIVERED",
Rejected = "REJECTED",
Cancelled = "CANCELLED",
PendingConsolidation = "PENDING_CONSOLIDATION",
Consolidated = "CONSOLIDATED",
}
export enum ConsignmentStatus {
@@ -42,14 +88,65 @@ export enum PaymentStatus {
Refunded = "REFUNDED",
}
export type CustomerStatus = "Active" | "Pending" | "Inactive";
export type CustomerType = "Importer" | "Exporter" | "Supplier";
export interface ICustomer extends BaseEntity {
name: string;
userId: string;
firstName: string;
lastName: string;
email: string;
phone: string;
address?: string;
taxId?: string;
companyName: string;
companyEmail: string;
companyPhone: string;
companyLocation: string;
companyAddress: string;
contactPersonName: string;
contactPersonPhone: string;
tinNumber: string;
vatNumber?: string | null;
fanNumber: string;
generalManagerName: string;
generalManagerEmail: string;
generalManagerPhone: string;
poaName?: string | null;
poaPhone?: string | null;
poaAddress?: string | null;
poaEmail?: string | null;
poaLocation?: string | null;
notes?: string | null;
}
export interface CreateCustomerDto {
userId: string;
firstName: string;
lastName: string;
email: string;
phone: string;
companyName: string;
companyEmail: string;
companyPhone: string;
companyLocation: string;
companyAddress: string;
contactPersonName: string;
contactPersonPhone: string;
tinNumber: string;
vatNumber: string;
fanNumber: string;
generalManagerName: string;
generalManagerEmail: string;
generalManagerPhone: string;
poaName?: string;
poaPhone?: string;
poaAddress?: string;
poaEmail?: string;
poaLocation?: string;
notes?: string;
}
export type UpdateCustomerDto = Partial<CreateCustomerDto>;
export interface ITrain extends BaseEntity {
code: string;
capacityTons: number;
@@ -75,14 +172,93 @@ export interface IConsignment extends BaseEntity {
destinationStation: string;
}
export interface IYard extends BaseEntity {
code: string;
label: string;
country: string;
isActive: boolean;
displayOrder: number;
}
export interface IBooking extends BaseEntity {
reference: string;
customerId: string;
trainId?: string;
trainId?: string | null;
status: BookingStatus;
scheduledDate: string;
totalAmount: number;
paymentStatus: PaymentStatus;
contractType: "NEW" | "RENEWAL";
previousContractId?: string | null;
serviceType: "RAIL_ONLY" | "RAIL_AND_FORWARDING";
firstMileEnabled: boolean;
firstMilePickupAddress?: string | null;
lastMileEnabled: boolean;
lastMileDeliveryAddress?: string | null;
equipmentReturn: "WITH_RETURN" | "WITHOUT_RETURN";
originYard?: IYard | null;
destinationYard?: IYard | null;
cargoTotalWeightVgm: number;
freightType: FreightType;
freightSubtype?: string | null;
isHazardous: boolean;
isRefrigerated: boolean;
tradeDirection: "IMPORT" | "EXPORT";
paymentCurrency: string;
allowConsolidation: boolean;
consolidationPartnerId?: string | null;
startDate?: string | null;
endDate?: string | null;
financialTerms?: string | null;
containers?: Array<{ type: string; qty: number; vgm: number }> | null;
versionNumber: number;
priorityScore: number;
approvedByStaffId?: string | null;
approvedByStaffAt?: string | null;
signedByDirectorId?: string | null;
signedByDirectorAt?: string | null;
signedByCeoId?: string | null;
signedByCeoAt?: string | null;
files?: Array<{
id: string;
code: string;
name: string;
url: string;
mimeType: string;
size: number;
resourceId: string;
resource: string;
signedUrl?: string | null;
}>;
latestChangeRequestNote?: string | null;
contractSummary?: string | null;
pricingBreakdown?: PricingBreakdown | null;
}
export interface PricingBreakdownLineItem {
code: string;
amount: number;
currency: string;
description: string;
}
export interface PricingBreakdown {
currency: string;
lineItems: PricingBreakdownLineItem[];
generatedAt: string;
totalAmount: number;
}
export interface IInvoice extends BaseEntity {
@@ -94,3 +270,97 @@ export interface IInvoice extends BaseEntity {
issuedAt: string;
dueAt: string;
}
// ── Reference Data (booking form catalog) ──────────────────────────────────────
export interface BookingReferenceYard {
id: string;
name: string;
code: string;
country: string;
}
export interface BookingReferenceContainerType {
id: string;
name: string;
code: string;
is_reefer: boolean;
wagons_per_unit: number;
}
export interface BookingReferenceContainerSizeGroup {
size: string;
types: BookingReferenceContainerType[];
}
export interface BookingReferenceService {
id: string;
name: string;
code: string;
}
export interface BookingReferenceShippingLine {
id: string;
name: string;
code: string;
}
export interface BookingReferenceCargoTypeChild {
id: string;
name: string;
code: string;
show_free_text_box: boolean;
}
export interface BookingReferenceCargoTypeGroup {
id: string;
name: string;
code: string;
children?: BookingReferenceCargoTypeChild[];
}
export interface BookingReferenceData {
yard: BookingReferenceYard[];
containers: BookingReferenceContainerSizeGroup[];
service: BookingReferenceService[];
shipping_line: BookingReferenceShippingLine[];
cargo_type: BookingReferenceCargoTypeGroup[];
}
// ── DTOs ───────────────────────────────────────────────────────────────────────
export interface CreateBookingContainerDto {
containerTypeId: string;
quantity: number;
vgmPerUnitTons: number;
}
export interface CreateBookingDto {
reference?: string;
customerId?: string;
companyId?: string;
trainId?: string;
scheduledDate: string;
contractType: "NEW" | "RENEWAL";
previousContractId?: string;
serviceTypeId: string;
firstMilePickupAddress?: string;
lastMileDeliveryAddress?: string;
equipmentReturn: "WITH_RETURN" | "WITHOUT_RETURN" | "NA";
originYardId: string;
destinationYardId: string;
tradeDirection: "IMPORT" | "EXPORT" | "DOMESTIC";
freightType: FreightType;
cargoTypeId?: string;
cargoFreeText?: string;
shippingLineId?: string;
cargoTotalWeightVgm: number;
isHazardous?: boolean;
paymentCurrency: "ETB" | "USD";
pnrCode?: string;
startDate?: string;
endDate?: string;
financialTerms?: string;
containers?: CreateBookingContainerDto[];
allowConsolidation?: boolean;
}