import { BadRequestException, Body, Controller, Delete, Get, Param, Patch, Post, Query, UseGuards, } from '@nestjs/common'; import { JwtAuthGuard } from '../../auth/jwt-auth.guard'; import { CurrentUser } from '../../auth/current-user.decorator'; import { Roles } from '../../auth/roles.decorator'; import { RolesGuard } from '../../auth/roles.guard'; import { OrgModuleGuard } from '../../billing/org-module.guard'; import { RequireOrgModule } from '../../billing/org-module.decorator'; import { TahesabConnectionsService } from './connections.service'; import { TahesabPersonLinkService } from './person-link.service'; import { TahesabProductLinkService } from './product-link.service'; import { TahesabBalanceService } from './balance.service'; import { TahesabDocumentsService } from './documents.service'; import { TahesabAsnadReportService } from './asnad-report.service'; import { TahesabInvoiceSettingsService } from './invoice-settings.service'; import { CreateInTahesabDto, CreateTahesabConnectionDto, ExportPeopleToTahesabDto, ImportBulkFromTahesabDto, ImportBulkProductsFromTahesabDto, ImportFromTahesabDto, ImportProductFromTahesabDto, ManualLinkDto, MatchPersonDto, PushSanadDto, SearchTahesabDto, SearchTahesabProductsDto, SyncAsnadReportDto, SyncAllCustomersDto, SyncGroupsPageDto, UpdateTahesabConnectionDto, UpdateTahesabInvoiceSettingsDto, } from './dto'; @Controller('integrations/tahesab') @UseGuards(JwtAuthGuard, RolesGuard, OrgModuleGuard) @RequireOrgModule('tahesab') export class TahesabController { constructor( private readonly connections: TahesabConnectionsService, private readonly links: TahesabPersonLinkService, private readonly products: TahesabProductLinkService, private readonly balance: TahesabBalanceService, private readonly documents: TahesabDocumentsService, private readonly asnadReport: TahesabAsnadReportService, private readonly invoiceSettings: TahesabInvoiceSettingsService, ) {} // —— Connections —— /** لیست اتصالات برای انتخاب در پروفایل (بدون توکن خام) */ @Get('connections/public') listPublicConnections(@CurrentUser() user: { organizationId: string }) { return this.connections.list(user.organizationId); } @Get('connections') @Roles('owner', 'admin') listConnections(@CurrentUser() user: { organizationId: string }) { return this.connections.list(user.organizationId); } @Post('connections') @Roles('owner', 'admin') createConnection( @CurrentUser() user: { organizationId: string }, @Body() dto: CreateTahesabConnectionDto, ) { return this.connections.create(user.organizationId, dto); } @Patch('connections/:id') @Roles('owner', 'admin') updateConnection( @CurrentUser() user: { organizationId: string }, @Param('id') id: string, @Body() dto: UpdateTahesabConnectionDto, ) { return this.connections.update(user.organizationId, id, dto); } @Delete('connections/:id') @Roles('owner', 'admin') removeConnection( @CurrentUser() user: { organizationId: string }, @Param('id') id: string, ) { return this.connections.remove(user.organizationId, id); } @Post('connections/:id/test') @Roles('owner', 'admin') testConnection( @CurrentUser() user: { organizationId: string }, @Param('id') id: string, ) { return this.connections.test(user.organizationId, id); } // —— Person links (all authenticated CRM users in org) —— @Get('people/:personId/links') listLinks( @CurrentUser() user: { organizationId: string }, @Param('personId') personId: string, ) { return this.links.listLinks(user.organizationId, personId); } @Post('people/:personId/links') manualLink( @CurrentUser() user: { organizationId: string }, @Param('personId') personId: string, @Body() dto: ManualLinkDto, ) { return this.links.manualLink(user.organizationId, personId, dto); } @Delete('people/:personId/links/:connectionId') unlink( @CurrentUser() user: { organizationId: string }, @Param('personId') personId: string, @Param('connectionId') connectionId: string, ) { return this.links.unlink(user.organizationId, personId, connectionId); } @Post('people/:personId/match') match( @CurrentUser() user: { organizationId: string }, @Param('personId') personId: string, @Body() dto: MatchPersonDto, ) { return this.links.match(user.organizationId, personId, dto); } @Post('people/:personId/create-in-tahesab') createInTahesab( @CurrentUser() user: { organizationId: string }, @Param('personId') personId: string, @Body() dto: CreateInTahesabDto, ) { return this.links.createInTahesab(user.organizationId, personId, dto); } @Get('people/:personId/assets') getAssets( @CurrentUser() user: { organizationId: string }, @Param('personId') personId: string, @Query('connectionId') connectionId?: string, @Query('refresh') refresh?: string, ) { return this.balance.getCachedOrFetch( user.organizationId, personId, connectionId, refresh !== '0', ); } @Get('people/:personId/asnad') listAsnad( @CurrentUser() user: { organizationId: string }, @Param('personId') personId: string, @Query('from') from: string, @Query('to') to: string, @Query('connectionId') connectionId?: string, ) { return this.documents.listAsnad( user.organizationId, personId, from, to, connectionId, ); } @Get('lookups') lookups( @CurrentUser() user: { organizationId: string }, @Query('connectionId') connectionId?: string, ) { return this.documents.listLookups(user.organizationId, connectionId); } @Post('people/:personId/sanad') @Roles('owner', 'admin', 'sales') pushSanad( @CurrentUser() user: { organizationId: string }, @Param('personId') personId: string, @Body() dto: PushSanadDto, ) { return this.documents.pushSanad(user.organizationId, personId, dto); } @Post('sync/search') @Roles('owner', 'admin', 'sales') search( @CurrentUser() user: { organizationId: string }, @Body() dto: SearchTahesabDto, ) { return this.links.search(user.organizationId, dto); } @Post('sync/import') @Roles('owner', 'admin', 'sales') importFromTahesab( @CurrentUser() user: { organizationId: string }, @Body() dto: ImportFromTahesabDto, ) { return this.links.importFromTahesab(user.organizationId, dto); } @Post('sync/import-bulk') @Roles('owner', 'admin') importBulk( @CurrentUser() user: { organizationId: string }, @Body() dto: ImportBulkFromTahesabDto, ) { return this.links.importBulk(user.organizationId, dto); } @Post('sync/groups') @Roles('owner', 'admin') syncGroups( @CurrentUser() user: { organizationId: string }, @Body() dto: SyncGroupsPageDto, ) { return this.links.syncGroupsPage(user.organizationId, dto); } /** همهٔ مشتریان ته‌حساب: تازه‌ها وارد، لینک‌شده‌ها (نام/موبایل/کدملی/آدرس/شهر/رسته/گروه) به‌روز */ @Post('sync/customers-now') @Roles('owner', 'admin') syncCustomersNow( @CurrentUser() user: { organizationId: string }, @Body() dto: SyncAllCustomersDto, ) { return this.links.syncAllCustomers(user.organizationId, dto.connectionId); } @Get('sync/unlinked-people') @Roles('owner', 'admin') unlinkedPeople( @CurrentUser() user: { organizationId: string }, @Query('connectionId') connectionId: string, ) { return this.links.unlinkedPeople(user.organizationId, connectionId); } @Post('sync/export-people') @Roles('owner', 'admin') exportPeople( @CurrentUser() user: { organizationId: string }, @Body() dto: ExportPeopleToTahesabDto, ) { return this.links.exportPeople(user.organizationId, dto); } @Post('sync/products/search') @Roles('owner', 'admin', 'sales') searchProducts( @CurrentUser() user: { organizationId: string }, @Body() dto: SearchTahesabProductsDto, ) { return this.products.search(user.organizationId, dto); } @Post('sync/products/import') @Roles('owner', 'admin', 'sales') importProduct( @CurrentUser() user: { organizationId: string }, @Body() dto: ImportProductFromTahesabDto, ) { return this.products.importOne(user.organizationId, dto); } @Post('sync/products/import-bulk') @Roles('owner', 'admin') importProductsBulk( @CurrentUser() user: { organizationId: string }, @Body() dto: ImportBulkProductsFromTahesabDto, ) { return this.products.importBulk(user.organizationId, dto); } // —— تنظیمات فاکتور PDF اسناد ته‌حساب —— @Get('invoice-settings') getInvoiceSettings(@CurrentUser() user: { organizationId: string }) { return this.invoiceSettings.getOrCreate(user.organizationId); } @Patch('invoice-settings') @Roles('owner', 'admin') updateInvoiceSettings( @CurrentUser() user: { organizationId: string }, @Body() dto: UpdateTahesabInvoiceSettingsDto, ) { return this.invoiceSettings.update(user.organizationId, dto); } // —— گزارش فروش اسناد (اسنپ‌شات DoListAsnad) —— @Post('reports/asnad/sync') @Roles('owner', 'admin', 'sales') syncAsnadReport( @CurrentUser() user: { organizationId: string }, @Body() dto: SyncAsnadReportDto, ) { return this.asnadReport.startSync( user.organizationId, dto.connectionId, dto.from, dto.to, { personId: dto.personId, groupIds: dto.groupIds }, ); } @Get('reports/asnad/customers') searchAsnadCustomers( @CurrentUser() user: { organizationId: string }, @Query('connectionId') connectionId?: string, @Query('q') q?: string, ) { return this.asnadReport.searchCustomers( user.organizationId, connectionId, q ?? '', ); } @Get('reports/asnad/sync/:jobId') syncAsnadReportStatus( @CurrentUser() user: { organizationId: string }, @Param('jobId') jobId: string, ) { return this.asnadReport.getSyncJob(user.organizationId, jobId); } @Get('reports/asnad/overview') asnadReportOverview( @CurrentUser() user: { organizationId: string }, @Query('connectionId') connectionId?: string, @Query('from') from?: string, @Query('to') to?: string, @Query('groupIds') groupIds?: string | string[], @Query('personId') personId?: string, @Query('jensFelez') jensFelez?: string, ) { if (!from || !to) { throw new BadRequestException('پارامتر from و to الزامی است.'); } return this.asnadReport.overview(user.organizationId, { connectionId, from, to, ...this.parseAsnadFilters(groupIds, personId, undefined, jensFelez), }); } @Get('reports/asnad/summary') asnadReportSummary( @CurrentUser() user: { organizationId: string }, @Query('connectionId') connectionId?: string, @Query('from') from?: string, @Query('to') to?: string, @Query('groupIds') groupIds?: string | string[], @Query('personId') personId?: string, @Query('kinds') kinds?: string | string[], @Query('jensFelez') jensFelez?: string, ) { if (!from || !to) { throw new BadRequestException('پارامتر from و to الزامی است.'); } return this.asnadReport.summary(user.organizationId, { connectionId, from, to, ...this.parseAsnadFilters(groupIds, personId, kinds, jensFelez), }); } @Get('reports/asnad') listAsnadReport( @CurrentUser() user: { organizationId: string }, @Query('connectionId') connectionId?: string, @Query('from') from?: string, @Query('to') to?: string, @Query('groupIds') groupIds?: string | string[], @Query('personId') personId?: string, @Query('kinds') kinds?: string | string[], @Query('jensFelez') jensFelez?: string, @Query('page') page?: string, @Query('pageSize') pageSize?: string, ) { if (!from || !to) { throw new BadRequestException('پارامتر from و to الزامی است.'); } return this.asnadReport.query(user.organizationId, { connectionId, from, to, ...this.parseAsnadFilters(groupIds, personId, kinds, jensFelez), page: page ? Number(page) : undefined, pageSize: pageSize ? Number(pageSize) : undefined, }); } private parseAsnadFilters( groupIds?: string | string[], personId?: string, kinds?: string | string[], jensFelez?: string, ) { const groups = Array.isArray(groupIds) ? groupIds : groupIds ? groupIds .split(',') .map((s) => s.trim()) .filter(Boolean) : undefined; const kindList = Array.isArray(kinds) ? kinds : kinds ? kinds .split(',') .map((s) => s.trim()) .filter(Boolean) : undefined; const jf = jensFelez != null && jensFelez !== '' ? Number(jensFelez) : undefined; return { groupIds: groups, personId, kinds: kindList, jensFelez: jf != null && Number.isFinite(jf) ? jf : undefined, }; } }