feat(facturacion): fecha de emisión personalizable para I, E, T
- Frontend: input datetime-local visible solo para tipos I, E, T (no P). Default al día actual a las 12:00. Se resetea al cambiar tipo. - Frontend: validación en handleSubmit: fecha ≤ ahora y ≥ ahora-72h - Backend controller: validación idéntica antes de consumir timbre - Backend servicios: pasa campo 'date' al payload de Facturapi cuando viene 'fechaEmision' en el body - Build y deploy exitosos
This commit is contained in:
@@ -303,6 +303,11 @@ export default function FacturacionPage() {
|
||||
const [serie, setSerie] = useState('');
|
||||
const [folio, setFolio] = useState('');
|
||||
const [condiciones, setCondiciones] = useState('');
|
||||
const [fechaEmision, setFechaEmision] = useState(() => {
|
||||
const d = new Date();
|
||||
d.setHours(12, 0, 0, 0);
|
||||
return d.toISOString().slice(0, 16);
|
||||
});
|
||||
|
||||
// Conceptos
|
||||
const [conceptos, setConceptos] = useState<ConceptoForm[]>([{ ...emptyConcepto }]);
|
||||
@@ -535,6 +540,10 @@ export default function FacturacionPage() {
|
||||
// Resetear conceptos con unidad default según tipo
|
||||
const defaultUnit = tipo === 'T' ? 'H87' : 'E48';
|
||||
setConceptos([{ ...emptyConcepto, unitKey: defaultUnit }]);
|
||||
// Resetear fecha de emisión al día actual (12:00)
|
||||
const d = new Date();
|
||||
d.setHours(12, 0, 0, 0);
|
||||
setFechaEmision(d.toISOString().slice(0, 16));
|
||||
};
|
||||
|
||||
// Unidades de servicio que no aplican para Traslado
|
||||
@@ -651,6 +660,20 @@ export default function FacturacionPage() {
|
||||
if (folio) data.folioNumber = parseInt(folio) || undefined;
|
||||
if (condiciones) data.conditions = condiciones;
|
||||
|
||||
// Validar fecha de emisión para I, E, T
|
||||
if (tipoComprobante !== 'P' && fechaEmision) {
|
||||
const now = new Date();
|
||||
const selected = new Date(fechaEmision);
|
||||
const minDate = new Date(now.getTime() - 72 * 60 * 60 * 1000);
|
||||
if (selected > now) {
|
||||
alert('La fecha de emisión no puede ser a futuro'); return;
|
||||
}
|
||||
if (selected < minDate) {
|
||||
alert('La fecha de emisión no puede ser mayor a 72 horas en el pasado'); return;
|
||||
}
|
||||
data.fechaEmision = selected.toISOString();
|
||||
}
|
||||
|
||||
if (config.needsConceptos) {
|
||||
if (conceptos.some(c => !c.description || !c.productKey)) {
|
||||
alert('Completa todos los conceptos'); return;
|
||||
@@ -1077,6 +1100,17 @@ export default function FacturacionPage() {
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
{tipoComprobante !== 'P' && (
|
||||
<div className="space-y-2">
|
||||
<Label>Fecha de Emisión</Label>
|
||||
<Input
|
||||
type="datetime-local"
|
||||
value={fechaEmision}
|
||||
onChange={e => setFechaEmision(e.target.value)}
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">Máximo 72 horas en el pasado. No se permiten fechas a futuro.</p>
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-2">
|
||||
<Label>Serie (opcional)</Label>
|
||||
<Input value={serie} onChange={e => setSerie(e.target.value.toUpperCase())} placeholder="P" maxLength={10} />
|
||||
|
||||
@@ -69,6 +69,7 @@ export interface InvoiceData {
|
||||
series?: string;
|
||||
folioNumber?: number;
|
||||
conditions?: string;
|
||||
fechaEmision?: string;
|
||||
}
|
||||
|
||||
export interface InvoiceResult {
|
||||
|
||||
Reference in New Issue
Block a user