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:
@@ -14,7 +14,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '
|
||||
import Link from 'next/link';
|
||||
import { cn } from '@horux/shared-ui';
|
||||
import { isDespachoTenant } from '@horux/shared';
|
||||
import type { Role } from '@horux/shared';
|
||||
import type { Role, UserInvite } from '@horux/shared';
|
||||
|
||||
// ── Horux360 legacy roles ─────────────────────────────────────────────────────
|
||||
const legacyRoleLabels: Record<string, { label: string; icon: React.ElementType; color: string }> = {
|
||||
@@ -83,10 +83,10 @@ export default function UsuariosPage() {
|
||||
const isAdmin = currentUser?.role === 'owner' || currentUser?.role === 'cfo';
|
||||
|
||||
const [showInvite, setShowInvite] = useState(false);
|
||||
const [inviteForm, setInviteForm] = useState<{ email: string; nombre: string; role: Role; supervisorUserId?: string }>({
|
||||
const [inviteForm, setInviteForm] = useState<{ email: string; nombre: string; role: UserInvite['role']; supervisorUserId?: string }>({
|
||||
email: '',
|
||||
nombre: '',
|
||||
role: defaultInviteRole as Role,
|
||||
role: defaultInviteRole as UserInvite['role'],
|
||||
});
|
||||
const [selectedRfcIds, setSelectedRfcIds] = useState<string[]>([]);
|
||||
|
||||
@@ -183,7 +183,7 @@ export default function UsuariosPage() {
|
||||
);
|
||||
}
|
||||
setShowInvite(false);
|
||||
setInviteForm({ email: '', nombre: '', role: defaultInviteRole as Role, supervisorUserId: undefined });
|
||||
setInviteForm({ email: '', nombre: '', role: defaultInviteRole as UserInvite['role'], supervisorUserId: undefined });
|
||||
setSelectedRfcIds([]);
|
||||
} catch (error: any) {
|
||||
alert(error.response?.data?.message || 'Error al invitar usuario');
|
||||
@@ -211,11 +211,11 @@ export default function UsuariosPage() {
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{isAdmin && isDespacho && (
|
||||
<Link href="/carteras">
|
||||
<Button variant="outline" className="flex items-center gap-2">
|
||||
<Button variant="outline" className="flex items-center gap-2" asChild>
|
||||
<Link href="/carteras">
|
||||
<FolderOpen className="h-4 w-4" /> Gestionar Carteras
|
||||
</Button>
|
||||
</Link>
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<Button onClick={() => setShowInvite(true)}>
|
||||
@@ -263,13 +263,13 @@ export default function UsuariosPage() {
|
||||
<Label htmlFor="role">Rol</Label>
|
||||
<Select
|
||||
value={inviteForm.role}
|
||||
onValueChange={(v) => { setInviteForm({ ...inviteForm, role: v as Role, supervisorUserId: undefined }); if (v !== 'cliente') setSelectedRfcIds([]); }}
|
||||
onValueChange={(v) => { setInviteForm({ ...inviteForm, role: v as UserInvite['role'], supervisorUserId: undefined }); if (v !== 'cliente') setSelectedRfcIds([]); }}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{inviteRoles.map(r => (
|
||||
{inviteRoles.map((r: { value: string; label: string; description?: string }) => (
|
||||
<SelectItem key={r.value} value={r.value}>
|
||||
<div className="flex flex-col">
|
||||
<span>{r.label}</span>
|
||||
|
||||
Reference in New Issue
Block a user