Files
crm/apps/api/src/invoices/dto.ts
T
MahdiandCursor 2ece79b3d6 Unify invoices and quotes UI with discount, tax, and credit notes.
Match list/form mockups on desktop and mobile, wire real APIs for billing fields, and merge proforma into the invoices section.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-05 17:53:50 +03:30

318 lines
4.9 KiB
TypeScript

import {
ArrayMinSize,
IsArray,
IsIn,
IsNumber,
IsOptional,
IsString,
MaxLength,
Min,
MinLength,
ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
const INVOICE_STATUSES = [
'draft',
'issued',
'partially_paid',
'paid',
'canceled',
] as const;
const INVOICE_KINDS = ['invoice', 'credit'] as const;
const PAYMENT_METHODS = [
'cash',
'transfer',
'card',
'check',
'other',
] as const;
export class InvoiceLineItemInputDto {
@IsOptional()
@IsString()
productId?: string | null;
@IsString()
@MinLength(1)
@MaxLength(200)
name!: string;
@Type(() => Number)
@IsNumber()
@Min(0.01)
quantity!: number;
@Type(() => Number)
@IsNumber()
@Min(0)
unitPrice!: number;
@IsOptional()
@IsString()
@MaxLength(8)
currency?: string;
}
export class InvoiceInstallmentInputDto {
@IsString()
dueDate!: string;
@Type(() => Number)
@IsNumber()
@Min(0.01)
amount!: number;
@IsOptional()
@IsString()
@MaxLength(8)
currency?: string;
}
export class CreateInvoiceDto {
@IsString()
@MinLength(2)
@MaxLength(200)
title!: string;
@IsOptional()
@IsIn(INVOICE_STATUSES)
status?: (typeof INVOICE_STATUSES)[number];
@IsOptional()
@IsIn(INVOICE_KINDS)
kind?: (typeof INVOICE_KINDS)[number];
@IsOptional()
@IsString()
@MaxLength(8)
currency?: string;
@IsOptional()
@IsString()
@MaxLength(4000)
notes?: string;
@IsOptional()
@Type(() => Number)
@IsNumber()
@Min(0)
discountAmount?: number;
@IsOptional()
@Type(() => Number)
@IsNumber()
@Min(0)
taxPercent?: number;
@IsOptional()
@IsString()
issuedAt?: string | null;
@IsOptional()
@IsString()
dueDate?: string | null;
@IsOptional()
@IsString()
personId?: string | null;
@IsOptional()
@IsString()
companyId?: string | null;
@IsOptional()
@IsString()
dealId?: string | null;
@IsOptional()
@IsString()
quoteId?: string | null;
@IsOptional()
@IsString()
fromDealId?: string | null;
@IsOptional()
@IsString()
fromQuoteId?: string | null;
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => InvoiceLineItemInputDto)
@ArrayMinSize(0)
lineItems?: InvoiceLineItemInputDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => InvoiceInstallmentInputDto)
installments?: InvoiceInstallmentInputDto[];
}
export class UpdateInvoiceDto {
@IsOptional()
@IsString()
@MinLength(2)
@MaxLength(200)
title?: string;
@IsOptional()
@IsIn(INVOICE_STATUSES)
status?: (typeof INVOICE_STATUSES)[number];
@IsOptional()
@IsIn(INVOICE_KINDS)
kind?: (typeof INVOICE_KINDS)[number];
@IsOptional()
@IsString()
@MaxLength(8)
currency?: string;
@IsOptional()
@IsString()
@MaxLength(4000)
notes?: string | null;
@IsOptional()
@Type(() => Number)
@IsNumber()
@Min(0)
discountAmount?: number;
@IsOptional()
@Type(() => Number)
@IsNumber()
@Min(0)
taxPercent?: number;
@IsOptional()
@IsString()
issuedAt?: string | null;
@IsOptional()
@IsString()
dueDate?: string | null;
@IsOptional()
@IsString()
personId?: string | null;
@IsOptional()
@IsString()
companyId?: string | null;
@IsOptional()
@IsString()
dealId?: string | null;
@IsOptional()
@IsString()
quoteId?: string | null;
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => InvoiceLineItemInputDto)
lineItems?: InvoiceLineItemInputDto[];
@IsOptional()
@IsArray()
@ValidateNested({ each: true })
@Type(() => InvoiceInstallmentInputDto)
installments?: InvoiceInstallmentInputDto[];
}
export class CreatePaymentDto {
@Type(() => Number)
@IsNumber()
@Min(0.01)
amount!: number;
@IsOptional()
@IsString()
@MaxLength(8)
currency?: string;
@IsOptional()
@IsIn(PAYMENT_METHODS)
method?: (typeof PAYMENT_METHODS)[number];
@IsOptional()
@IsString()
paidAt?: string;
@IsOptional()
@IsString()
@MaxLength(2000)
notes?: string;
@IsOptional()
@IsString()
installmentId?: string | null;
}
export class ListInvoicesQueryDto {
@IsOptional()
@IsString()
q?: string;
@IsOptional()
@IsIn(INVOICE_STATUSES)
status?: (typeof INVOICE_STATUSES)[number];
@IsOptional()
@IsIn(INVOICE_KINDS)
kind?: (typeof INVOICE_KINDS)[number];
@IsOptional()
@IsString()
dealId?: string;
@IsOptional()
@IsString()
personId?: string;
@IsOptional()
@IsString()
companyId?: string;
@IsOptional()
@IsString()
quoteId?: string;
@IsOptional()
@IsString()
from?: string;
@IsOptional()
@IsString()
to?: string;
@IsOptional()
@Type(() => Number)
@IsNumber()
amountMin?: number;
@IsOptional()
@Type(() => Number)
@IsNumber()
amountMax?: number;
@IsOptional()
@Type(() => Number)
@IsNumber()
@Min(1)
page?: number;
@IsOptional()
@Type(() => Number)
@IsNumber()
@Min(1)
limit?: number;
}