313 lines
9.6 KiB
TypeScript
313 lines
9.6 KiB
TypeScript
import {
|
|
ApiProperty,
|
|
ApiPropertyOptional,
|
|
IntersectionType,
|
|
OmitType,
|
|
PartialType,
|
|
} from '@nestjs/swagger';
|
|
import { Transform, Type } from 'class-transformer';
|
|
import {
|
|
IsBoolean,
|
|
IsDefined,
|
|
IsIn,
|
|
IsInt,
|
|
IsOptional,
|
|
IsString,
|
|
IsUUID,
|
|
Matches,
|
|
Max,
|
|
MaxLength,
|
|
Min,
|
|
MinLength,
|
|
ValidateIf,
|
|
ValidateNested,
|
|
} from 'class-validator';
|
|
|
|
import { MoneyDto } from '../../../../common/money.dto';
|
|
import {
|
|
FIRST_PAGE,
|
|
MAX_CARD_NAME_LENGTH,
|
|
MAX_CATEGORY_NAME_LENGTH,
|
|
MAX_OPERATION_DESCRIPTION_LENGTH,
|
|
MIN_CARD_NAME_LENGTH,
|
|
MIN_CATEGORY_NAME_LENGTH,
|
|
MIN_ENTITY_VERSION,
|
|
} from '../domain/bank-card-limits';
|
|
import { BankOperationKind } from '../domain/bank-operation-kind';
|
|
|
|
const MIN_PAGE_SIZE = 1;
|
|
|
|
export const DEFAULT_BANK_PAGE_SIZE = 50;
|
|
const MAX_BANK_PAGE_SIZE = 100;
|
|
|
|
const trim = ({ value }: { value: unknown }) =>
|
|
typeof value === 'string' ? value.trim() : value;
|
|
export const bankPaymentSystems = ['mir', 'visa', 'mastercard'] as const;
|
|
const bankOperationKinds = Object.values(BankOperationKind);
|
|
export class RubMoneyInputDto {
|
|
@ApiProperty({ enum: ['RUB'] }) @IsIn(['RUB']) declare currency: string;
|
|
@ApiProperty({ pattern: '^-?[0-9]+(\\.[0-9]{1,2})?$' })
|
|
@IsString()
|
|
// eslint-disable-next-line security/detect-unsafe-regex -- Anchored decimal grammar: the literal decimal point separates disjoint digit runs; backtracking is linear.
|
|
@Matches(/^-?\d+(\.\d{1,2})?$/)
|
|
declare value: string;
|
|
}
|
|
export class CreateBankCardDto {
|
|
@ApiProperty({ format: 'uuid' }) @IsUUID() declare id: string;
|
|
@ApiProperty({ minLength: MIN_CARD_NAME_LENGTH, maxLength: MAX_CARD_NAME_LENGTH })
|
|
@Transform(trim)
|
|
@IsString()
|
|
@MinLength(MIN_CARD_NAME_LENGTH)
|
|
@MaxLength(MAX_CARD_NAME_LENGTH)
|
|
declare name: string;
|
|
|
|
@ApiPropertyOptional({
|
|
type: String,
|
|
nullable: true,
|
|
maxLength: MAX_CARD_NAME_LENGTH,
|
|
})
|
|
@IsOptional()
|
|
@Transform(trim)
|
|
@IsString()
|
|
@MaxLength(MAX_CARD_NAME_LENGTH)
|
|
bankName?: null | string;
|
|
|
|
@ApiPropertyOptional({ type: String, nullable: true, pattern: '^[0-9]{4}$' })
|
|
@IsOptional()
|
|
@Matches(/^\d{4}$/)
|
|
lastFour?: null | string;
|
|
|
|
@ApiPropertyOptional({ enum: bankPaymentSystems, nullable: true })
|
|
@IsOptional()
|
|
@IsIn(bankPaymentSystems)
|
|
paymentSystem?: null | string;
|
|
|
|
@ApiProperty({ type: RubMoneyInputDto })
|
|
@IsDefined()
|
|
@ValidateNested()
|
|
@Type(() => RubMoneyInputDto)
|
|
declare openingBalance: MoneyDto;
|
|
|
|
@ApiProperty({ format: 'date' })
|
|
@IsString()
|
|
@Matches(/^\d{4}-\d{2}-\d{2}$/)
|
|
declare openingDate: string;
|
|
}
|
|
export class UpdateBankCardDto extends PartialType(
|
|
OmitType(CreateBankCardDto, ['id'] as const),
|
|
{ skipNullProperties: false },
|
|
) {
|
|
@ApiProperty({ minimum: MIN_ENTITY_VERSION }) @IsInt() @Min(MIN_ENTITY_VERSION) declare version: number;
|
|
@ApiPropertyOptional()
|
|
@ValidateIf((_object, value: unknown) => value !== undefined)
|
|
@IsBoolean()
|
|
archived?: boolean;
|
|
}
|
|
export class CategoryNameDto {
|
|
@ApiProperty({ minLength: MIN_CATEGORY_NAME_LENGTH, maxLength: MAX_CATEGORY_NAME_LENGTH })
|
|
@Transform(trim)
|
|
@IsString()
|
|
@MinLength(MIN_CATEGORY_NAME_LENGTH)
|
|
@MaxLength(MAX_CATEGORY_NAME_LENGTH)
|
|
declare name: string;
|
|
}
|
|
export class UpdateExpenseCategoryDto extends CategoryNameDto {
|
|
@ApiProperty({ minimum: MIN_ENTITY_VERSION }) @IsInt() @Min(MIN_ENTITY_VERSION) declare version: number;
|
|
}
|
|
export class CreateBankOperationDto {
|
|
@ApiProperty({ format: 'uuid' }) @IsUUID() declare id: string;
|
|
@ApiProperty({ enum: bankOperationKinds })
|
|
@IsIn(bankOperationKinds)
|
|
declare kind: BankOperationKind;
|
|
|
|
@ApiProperty({ format: 'uuid' }) @IsUUID() declare cardId: string;
|
|
@ApiPropertyOptional({ format: 'uuid' })
|
|
@ValidateIf((_object, value: unknown) => value !== undefined)
|
|
@IsUUID()
|
|
targetCardId?: string;
|
|
|
|
@ApiPropertyOptional({ format: 'uuid' })
|
|
@ValidateIf((_object, value: unknown) => value !== undefined)
|
|
@IsUUID()
|
|
categoryId?: string;
|
|
|
|
@ApiPropertyOptional({ format: 'uuid' })
|
|
@ValidateIf((_object, value: unknown) => value !== undefined)
|
|
@IsUUID()
|
|
originalExpenseId?: string;
|
|
|
|
@ApiProperty({ type: RubMoneyInputDto })
|
|
@IsDefined()
|
|
@ValidateNested()
|
|
@Type(() => RubMoneyInputDto)
|
|
declare amount: MoneyDto;
|
|
|
|
@ApiProperty({ format: 'date' })
|
|
@IsString()
|
|
@Matches(/^\d{4}-\d{2}-\d{2}$/)
|
|
declare date: string;
|
|
|
|
@ApiPropertyOptional({ maxLength: MAX_OPERATION_DESCRIPTION_LENGTH })
|
|
@ValidateIf((_object, value: unknown) => value !== undefined)
|
|
@IsString()
|
|
@MaxLength(MAX_OPERATION_DESCRIPTION_LENGTH)
|
|
description?: string;
|
|
}
|
|
export class UpdateBankOperationDto extends PartialType(
|
|
OmitType(CreateBankOperationDto, ['id'] as const),
|
|
{ skipNullProperties: false },
|
|
) {
|
|
@ApiProperty({ minimum: MIN_ENTITY_VERSION }) @IsInt() @Min(MIN_ENTITY_VERSION) declare version: number;
|
|
}
|
|
export class BankCardsQueryDto {
|
|
@ApiPropertyOptional({ enum: ['true', 'false'] })
|
|
@IsOptional()
|
|
@IsIn(['true', 'false'])
|
|
includeArchived?: string;
|
|
}
|
|
export class BankPageQueryDto {
|
|
@ApiPropertyOptional({ minimum: FIRST_PAGE, default: FIRST_PAGE })
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(FIRST_PAGE)
|
|
page?: number;
|
|
|
|
@ApiPropertyOptional({
|
|
minimum: MIN_PAGE_SIZE,
|
|
maximum: MAX_BANK_PAGE_SIZE,
|
|
default: DEFAULT_BANK_PAGE_SIZE,
|
|
})
|
|
@IsOptional()
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(MIN_PAGE_SIZE)
|
|
@Max(MAX_BANK_PAGE_SIZE)
|
|
limit?: number;
|
|
}
|
|
export class BankPeriodQueryDto {
|
|
@ApiPropertyOptional({
|
|
pattern: '^[0-9]{4}-(0[1-9]|1[0-2])$',
|
|
description: 'Use month or both from and to, exclusively.',
|
|
})
|
|
@IsOptional()
|
|
@Matches(/^\d{4}-(0[1-9]|1[0-2])$/)
|
|
month?: string;
|
|
|
|
@ApiPropertyOptional({
|
|
format: 'date',
|
|
description: 'Inclusive start date; requires to.',
|
|
})
|
|
@IsOptional()
|
|
@Matches(/^\d{4}-\d{2}-\d{2}$/)
|
|
from?: string;
|
|
|
|
@ApiPropertyOptional({
|
|
format: 'date',
|
|
description: 'Inclusive end date; requires from.',
|
|
})
|
|
@IsOptional()
|
|
@Matches(/^\d{4}-\d{2}-\d{2}$/)
|
|
to?: string;
|
|
}
|
|
export class BankOperationsQueryDto extends IntersectionType(
|
|
BankPageQueryDto,
|
|
BankPeriodQueryDto,
|
|
) {}
|
|
export class BankSummaryQueryDto extends BankPeriodQueryDto {
|
|
@ApiPropertyOptional({ format: 'uuid' })
|
|
@IsOptional()
|
|
@IsUUID()
|
|
cardId?: string;
|
|
}
|
|
export class BankVersionQueryDto {
|
|
@ApiProperty({ minimum: MIN_ENTITY_VERSION })
|
|
@Type(() => Number)
|
|
@IsInt()
|
|
@Min(MIN_ENTITY_VERSION)
|
|
declare version: number;
|
|
}
|
|
export class ExpenseCategoryDto {
|
|
@ApiProperty({ type: String }) declare id: string;
|
|
@ApiProperty({ type: String }) declare name: string;
|
|
@ApiProperty({ type: Boolean }) declare isDefault: boolean;
|
|
@ApiProperty({ type: Number }) declare version: number;
|
|
}
|
|
export class BankCardDto {
|
|
@ApiProperty({ type: String }) declare id: string;
|
|
@ApiProperty({ type: String }) declare name: string;
|
|
@ApiProperty({ type: String, nullable: true }) declare bankName:
|
|
null | string;
|
|
|
|
@ApiProperty({ type: String, nullable: true }) declare lastFour:
|
|
null | string;
|
|
|
|
@ApiProperty({ enum: bankPaymentSystems, nullable: true })
|
|
declare paymentSystem: null | string;
|
|
|
|
@ApiProperty({ type: MoneyDto }) declare openingBalance: MoneyDto;
|
|
@ApiProperty({ type: String }) declare openingDate: string;
|
|
@ApiProperty({ type: MoneyDto }) declare balance: MoneyDto;
|
|
@ApiProperty({ type: String, nullable: true }) declare archivedAt:
|
|
null | string;
|
|
|
|
@ApiProperty({ type: Number }) declare version: number;
|
|
}
|
|
export class BankOperationDto {
|
|
@ApiProperty({ type: String }) declare id: string;
|
|
@ApiProperty({ enum: bankOperationKinds }) declare kind: BankOperationKind;
|
|
@ApiProperty({ type: String }) declare cardId: string;
|
|
@ApiProperty({ type: String }) declare cardName: string;
|
|
@ApiProperty({ type: String, nullable: true }) declare targetCardId:
|
|
null | string;
|
|
|
|
@ApiProperty({ type: String, nullable: true }) declare targetCardName:
|
|
null | string;
|
|
|
|
@ApiProperty({ type: String, nullable: true }) declare categoryId:
|
|
null | string;
|
|
|
|
@ApiProperty({ type: String, nullable: true }) declare categoryName:
|
|
null | string;
|
|
|
|
@ApiProperty({ type: String, nullable: true }) declare originalExpenseId:
|
|
null | string;
|
|
|
|
@ApiProperty({ type: MoneyDto }) declare amount: MoneyDto;
|
|
@ApiProperty({ type: MoneyDto, nullable: true })
|
|
declare availableRefund: MoneyDto | null;
|
|
|
|
@ApiProperty({ type: String }) declare date: string;
|
|
@ApiProperty({ type: String, nullable: true }) declare description:
|
|
null | string;
|
|
|
|
@ApiProperty({ type: Number }) declare version: number;
|
|
@ApiProperty({ type: String }) declare createdAt: string;
|
|
}
|
|
export class BankOperationsPageDto {
|
|
@ApiProperty({ type: BankOperationDto, isArray: true })
|
|
declare items: BankOperationDto[];
|
|
|
|
@ApiProperty({ type: Number }) declare page: number;
|
|
@ApiProperty({ type: Number }) declare total: number;
|
|
@ApiProperty({ type: Boolean }) declare hasNext: boolean;
|
|
}
|
|
export class BankSummaryCategoryDto {
|
|
@ApiProperty({ type: String }) declare categoryId: string;
|
|
@ApiProperty({ type: String }) declare categoryName: string;
|
|
@ApiProperty({ type: MoneyDto }) declare expenses: MoneyDto;
|
|
@ApiProperty({ type: MoneyDto }) declare refunds: MoneyDto;
|
|
@ApiProperty({ type: MoneyDto }) declare net: MoneyDto;
|
|
}
|
|
export class BankSummaryDto {
|
|
@ApiPropertyOptional({ type: String }) month?: string;
|
|
@ApiPropertyOptional({ format: 'date' }) from?: string;
|
|
@ApiPropertyOptional({ format: 'date' }) to?: string;
|
|
@ApiProperty({ type: String, nullable: true }) declare cardId: null | string;
|
|
@ApiProperty({ type: MoneyDto }) declare expenses: MoneyDto;
|
|
@ApiProperty({ type: MoneyDto }) declare refunds: MoneyDto;
|
|
@ApiProperty({ type: MoneyDto }) declare net: MoneyDto;
|
|
@ApiProperty({ type: BankSummaryCategoryDto, isArray: true })
|
|
declare categories: BankSummaryCategoryDto[];
|
|
}
|