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>
This commit is contained in:
2026-09-05 17:53:50 +03:30
co-authored by Cursor
parent 7862b6f374
commit 2ece79b3d6
15 changed files with 3101 additions and 1995 deletions
+60
View File
@@ -20,6 +20,8 @@ const INVOICE_STATUSES = [
'canceled',
] as const;
const INVOICE_KINDS = ['invoice', 'credit'] as const;
const PAYMENT_METHODS = [
'cash',
'transfer',
@@ -79,6 +81,10 @@ export class CreateInvoiceDto {
@IsIn(INVOICE_STATUSES)
status?: (typeof INVOICE_STATUSES)[number];
@IsOptional()
@IsIn(INVOICE_KINDS)
kind?: (typeof INVOICE_KINDS)[number];
@IsOptional()
@IsString()
@MaxLength(8)
@@ -89,6 +95,18 @@ export class CreateInvoiceDto {
@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;
@@ -146,6 +164,10 @@ export class UpdateInvoiceDto {
@IsIn(INVOICE_STATUSES)
status?: (typeof INVOICE_STATUSES)[number];
@IsOptional()
@IsIn(INVOICE_KINDS)
kind?: (typeof INVOICE_KINDS)[number];
@IsOptional()
@IsString()
@MaxLength(8)
@@ -156,6 +178,18 @@ export class UpdateInvoiceDto {
@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;
@@ -231,6 +265,10 @@ export class ListInvoicesQueryDto {
@IsIn(INVOICE_STATUSES)
status?: (typeof INVOICE_STATUSES)[number];
@IsOptional()
@IsIn(INVOICE_KINDS)
kind?: (typeof INVOICE_KINDS)[number];
@IsOptional()
@IsString()
dealId?: string;
@@ -239,10 +277,32 @@ export class ListInvoicesQueryDto {
@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()