feat: facturación primer pago, fixes SAT/MP, autocompletado RFCs/conceptos
Backend: - Notificación email al admin cuando llega primer pago aprobado (sin factura auto) - Endpoints GET /pagos-sin-factura y POST /emitir-factura-pago para admin global - Fix vinculación org Facturapi Horux 360 (69f23a5a242e0af47a41fa0d) - Fix webhook MP: validación defensiva de x-signature header - Fix autocompleto RFCs: eliminado filtro por contribuyenteId - Fix autocompleto conceptos: eliminado filtro por contribuyenteId - SAT fixes: anti-bot CSF scraper, request reuse, date range fix, stale job thresholds - SAT sync request reuse across jobs para evitar agotar cuota diaria - Typo fix MP_ACCESS_TOKEN en .env - Trial invitations system backend Frontend: - Nueva página /admin/facturas-pendientes con tabla y emisión manual - Métrica 'Facturas pendientes' en /clientes (clickable) - Navegación onboarding FIEL/CSD corregida - Sidebar themes sincronizados - Fix SAT portal migration scraper (NetIQ) - Trial invitation acceptance pages
This commit is contained in:
@@ -6,16 +6,24 @@ import { isGlobalAdmin } from '../utils/global-admin.js';
|
||||
import { isOwnerSomewhere } from '../utils/memberships.js';
|
||||
|
||||
async function requireGlobalAdmin(req: Request): Promise<void> {
|
||||
if (!(await isGlobalAdmin(req.user!.tenantId, req.user!.role))) {
|
||||
if (!(await isGlobalAdmin(req.user!.tenantId, req.user!.role, req.user!.userId))) {
|
||||
throw new AppError(403, 'Solo el administrador global puede gestionar clientes');
|
||||
}
|
||||
}
|
||||
|
||||
export async function getAllTenants(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
await requireGlobalAdmin(req);
|
||||
const isAdmin = await isGlobalAdmin(req.user!.tenantId, req.user!.role, req.user!.userId);
|
||||
if (!isAdmin) {
|
||||
// Evita 403 en consola del frontend cuando componentes sin-gate hacen polling
|
||||
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
||||
return res.json([]);
|
||||
}
|
||||
|
||||
const tenants = await tenantsService.getAllTenants();
|
||||
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
||||
res.setHeader('Pragma', 'no-cache');
|
||||
res.setHeader('Expires', '0');
|
||||
res.json(tenants);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -24,7 +32,10 @@ export async function getAllTenants(req: Request, res: Response, next: NextFunct
|
||||
|
||||
export async function getTenant(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
await requireGlobalAdmin(req);
|
||||
const isAdmin = await isGlobalAdmin(req.user!.tenantId, req.user!.role, req.user!.userId);
|
||||
if (!isAdmin) {
|
||||
return res.status(404).json({ message: 'Cliente no encontrado' });
|
||||
}
|
||||
|
||||
const tenant = await tenantsService.getTenantById(String(req.params.id));
|
||||
if (!tenant) {
|
||||
@@ -68,13 +79,15 @@ export async function updateTenant(req: Request, res: Response, next: NextFuncti
|
||||
await requireGlobalAdmin(req);
|
||||
|
||||
const id = String(req.params.id);
|
||||
const { nombre, rfc, plan, active } = req.body;
|
||||
const { nombre, rfc, plan, active, amount, firstPaymentDueAt } = req.body;
|
||||
|
||||
const tenant = await tenantsService.updateTenant(id, {
|
||||
nombre,
|
||||
rfc,
|
||||
plan,
|
||||
active,
|
||||
amount,
|
||||
firstPaymentDueAt: firstPaymentDueAt || null,
|
||||
});
|
||||
|
||||
res.json(tenant);
|
||||
|
||||
Reference in New Issue
Block a user