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:
Horux Dev
2026-05-09 21:56:42 +00:00
parent b00b677c54
commit 9f11a0ba39
70 changed files with 2801 additions and 609 deletions

View File

@@ -17,7 +17,17 @@ export async function getAllTenants() {
createdAt: true,
_count: {
select: { memberships: { where: { active: true } } as any }
}
},
subscriptions: {
orderBy: { createdAt: 'desc' },
take: 1,
select: {
id: true,
amount: true,
currentPeriodEnd: true,
status: true,
},
},
},
orderBy: { nombre: 'asc' }
});
@@ -266,8 +276,10 @@ export async function updateTenant(id: string, data: {
rfc?: string;
plan?: DespachoPlan;
active?: boolean;
amount?: number;
firstPaymentDueAt?: string | null;
}) {
return prisma.tenant.update({
const tenant = await prisma.tenant.update({
where: { id },
data: {
...(data.nombre && { nombre: data.nombre }),
@@ -285,6 +297,29 @@ export async function updateTenant(id: string, data: {
createdAt: true,
}
});
// Actualizar subscription del tenant (plan custom o cualquier plan con amount)
if (data.amount !== undefined || data.firstPaymentDueAt !== undefined) {
const subscription = await prisma.subscription.findFirst({
where: { tenantId: id },
orderBy: { createdAt: 'desc' },
});
if (subscription) {
const updateData: any = {};
if (data.amount !== undefined) {
updateData.amount = data.amount;
}
if (data.firstPaymentDueAt !== undefined) {
updateData.currentPeriodEnd = data.firstPaymentDueAt ? new Date(data.firstPaymentDueAt) : null;
}
await prisma.subscription.update({
where: { id: subscription.id },
data: updateData,
});
}
}
return tenant;
}
export async function getDatosFiscales(id: string) {