feat(tahesab): net sales by factor, group, and commission
Report weight after returns for the selected period, with parent customer groups and separate silver and investment commission. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -38,6 +38,11 @@ class UpdateCustomerGroupDto {
|
||||
@IsOptional()
|
||||
@IsIn([...CUSTOMER_GROUP_COLORS])
|
||||
color?: CustomerGroupColor;
|
||||
|
||||
/** null گروه را اصلی میکند */
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
parentId?: string | null;
|
||||
}
|
||||
|
||||
@Controller('customer-groups')
|
||||
|
||||
@@ -48,7 +48,10 @@ export class CustomerGroupsService {
|
||||
const rows = await this.prisma.customerGroup.findMany({
|
||||
where: { organizationId },
|
||||
orderBy: [{ position: 'asc' }, { name: 'asc' }],
|
||||
include: { _count: { select: { people: true, companies: true } } },
|
||||
include: {
|
||||
_count: { select: { people: true, companies: true } },
|
||||
parent: { select: { name: true } },
|
||||
},
|
||||
});
|
||||
return rows.map((r) => ({
|
||||
id: r.id,
|
||||
@@ -58,6 +61,8 @@ export class CustomerGroupsService {
|
||||
fromTahesab: Boolean(r.tahesabGid),
|
||||
personCount: r._count.people,
|
||||
companyCount: r._count.companies,
|
||||
parentId: r.parentId,
|
||||
parentName: r.parent?.name ?? null,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -88,15 +93,21 @@ export class CustomerGroupsService {
|
||||
async update(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
dto: { name?: string; color?: CustomerGroupColor },
|
||||
dto: { name?: string; color?: CustomerGroupColor; parentId?: string | null },
|
||||
): Promise<CustomerGroupDto> {
|
||||
await this.require(organizationId, id);
|
||||
const parentId =
|
||||
dto.parentId === undefined ? undefined : dto.parentId?.trim() || null;
|
||||
if (parentId !== undefined) {
|
||||
await this.assertParent(organizationId, id, parentId);
|
||||
}
|
||||
try {
|
||||
await this.prisma.customerGroup.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...(dto.name != null ? { name: this.cleanName(dto.name) } : {}),
|
||||
...(dto.color != null ? { color: this.cleanColor(dto.color) } : {}),
|
||||
...(parentId !== undefined ? { parentId } : {}),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
@@ -105,6 +116,32 @@ export class CustomerGroupsService {
|
||||
return (await this.list(organizationId)).find((g) => g.id === id)!;
|
||||
}
|
||||
|
||||
/** فقط یک سطح: گروه اصلی خودش زیرگروه نیست و گروه فرعی زیرگروه ندارد. */
|
||||
private async assertParent(
|
||||
organizationId: string,
|
||||
id: string,
|
||||
parentId: string | null,
|
||||
) {
|
||||
if (parentId == null) return;
|
||||
if (parentId === id) {
|
||||
throw new BadRequestException('گروه نمیتواند زیرمجموعهٔ خودش باشد.');
|
||||
}
|
||||
const parent = await this.prisma.customerGroup.findFirst({
|
||||
where: { id: parentId, organizationId },
|
||||
select: { id: true, parentId: true },
|
||||
});
|
||||
if (!parent) throw new NotFoundException('گروه اصلی پیدا نشد.');
|
||||
if (parent.parentId) {
|
||||
throw new BadRequestException('فقط یک سطح گروه اصلی و فرعی ممکن است.');
|
||||
}
|
||||
const childCount = await this.prisma.customerGroup.count({
|
||||
where: { organizationId, parentId: id },
|
||||
});
|
||||
if (childCount > 0) {
|
||||
throw new BadRequestException('این گروه خودش زیرگروه دارد و نمیتواند فرعی شود.');
|
||||
}
|
||||
}
|
||||
|
||||
/** مشتریها حذف نمیشوند؛ فقط این گروه از رویشان برداشته میشود */
|
||||
async remove(organizationId: string, id: string): Promise<void> {
|
||||
await this.require(organizationId, id);
|
||||
|
||||
Reference in New Issue
Block a user