diff --git a/AdminFinanceRCA/ComboMessageBox.cs b/AdminFinanceRCA/ComboMessageBox.cs new file mode 100644 index 0000000..b1c94a9 --- /dev/null +++ b/AdminFinanceRCA/ComboMessageBox.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Layout; + +namespace AdminFinanceRCA; + +public class ComboMessageBox : Window +{ + private ComboBox _comboBox; + private Button _okButton; + private Button _cancelButton; + private object _result; + + public ComboMessageBox(IEnumerable items, string title = "Seleccione una opción") + { + Title = title; + Width = 300; + Height = 150; + + var stackPanel = new StackPanel { Margin = new Thickness(10) }; + + _comboBox = new ComboBox + { + ItemsSource = items, + SelectedIndex = 0 + }; + stackPanel.Children.Add(_comboBox); + + var buttonPanel = new StackPanel { Orientation = Orientation.Horizontal, HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right }; + _okButton = new Button { Content = "Aceptar", Margin = new Thickness(5) }; + _cancelButton = new Button { Content = "Cancelar", Margin = new Thickness(5) }; + + _okButton.Click += (_, __) => + { + _result = _comboBox.SelectedItem;//?.ToString(); + Close(_result); + }; + + _cancelButton.Click += (_, __) => + { + Close(null); + }; + + buttonPanel.Children.Add(_okButton); + buttonPanel.Children.Add(_cancelButton); + + stackPanel.Children.Add(buttonPanel); + Content = stackPanel; + } + + public static async Task ShowDialog(Window parent, IEnumerable items, string title = "Seleccione") + { + var msgBox = new ComboMessageBox(items, title); + return await msgBox.ShowDialog(parent); + } +} \ No newline at end of file diff --git a/AdminFinanceRCA/Models/Entidades.cs b/AdminFinanceRCA/Models/Entidades.cs index b6e3eeb..f5e9934 100644 --- a/AdminFinanceRCA/Models/Entidades.cs +++ b/AdminFinanceRCA/Models/Entidades.cs @@ -124,3 +124,10 @@ public class MovimientoCompleto : ObservableObject public IBrush ForegroundColor => TipoMovNombre == "Egreso" ? Brushes.Red : Brushes.Black; } + + +public class MesItem +{ + public int Value { get; set; } // Número del mes (1-12) + public string Nombre { get; set; } // Nombre del mes +} diff --git a/AdminFinanceRCA/Repository.cs b/AdminFinanceRCA/Repository.cs index eebc4c6..51a57c8 100644 --- a/AdminFinanceRCA/Repository.cs +++ b/AdminFinanceRCA/Repository.cs @@ -122,6 +122,27 @@ public class FinanzasRepository } } + + public async Task> GetMovimientoByGrupoTrabajoAsync(int idGrupoTrabajo) + { + using (var connection = GetConnection()) + { + var sql = @"SELECT + m.ID, m.Monto, m.FechaMovimiento, m.FechaRegistro, m.Descripcion, + tm.ID as TipoMovID, tm.Nombre as TipoMovNombre, + dt.Id as DeptoTrabajoID, dt.Nombre as DeptoTrabajoNombre, + c.ID as ConceptoID, c.Nombre as ConceptoNombre, + m.Nota as Nota + FROM Movimientos m + INNER JOIN TipoMovimiento tm ON m.TipoMov = tm.ID + INNER JOIN DepartTrabajo dt ON m.DeptoTrabajo = dt.Id + INNER JOIN Concepto c ON m.Concepto = c.ID + WHERE DeptoTrabajo = @idGrupoTrabajo + ORDER BY m.FechaMovimiento DESC"; + return await connection.QueryAsync(sql, new { idGrupoTrabajo = idGrupoTrabajo }); + } + } + public async Task GetMovimientoByIdAsync(int id) { using (var connection = GetConnection()) @@ -131,6 +152,64 @@ public class FinanzasRepository } } + public async Task> GetMovimientosFiltradosAsync( + int? deptoTrabajo = null, + int? mes = null, + int? anio = null, + int? tipoMov = null, + int? concepto = null) + { + using (var connection = GetConnection()) + { + var sql = @"SELECT + m.ID, m.Monto, m.FechaMovimiento, m.FechaRegistro, m.Descripcion, + tm.ID as TipoMovID, tm.Nombre as TipoMovNombre, + dt.Id as DeptoTrabajoID, dt.Nombre as DeptoTrabajoNombre, + c.ID as ConceptoID, c.Nombre as ConceptoNombre, + m.Nota as Nota + FROM Movimientos m + INNER JOIN TipoMovimiento tm ON m.TipoMov = tm.ID + INNER JOIN DepartTrabajo dt ON m.DeptoTrabajo = dt.Id + INNER JOIN Concepto c ON m.Concepto = c.ID + WHERE 1=1"; + var parameters = new DynamicParameters(); + + if (deptoTrabajo.HasValue) + { + sql += " AND DeptoTrabajo = @DeptoTrabajo"; + parameters.Add("DeptoTrabajo", deptoTrabajo.Value); + } + + if (mes.HasValue) + { + sql += " AND strftime('%m', FechaMovimiento) = @Mes"; + // strftime devuelve con cero adelante ("01","02"...) + parameters.Add("Mes", mes.Value.ToString("D2")); + } + + if (anio.HasValue) + { + sql += " AND strftime('%Y', FechaMovimiento) = @Anio"; + parameters.Add("Anio", anio.Value.ToString()); + } + + if (tipoMov.HasValue) + { + sql += " AND TipoMov = @TipoMov"; + parameters.Add("TipoMov", tipoMov.Value); + } + + if (concepto.HasValue) + { + sql += " AND Concepto = @Concepto"; + parameters.Add("Concepto", concepto.Value); + } + + return await connection.QueryAsync(sql, parameters); + } + } + + public async Task UpdateMovimientoAsync(Movimiento movimiento) { using (var connection = GetConnection()) @@ -303,7 +382,7 @@ public class FinanzasRepository // Datos foreach (var mov in movimientos) { - csv.AppendLine($"{mov.ID};{mov.FechaMovimiento:yyyy-MM-dd};{mov.Monto};" + + csv.AppendLine($"{mov.ID};{mov.FechaMovimiento:yyyy-MM-dd};{mov.Monto.ToString().Replace('.', ',')};" + $"{mov.TipoMovNombre};{mov.DeptoTrabajoNombre};{mov.ConceptoNombre};" + $"\"{mov.Descripcion}\""); } @@ -314,5 +393,27 @@ public class FinanzasRepository return filePath; } + public async Task ExportToCsvAsync(string filePath, int departamentoTrabajo) + { + var movimientos = await GetMovimientoByGrupoTrabajoAsync(departamentoTrabajo); + var csv = new StringBuilder(); + + // Encabezados + csv.AppendLine("ID;FechaMovimiento;Monto;TipoMovimiento;Departamento;Concepto;Descripcion"); + + // Datos + foreach (var mov in movimientos) + { + csv.AppendLine($"{mov.ID};{mov.FechaMovimiento:yyyy-MM-dd};{mov.Monto.ToString().Replace('.', ',')};" + + $"\"{mov.TipoMovNombre}\";\"{mov.DeptoTrabajoNombre}\";\"{mov.ConceptoNombre}\";" + + $"\"{mov.Descripcion}\""); + } + + // Guardar archivo + await File.WriteAllTextAsync(filePath, csv.ToString(), Encoding.UTF8); + + return filePath; + } + #endregion } diff --git a/AdminFinanceRCA/ViewModels/MainWindowViewModel.cs b/AdminFinanceRCA/ViewModels/MainWindowViewModel.cs index 8b371f8..850c092 100644 --- a/AdminFinanceRCA/ViewModels/MainWindowViewModel.cs +++ b/AdminFinanceRCA/ViewModels/MainWindowViewModel.cs @@ -2,11 +2,11 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using AdminFinanceRCA.Models; using AdminFinanceRCA.Views; using Avalonia.Controls; -using Avalonia.Media; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; @@ -20,7 +20,13 @@ public partial class MainWindowViewModel : ViewModelBase [ObservableProperty] private ObservableCollection _departamentos; [ObservableProperty] private ObservableCollection _tiposMovimiento; [ObservableProperty] private MovimientoCompleto _movimientoSeleccionado; - + [ObservableProperty] private DepartTrabajo _departamentoSeleccionado; + [ObservableProperty] private ObservableCollection _anios; + [ObservableProperty] private ObservableCollection _meses; + [ObservableProperty] private int _anioSelect; + [ObservableProperty] private MesItem _mesSelect; + [ObservableProperty] private int _anioIndex; + [ObservableProperty] private int _mesIndex; public MainWindowViewModel() { _repository = new FinanzasRepository(); @@ -126,6 +132,39 @@ public partial class MainWindowViewModel : ViewModelBase return result; } + + private async Task<(string, DepartTrabajo)> ShowSaveFileDialogForGrupoTrabajoAsync(Window window, string defaultFileName = "movimientos.csv") + { + + var dpt = await _repository.GetAllDepartamentosAsync(); + List dt = dpt.ToList(); + var seleccion = await ComboMessageBox.ShowDialog(GetCurrentWindow(), dt, "Escoge una opción"); + + + var saveFileDialog = new SaveFileDialog + { + Title = "Guardar archivo CSV", + InitialFileName = defaultFileName, + DefaultExtension = "csv", + Filters = new List + { + new FileDialogFilter + { + Name = "Archivos CSV", + Extensions = new List { "csv" } + }, + new FileDialogFilter + { + Name = "Todos los archivos", + Extensions = new List { "*" } + } + } + }; + + var result = await saveFileDialog.ShowAsync(window); + return (result, seleccion); + } + [RelayCommand] public async void CrearCsv() { @@ -134,10 +173,12 @@ public partial class MainWindowViewModel : ViewModelBase var window = GetCurrentWindow(); //WindowService?.GetActiveWindow() ?? WindowService?.GetWindowForViewModel(this); if (window != null) { - string result = await ShowSaveFileDialogAsync(window); - if (!string.IsNullOrEmpty(result)) + //string result = await ShowSaveFileDialogAsync(window); + var dt = await ShowSaveFileDialogForGrupoTrabajoAsync(window); + + if (!string.IsNullOrEmpty(dt.Item1)) { - var filePath = await _repository.ExportToCsvAsync(result); + var filePath = await _repository.ExportToCsvAsync(dt.Item1, dt.Item2.Id); Console.WriteLine($"CSV creado en: {filePath}"); // Opcional: abrir el archivo con la aplicación predeterminada @@ -152,6 +193,47 @@ public partial class MainWindowViewModel : ViewModelBase } } + [RelayCommand] + private async Task FiltrarDatos() + { + if (DepartamentoSeleccionado != null) + { + var movFilter = _repository.GetMovimientosFiltradosAsync(DepartamentoSeleccionado.Id, MesSelect.Value, AnioSelect); + Movimientos = new ObservableCollection(await movFilter); + } + } + + private void ObtenerAnios() + { + Anios = new ObservableCollection(); + int añoActual = DateTime.Now.Year; + for (int i = 0; i <= 5; i++) + { + Anios.Add(añoActual - i); + } + } + + private void ObtenerMeses(int añoSeleccionado) + { + Meses = new ObservableCollection(); + + // Obtenemos el mes actual + int mesActual = DateTime.Now.Month; + + // Definimos hasta qué mes llenar según el año + int mesLimite = añoSeleccionado == DateTime.Now.Year ? mesActual : 12; + + for (int i = 1; i <= mesLimite; i++) + { + Meses.Add(new MesItem + { + Value = i, + Nombre = new DateTime(añoSeleccionado, i, 1).ToString("MMMM") // Nombre completo del mes + }); + } + + } + public async void CargarDatos() { try @@ -168,6 +250,10 @@ public partial class MainWindowViewModel : ViewModelBase Conceptos = new ObservableCollection(await conceptosTask); Departamentos = new ObservableCollection(await departamentosTask); TiposMovimiento = new ObservableCollection(await tiposTask); + ObtenerAnios(); + ObtenerMeses(DateTime.Now.Year); + MesIndex = 0; + AnioIndex = 0; } catch (Exception ex) { diff --git a/AdminFinanceRCA/Views/MainWindow.axaml b/AdminFinanceRCA/Views/MainWindow.axaml index b835225..1abe389 100644 --- a/AdminFinanceRCA/Views/MainWindow.axaml +++ b/AdminFinanceRCA/Views/MainWindow.axaml @@ -47,7 +47,27 @@ - + + + + + + + + + + + + + + + + + + + + +