commit be8111c2b89c4fdb3627ff63aee6a5001e4c3645 Author: adalberto Date: Sat Sep 13 17:08:14 2025 -0600 Primer comit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..add57be --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +/packages/ +riderModule.iml +/_ReSharper.Caches/ \ No newline at end of file diff --git a/.idea/.idea.AdminFinanceRCA/.idea/.gitignore b/.idea/.idea.AdminFinanceRCA/.idea/.gitignore new file mode 100644 index 0000000..ebf329a --- /dev/null +++ b/.idea/.idea.AdminFinanceRCA/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/projectSettingsUpdater.xml +/.idea.AdminFinanceRCA.iml +/contentModel.xml +/modules.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/.idea.AdminFinanceRCA/.idea/avalonia.xml b/.idea/.idea.AdminFinanceRCA/.idea/avalonia.xml new file mode 100644 index 0000000..7e34302 --- /dev/null +++ b/.idea/.idea.AdminFinanceRCA/.idea/avalonia.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/.idea/.idea.AdminFinanceRCA/.idea/encodings.xml b/.idea/.idea.AdminFinanceRCA/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/.idea/.idea.AdminFinanceRCA/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/.idea.AdminFinanceRCA/.idea/indexLayout.xml b/.idea/.idea.AdminFinanceRCA/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.AdminFinanceRCA/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.AdminFinanceRCA/.idea/vcs.xml b/.idea/.idea.AdminFinanceRCA/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/.idea.AdminFinanceRCA/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AdminFinanceRCA.sln b/AdminFinanceRCA.sln new file mode 100644 index 0000000..d95abaa --- /dev/null +++ b/AdminFinanceRCA.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdminFinanceRCA", "AdminFinanceRCA\AdminFinanceRCA.csproj", "{1917BD06-B762-42E3-BD49-67C08B3848BD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1917BD06-B762-42E3-BD49-67C08B3848BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1917BD06-B762-42E3-BD49-67C08B3848BD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1917BD06-B762-42E3-BD49-67C08B3848BD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1917BD06-B762-42E3-BD49-67C08B3848BD}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/AdminFinanceRCA.sln.DotSettings.user b/AdminFinanceRCA.sln.DotSettings.user new file mode 100644 index 0000000..1097a88 --- /dev/null +++ b/AdminFinanceRCA.sln.DotSettings.user @@ -0,0 +1,6 @@ + + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded + ForceIncluded \ No newline at end of file diff --git a/AdminFinanceRCA/AdminFinanceRCA.csproj b/AdminFinanceRCA/AdminFinanceRCA.csproj new file mode 100644 index 0000000..7b8d1c1 --- /dev/null +++ b/AdminFinanceRCA/AdminFinanceRCA.csproj @@ -0,0 +1,42 @@ + + + WinExe + net8.0 + enable + true + app.manifest + true + + + + + + + + + + + + + + + None + All + + + + + + + + + + + + + + + Always + + + diff --git a/AdminFinanceRCA/App.axaml b/AdminFinanceRCA/App.axaml new file mode 100644 index 0000000..9fd7e19 --- /dev/null +++ b/AdminFinanceRCA/App.axaml @@ -0,0 +1,17 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/AdminFinanceRCA/App.axaml.cs b/AdminFinanceRCA/App.axaml.cs new file mode 100644 index 0000000..a6a3a36 --- /dev/null +++ b/AdminFinanceRCA/App.axaml.cs @@ -0,0 +1,47 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Data.Core; +using Avalonia.Data.Core.Plugins; +using System.Linq; +using Avalonia.Markup.Xaml; +using AdminFinanceRCA.ViewModels; +using AdminFinanceRCA.Views; + +namespace AdminFinanceRCA; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + // Avoid duplicate validations from both Avalonia and the CommunityToolkit. + // More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins + DisableAvaloniaDataAnnotationValidation(); + desktop.MainWindow = new MainWindow + { + DataContext = new MainWindowViewModel(), + }; + } + + base.OnFrameworkInitializationCompleted(); + } + + private void DisableAvaloniaDataAnnotationValidation() + { + // Get an array of plugins to remove + var dataValidationPluginsToRemove = + BindingPlugins.DataValidators.OfType().ToArray(); + + // remove each entry found + foreach (var plugin in dataValidationPluginsToRemove) + { + BindingPlugins.DataValidators.Remove(plugin); + } + } +} \ No newline at end of file diff --git a/AdminFinanceRCA/AppConfig.cs b/AdminFinanceRCA/AppConfig.cs new file mode 100644 index 0000000..d3e0494 --- /dev/null +++ b/AdminFinanceRCA/AppConfig.cs @@ -0,0 +1,24 @@ +using System; +using System.IO; +using Microsoft.Extensions.Configuration; + +namespace AdminFinanceRCA; + +public class AppConfig +{ + private readonly IConfiguration _configuration; + + public AppConfig() + { + var builder = new ConfigurationBuilder() + .SetBasePath(AppDomain.CurrentDomain.BaseDirectory) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true); + + _configuration = builder.Build(); + } + + public string GetConnectionString(string name = "DefaultConnection") + { + return _configuration.GetConnectionString(name); + } +} \ No newline at end of file diff --git a/AdminFinanceRCA/Assets/avalonia-logo.ico b/AdminFinanceRCA/Assets/avalonia-logo.ico new file mode 100644 index 0000000..f7da8bb Binary files /dev/null and b/AdminFinanceRCA/Assets/avalonia-logo.ico differ diff --git a/AdminFinanceRCA/Models/Entidades.cs b/AdminFinanceRCA/Models/Entidades.cs new file mode 100644 index 0000000..58e4181 --- /dev/null +++ b/AdminFinanceRCA/Models/Entidades.cs @@ -0,0 +1,107 @@ +using System; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace AdminFinanceRCA.Models; + +// Clase para la tabla Concepto +public class Concepto +{ + public int ID { get; set; } + public string Nombre { get; set; } = string.Empty; + + public Concepto() { } + + public Concepto(string nombre) + { + Nombre = nombre; + } +} + +// Clase para la tabla DepartTrabajo +public class DepartTrabajo : ObservableObject +{ + public int Id { get; set; } + public string Nombre { get; set; } = string.Empty; + public string Descripcion { get; set; } = string.Empty; + + + public DepartTrabajo() { } + + public DepartTrabajo(string nombre) + { + Nombre = nombre; + } + public DepartTrabajo(string nombre, string descripcion) + { + Nombre = nombre; + Descripcion = descripcion; + } +} + +// Clase para la tabla TipoMovimiento +public class TipoMovimiento : ObservableObject +{ + public int ID { get; set; } + public string Nombre { get; set; } = string.Empty; + + public TipoMovimiento() { } + + public TipoMovimiento(string nombre) + { + Nombre = nombre; + } +} + +// Clase principal para la tabla Movimientos con relaciones +public class Movimiento : ObservableObject +{ + public int ID { get; set; } + public decimal Monto { get; set; } + public int TipoMov { get; set; } + public int DeptoTrabajo { get; set; } + public DateTime FechaMovimiento { get; set; } + public DateTime FechaRegistro { get; set; } + public int Concepto { get; set; } + public string Descripcion { get; set; } = string.Empty; + + // Propiedades de navegación para las relaciones (opcionales pero útiles) + public TipoMovimiento TipoMovimientoNav { get; set; } = new TipoMovimiento(); + public DepartTrabajo DepartTrabajoNav { get; set; } = new DepartTrabajo(); + public Concepto ConceptoNav { get; set; } = new Concepto(); + + public Movimiento() { } + + public Movimiento(decimal monto, int tipoMov, int deptoTrabajo, + DateTime fechaMovimiento, int concepto, string descripcion = "") + { + Monto = monto; + TipoMov = tipoMov; + DeptoTrabajo = deptoTrabajo; + FechaMovimiento = fechaMovimiento; + FechaRegistro = DateTime.Now; + Concepto = concepto; + Descripcion = descripcion; + } +} + +// Clase para resultados de consultas JOIN +public class MovimientoCompleto : ObservableObject +{ + public int ID { get; set; } + public decimal Monto { get; set; } + public DateTime FechaMovimiento { get; set; } + public DateTime FechaRegistro { get; set; } + public string Descripcion { get; set; } = string.Empty; + + // Datos de TipoMovimiento + public int TipoMovID { get; set; } + public string TipoMovNombre { get; set; } = string.Empty; + + // Datos de DepartTrabajo + public int DeptoTrabajoID { get; set; } + public string DeptoTrabajoNombre { get; set; } = string.Empty; + + // Datos de Concepto + public int ConceptoID { get; set; } + public string ConceptoNombre { get; set; } = string.Empty; +} diff --git a/AdminFinanceRCA/Program.cs b/AdminFinanceRCA/Program.cs new file mode 100644 index 0000000..af71583 --- /dev/null +++ b/AdminFinanceRCA/Program.cs @@ -0,0 +1,21 @@ +using Avalonia; +using System; + +namespace AdminFinanceRCA; + +sealed class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace(); +} \ No newline at end of file diff --git a/AdminFinanceRCA/Repository.cs b/AdminFinanceRCA/Repository.cs new file mode 100644 index 0000000..45b74c6 --- /dev/null +++ b/AdminFinanceRCA/Repository.cs @@ -0,0 +1,302 @@ +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using AdminFinanceRCA.Models; + +namespace AdminFinanceRCA; + +using System.Data; +using Dapper; +using Microsoft.Data.Sqlite; + +public class FinanzasRepository +{ + private readonly string _connectionString; + + public FinanzasRepository() + { + var config = new AppConfig(); + _connectionString = config.GetConnectionString(); + InitializeDatabase(); + } + // Constructor alternativo para testing + public FinanzasRepository(string connectionString) + { + _connectionString = connectionString; + InitializeDatabase(); + } + private void InitializeDatabase() + { + using (var connection = new SqliteConnection(_connectionString)) + { + connection.Open(); + + // Crear tablas si no existen + connection.Execute(@" + CREATE TABLE IF NOT EXISTS Concepto ( + ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + Nombre TEXT NOT NULL + )"); + + connection.Execute(@" + CREATE TABLE IF NOT EXISTS DepartTrabajo ( + Id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + Nombre TEXT NOT NULL + )"); + + connection.Execute(@" + CREATE TABLE IF NOT EXISTS TipoMovimiento ( + ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + Nombre TEXT NOT NULL + )"); + + connection.Execute(@" + CREATE TABLE IF NOT EXISTS Movimientos ( + ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + Monto REAL NOT NULL, + TipoMov INTEGER NOT NULL, + DeptoTrabajo INTEGER NOT NULL, + FechaMovimiento TEXT NOT NULL, + FechaRegistro TEXT NOT NULL, + Concepto INTEGER NOT NULL, + Descripcion TEXT, + FOREIGN KEY(TipoMov) REFERENCES TipoMovimiento(ID), + FOREIGN KEY(DeptoTrabajo) REFERENCES DepartTrabajo(Id), + FOREIGN KEY(Concepto) REFERENCES Concepto(ID) + )"); + + // Crear índice + connection.Execute(@" + CREATE INDEX IF NOT EXISTS Mov ON Movimientos (Concepto ASC, TipoMov ASC)"); + + // Insertar datos básicos si las tablas están vacías + InsertarDatosBasicos(connection); + } + } + + private void InsertarDatosBasicos(SqliteConnection connection) + { + // Verificar si TipoMovimiento está vacío + var countTipos = connection.ExecuteScalar("SELECT COUNT(*) FROM TipoMovimiento"); + if (countTipos == 0) + { + connection.Execute("INSERT INTO TipoMovimiento (Nombre) VALUES ('Ingreso')"); + connection.Execute("INSERT INTO TipoMovimiento (Nombre) VALUES ('Egreso')"); + } + + // Verificar si Concepto está vacío + var countConceptos = connection.ExecuteScalar("SELECT COUNT(*) FROM Concepto"); + if (countConceptos == 0) + { + connection.Execute("INSERT INTO Concepto (Nombre) VALUES ('Ventas')"); + connection.Execute("INSERT INTO Concepto (Nombre) VALUES ('Compras')"); + connection.Execute("INSERT INTO Concepto (Nombre) VALUES ('Nómina')"); + connection.Execute("INSERT INTO Concepto (Nombre) VALUES ('Servicios')"); + } + + // Verificar si DepartTrabajo está vacío + var countDeptos = connection.ExecuteScalar("SELECT COUNT(*) FROM DepartTrabajo"); + if (countDeptos == 0) + { + connection.Execute("INSERT INTO DepartTrabajo (Nombre) VALUES ('Administración')"); + connection.Execute("INSERT INTO DepartTrabajo (Nombre) VALUES ('Ventas')"); + connection.Execute("INSERT INTO DepartTrabajo (Nombre) VALUES ('Producción')"); + connection.Execute("INSERT INTO DepartTrabajo (Nombre) VALUES ('Logística')"); + } + } + + public IDbConnection GetConnection() + { + return new SqliteConnection(_connectionString); + } + + #region Operaciones para Movimientos + + public async Task CreateMovimientoAsync(Movimiento movimiento) + { + using (var connection = GetConnection()) + { + var sql = @"INSERT INTO Movimientos + (Monto, TipoMov, DeptoTrabajo, FechaMovimiento, FechaRegistro, Concepto, Descripcion) + VALUES (@Monto, @TipoMov, @DeptoTrabajo, @FechaMovimiento, @FechaRegistro, @Concepto, @Descripcion); + SELECT last_insert_rowid();"; + + var id = await connection.ExecuteScalarAsync(sql, movimiento); + return id; + } + } + + public async Task> GetAllMovimientosAsync() + { + using (var connection = GetConnection()) + { + var sql = "SELECT * FROM Movimientos ORDER BY FechaMovimiento DESC"; + return await connection.QueryAsync(sql); + } + } + + public async Task> GetAllMovimientosCompletosAsync() + { + 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 + 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 + ORDER BY m.FechaMovimiento DESC"; + + return await connection.QueryAsync(sql); + } + } + + public async Task GetMovimientoByIdAsync(int id) + { + using (var connection = GetConnection()) + { + var sql = "SELECT * FROM Movimientos WHERE ID = @Id"; + return await connection.QueryFirstOrDefaultAsync(sql, new { Id = id }); + } + } + + public async Task UpdateMovimientoAsync(Movimiento movimiento) + { + using (var connection = GetConnection()) + { + var sql = @"UPDATE Movimientos SET + Monto = @Monto, + TipoMov = @TipoMov, + DeptoTrabajo = @DeptoTrabajo, + FechaMovimiento = @FechaMovimiento, + Concepto = @Concepto, + Descripcion = @Descripcion + WHERE ID = @ID"; + + var affectedRows = await connection.ExecuteAsync(sql, movimiento); + return affectedRows > 0; + } + } + + public async Task DeleteMovimientoAsync(int id) + { + using (var connection = GetConnection()) + { + var sql = "DELETE FROM Movimientos WHERE ID = @Id"; + var affectedRows = await connection.ExecuteAsync(sql, new { Id = id }); + return affectedRows > 0; + } + } + + #endregion + + #region Operaciones para Concepto + + public async Task> GetAllConceptosAsync() + { + using (var connection = GetConnection()) + { + var sql = "SELECT * FROM Concepto ORDER BY Nombre"; + return await connection.QueryAsync(sql); + } + } + + public async Task CreateConceptoAsync(Concepto concepto) + { + using (var connection = GetConnection()) + { + var sql = "INSERT INTO Concepto (Nombre) VALUES (@Nombre); SELECT last_insert_rowid();"; + return await connection.ExecuteScalarAsync(sql, concepto); + } + } + + #endregion + + #region Operaciones para DepartTrabajo + + public async Task> GetAllDepartamentosAsync() + { + using (var connection = GetConnection()) + { + var sql = "SELECT * FROM DepartTrabajo ORDER BY Nombre"; + return await connection.QueryAsync(sql); + } + } + + public async Task CreateDepartamentoAsync(DepartTrabajo departamento) + { + using (var connection = GetConnection()) + { + var sql = "INSERT INTO DepartTrabajo (Nombre, Descripcion) VALUES (@Nombre, @Descripcion); SELECT last_insert_rowid();"; + return await connection.ExecuteScalarAsync(sql, departamento); + } + } + public async Task UpdateDepartamentoAsync(DepartTrabajo departamento) + { + using (var connection = GetConnection()) + { + var sql = @"UPDATE DepartTrabajo + SET Nombre = @Nombre, + Descripcion = @Descripcion + WHERE Id = @Id;"; + + var rowsAffected = await connection.ExecuteAsync(sql, departamento); + return rowsAffected > 0; + } + } + + #endregion + + #region Operaciones para TipoMovimiento + + public async Task> GetAllTiposMovimientoAsync() + { + using (var connection = GetConnection()) + { + var sql = "SELECT * FROM TipoMovimiento ORDER BY Nombre"; + return await connection.QueryAsync(sql); + } + } + + public async Task CreateTipoMovimientoAsync(TipoMovimiento tipoMovimiento) + { + using (var connection = GetConnection()) + { + var sql = "INSERT INTO TipoMovimiento (Nombre) VALUES (@Nombre); SELECT last_insert_rowid();"; + return await connection.ExecuteScalarAsync(sql, tipoMovimiento); + } + } + + #endregion + + #region Métodos de exportación + + public async Task ExportToCsvAsync(string filePath) + { + var movimientos = await GetAllMovimientosCompletosAsync(); + 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};" + + $"{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/Ventanas.cs b/AdminFinanceRCA/Ventanas.cs new file mode 100644 index 0000000..304b5fb --- /dev/null +++ b/AdminFinanceRCA/Ventanas.cs @@ -0,0 +1,42 @@ +using System.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; + +namespace AdminFinanceRCA; + +// Interface +public interface IWindowService +{ + Window? GetWindowForViewModel(object viewModel); + Window? GetActiveWindow(); + Window? GetMainWindow(); +} + +// Implementación +public class WindowService : IWindowService +{ + public Window? GetWindowForViewModel(object viewModel) + { + if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) + return null; + + return desktop.Windows.FirstOrDefault(window => ReferenceEquals(window.DataContext, viewModel)); + } + + public Window? GetActiveWindow() + { + if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) + return null; + + return desktop.Windows.FirstOrDefault(w => w.IsActive); + } + + public Window? GetMainWindow() + { + if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) + return null; + + return desktop.MainWindow; + } +} \ No newline at end of file diff --git a/AdminFinanceRCA/ViewLocator.cs b/AdminFinanceRCA/ViewLocator.cs new file mode 100644 index 0000000..fb36e38 --- /dev/null +++ b/AdminFinanceRCA/ViewLocator.cs @@ -0,0 +1,30 @@ +using System; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using AdminFinanceRCA.ViewModels; + +namespace AdminFinanceRCA; + +public class ViewLocator : IDataTemplate +{ + public Control? Build(object? param) + { + if (param is null) + return null; + + var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal); + var type = Type.GetType(name); + + if (type != null) + { + return (Control)Activator.CreateInstance(type)!; + } + + return new TextBlock { Text = "Not Found: " + name }; + } + + public bool Match(object? data) + { + return data is ViewModelBase; + } +} \ No newline at end of file diff --git a/AdminFinanceRCA/ViewModels/MainWindowViewModel.cs b/AdminFinanceRCA/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..68b3efe --- /dev/null +++ b/AdminFinanceRCA/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,133 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Threading.Tasks; +using AdminFinanceRCA.Models; +using AdminFinanceRCA.Views; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +namespace AdminFinanceRCA.ViewModels; + +public partial class MainWindowViewModel : ViewModelBase +{ + private readonly FinanzasRepository _repository; + [ObservableProperty] + private ObservableCollection _movimientos; + [ObservableProperty] + private ObservableCollection _conceptos; + [ObservableProperty] + private ObservableCollection _departamentos; + [ObservableProperty] + private ObservableCollection _tiposMovimiento; + + public MainWindowViewModel() + { + _repository = new FinanzasRepository(); + CargarDatos(); + } + [RelayCommand] + public void AgregarDepartamento() + { + Mantenimiento_DepartTrabajoViewModel mantenimientoDepart = new Mantenimiento_DepartTrabajoViewModel(); + Mantenimiento_DepartTrabajo departTrabajo = new Mantenimiento_DepartTrabajo() + { + DataContext = mantenimientoDepart + }; + departTrabajo.ShowDialog(GetCurrentWindow()); + } + + [RelayCommand] + public void AgregarMovimiento() + { + MovimientoWindowViewModel mwvm = new MovimientoWindowViewModel(); + + MovimientoWindow mw = new MovimientoWindow(); + mw.DataContext = mwvm; + mw.ShowDialog(GetCurrentWindow()); + } + + [RelayCommand] + public void Salir() + { + GetCurrentWindow().Close(); + } + private async Task ShowSaveFileDialogAsync(Window window, string defaultFileName = "movimientos.csv") + { + 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; + } + + [RelayCommand] + public async void CrearCsv() + { + try + { + var window = GetCurrentWindow(); //WindowService?.GetActiveWindow() ?? WindowService?.GetWindowForViewModel(this); + if (window != null) + { + string result = await ShowSaveFileDialogAsync(window); + if (!string.IsNullOrEmpty(result)) + { + var filePath = await _repository.ExportToCsvAsync(result); + Console.WriteLine($"CSV creado en: {filePath}"); + + // Opcional: abrir el archivo con la aplicación predeterminada + Process.Start(new ProcessStartInfo(filePath) { UseShellExecute = true }); + + } + } + } + catch (Exception ex) + { + Console.WriteLine($"Error al crear CSV: {ex.Message}"); + } + } + + public async void CargarDatos() + { + try + { + var movimientosTask = _repository.GetAllMovimientosCompletosAsync(); + var conceptosTask = _repository.GetAllConceptosAsync(); + var departamentosTask = _repository.GetAllDepartamentosAsync(); + var tiposTask = _repository.GetAllTiposMovimientoAsync(); + + await Task.WhenAll(movimientosTask, conceptosTask, departamentosTask, tiposTask); + + // Actualizar collections + Movimientos = new ObservableCollection(await movimientosTask); + Conceptos = new ObservableCollection(await conceptosTask); + Departamentos = new ObservableCollection(await departamentosTask); + TiposMovimiento = new ObservableCollection(await tiposTask); + } + catch (Exception ex) + { + Console.WriteLine($"Error al cargar datos: {ex.Message}"); + } + } +} \ No newline at end of file diff --git a/AdminFinanceRCA/ViewModels/Mantenimiento_DepartTrabajoViewModel.cs b/AdminFinanceRCA/ViewModels/Mantenimiento_DepartTrabajoViewModel.cs new file mode 100644 index 0000000..faf8d3b --- /dev/null +++ b/AdminFinanceRCA/ViewModels/Mantenimiento_DepartTrabajoViewModel.cs @@ -0,0 +1,68 @@ +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using System.Threading.Tasks; +using AdminFinanceRCA.Models; +using AdminFinanceRCA.Views; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Material.Dialog.Views; + +namespace AdminFinanceRCA.ViewModels; + +public partial class Mantenimiento_DepartTrabajoViewModel : ViewModelBase +{ + private readonly FinanzasRepository _departTrabajoService; + + [ObservableProperty] private ObservableCollection _gruposTrabajo; + [ObservableProperty] private DepartTrabajo _grupoSeleccionado; + [ObservableProperty] private DepartTrabajo _grupoEditando; + [ObservableProperty] private bool _estaEditando; + [ObservableProperty] private string _nombre; + [ObservableProperty] private string _descripcion; + + public Mantenimiento_DepartTrabajoViewModel() + { + _departTrabajoService = new FinanzasRepository(); + _ = CargarDatosAsync(); + } + + async Task CargarDatosAsync() + { + var dt = await _departTrabajoService.GetAllDepartamentosAsync(); + GruposTrabajo = new ObservableCollection(dt); + } + + [RelayCommand] + private void NuevoDepartTrabajo() + { + Nombre = string.Empty; + Descripcion = string.Empty; + EstaEditando = false; + } + + [RelayCommand] + private async Task GuardarRegistros() + { + + if (EstaEditando) + { + GrupoSeleccionado.Nombre = Nombre; + GrupoSeleccionado.Descripcion = Descripcion; + _departTrabajoService.UpdateDepartamentoAsync(GrupoSeleccionado); + } + else + { + if(string.IsNullOrEmpty(Nombre)) + return; + + DepartTrabajo depart = new DepartTrabajo(Nombre, Descripcion); + await _departTrabajoService.CreateDepartamentoAsync(depart); + Nombre = string.Empty; + Descripcion = string.Empty; + AlertDialog ad = new AlertDialog(); + ad.Title = "Exito"; + ad.Content = "Registro agregado con exito"; + ad.Show(GetCurrentWindow()); + } + } +} \ No newline at end of file diff --git a/AdminFinanceRCA/ViewModels/MovimientoWindowViewModel.cs b/AdminFinanceRCA/ViewModels/MovimientoWindowViewModel.cs new file mode 100644 index 0000000..2b50a91 --- /dev/null +++ b/AdminFinanceRCA/ViewModels/MovimientoWindowViewModel.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.ObjectModel; +using System.Threading.Tasks; +using AdminFinanceRCA.Models; +using AdminFinanceRCA.Views; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +namespace AdminFinanceRCA.ViewModels; + +public partial class MovimientoWindowViewModel : ViewModelBase +{ + private readonly FinanzasRepository _finanzasRepository; + + [ObservableProperty] private ObservableCollection _departTrabajos; + [ObservableProperty] private ObservableCollection _tipoMovimientos; + [ObservableProperty] private ObservableCollection _conceptos; + [ObservableProperty] private DateTime _fechaMov; + [ObservableProperty] private decimal _montoDecimal; + [ObservableProperty] private string _descripcion; + [ObservableProperty] private TipoMovimiento _tipoMovimientoSelecionado; + [ObservableProperty] private DepartTrabajo _departaDepartTrabajoSeleccionado; + [ObservableProperty] private Concepto _conceptoSeleccionado; + + public MovimientoWindowViewModel() + { + _fechaMov = DateTime.Now; + _finanzasRepository = new FinanzasRepository(); + _ = CargarDatosAsync(); + } + + private async Task CargarDatosAsync() + { + var dt = await _finanzasRepository.GetAllDepartamentosAsync(); + DepartTrabajos = new ObservableCollection(dt); + + var tm = await _finanzasRepository.GetAllTiposMovimientoAsync(); + TipoMovimientos = new ObservableCollection(tm); + + var cpt = await _finanzasRepository.GetAllConceptosAsync(); + Conceptos = new ObservableCollection(cpt); + } + + [RelayCommand] + public async void GuardarDatosAsync() + { + Movimiento mv = new Movimiento(MontoDecimal, TipoMovimientoSelecionado.ID, DepartaDepartTrabajoSeleccionado.Id, + FechaMov, ConceptoSeleccionado.ID, Descripcion); + await _finanzasRepository.CreateMovimientoAsync(mv); + } + + [RelayCommand] + public void Salir() + { + GetCurrentWindow().Close(); + } +} \ No newline at end of file diff --git a/AdminFinanceRCA/ViewModels/ViewModelBase.cs b/AdminFinanceRCA/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..3a40270 --- /dev/null +++ b/AdminFinanceRCA/ViewModels/ViewModelBase.cs @@ -0,0 +1,28 @@ +using System.Linq; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; +using CommunityToolkit.Mvvm.ComponentModel; + +namespace AdminFinanceRCA.ViewModels; + +public class ViewModelBase : ObservableObject +{ + protected IWindowService? WindowService { get; set; } + + + /// + /// Obtiene la ventana asociada al ViewModel actual. + /// + /// El tipo de ventana a obtener (por ejemplo, MainWindow, LoginWindow). + /// La ventana asociada al ViewModel, o null si no se encuentra. + public TWindow? GetCurrentWindow() where TWindow : Window + { + if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + //return desktop.Windows.OfType().FirstOrDefault(w => w.DataContext == this); + return desktop.Windows.OfType().FirstOrDefault(w => w.DataContext == this); + } + return null; + } +} \ No newline at end of file diff --git a/AdminFinanceRCA/Views/MainWindow.axaml b/AdminFinanceRCA/Views/MainWindow.axaml new file mode 100644 index 0000000..ceacdaa --- /dev/null +++ b/AdminFinanceRCA/Views/MainWindow.axaml @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +