Add salesperson commissions on issued invoices and a cheque register.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-11 01:09:59 +03:30
co-authored by Cursor
parent 535c428270
commit 9a991fc1f1
22 changed files with 1238 additions and 11 deletions
+12
View File
@@ -1,8 +1,12 @@
import { Type } from 'class-transformer';
import {
IsEmail,
IsIn,
IsNumber,
IsString,
Max,
MaxLength,
Min,
MinLength,
} from 'class-validator';
@@ -31,3 +35,11 @@ export class UpdateUserRoleDto {
@IsIn(ALL_ROLES)
role!: (typeof ALL_ROLES)[number];
}
export class UpdateUserCommissionDto {
@Type(() => Number)
@IsNumber()
@Min(0)
@Max(100)
commissionPercent!: number;
}
+16 -1
View File
@@ -12,7 +12,7 @@ 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 { InviteUserDto, UpdateUserRoleDto } from './dto';
import { InviteUserDto, UpdateUserCommissionDto, UpdateUserRoleDto } from './dto';
import { UsersService } from './users.service';
@Controller('users')
@@ -34,6 +34,21 @@ export class UsersController {
return this.usersService.invite(user.organizationId, user.role, dto);
}
@Patch(':id/commission')
@Roles('owner', 'admin')
updateCommission(
@CurrentUser() user: { organizationId: string; role: UserRole },
@Param('id') id: string,
@Body() dto: UpdateUserCommissionDto,
) {
return this.usersService.updateCommission(
user.organizationId,
user.role,
id,
dto.commissionPercent,
);
}
@Patch(':id/role')
@Roles('owner', 'admin')
updateRole(
+26
View File
@@ -7,6 +7,7 @@ import {
} from '@nestjs/common';
import type { InviteUserRequest, OrgUserDto, UserRole } from '@mento/shared';
import * as bcrypt from 'bcryptjs';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
@@ -56,6 +57,29 @@ export class UsersService {
return this.toDto(row);
}
async updateCommission(
organizationId: string,
actorRole: UserRole,
targetId: string,
percent: number,
): Promise<OrgUserDto> {
if (actorRole !== 'owner' && actorRole !== 'admin') {
throw new ForbiddenException('فقط مالک یا مدیر می‌تواند پورسانت را تعیین کند');
}
if (percent < 0 || percent > 100) {
throw new BadRequestException('درصد پورسانت باید بین ۰ تا ۱۰۰ باشد');
}
const target = await this.prisma.user.findFirst({
where: { id: targetId, organizationId },
});
if (!target) throw new NotFoundException('کاربر پیدا نشد');
const row = await this.prisma.user.update({
where: { id: target.id },
data: { commissionPercent: new Prisma.Decimal(percent) },
});
return this.toDto(row);
}
async updateRole(
organizationId: string,
actor: { userId: string; role: UserRole },
@@ -100,6 +124,7 @@ export class UsersService {
email: string;
fullName: string;
role: UserRole;
commissionPercent?: { toString(): string } | number;
createdAt: Date;
}): OrgUserDto {
return {
@@ -107,6 +132,7 @@ export class UsersService {
email: row.email,
fullName: row.fullName,
role: row.role,
commissionPercent: Number(row.commissionPercent ?? 0),
createdAt: row.createdAt.toISOString(),
};
}