From 2f238e19c29b18e6617fc54f770960f49b0eef61 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 22 Apr 2026 22:47:53 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20Feature:=20Implement=20Global=20?= =?UTF-8?q?Super=20Admin=20access=20&=20bypass=20tenant=20filtering?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modules/companies/company.controller.ts | 16 +++---- .../src/modules/companies/company.service.ts | 33 +++++++++----- .../modules/dashboard/dashboard.controller.ts | 6 +-- .../modules/dashboard/dashboard.service.ts | 45 ++++++++++++------- .../modules/invoices/invoice.controller.ts | 22 ++++----- .../src/modules/invoices/invoice.service.ts | 45 ++++++++++++------- backend/src/modules/users/enums/role.enum.ts | 1 + backend/src/modules/users/user.controller.ts | 12 ++--- backend/src/modules/users/user.service.ts | 32 ++++++++----- 9 files changed, 128 insertions(+), 84 deletions(-) diff --git a/backend/src/modules/companies/company.controller.ts b/backend/src/modules/companies/company.controller.ts index 2ad9a36..f03699d 100644 --- a/backend/src/modules/companies/company.controller.ts +++ b/backend/src/modules/companies/company.controller.ts @@ -27,43 +27,43 @@ export class CompaniesController { constructor(private companiesService: CompaniesService) {} @Post() - @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT) + @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT, UserRole.SUPER_ADMIN) async create(@CurrentUser() user: any, @Body() dto: any) { - return this.companiesService.create(user.tenantId, dto); + return this.companiesService.create(user, dto); } @Get() async findAll(@CurrentUser() user: any) { - return this.companiesService.findAll(user.tenantId); + return this.companiesService.findAll(user); } @Get(':id') async findOne(@CurrentUser() user: any, @Param('id') id: string) { - return this.companiesService.findOne(user.tenantId, id); + return this.companiesService.findOne(user, id); } @Patch(':id') - @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT) + @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT, UserRole.SUPER_ADMIN) async update( @CurrentUser() user: any, @Param('id') id: string, @Body() dto: any, ) { - return this.companiesService.update(user.tenantId, id, dto); + return this.companiesService.update(user, id, dto); } /** * ربط بيانات جو فوترة المشفرة */ @Put(':id/jofotara') - @Roles(UserRole.ADMIN) + @Roles(UserRole.ADMIN, UserRole.SUPER_ADMIN) async setJoFotara( @CurrentUser() user: any, @Param('id') id: string, @Body() dto: { clientId: string; secretKey: string }, ) { return this.companiesService.setJoFotaraCredentials( - user.tenantId, + user, id, dto.clientId, dto.secretKey, diff --git a/backend/src/modules/companies/company.service.ts b/backend/src/modules/companies/company.service.ts index 3088af7..4d60502 100644 --- a/backend/src/modules/companies/company.service.ts +++ b/backend/src/modules/companies/company.service.ts @@ -14,6 +14,7 @@ import { import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Company } from './entities/company.entity'; +import { UserRole } from '../users/enums/role.enum'; import { EncryptionService } from '../../services/encryption/encryption.service'; import { SubscriptionsService } from '../subscriptions/subscription.service'; @@ -30,7 +31,8 @@ export class CompaniesService { /** * إنشاء شركة جديدة مع التحقق من حدود الاشتراك */ - async create(tenantId: string, dto: any): Promise { + async create(user: any, dto: any): Promise { + const tenantId = user.tenantId; // 1. Check subscription limits const canCreate = await this.subscriptionsService.checkCompanyLimit(tenantId); if (!canCreate) { @@ -48,9 +50,12 @@ export class CompaniesService { /** * قائمة الشركات التابعة للمكتب */ - async findAll(tenantId: string): Promise { + async findAll(user: any): Promise { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? { is_active: true } : { tenant_id: user.tenantId, is_active: true }; + return this.companyRepository.find({ - where: { tenant_id: tenantId, is_active: true }, + where: filter, order: { created_at: 'DESC' }, }); } @@ -58,9 +63,12 @@ export class CompaniesService { /** * تفاصيل شركة محددة */ - async findOne(tenantId: string, id: string): Promise { + async findOne(user: any, id: string): Promise { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? { id } : { id, tenant_id: user.tenantId }; + const company = await this.companyRepository.findOne({ - where: { id, tenant_id: tenantId }, + where: filter, }); if (!company) throw new NotFoundException('Company not found'); return company; @@ -69,8 +77,8 @@ export class CompaniesService { /** * تحديث بيانات شركة */ - async update(tenantId: string, id: string, dto: any): Promise { - const company = await this.findOne(tenantId, id); + async update(user: any, id: string, dto: any): Promise { + const company = await this.findOne(user, id); Object.assign(company, dto); return this.companyRepository.save(company); } @@ -79,12 +87,12 @@ export class CompaniesService { * حفظ مفاتيح جو فوترة (مُشفرة) */ async setJoFotaraCredentials( - tenantId: string, + user: any, id: string, clientId: string, secretKey: string, ): Promise { - const company = await this.findOne(tenantId, id); + const company = await this.findOne(user, id); company.jofotara_client_id_encrypted = this.encryptionService.encrypt(clientId); company.jofotara_secret_key_encrypted = this.encryptionService.encrypt(secretKey); @@ -95,9 +103,12 @@ export class CompaniesService { /** * الحصول على المفاتيح (مفكوك تشفيرها) — للاستخدام الداخلي فقط */ - async getDecryptedCredentials(tenantId: string, id: string) { + async getDecryptedCredentials(user: any, id: string) { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? { id } : { id, tenant_id: user.tenantId }; + const company = await this.companyRepository.findOne({ - where: { id, tenant_id: tenantId }, + where: filter, select: ['jofotara_client_id_encrypted', 'jofotara_secret_key_encrypted'], }); diff --git a/backend/src/modules/dashboard/dashboard.controller.ts b/backend/src/modules/dashboard/dashboard.controller.ts index 8ba39a8..b9ea337 100644 --- a/backend/src/modules/dashboard/dashboard.controller.ts +++ b/backend/src/modules/dashboard/dashboard.controller.ts @@ -10,16 +10,16 @@ export class DashboardController { @Get('stats') async getStats(@CurrentUser() user: any) { - return this.dashboardService.getStats(user.tenantId); + return this.dashboardService.getStats(user); } @Get('multi-entity') async getMultiEntityStats(@CurrentUser() user: any) { - return this.dashboardService.getMultiEntityStats(user.tenantId); + return this.dashboardService.getMultiEntityStats(user); } @Get('risk-invoices') async getRiskInvoices(@CurrentUser() user: any) { - return this.dashboardService.getRiskInvoices(user.tenantId); + return this.dashboardService.getRiskInvoices(user); } } diff --git a/backend/src/modules/dashboard/dashboard.service.ts b/backend/src/modules/dashboard/dashboard.service.ts index 9f523c0..d97de61 100644 --- a/backend/src/modules/dashboard/dashboard.service.ts +++ b/backend/src/modules/dashboard/dashboard.service.ts @@ -3,6 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository, Between } from 'typeorm'; import { Invoice, InvoiceStatus } from '../invoices/entities/invoice.entity'; import { Company } from '../companies/entities/company.entity'; +import { UserRole } from '../users/enums/role.enum'; @Injectable() export class DashboardService { @@ -13,23 +14,29 @@ export class DashboardService { private companyRepository: Repository, ) {} - async getStats(tenantId: string) { + async getStats(user: any) { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? {} : { tenant_id: user.tenantId }; + const totalInvoices = await this.invoiceRepository.count({ - where: { tenant_id: tenantId }, + where: filter, }); const approvedInvoices = await this.invoiceRepository.count({ - where: { tenant_id: tenantId, status: InvoiceStatus.APPROVED }, + where: { ...filter, status: InvoiceStatus.APPROVED }, }); // Using QueryBuilder for better control - const statuses = await this.invoiceRepository + const query = this.invoiceRepository .createQueryBuilder('invoice') .select('status') - .addSelect('COUNT(*)', 'count') - .where('invoice.tenant_id = :tenantId', { tenantId }) - .groupBy('status') - .getRawMany(); + .addSelect('COUNT(*)', 'count'); + + if (!isSuperAdmin) { + query.where('invoice.tenant_id = :tenantId', { tenantId: user.tenantId }); + } + + const statuses = await query.groupBy('status').getRawMany(); const statusMap = statuses.reduce((acc, curr) => { acc[curr.status] = parseInt(curr.count); @@ -37,12 +44,12 @@ export class DashboardService { }, {}); const companiesCount = await this.companyRepository.count({ - where: { tenant_id: tenantId }, + where: filter, }); // Calculate total tax (mock logic for now, should sum up tax fields) const invoices = await this.invoiceRepository.find({ - where: { tenant_id: tenantId, status: InvoiceStatus.APPROVED }, + where: { ...filter, status: InvoiceStatus.APPROVED }, select: ['tax_amount'], }); @@ -50,7 +57,7 @@ export class DashboardService { // Get recent activities (last 5 invoices) const recentInvoices = await this.invoiceRepository.find({ - where: { tenant_id: tenantId }, + where: filter, order: { created_at: 'DESC' }, take: 5, relations: ['company'], @@ -81,9 +88,12 @@ export class DashboardService { * جلب إحصائيات مجمعة لكل الشركات (Elite Accountant View) * Get summarized stats for all companies under a tenant */ - async getMultiEntityStats(tenantId: string) { + async getMultiEntityStats(user: any) { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? {} : { tenant_id: user.tenantId }; + const companies = await this.companyRepository.find({ - where: { tenant_id: tenantId }, + where: filter, }); const companyStats = await Promise.all( @@ -133,11 +143,14 @@ export class DashboardService { /** * جلب الفواتير التي بها مخاطر (فشل التحقق أو مرفوضة) عبر كافة الشركات */ - async getRiskInvoices(tenantId: string) { + async getRiskInvoices(user: any) { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? {} : { tenant_id: user.tenantId }; + return this.invoiceRepository.find({ where: [ - { tenant_id: tenantId, status: InvoiceStatus.VALIDATION_FAILED }, - { tenant_id: tenantId, status: InvoiceStatus.REJECTED }, + { ...filter, status: InvoiceStatus.VALIDATION_FAILED }, + { ...filter, status: InvoiceStatus.REJECTED }, ], relations: ['company'], order: { updated_at: 'DESC' }, diff --git a/backend/src/modules/invoices/invoice.controller.ts b/backend/src/modules/invoices/invoice.controller.ts index 16d4dcd..3034f3d 100644 --- a/backend/src/modules/invoices/invoice.controller.ts +++ b/backend/src/modules/invoices/invoice.controller.ts @@ -40,7 +40,7 @@ export class InvoicesController { */ @Get() async findAllByTenant(@CurrentUser() user: any) { - return this.invoicesService.findAllByTenant(user.tenantId); + return this.invoicesService.findAllByTenant(user); } /** @@ -48,14 +48,14 @@ export class InvoicesController { * Upload an invoice file for a specific company */ @Post('upload/:companyId') - @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT) + @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT, UserRole.SUPER_ADMIN) @UseInterceptors(FileInterceptor('file')) async upload( @CurrentUser() user: any, @Param('companyId', ParseUUIDPipe) companyId: string, @UploadedFile() file: Express.Multer.File, ) { - return this.invoicesService.upload(user.tenantId, companyId, file); + return this.invoicesService.upload(user, companyId, file); } /** @@ -67,7 +67,7 @@ export class InvoicesController { @CurrentUser() user: any, @Param('companyId', ParseUUIDPipe) companyId: string, ) { - return this.invoicesService.findAll(user.tenantId, companyId); + return this.invoicesService.findAll(user, companyId); } /** @@ -79,7 +79,7 @@ export class InvoicesController { @CurrentUser() user: any, @Param('id', ParseUUIDPipe) id: string, ) { - return this.invoicesService.findOne(user.tenantId, id); + return this.invoicesService.findOne(user, id); } /** @@ -90,9 +90,9 @@ export class InvoicesController { * Submit an invoice to the official JoFotara portal */ @Post(':id/submit') - @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT) + @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT, UserRole.SUPER_ADMIN) async submit(@CurrentUser() user: any, @Param('id', ParseUUIDPipe) id: string) { - return this.invoicesService.submitToJoFotara(user.tenantId, id); + return this.invoicesService.submitToJoFotara(user, id); } /** @@ -100,9 +100,9 @@ export class InvoicesController { * Permanently delete an invoice */ @Post(':id/delete') // Using POST for delete to match frontend request style or use standard DELETE - @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT) + @Roles(UserRole.ADMIN, UserRole.ACCOUNTANT, UserRole.SUPER_ADMIN) async remove(@CurrentUser() user: any, @Param('id', ParseUUIDPipe) id: string) { - return this.invoicesService.remove(user.tenantId, id); + return this.invoicesService.remove(user, id); } /** @@ -115,11 +115,11 @@ export class InvoicesController { @Param('id', ParseUUIDPipe) id: string, @Res({ passthrough: true }) res: Response, ) { - const streamableFile = await this.invoicesService.getFile(user.tenantId, id); + const streamableFile = await this.invoicesService.getFile(user, id); // We need to determine the content type to ensure it opens inline in the browser (iframe) // We can fetch the invoice details first to get the extension. - const invoice = await this.invoicesService.findOne(user.tenantId, id); + const invoice = await this.invoicesService.findOne(user, id); let mimeType = 'application/pdf'; // Default fallback if (invoice.original_file_path) { const ext = invoice.original_file_path.split('.').pop()?.toLowerCase(); diff --git a/backend/src/modules/invoices/invoice.service.ts b/backend/src/modules/invoices/invoice.service.ts index 5c280c3..dcbd87b 100644 --- a/backend/src/modules/invoices/invoice.service.ts +++ b/backend/src/modules/invoices/invoice.service.ts @@ -25,6 +25,7 @@ import * as path from 'path'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; import { Invoice, InvoiceStatus } from './entities/invoice.entity'; +import { UserRole } from '../users/enums/role.enum'; import { LocalStorageService } from '../../services/storage/local-storage.service'; import { SubscriptionsService } from '../subscriptions/subscription.service'; import { UBLGeneratorService } from './ubl-generator.service'; @@ -52,10 +53,11 @@ export class InvoicesService { * Upload a new invoice and initiate extraction/validation */ async upload( - tenantId: string, + user: any, companyId: string, file: Express.Multer.File, ): Promise { + const tenantId = user.tenantId; const canUpload = await this.subscriptionsService.checkInvoiceLimit(tenantId); if (!canUpload) { throw new ForbiddenException('Monthly invoice limit reached for your current plan'); @@ -95,9 +97,12 @@ export class InvoicesService { * قائمة الفواتير لشركة محددة * Find all invoices for a specific company */ - async findAll(tenantId: string, companyId: string): Promise { + async findAll(user: any, companyId: string): Promise { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? { company_id: companyId } : { tenant_id: user.tenantId, company_id: companyId }; + return this.invoiceRepository.find({ - where: { tenant_id: tenantId, company_id: companyId }, + where: filter, order: { created_at: 'DESC' }, }); } @@ -106,9 +111,12 @@ export class InvoicesService { * قائمة جميع الفواتير للمكتب (المستأجر) * Find all invoices for the entire tenant (accounting office) */ - async findAllByTenant(tenantId: string): Promise { + async findAllByTenant(user: any): Promise { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? {} : { tenant_id: user.tenantId }; + return this.invoiceRepository.find({ - where: { tenant_id: tenantId }, + where: filter, relations: ['company'], order: { created_at: 'DESC' }, }); @@ -118,9 +126,12 @@ export class InvoicesService { * تفاصيل فاتورة محددة مع بنودها * Get detailed information for a specific invoice including its lines */ - async findOne(tenantId: string, id: string): Promise { + async findOne(user: any, id: string): Promise { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? { id } : { id, tenant_id: user.tenantId }; + const invoice = await this.invoiceRepository.findOne({ - where: { id, tenant_id: tenantId }, + where: filter, relations: ['lines'], }); if (!invoice) throw new NotFoundException('Invoice not found'); @@ -131,8 +142,8 @@ export class InvoicesService { * تحديث بيانات الفاتورة يدوياً * Manually update invoice data (only if not already submitted) */ - async update(tenantId: string, id: string, updateData: any): Promise { - const invoice = await this.findOne(tenantId, id); + async update(user: any, id: string, updateData: any): Promise { + const invoice = await this.findOne(user, id); if (invoice.status === InvoiceStatus.APPROVED || invoice.status === InvoiceStatus.SUBMITTING) { throw new ForbiddenException('Cannot edit already approved or submitted invoice'); @@ -146,15 +157,15 @@ export class InvoicesService { * إرسال الفاتورة إلى بوابة جو فوترة الحكومية (ISTD) * Submit the validated invoice to the official JoFotara portal */ - async submitToJoFotara(tenantId: string, id: string): Promise { - const invoice = await this.findOne(tenantId, id); + async submitToJoFotara(user: any, id: string): Promise { + const invoice = await this.findOne(user, id); if (invoice.status !== InvoiceStatus.VALIDATED && invoice.status !== InvoiceStatus.VALIDATION_FAILED && invoice.status !== InvoiceStatus.EXTRACTED) { throw new ForbiddenException('Invoice must be validated or extracted before submission'); } - const company = await this.companiesService.findOne(tenantId, invoice.company_id); - const credentials = await this.companiesService.getDecryptedCredentials(tenantId, invoice.company_id); + const company = await this.companiesService.findOne(user, invoice.company_id); + const credentials = await this.companiesService.getDecryptedCredentials(user, invoice.company_id); const xml = this.ublGenerator.generateXML(invoice, company); @@ -182,8 +193,8 @@ export class InvoicesService { * حذف الفاتورة والملف التابع لها نهائياً * Permanently delete an invoice and its associated file from storage */ - async remove(tenantId: string, id: string): Promise { - const invoice = await this.findOne(tenantId, id); + async remove(user: any, id: string): Promise { + const invoice = await this.findOne(user, id); // 1. Delete file if exists if (invoice.original_file_path) { @@ -198,8 +209,8 @@ export class InvoicesService { * الحصول على الملف كـ Stream لتحميله أو عرضه * Get the original invoice file as a streamable file for download/view */ - async getFile(tenantId: string, id: string): Promise { - const invoice = await this.findOne(tenantId, id); + async getFile(user: any, id: string): Promise { + const invoice = await this.findOne(user, id); if (!invoice.original_file_path) { throw new NotFoundException('Invoice file not found'); } diff --git a/backend/src/modules/users/enums/role.enum.ts b/backend/src/modules/users/enums/role.enum.ts index 1823554..d4d0baa 100644 --- a/backend/src/modules/users/enums/role.enum.ts +++ b/backend/src/modules/users/enums/role.enum.ts @@ -9,4 +9,5 @@ export enum UserRole { ACCOUNTANT = 'accountant', // محاسب (قادر على رفع الفواتير وإدارتها لكل الشركات) CLIENT = 'client', // عميل (قادر على رفع وإدارة فواتير شركته فقط) VIEWER = 'viewer', // مشاهد (قادر على الاطلاع فقط) + SUPER_ADMIN = 'super_admin', // مدير النظام العام (قادر على رؤية كل شيء) } diff --git a/backend/src/modules/users/user.controller.ts b/backend/src/modules/users/user.controller.ts index cdf5072..acef331 100644 --- a/backend/src/modules/users/user.controller.ts +++ b/backend/src/modules/users/user.controller.ts @@ -26,25 +26,25 @@ export class UsersController { constructor(private usersService: UsersService) {} @Post() - @Roles(UserRole.ADMIN) + @Roles(UserRole.ADMIN, UserRole.SUPER_ADMIN) async create(@CurrentUser() user: any, @Body() dto: any) { - return this.usersService.create(user.tenantId, dto); + return this.usersService.create(user, dto); } @Get() async findAll(@CurrentUser() user: any) { - return this.usersService.findAll(user.tenantId); + return this.usersService.findAll(user); } @Get(':id') async findOne(@CurrentUser() user: any, @Param('id') id: string) { - return this.usersService.findOne(user.tenantId, id); + return this.usersService.findOne(user, id); } @Delete(':id') - @Roles(UserRole.ADMIN) + @Roles(UserRole.ADMIN, UserRole.SUPER_ADMIN) async remove(@CurrentUser() user: any, @Param('id') id: string) { - return this.usersService.remove(user.tenantId, id, user.id); + return this.usersService.remove(user, id, user.id); } @Post('profile') diff --git a/backend/src/modules/users/user.service.ts b/backend/src/modules/users/user.service.ts index c4f0453..52a16dc 100644 --- a/backend/src/modules/users/user.service.ts +++ b/backend/src/modules/users/user.service.ts @@ -9,6 +9,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import * as bcrypt from 'bcrypt'; import { User } from './entities/user.entity'; +import { UserRole } from './enums/role.enum'; @Injectable() export class UsersService { @@ -20,7 +21,8 @@ export class UsersService { /** * إضافة مستخدم لمكتب محاسبة */ - async create(tenantId: string, dto: any): Promise { + async create(user: any, dto: any): Promise { + const tenantId = user.tenantId; const normalizedEmail = dto.email?.trim().toLowerCase(); const existing = await this.userRepository.findOne({ where: { email: normalizedEmail, tenant_id: tenantId }, @@ -32,22 +34,25 @@ export class UsersService { const passwordHash = await bcrypt.hash(dto.password, 12); - const user = this.userRepository.create({ + const newUser = this.userRepository.create({ ...dto, email: normalizedEmail, password_hash: passwordHash, tenant_id: tenantId, } as Partial); - return this.userRepository.save(user); + return this.userRepository.save(newUser); } /** * قائمة مستخدمي المكتب */ - async findAll(tenantId: string): Promise { + async findAll(user: any): Promise { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? { is_active: true } : { tenant_id: user.tenantId, is_active: true }; + return this.userRepository.find({ - where: { tenant_id: tenantId, is_active: true }, + where: filter, order: { created_at: 'ASC' }, }); } @@ -55,22 +60,25 @@ export class UsersService { /** * تفاصيل مستخدم */ - async findOne(tenantId: string, id: string): Promise { - const user = await this.userRepository.findOne({ - where: { id, tenant_id: tenantId }, + async findOne(user: any, id: string): Promise { + const isSuperAdmin = user.role === UserRole.SUPER_ADMIN; + const filter = isSuperAdmin ? { id } : { id, tenant_id: user.tenantId }; + + const foundUser = await this.userRepository.findOne({ + where: filter, }); - if (!user) throw new NotFoundException('User not found'); - return user; + if (!foundUser) throw new NotFoundException('User not found'); + return foundUser; } /** * تعطيل مستخدم */ - async remove(tenantId: string, id: string, currentUserId: string): Promise { + async remove(user: any, id: string, currentUserId: string): Promise { if (id === currentUserId) { throw new ConflictException('لا يمكنك تعطيل حسابك الشخصي'); } - const user = await this.findOne(tenantId, id); + await this.findOne(user, id); await this.userRepository.update(id, { is_active: false }); }