mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 02:58:11 +00:00
demurrage invoices
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Column, Entity, Index, JoinColumn, ManyToOne } from 'typeorm';
|
||||
|
||||
import { WarehouseFeeInvoice } from './warehouse-fee-invoice.entity';
|
||||
|
||||
export const WAREHOUSE_FEE_TYPES = [
|
||||
'CONTAINER_DEMURRAGE',
|
||||
'BULK_DEMURRAGE',
|
||||
'STORAGE_FEE',
|
||||
'HANDLING_FEE',
|
||||
] as const;
|
||||
export type WarehouseFeeType = (typeof WAREHOUSE_FEE_TYPES)[number];
|
||||
|
||||
@Entity({ schema: 'freight', name: 'warehouse_fee_invoice_items' })
|
||||
@Index(['invoiceId'])
|
||||
export class WarehouseFeeInvoiceItem extends BaseEntity {
|
||||
@Column({ name: 'invoice_id', type: 'uuid' })
|
||||
invoiceId!: string;
|
||||
|
||||
@ManyToOne(() => WarehouseFeeInvoice, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'invoice_id' })
|
||||
invoice?: WarehouseFeeInvoice;
|
||||
|
||||
@Column({ name: 'fee_rule_id', type: 'uuid', nullable: true })
|
||||
feeRuleId?: string | null;
|
||||
|
||||
@Column({ name: 'fee_type', type: 'varchar', length: 32 })
|
||||
feeType!: WarehouseFeeType;
|
||||
|
||||
@Column({ name: 'description', type: 'varchar', length: 255 })
|
||||
description!: string;
|
||||
|
||||
@Column({ name: 'quantity', type: 'numeric', precision: 12, scale: 2, default: 1 })
|
||||
quantity!: number;
|
||||
|
||||
@Column({ name: 'unit_rate', type: 'numeric', precision: 14, scale: 2, default: 0 })
|
||||
unitRate!: number;
|
||||
|
||||
@Column({ name: 'amount', type: 'numeric', precision: 14, scale: 2, default: 0 })
|
||||
amount!: number;
|
||||
|
||||
@Column({ name: 'currency', type: 'varchar', length: 8, default: 'USD' })
|
||||
currency!: string;
|
||||
|
||||
@Column({ name: 'chargeable_days', type: 'int', nullable: true })
|
||||
chargeableDays?: number | null;
|
||||
|
||||
@Column({ name: 'free_days', type: 'int', nullable: true })
|
||||
freeDays?: number | null;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import { BaseEntity } from '@edr/api-common';
|
||||
import { Column, Entity, Index } from 'typeorm';
|
||||
|
||||
export const WAREHOUSE_INVOICE_TYPES = ['DEMURRAGE', 'STORAGE_FEE', 'MIXED_WAREHOUSE_FEES'] as const;
|
||||
export type WarehouseInvoiceType = (typeof WAREHOUSE_INVOICE_TYPES)[number];
|
||||
|
||||
export const WAREHOUSE_INVOICE_STATUSES = [
|
||||
'DRAFT',
|
||||
'ISSUED',
|
||||
'PARTIALLY_PAID',
|
||||
'PAID',
|
||||
'CANCELLED',
|
||||
] as const;
|
||||
export type WarehouseInvoiceStatus = (typeof WAREHOUSE_INVOICE_STATUSES)[number];
|
||||
|
||||
/** A single recorded payment against a warehouse fee invoice (history). */
|
||||
export interface WarehouseInvoicePayment {
|
||||
amount: number;
|
||||
method?: string | null;
|
||||
reference?: string | null;
|
||||
paidAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch 6 — invoice generated from Batch 5 demurrage/storage fee calculation.
|
||||
* Owns warehouse fees; links to booking/customer/inventory/location so it can
|
||||
* connect to the existing payment module without duplicating it.
|
||||
*/
|
||||
@Entity({ schema: 'freight', name: 'warehouse_fee_invoices' })
|
||||
@Index(['invoiceNumber'], { unique: true })
|
||||
@Index(['bookingId'])
|
||||
@Index(['inventoryId'])
|
||||
@Index(['status'])
|
||||
export class WarehouseFeeInvoice extends BaseEntity {
|
||||
@Column({ name: 'invoice_number', type: 'varchar', length: 40, unique: true })
|
||||
invoiceNumber!: string;
|
||||
|
||||
@Column({ name: 'booking_id', type: 'uuid', nullable: true })
|
||||
bookingId?: string | null;
|
||||
|
||||
@Column({ name: 'customer_id', type: 'uuid', nullable: true })
|
||||
customerId?: string | null;
|
||||
|
||||
@Column({ name: 'inventory_id', type: 'uuid' })
|
||||
inventoryId!: string;
|
||||
|
||||
@Column({ name: 'facility_id', type: 'uuid', nullable: true })
|
||||
facilityId?: string | null;
|
||||
|
||||
@Column({ name: 'warehouse_id', type: 'uuid', nullable: true })
|
||||
warehouseId?: string | null;
|
||||
|
||||
@Column({ name: 'yard_id', type: 'uuid', nullable: true })
|
||||
yardId?: string | null;
|
||||
|
||||
@Column({ name: 'zone_id', type: 'uuid', nullable: true })
|
||||
zoneId?: string | null;
|
||||
|
||||
@Column({ name: 'invoice_type', type: 'varchar', length: 32, default: 'MIXED_WAREHOUSE_FEES' })
|
||||
invoiceType!: WarehouseInvoiceType;
|
||||
|
||||
@Column({ name: 'status', type: 'varchar', length: 20, default: 'DRAFT' })
|
||||
status!: WarehouseInvoiceStatus;
|
||||
|
||||
@Column({ name: 'subtotal_amount', type: 'numeric', precision: 14, scale: 2, default: 0 })
|
||||
subtotalAmount!: number;
|
||||
|
||||
@Column({ name: 'tax_amount', type: 'numeric', precision: 14, scale: 2, default: 0 })
|
||||
taxAmount!: number;
|
||||
|
||||
@Column({ name: 'total_amount', type: 'numeric', precision: 14, scale: 2, default: 0 })
|
||||
totalAmount!: number;
|
||||
|
||||
@Column({ name: 'paid_amount', type: 'numeric', precision: 14, scale: 2, default: 0 })
|
||||
paidAmount!: number;
|
||||
|
||||
@Column({ name: 'balance_amount', type: 'numeric', precision: 14, scale: 2, default: 0 })
|
||||
balanceAmount!: number;
|
||||
|
||||
@Column({ name: 'currency', type: 'varchar', length: 8, default: 'USD' })
|
||||
currency!: string;
|
||||
|
||||
/** Charge window covered by this invoice — used to allow a later invoice for a new period. */
|
||||
@Column({ name: 'period_start', type: 'timestamptz', nullable: true })
|
||||
periodStart?: Date | null;
|
||||
|
||||
@Column({ name: 'period_end', type: 'timestamptz', nullable: true })
|
||||
periodEnd?: Date | null;
|
||||
|
||||
@Column({ name: 'issued_at', type: 'timestamptz', nullable: true })
|
||||
issuedAt?: Date | null;
|
||||
|
||||
@Column({ name: 'due_date', type: 'timestamptz', nullable: true })
|
||||
dueDate?: Date | null;
|
||||
|
||||
@Column({ name: 'paid_at', type: 'timestamptz', nullable: true })
|
||||
paidAt?: Date | null;
|
||||
|
||||
@Column({ name: 'cancelled_at', type: 'timestamptz', nullable: true })
|
||||
cancelledAt?: Date | null;
|
||||
|
||||
@Column({ name: 'payments', type: 'jsonb', default: () => "'[]'" })
|
||||
payments!: WarehouseInvoicePayment[];
|
||||
|
||||
@Column({ name: 'notes', type: 'text', nullable: true })
|
||||
notes?: string | null;
|
||||
}
|
||||
Reference in New Issue
Block a user