ultimos cambios

This commit is contained in:
2025-09-20 18:19:49 -06:00
parent 1fde928d5e
commit 8d8bd859c4
5 changed files with 279 additions and 7 deletions

View File

@@ -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<T> : Window
{
private ComboBox _comboBox;
private Button _okButton;
private Button _cancelButton;
private object _result;
public ComboMessageBox(IEnumerable<T> 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<T> ShowDialog(Window parent, IEnumerable<T> items, string title = "Seleccione")
{
var msgBox = new ComboMessageBox<T>(items, title);
return await msgBox.ShowDialog<T>(parent);
}
}

View File

@@ -124,3 +124,10 @@ public class MovimientoCompleto : ObservableObject
public IBrush ForegroundColor => TipoMovNombre == "Egreso" ? Brushes.Red : Brushes.Black; 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
}

View File

@@ -122,6 +122,27 @@ public class FinanzasRepository
} }
} }
public async Task<IEnumerable<MovimientoCompleto>> 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<MovimientoCompleto>(sql, new { idGrupoTrabajo = idGrupoTrabajo });
}
}
public async Task<Movimiento> GetMovimientoByIdAsync(int id) public async Task<Movimiento> GetMovimientoByIdAsync(int id)
{ {
using (var connection = GetConnection()) using (var connection = GetConnection())
@@ -131,6 +152,64 @@ public class FinanzasRepository
} }
} }
public async Task<IEnumerable<MovimientoCompleto>> 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<MovimientoCompleto>(sql, parameters);
}
}
public async Task<bool> UpdateMovimientoAsync(Movimiento movimiento) public async Task<bool> UpdateMovimientoAsync(Movimiento movimiento)
{ {
using (var connection = GetConnection()) using (var connection = GetConnection())
@@ -303,7 +382,7 @@ public class FinanzasRepository
// Datos // Datos
foreach (var mov in movimientos) 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.TipoMovNombre};{mov.DeptoTrabajoNombre};{mov.ConceptoNombre};" +
$"\"{mov.Descripcion}\""); $"\"{mov.Descripcion}\"");
} }
@@ -314,5 +393,27 @@ public class FinanzasRepository
return filePath; return filePath;
} }
public async Task<string> 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 #endregion
} }

View File

@@ -2,11 +2,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using AdminFinanceRCA.Models; using AdminFinanceRCA.Models;
using AdminFinanceRCA.Views; using AdminFinanceRCA.Views;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
@@ -20,7 +20,13 @@ public partial class MainWindowViewModel : ViewModelBase
[ObservableProperty] private ObservableCollection<DepartTrabajo> _departamentos; [ObservableProperty] private ObservableCollection<DepartTrabajo> _departamentos;
[ObservableProperty] private ObservableCollection<TipoMovimiento> _tiposMovimiento; [ObservableProperty] private ObservableCollection<TipoMovimiento> _tiposMovimiento;
[ObservableProperty] private MovimientoCompleto _movimientoSeleccionado; [ObservableProperty] private MovimientoCompleto _movimientoSeleccionado;
[ObservableProperty] private DepartTrabajo _departamentoSeleccionado;
[ObservableProperty] private ObservableCollection<int> _anios;
[ObservableProperty] private ObservableCollection<MesItem> _meses;
[ObservableProperty] private int _anioSelect;
[ObservableProperty] private MesItem _mesSelect;
[ObservableProperty] private int _anioIndex;
[ObservableProperty] private int _mesIndex;
public MainWindowViewModel() public MainWindowViewModel()
{ {
_repository = new FinanzasRepository(); _repository = new FinanzasRepository();
@@ -126,6 +132,39 @@ public partial class MainWindowViewModel : ViewModelBase
return result; return result;
} }
private async Task<(string, DepartTrabajo)> ShowSaveFileDialogForGrupoTrabajoAsync(Window window, string defaultFileName = "movimientos.csv")
{
var dpt = await _repository.GetAllDepartamentosAsync();
List<DepartTrabajo> dt = dpt.ToList();
var seleccion = await ComboMessageBox<DepartTrabajo>.ShowDialog(GetCurrentWindow<MainWindow>(), dt, "Escoge una opción");
var saveFileDialog = new SaveFileDialog
{
Title = "Guardar archivo CSV",
InitialFileName = defaultFileName,
DefaultExtension = "csv",
Filters = new List<FileDialogFilter>
{
new FileDialogFilter
{
Name = "Archivos CSV",
Extensions = new List<string> { "csv" }
},
new FileDialogFilter
{
Name = "Todos los archivos",
Extensions = new List<string> { "*" }
}
}
};
var result = await saveFileDialog.ShowAsync(window);
return (result, seleccion);
}
[RelayCommand] [RelayCommand]
public async void CrearCsv() public async void CrearCsv()
{ {
@@ -134,10 +173,12 @@ public partial class MainWindowViewModel : ViewModelBase
var window = GetCurrentWindow<MainWindow>(); //WindowService?.GetActiveWindow() ?? WindowService?.GetWindowForViewModel(this); var window = GetCurrentWindow<MainWindow>(); //WindowService?.GetActiveWindow() ?? WindowService?.GetWindowForViewModel(this);
if (window != null) if (window != null)
{ {
string result = await ShowSaveFileDialogAsync(window); //string result = await ShowSaveFileDialogAsync(window);
if (!string.IsNullOrEmpty(result)) 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}"); Console.WriteLine($"CSV creado en: {filePath}");
// Opcional: abrir el archivo con la aplicación predeterminada // 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<MovimientoCompleto>(await movFilter);
}
}
private void ObtenerAnios()
{
Anios = new ObservableCollection<int>();
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<MesItem>();
// 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() public async void CargarDatos()
{ {
try try
@@ -168,6 +250,10 @@ public partial class MainWindowViewModel : ViewModelBase
Conceptos = new ObservableCollection<Concepto>(await conceptosTask); Conceptos = new ObservableCollection<Concepto>(await conceptosTask);
Departamentos = new ObservableCollection<DepartTrabajo>(await departamentosTask); Departamentos = new ObservableCollection<DepartTrabajo>(await departamentosTask);
TiposMovimiento = new ObservableCollection<TipoMovimiento>(await tiposTask); TiposMovimiento = new ObservableCollection<TipoMovimiento>(await tiposTask);
ObtenerAnios();
ObtenerMeses(DateTime.Now.Year);
MesIndex = 0;
AnioIndex = 0;
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -47,7 +47,27 @@
<TextBlock FontSize="24" Margin="0 15" Text="Administrador Financiero Grupos" <TextBlock FontSize="24" Margin="0 15" Text="Administrador Financiero Grupos"
Foreground="Black" FontWeight="SemiBold"/> Foreground="Black" FontWeight="SemiBold"/>
</StackPanel> </StackPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Spacing="10">
<Label VerticalAlignment="Center" >Grupo:</Label>
<ComboBox ItemsSource="{Binding Departamentos}" SelectedItem="{Binding DepartamentoSeleccionado}" Width="200" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Nombre}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label VerticalAlignment="Center">Mes:</Label>
<ComboBox ItemsSource="{Binding Meses}" SelectedIndex="{Binding MesIndex}" SelectedItem="{Binding MesSelect}" Width="150" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Nombre}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label VerticalAlignment="Center">Año:</Label>
<ComboBox ItemsSource="{Binding Anios}" SelectedIndex="{Binding AnioIndex}" SelectedItem="{Binding AnioSelect}" Width="100"></ComboBox>
<Button VerticalAlignment="Center" Command="{Binding FiltrarDatosCommand}">Filtrar</Button>
</StackPanel>
<!-- Botones flotantes estilo moderno --> <!-- Botones flotantes estilo moderno -->
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 10 20 20"> <StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0 10 20 20">
<Button Content="Agregar Movimiento" <Button Content="Agregar Movimiento"