Ir al contenido

Gestionar Hitos

Los hitos dividen el proyecto en objetivos intermedios con presupuesto y fechas específicas.

  • Componente: MilestoneForm.tsx
  • Acceso: Desde la vista de detalle del proyecto
  • Modal: Dialog para crear/editar hitos
  • Nombre (requerido): Identificador del hito
  • Descripción: Detalles del objetivo
  • Fecha de Vencimiento: Fecha objetivo de completación
  • Estado: Pendiente, En Progreso, Completado, Cancelado
  • Presupuesto Asignado: Monto específico para este hito

Integrado mediante el componente MilestoneInvoiceConfig.tsx:

  • Auto-facturación: Toggle para activar facturación automática
  • Monto a Facturar: Cantidad que se facturará al completar el hito
  • Descripción de Factura: Texto que aparecerá en la factura generada
  • Cálculo de IVA: Automático al 21%
  • Preview de Total: Muestra subtotal, IVA y total
  1. Usuario marca hito como “Completado”
  2. Si auto_invoice está activado:
    • Se genera automáticamente una factura
    • Monto = invoice_amount
    • Descripción = invoice_description
    • IVA se aplica según configuración (por defecto 21%)
  3. Se asocia invoice_id al hito
  4. Se registra invoiced_at con timestamp
  5. Usuario puede editar la factura antes de enviarla
type MilestoneStatus =
| 'pending' // Aún no iniciado
| 'in_progress' // En ejecución
| 'completed' // Finalizado (dispara facturación si está activada)
| 'cancelled'; // Cancelado

Cada hito mantiene:

  • budget_allocated: Presupuesto asignado al hito
  • budget_used: Gasto real acumulado
  • Cálculo de porcentaje usado
  • Alertas si excede el presupuesto
Interfaz de MilestoneInvoiceConfig
MilestoneInvoiceConfig.tsx
interface MilestoneInvoiceConfigProps {
milestone: ProjectMilestone;
onUpdate: (data: Partial<ProjectMilestone>) => void;
disabled?: boolean;
}
// Campos de configuración
const handleAutoInvoiceChange = (checked: boolean) => {
setAutoInvoice(checked);
onUpdate({ auto_invoice: checked });
};
// Vista de resumen
{autoInvoice && parseFloat(invoiceAmount) > 0 && (
<div className="border-t pt-4">
<div className="flex items-center justify-between text-sm">
<span className="text-muted-foreground">Subtotal:</span>
<span className="font-medium">{formatCurrency(parseFloat(invoiceAmount))}</span>
</div>
<div className="flex items-center justify-between text-sm mt-1">
<span className="text-muted-foreground">IVA (21%):</span>
<span className="font-medium">{formatCurrency(parseFloat(invoiceAmount) * 0.21)}</span>
</div>
<div className="flex items-center justify-between text-base font-bold mt-2 pt-2 border-t">
<span>Total:</span>
<span>{formatCurrency(parseFloat(invoiceAmount) * 1.21)}</span>
</div>
</div>
)}