import React, { useState, useEffect } from 'react'; import { Modal, Box, Typography, TextField, Button, CircularProgress, Alert, FormControl, InputLabel, Select, MenuItem } from '@mui/material'; import type { CrearCierreDto } from '../../../models/dtos/Contables/CrearCierreDto'; import type { CierreCuentaCorrienteDto } from '../../../models/dtos/Contables/CierreCuentaCorrienteDto'; import type { DistribuidorDropdownDto } from '../../../models/dtos/Distribucion/DistribuidorDropdownDto'; import type { EmpresaDropdownDto } from '../../../models/dtos/Distribucion/EmpresaDropdownDto'; import distribuidorService from '../../../services/Distribucion/distribuidorService'; import empresaService from '../../../services/Distribucion/empresaService'; const modalStyle = { position: 'absolute' as const, top: '50%', left: '50%', transform: 'translate(-50%, -50%)', width: { xs: '90%', sm: 600 }, bgcolor: 'background.paper', border: '2px solid #000', boxShadow: 24, p: 4, maxHeight: '90vh', overflowY: 'auto' }; interface NuevoCierreModalProps { open: boolean; onClose: () => void; onSubmit: (data: CrearCierreDto) => Promise; initialIdDistribuidor?: number | null; initialIdEmpresa?: number | null; errorMessage?: string | null; clearErrorMessage: () => void; } const todayIso = () => new Date().toISOString().split('T')[0]; const NuevoCierreModal: React.FC = ({ open, onClose, onSubmit, initialIdDistribuidor, initialIdEmpresa, errorMessage, clearErrorMessage }) => { const [idDistribuidor, setIdDistribuidor] = useState(''); const [idEmpresa, setIdEmpresa] = useState(''); const [fechaCorte, setFechaCorte] = useState(todayIso()); const [justificacion, setJustificacion] = useState(''); const [distribuidores, setDistribuidores] = useState([]); const [empresas, setEmpresas] = useState([]); const [loading, setLoading] = useState(false); const [loadingDropdowns, setLoadingDropdowns] = useState(false); const [localErrors, setLocalErrors] = useState<{ [key: string]: string | null }>({}); useEffect(() => { const fetchDropdownData = async () => { setLoadingDropdowns(true); try { const [distData, empData] = await Promise.all([ distribuidorService.getAllDistribuidoresDropdown(), empresaService.getEmpresasDropdown() ]); setDistribuidores(distData); setEmpresas(empData); } catch (err) { console.error('Error al cargar dropdowns', err); setLocalErrors(prev => ({ ...prev, dropdowns: 'Error al cargar distribuidores o empresas.' })); } finally { setLoadingDropdowns(false); } }; if (open) { fetchDropdownData(); setIdDistribuidor(initialIdDistribuidor ?? ''); setIdEmpresa(initialIdEmpresa ?? ''); setFechaCorte(todayIso()); setJustificacion(''); setLocalErrors({}); clearErrorMessage(); } }, [open, initialIdDistribuidor, initialIdEmpresa, clearErrorMessage]); const validate = (): boolean => { const errors: { [key: string]: string | null } = {}; if (!idDistribuidor) errors.idDistribuidor = 'Seleccione un distribuidor.'; if (!idEmpresa) errors.idEmpresa = 'Seleccione una empresa.'; if (!fechaCorte) { errors.fechaCorte = 'La fecha de corte es obligatoria.'; } else if (new Date(fechaCorte) > new Date(todayIso())) { errors.fechaCorte = 'La fecha de corte no puede ser futura.'; } if (justificacion.length > 500) { errors.justificacion = 'La justificación no puede superar los 500 caracteres.'; } setLocalErrors(errors); return Object.keys(errors).length === 0; }; const handleInputChange = (fieldName: string) => { if (localErrors[fieldName]) setLocalErrors(prev => ({ ...prev, [fieldName]: null })); if (errorMessage) clearErrorMessage(); }; const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); clearErrorMessage(); if (!validate()) return; const distSeleccionado = distribuidores.find(d => d.idDistribuidor === Number(idDistribuidor)); const empSeleccionada = empresas.find(e => e.idEmpresa === Number(idEmpresa)); const confirmMsg = `Estás por cerrar el período hasta ${fechaCorte} para "${distSeleccionado?.nombre ?? ''}" en "${empSeleccionada?.nombre ?? ''}".\n\n` + `Después de cerrar no se podrán registrar movimientos, pagos, notas ni ajustes con fecha menor o igual a esta. ¿Continuar?`; if (!window.confirm(confirmMsg)) return; setLoading(true); try { const dto: CrearCierreDto = { idDistribuidor: Number(idDistribuidor), idEmpresa: Number(idEmpresa), fechaCorte, justificacion: justificacion.trim() || null }; await onSubmit(dto); onClose(); } catch (err) { console.error('Error al crear cierre:', err); } finally { setLoading(false); } }; return ( Nuevo Cierre de Cuenta Corriente Distribuidor {localErrors.idDistribuidor && {localErrors.idDistribuidor}} Empresa {localErrors.idEmpresa && {localErrors.idEmpresa}} { setFechaCorte(e.target.value); handleInputChange('fechaCorte'); }} margin="dense" fullWidth error={!!localErrors.fechaCorte} helperText={localErrors.fechaCorte || 'No puede ser una fecha futura.'} disabled={loading} InputLabelProps={{ shrink: true }} inputProps={{ max: todayIso() }} /> La fecha de corte es inclusive: los movimientos con fecha igual a la seleccionada se incluyen en el cálculo del saldo de cierre. { setJustificacion(e.target.value); handleInputChange('justificacion'); }} margin="dense" fullWidth multiline rows={3} disabled={loading} error={!!localErrors.justificacion} helperText={localErrors.justificacion || `${justificacion.length}/500 caracteres`} inputProps={{ maxLength: 500 }} /> Una vez creado el cierre, no podrán registrarse, modificarse ni eliminarse pagos, notas, ajustes o movimientos cuya fecha de operación sea menor o igual a la fecha de corte. {errorMessage && {errorMessage}} {localErrors.dropdowns && {localErrors.dropdowns}} ); }; export default NuevoCierreModal;