210 lines
6.1 KiB
TypeScript
210 lines
6.1 KiB
TypeScript
import { query, queryOne } from '../../config/database.js';
|
|
import { NotFoundError, ConflictError } from '../../shared/errors/index.js';
|
|
|
|
export interface CustomerGroupMember {
|
|
id: string;
|
|
customer_group_id: string;
|
|
partner_id: string;
|
|
partner_name?: string;
|
|
joined_at: Date;
|
|
}
|
|
|
|
export interface CustomerGroup {
|
|
id: string;
|
|
tenant_id: string;
|
|
name: string;
|
|
description?: string;
|
|
discount_percentage: number;
|
|
members?: CustomerGroupMember[];
|
|
member_count?: number;
|
|
created_at: Date;
|
|
}
|
|
|
|
export interface CreateCustomerGroupDto {
|
|
name: string;
|
|
description?: string;
|
|
discount_percentage?: number;
|
|
}
|
|
|
|
export interface UpdateCustomerGroupDto {
|
|
name?: string;
|
|
description?: string | null;
|
|
discount_percentage?: number;
|
|
}
|
|
|
|
export interface CustomerGroupFilters {
|
|
search?: string;
|
|
page?: number;
|
|
limit?: number;
|
|
}
|
|
|
|
class CustomerGroupsService {
|
|
async findAll(tenantId: string, filters: CustomerGroupFilters = {}): Promise<{ data: CustomerGroup[]; total: number }> {
|
|
const { search, page = 1, limit = 20 } = filters;
|
|
const offset = (page - 1) * limit;
|
|
|
|
let whereClause = 'WHERE cg.tenant_id = $1';
|
|
const params: any[] = [tenantId];
|
|
let paramIndex = 2;
|
|
|
|
if (search) {
|
|
whereClause += ` AND (cg.name ILIKE $${paramIndex} OR cg.description ILIKE $${paramIndex})`;
|
|
params.push(`%${search}%`);
|
|
paramIndex++;
|
|
}
|
|
|
|
const countResult = await queryOne<{ count: string }>(
|
|
`SELECT COUNT(*) as count FROM sales.customer_groups cg ${whereClause}`,
|
|
params
|
|
);
|
|
|
|
params.push(limit, offset);
|
|
const data = await query<CustomerGroup>(
|
|
`SELECT cg.*,
|
|
(SELECT COUNT(*) FROM sales.customer_group_members cgm WHERE cgm.customer_group_id = cg.id) as member_count
|
|
FROM sales.customer_groups cg
|
|
${whereClause}
|
|
ORDER BY cg.name
|
|
LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`,
|
|
params
|
|
);
|
|
|
|
return {
|
|
data,
|
|
total: parseInt(countResult?.count || '0', 10),
|
|
};
|
|
}
|
|
|
|
async findById(id: string, tenantId: string): Promise<CustomerGroup> {
|
|
const group = await queryOne<CustomerGroup>(
|
|
`SELECT cg.*,
|
|
(SELECT COUNT(*) FROM sales.customer_group_members cgm WHERE cgm.customer_group_id = cg.id) as member_count
|
|
FROM sales.customer_groups cg
|
|
WHERE cg.id = $1 AND cg.tenant_id = $2`,
|
|
[id, tenantId]
|
|
);
|
|
|
|
if (!group) {
|
|
throw new NotFoundError('Grupo de clientes no encontrado');
|
|
}
|
|
|
|
// Get members
|
|
const members = await query<CustomerGroupMember>(
|
|
`SELECT cgm.*,
|
|
p.name as partner_name
|
|
FROM sales.customer_group_members cgm
|
|
LEFT JOIN core.partners p ON cgm.partner_id = p.id
|
|
WHERE cgm.customer_group_id = $1
|
|
ORDER BY p.name`,
|
|
[id]
|
|
);
|
|
|
|
group.members = members;
|
|
|
|
return group;
|
|
}
|
|
|
|
async create(dto: CreateCustomerGroupDto, tenantId: string, userId: string): Promise<CustomerGroup> {
|
|
// Check unique name
|
|
const existing = await queryOne(
|
|
`SELECT id FROM sales.customer_groups WHERE tenant_id = $1 AND name = $2`,
|
|
[tenantId, dto.name]
|
|
);
|
|
|
|
if (existing) {
|
|
throw new ConflictError('Ya existe un grupo de clientes con ese nombre');
|
|
}
|
|
|
|
const group = await queryOne<CustomerGroup>(
|
|
`INSERT INTO sales.customer_groups (tenant_id, name, description, discount_percentage, created_by)
|
|
VALUES ($1, $2, $3, $4, $5)
|
|
RETURNING *`,
|
|
[tenantId, dto.name, dto.description, dto.discount_percentage || 0, userId]
|
|
);
|
|
|
|
return group!;
|
|
}
|
|
|
|
async update(id: string, dto: UpdateCustomerGroupDto, tenantId: string): Promise<CustomerGroup> {
|
|
await this.findById(id, tenantId);
|
|
|
|
const updateFields: string[] = [];
|
|
const values: any[] = [];
|
|
let paramIndex = 1;
|
|
|
|
if (dto.name !== undefined) {
|
|
// Check unique name
|
|
const existing = await queryOne(
|
|
`SELECT id FROM sales.customer_groups WHERE tenant_id = $1 AND name = $2 AND id != $3`,
|
|
[tenantId, dto.name, id]
|
|
);
|
|
if (existing) {
|
|
throw new ConflictError('Ya existe un grupo de clientes con ese nombre');
|
|
}
|
|
updateFields.push(`name = $${paramIndex++}`);
|
|
values.push(dto.name);
|
|
}
|
|
if (dto.description !== undefined) {
|
|
updateFields.push(`description = $${paramIndex++}`);
|
|
values.push(dto.description);
|
|
}
|
|
if (dto.discount_percentage !== undefined) {
|
|
updateFields.push(`discount_percentage = $${paramIndex++}`);
|
|
values.push(dto.discount_percentage);
|
|
}
|
|
|
|
values.push(id, tenantId);
|
|
|
|
await query(
|
|
`UPDATE sales.customer_groups SET ${updateFields.join(', ')}
|
|
WHERE id = $${paramIndex++} AND tenant_id = $${paramIndex}`,
|
|
values
|
|
);
|
|
|
|
return this.findById(id, tenantId);
|
|
}
|
|
|
|
async delete(id: string, tenantId: string): Promise<void> {
|
|
const group = await this.findById(id, tenantId);
|
|
|
|
if (group.member_count && group.member_count > 0) {
|
|
throw new ConflictError('No se puede eliminar un grupo con miembros');
|
|
}
|
|
|
|
await query(`DELETE FROM sales.customer_groups WHERE id = $1 AND tenant_id = $2`, [id, tenantId]);
|
|
}
|
|
|
|
async addMember(groupId: string, partnerId: string, tenantId: string): Promise<CustomerGroupMember> {
|
|
await this.findById(groupId, tenantId);
|
|
|
|
// Check if already member
|
|
const existing = await queryOne(
|
|
`SELECT id FROM sales.customer_group_members WHERE customer_group_id = $1 AND partner_id = $2`,
|
|
[groupId, partnerId]
|
|
);
|
|
if (existing) {
|
|
throw new ConflictError('El cliente ya es miembro de este grupo');
|
|
}
|
|
|
|
const member = await queryOne<CustomerGroupMember>(
|
|
`INSERT INTO sales.customer_group_members (customer_group_id, partner_id)
|
|
VALUES ($1, $2)
|
|
RETURNING *`,
|
|
[groupId, partnerId]
|
|
);
|
|
|
|
return member!;
|
|
}
|
|
|
|
async removeMember(groupId: string, memberId: string, tenantId: string): Promise<void> {
|
|
await this.findById(groupId, tenantId);
|
|
|
|
await query(
|
|
`DELETE FROM sales.customer_group_members WHERE id = $1 AND customer_group_id = $2`,
|
|
[memberId, groupId]
|
|
);
|
|
}
|
|
}
|
|
|
|
export const customerGroupsService = new CustomerGroupsService();
|