Files
HoruxDespachosNuevo/apps/api/src/controllers/plan-catalogo.controller.ts

93 lines
3.8 KiB
TypeScript

import type { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import * as planService from '../services/plan-catalogo.service.js';
import { prisma } from '../config/database.js';
import { invalidateDespachoPlanCache } from '../services/plan-catalogo.service.js';
import { canEditPrices } from '../utils/platform-admin.js';
import { AppError } from '../middlewares/error.middleware.js';
export async function getPlans(req: Request, res: Response, next: NextFunction) {
try {
const vertical = req.query.vertical as string | undefined;
const plans = await planService.listPlans(vertical);
return res.json({ data: plans });
} catch (err) { return next(err); }
}
export async function getAddons(req: Request, res: Response, next: NextFunction) {
try {
const vertical = req.query.vertical as string | undefined;
const addons = await planService.listAddons(vertical);
return res.json({ data: addons });
} catch (err) { return next(err); }
}
export async function getPlan(req: Request, res: Response, next: NextFunction) {
try {
const plan = await planService.getPlanByCodename(String(req.params.codename));
if (!plan) return res.status(404).json({ message: 'Plan no encontrado' });
return res.json(plan);
} catch (err) { return next(err); }
}
// ============================================================================
// Catálogo despacho — limits + precios editables por admin global
// ============================================================================
/** GET /api/planes/despacho — devuelve los 6 planes con limits + precios. */
export async function listDespachoCatalogo(_req: Request, res: Response, next: NextFunction) {
try {
const plans = await planService.getAllDespachoPlanLimits();
return res.json({ data: plans });
} catch (err) { return next(err); }
}
const updateSchema = z.object({
nombre: z.string().min(1).max(50).optional(),
monthly: z.number().nullable().optional(),
firstYear: z.number().nullable().optional(),
renewal: z.number().nullable().optional(),
permiteMonthly: z.boolean().optional(),
maxRfcs: z.number().int().optional(),
maxUsers: z.number().int().optional(),
timbresIncluidosMes: z.number().int().nonnegative().optional(),
dbMode: z.enum(['BYO', 'MANAGED']).optional(),
permiteServidorBackup: z.boolean().optional(),
permiteSatIncremental: z.boolean().optional(),
});
/** PATCH /api/planes/despacho/:plan — actualiza precios/limits. Solo admin con canEditPrices. */
export async function updateDespachoCatalogo(req: Request, res: Response, next: NextFunction) {
try {
if (!req.user || !(await canEditPrices(req.user.userId))) {
throw new AppError(403, 'Solo admin global puede editar el catálogo');
}
const plan = String(req.params.plan);
const data = updateSchema.parse(req.body);
const existing = await prisma.despachoPlanPrice.findUnique({ where: { plan } });
if (!existing) throw new AppError(404, `Plan '${plan}' no encontrado`);
const updated = await prisma.despachoPlanPrice.update({ where: { plan }, data });
invalidateDespachoPlanCache();
return res.json({
plan: updated.plan,
nombre: updated.nombre,
monthly: updated.monthly !== null ? Number(updated.monthly) : null,
firstYear: updated.firstYear !== null ? Number(updated.firstYear) : null,
renewal: updated.renewal !== null ? Number(updated.renewal) : null,
permiteMonthly: updated.permiteMonthly,
maxRfcs: updated.maxRfcs,
maxUsers: updated.maxUsers,
timbresIncluidosMes: updated.timbresIncluidosMes,
dbMode: updated.dbMode,
permiteServidorBackup: updated.permiteServidorBackup,
permiteSatIncremental: updated.permiteSatIncremental,
});
} catch (err) {
if (err instanceof z.ZodError) return next(new AppError(400, err.errors[0].message));
return next(err);
}
}