Files
CRM_Montzar/apps/api/src/integrations/tahesab/dto.ts
T
MahdiandCursor 94f0383c79 feat(tahesab): import and refresh customers at chosen hours
Each connection can set how many times a day and at which Tehran hours the server pulls Tahesab customers, including name updates for people already linked.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-05 12:31:27 +03:30

514 lines
8.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
ArrayMaxSize,
IsArray,
IsBoolean,
IsIn,
IsInt,
IsNumber,
IsOptional,
IsString,
IsUrl,
Max,
MaxLength,
Min,
MinLength,
ValidateNested,
} from 'class-validator';
import { Type } from 'class-transformer';
export class CreateTahesabConnectionDto {
@IsString()
@MinLength(1)
@MaxLength(120)
label!: string;
@IsString()
@MinLength(8)
@MaxLength(500)
baseUrl!: string;
@IsString()
@MinLength(1)
@MaxLength(120)
dbName!: string;
@IsString()
@MinLength(8)
@MaxLength(2000)
token!: string;
@IsOptional()
@IsBoolean()
enabled?: boolean;
@IsOptional()
@IsBoolean()
isDefault?: boolean;
@IsOptional()
@IsBoolean()
allowInsecureTls?: boolean;
}
export class UpdateTahesabConnectionDto {
@IsOptional()
@IsString()
@MinLength(1)
@MaxLength(120)
label?: string;
@IsOptional()
@IsString()
@MinLength(8)
@MaxLength(500)
baseUrl?: string;
@IsOptional()
@IsString()
@MinLength(1)
@MaxLength(120)
dbName?: string;
@IsOptional()
@IsString()
@MinLength(8)
@MaxLength(2000)
token?: string;
@IsOptional()
@IsBoolean()
enabled?: boolean;
@IsOptional()
@IsBoolean()
isDefault?: boolean;
@IsOptional()
@IsBoolean()
allowInsecureTls?: boolean;
/** ساعت‌های ۰ تا ۲۳. آرایهٔ خالی یعنی خاموش. */
@IsOptional()
@IsArray()
@ArrayMaxSize(6)
@Type(() => Number)
@IsInt({ each: true })
@Min(0, { each: true })
@Max(23, { each: true })
customerSyncHours?: number[];
}
export class ManualLinkDto {
@IsString()
connectionId!: string;
@IsInt()
@Min(1)
moshtariCode!: number;
}
export class MatchPersonDto {
@IsString()
connectionId!: string;
/** اگر true باشد و دقیقاً یک نتیجه باشد، لینک می‌شود */
@IsOptional()
@IsBoolean()
autoLinkIfUnique?: boolean;
}
export class CreateInTahesabDto {
@IsString()
connectionId!: string;
@IsOptional()
@IsString()
groupName?: string;
@IsOptional()
@IsInt()
jensFelez?: number;
}
export class ImportFromTahesabDto {
@IsString()
connectionId!: string;
@Type(() => Number)
@IsInt()
@Min(1)
moshtariCode!: number;
/** اگر داده شود به این شخص لینک می‌شود؛ وگرنه شخص جدید ساخته می‌شود */
@IsOptional()
@IsString()
personId?: string;
}
export class ImportBulkFromTahesabDto {
@IsString()
connectionId!: string;
@IsOptional()
@IsArray()
@Type(() => Number)
@IsInt({ each: true })
moshtariCodes?: number[];
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
fromCode?: number;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
toCode?: number;
/** اگر true باشد یک صفحه از مشتریان (بازهٔ کد) خوانده می‌شود */
@IsOptional()
@IsBoolean()
listAll?: boolean;
/** صفحهٔ بازهٔ کد هنگام listAll (پیش‌فرض ۱) */
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(100)
@Max(2000)
pageSize?: number;
}
export class SyncGroupsPageDto {
@IsString()
connectionId!: string;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(100)
@Max(2000)
pageSize?: number;
}
export class ExportPeopleToTahesabDto {
@IsString()
connectionId!: string;
/** هر درخواست چند نفر؛ هر نفر چند تماس با ته‌حساب دارد */
@IsArray()
@ArrayMaxSize(25)
@IsString({ each: true })
personIds!: string[];
}
export class SearchTahesabDto {
@IsString()
connectionId!: string;
@IsOptional()
@IsString()
phone?: string;
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@Type(() => Number)
@IsInt()
moshtariCode?: number;
/** لیست بازه‌ای با DoListMoshtari-ByCode */
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
fromCode?: number;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
toCode?: number;
/** بازه خالی در UI → مرور صفحه‌ای همهٔ مشتریان */
@IsOptional()
@IsBoolean()
listAll?: boolean;
/**
* صفحهٔ بازهٔ کد برای listAll (۱ = کد ۱…۲۰۰۰، ۲ = ۲۰۰۱…۴۰۰۰، …).
* سقف ۵۰۰۰ دیگر اعمال نمی‌شود.
*/
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
page?: number;
/** اندازهٔ پنجرهٔ کد در هر صفحه (پیش‌فرض ۲۰۰۰، حداکثر ۲۰۰۰) */
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(100)
@Max(2000)
pageSize?: number;
}
export class SearchTahesabProductsDto {
@IsString()
connectionId!: string;
/** فیلتر اختیاری روی نام کارساخته */
@IsOptional()
@IsString()
@MaxLength(200)
q?: string;
}
export class ImportProductFromTahesabDto {
@IsString()
connectionId!: string;
@IsString()
@MinLength(1)
@MaxLength(200)
name!: string;
/** اگر داده شود به این محصول لینک می‌شود؛ وگرنه محصول جدید ساخته می‌شود */
@IsOptional()
@IsString()
productId?: string;
}
export class ImportBulkProductsFromTahesabDto {
@IsString()
connectionId!: string;
/** اگر خالی باشد همهٔ نام‌های کارساخته وارد می‌شوند */
@IsOptional()
@IsArray()
@IsString({ each: true })
names?: string[];
}
export class ListAsnadQueryDto {
@IsOptional()
@IsString()
connectionId?: string;
/** تاریخ شمسی YYYY-MM-DD */
@IsString()
from!: string;
@IsString()
to!: string;
}
const SANAD_TYPES = [
'buy_sale_gold',
'buy_sale_sekeh',
'talab_bedehi',
'vkh_gold',
'vkh_bank',
] as const;
export class PushSanadDto {
@IsString()
connectionId!: string;
@IsIn(SANAD_TYPES)
docType!: (typeof SANAD_TYPES)[number];
@IsInt()
year!: number;
@IsInt()
@Min(1)
month!: number;
@IsInt()
@Min(1)
day!: number;
@IsOptional()
@IsString()
@MaxLength(500)
sharh?: string;
// gold / sekeh / vkh gold
@IsOptional()
@IsNumber()
vazn?: number;
@IsOptional()
@IsNumber()
ayar?: number;
@IsOptional()
@IsIn([0, 1])
buyOrSale?: 0 | 1;
@IsOptional()
@IsNumber()
mazaneh?: number;
@IsOptional()
@IsIn([0, 1])
mazanehIsGeram?: 0 | 1;
@IsOptional()
@IsIn([0, 1])
isAbshode?: 0 | 1;
@IsOptional()
@IsNumber()
mablaghKol?: number;
@IsOptional()
@IsNumber()
count?: number;
@IsOptional()
@IsString()
nameSekeh?: string;
@IsOptional()
@IsIn([0, 1])
isBedehiOrTalab?: 0 | 1;
@IsOptional()
@IsNumber()
mablagh?: number;
@IsOptional()
@IsString()
baabat?: string;
@IsOptional()
@IsInt()
havalehBeMcode?: number;
@IsOptional()
@IsString()
angNumber?: string;
@IsOptional()
@IsString()
nameAz?: string;
@IsOptional()
@IsIn([0, 1])
isVoroodOrKhorooj?: 0 | 1;
@IsOptional()
@IsIn([0, 1])
isDaryaftOrPardakht?: 0 | 1;
@IsOptional()
@IsString()
nameBank?: string;
@IsOptional()
@IsString()
codeRahgiri?: string;
@IsOptional()
@IsInt()
jensFelez?: number;
@IsOptional()
@IsString()
arzName?: string;
/** اگر ارسال شود و قبلاً success بوده، همان پاسخ برمی‌گردد (retry امن) */
@IsOptional()
@IsString()
@MinLength(20)
factorCode?: string;
}
export class SyncAsnadReportDto {
@IsOptional()
@IsString()
connectionId?: string;
@IsString()
@MinLength(8)
@MaxLength(12)
from!: string;
@IsString()
@MinLength(8)
@MaxLength(12)
to!: string;
/** اگر باشد، فقط سندهای همین شخص گرفته می‌شود */
@IsOptional()
@IsString()
personId?: string;
/** اگر باشد، فقط مشتریان این گروه‌ها (و زیرگروه‌ها) گرفته می‌شوند */
@IsOptional()
@IsArray()
@IsString({ each: true })
@ArrayMaxSize(200)
groupIds?: string[];
}
export class UpdateTahesabInvoiceSettingsDto {
/** null = حذف لوگو */
@IsOptional()
logoDataUrl?: string | null;
@IsOptional()
@IsString()
@MaxLength(200)
companyName?: string;
@IsOptional()
@IsString()
@MaxLength(200)
companySubtitle?: string;
@IsOptional()
@IsString()
@MaxLength(500)
address?: string;
@IsOptional()
@IsString()
@MaxLength(80)
phone?: string;
@IsOptional()
@IsString()
@MaxLength(1000)
headerText?: string;
@IsOptional()
@IsString()
@MaxLength(1000)
footerText?: string;
}
// silence unused import warnings for ValidateNested/Type if unused
void ValidateNested;
void Type;
void IsUrl;