add new
This commit is contained in:
161
RS_system/Controllers/ArticulosController.cs
Normal file
161
RS_system/Controllers/ArticulosController.cs
Normal file
@@ -0,0 +1,161 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Rs_system.Models.ViewModels;
|
||||
using Rs_system.Services;
|
||||
|
||||
namespace Rs_system.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class ArticulosController : Controller
|
||||
{
|
||||
private readonly IArticuloService _service;
|
||||
|
||||
public ArticulosController(IArticuloService service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
|
||||
// GET: Articulos
|
||||
public async Task<IActionResult> Index(string? search, int? categoriaId, int? ubicacionId, int? estadoId)
|
||||
{
|
||||
// Load filter lists
|
||||
var categorias = await _service.GetCategoriasAsync();
|
||||
ViewBag.Categorias = new SelectList(categorias.Select(c => new { c.Id, c.Nombre }), "Id", "Nombre", categoriaId);
|
||||
|
||||
var ubicaciones = await _service.GetUbicacionesAsync();
|
||||
ViewBag.Ubicaciones = new SelectList(ubicaciones.Select(u => new { u.Id, u.Nombre }), "Id", "Nombre", ubicacionId);
|
||||
|
||||
// Custom Estado SelectList
|
||||
var estados = await _service.GetEstadosAsync();
|
||||
ViewBag.Estados = new SelectList(estados.Select(e => new { e.Id, e.Nombre }), "Id", "Nombre", estadoId);
|
||||
|
||||
// Keep Search params
|
||||
ViewBag.CurrentSearch = search ?? "";
|
||||
ViewBag.CurrentCategoria = categoriaId;
|
||||
ViewBag.CurrentUbicacion = ubicacionId;
|
||||
ViewBag.CurrentEstado = estadoId;
|
||||
|
||||
var list = await _service.GetAllAsync(search, categoriaId, ubicacionId, estadoId);
|
||||
return View(list);
|
||||
}
|
||||
|
||||
// GET: Articulos/Details/5
|
||||
public async Task<IActionResult> Details(int? id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
|
||||
var articulo = await _service.GetByIdAsync(id.Value);
|
||||
if (articulo == null) return NotFound();
|
||||
|
||||
return View(articulo);
|
||||
}
|
||||
|
||||
// GET: Articulos/Create
|
||||
public async Task<IActionResult> Create()
|
||||
{
|
||||
await LoadDropdownsAsync();
|
||||
return View(new ArticuloViewModel());
|
||||
}
|
||||
|
||||
// POST: Articulos/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(ArticuloViewModel viewModel)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (await _service.ExistsCodigoAsync(viewModel.Codigo))
|
||||
{
|
||||
ModelState.AddModelError("Codigo", "Ya existe un artículo con este código.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var createdBy = User.Identity?.Name ?? "Sistema";
|
||||
var result = await _service.CreateAsync(viewModel, createdBy);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Artículo registrado exitosamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
ModelState.AddModelError("", "Ocurrió un error al guardar el artículo.");
|
||||
}
|
||||
}
|
||||
|
||||
await LoadDropdownsAsync();
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
// GET: Articulos/Edit/5
|
||||
public async Task<IActionResult> Edit(int? id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
|
||||
var articulo = await _service.GetByIdAsync(id.Value);
|
||||
if (articulo == null) return NotFound();
|
||||
|
||||
await LoadDropdownsAsync();
|
||||
return View(articulo);
|
||||
}
|
||||
|
||||
// POST: Articulos/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(int id, ArticuloViewModel viewModel)
|
||||
{
|
||||
if (id != viewModel.Id) return NotFound();
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (await _service.ExistsCodigoAsync(viewModel.Codigo, id))
|
||||
{
|
||||
ModelState.AddModelError("Codigo", "Ya existe otro artículo con este código.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await _service.UpdateAsync(viewModel);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Artículo actualizado exitosamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
ModelState.AddModelError("", "No se pudo actualizar el artículo.");
|
||||
}
|
||||
}
|
||||
|
||||
await LoadDropdownsAsync();
|
||||
return View(viewModel);
|
||||
}
|
||||
|
||||
// POST: Articulos/Delete/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var result = await _service.DeleteAsync(id);
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Artículo eliminado exitosamente.";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["ErrorMessage"] = "No se pudo eliminado el artículo.";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
private async Task LoadDropdownsAsync()
|
||||
{
|
||||
var categorias = await _service.GetCategoriasAsync();
|
||||
ViewBag.Categorias = new SelectList(categorias.Select(c => new { c.Id, c.Nombre }), "Id", "Nombre");
|
||||
|
||||
var ubicaciones = await _service.GetUbicacionesAsync();
|
||||
ViewBag.Ubicaciones = new SelectList(ubicaciones.Select(u => new { u.Id, u.Nombre }), "Id", "Nombre");
|
||||
|
||||
var estados = await _service.GetEstadosAsync();
|
||||
ViewBag.Estados = new SelectList(estados.Select(e => new { e.Id, e.Nombre }), "Id", "Nombre");
|
||||
}
|
||||
}
|
||||
125
RS_system/Controllers/CategoriasController.cs
Normal file
125
RS_system/Controllers/CategoriasController.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Services;
|
||||
|
||||
namespace Rs_system.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class CategoriasController : Controller
|
||||
{
|
||||
private readonly ICategoriaService _service;
|
||||
|
||||
public CategoriasController(ICategoriaService service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
|
||||
// GET: Categorias
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var list = await _service.GetAllAsync();
|
||||
return View(list);
|
||||
}
|
||||
|
||||
// GET: Categorias/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: Categorias/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create([Bind("Nombre,Descripcion,Activo")] Categoria categoria)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(categoria.Nombre))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "El nombre es obligatorio.");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (await _service.ExistsAsync(categoria.Nombre))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "Ya existe una categoría con ese nombre.");
|
||||
return View(categoria);
|
||||
}
|
||||
|
||||
categoria.CreadoPor = User.Identity?.Name ?? "Sistema";
|
||||
var result = await _service.CreateAsync(categoria);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Categoría creada exitosamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
ModelState.AddModelError("", "Ocurrió un error al guardar los datos.");
|
||||
}
|
||||
return View(categoria);
|
||||
}
|
||||
|
||||
// GET: Categorias/Edit/5
|
||||
public async Task<IActionResult> Edit(int? id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
|
||||
var categoria = await _service.GetByIdAsync(id.Value);
|
||||
if (categoria == null) return NotFound();
|
||||
|
||||
return View(categoria);
|
||||
}
|
||||
|
||||
// POST: Categorias/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(int id, [Bind("Id,Nombre,Descripcion,Activo")] Categoria categoria)
|
||||
{
|
||||
if (id != categoria.Id) return NotFound();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(categoria.Nombre))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "El nombre es obligatorio.");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (await _service.ExistsAsync(categoria.Nombre, id))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "Ya existe otra categoría con ese nombre.");
|
||||
return View(categoria);
|
||||
}
|
||||
|
||||
var result = await _service.UpdateAsync(categoria);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Categoría actualizada exitosamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
ModelState.AddModelError("", "No se pudo actualizar la categoría o no fue encontrada.");
|
||||
}
|
||||
|
||||
return View(categoria);
|
||||
}
|
||||
|
||||
// POST: Categorias/Delete/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var result = await _service.DeleteAsync(id);
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Categoría eliminada exitosamente.";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["ErrorMessage"] = "No se pudo eliminar la categoría.";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
221
RS_system/Controllers/ColaboracionController.cs
Normal file
221
RS_system/Controllers/ColaboracionController.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Rs_system.Models.ViewModels;
|
||||
using Rs_system.Services;
|
||||
|
||||
namespace Rs_system.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class ColaboracionController : Controller
|
||||
{
|
||||
private readonly IColaboracionService _colaboracionService;
|
||||
private readonly IMiembroService _miembroService;
|
||||
|
||||
public ColaboracionController(
|
||||
IColaboracionService colaboracionService,
|
||||
IMiembroService miembroService)
|
||||
{
|
||||
_colaboracionService = colaboracionService;
|
||||
_miembroService = miembroService;
|
||||
}
|
||||
|
||||
// GET: Colaboracion
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
try
|
||||
{
|
||||
var colaboraciones = await _colaboracionService.GetColaboracionesRecientesAsync();
|
||||
return View(colaboraciones);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = $"Error al cargar colaboraciones: {ex.Message}";
|
||||
return View(new List<Models.Colaboracion>());
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Colaboracion/Create
|
||||
public async Task<IActionResult> Create()
|
||||
{
|
||||
try
|
||||
{
|
||||
var viewModel = new RegistrarColaboracionViewModel
|
||||
{
|
||||
MesInicial = DateTime.Now.Month,
|
||||
AnioInicial = DateTime.Now.Year,
|
||||
MesFinal = DateTime.Now.Month,
|
||||
AnioFinal = DateTime.Now.Year,
|
||||
MontoTotal = 0,
|
||||
TiposDisponibles = await _colaboracionService.GetTiposActivosAsync()
|
||||
};
|
||||
|
||||
await CargarMiembrosAsync();
|
||||
return View(viewModel);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = $"Error al cargar formulario: {ex.Message}";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
|
||||
// POST: Colaboracion/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(RegistrarColaboracionViewModel model)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var registradoPor = User.Identity?.Name ?? "Sistema";
|
||||
await _colaboracionService.RegistrarColaboracionAsync(model, registradoPor);
|
||||
|
||||
TempData["Success"] = "Colaboración registrada exitosamente";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError("", $"Error al registrar: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
// Recargar datos para la vista
|
||||
model.TiposDisponibles = await _colaboracionService.GetTiposActivosAsync();
|
||||
await CargarMiembrosAsync();
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// GET: Colaboracion/Details/5
|
||||
public async Task<IActionResult> Details(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var colaboracion = await _colaboracionService.GetColaboracionByIdAsync(id);
|
||||
if (colaboracion == null)
|
||||
{
|
||||
TempData["Error"] = "Colaboración no encontrada";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
return View(colaboracion);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = $"Error al cargar detalle: {ex.Message}";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Colaboracion/Reportes
|
||||
public IActionResult Reportes()
|
||||
{
|
||||
ViewBag.FechaInicio = DateTime.Now.Date;
|
||||
ViewBag.FechaFin = DateTime.Now.Date;
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: Colaboracion/GenerarReporte
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> GenerarReporte(DateTime fechaInicio, DateTime fechaFin)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Ajustar fecha fin para incluir todo el día
|
||||
var fechaFinAjustada = fechaFin.Date.AddDays(1).AddSeconds(-1);
|
||||
|
||||
var reporte = await _colaboracionService.GenerarReportePorFechasAsync(
|
||||
fechaInicio.Date,
|
||||
fechaFinAjustada);
|
||||
|
||||
return View("Reporte", reporte);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = $"Error al generar reporte: {ex.Message}";
|
||||
return RedirectToAction(nameof(Reportes));
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Colaboracion/EstadoCuenta/5
|
||||
public async Task<IActionResult> EstadoCuenta(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var estado = await _colaboracionService.GenerarEstadoCuentaAsync(id);
|
||||
return View(estado);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = $"Error al generar estado de cuenta: {ex.Message}";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Colaboracion/BuscarMiembros?termino=juan
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> BuscarMiembros(string termino)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(termino) || termino.Length < 2)
|
||||
{
|
||||
return Json(new List<object>());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var miembros = await _miembroService.GetAllAsync();
|
||||
|
||||
var resultados = miembros
|
||||
.Where(m =>
|
||||
m.Nombres.Contains(termino, StringComparison.OrdinalIgnoreCase) ||
|
||||
m.Apellidos.Contains(termino, StringComparison.OrdinalIgnoreCase) ||
|
||||
$"{m.Nombres} {m.Apellidos}".Contains(termino, StringComparison.OrdinalIgnoreCase))
|
||||
.Take(10)
|
||||
.Select(m => new
|
||||
{
|
||||
id = m.Id,
|
||||
text = $"{m.Nombres} {m.Apellidos}",
|
||||
telefono = m.Telefono
|
||||
})
|
||||
.ToList();
|
||||
|
||||
return Json(resultados);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new List<object>());
|
||||
}
|
||||
}
|
||||
|
||||
// GET: Colaboracion/ObtenerUltimosPagos?miembroId=5
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> ObtenerUltimosPagos(long miembroId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var ultimosPagos = await _colaboracionService.GetUltimosPagosPorMiembroAsync(miembroId);
|
||||
return Json(ultimosPagos);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new List<object>());
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
private async Task CargarMiembrosAsync()
|
||||
{
|
||||
var miembros = await _miembroService.GetAllAsync();
|
||||
ViewBag.Miembros = new SelectList(
|
||||
miembros.Select(m => new
|
||||
{
|
||||
Id = m.Id,
|
||||
NombreCompleto = $"{m.Nombres} {m.Apellidos}"
|
||||
}),
|
||||
"Id",
|
||||
"NombreCompleto"
|
||||
);
|
||||
}
|
||||
}
|
||||
127
RS_system/Controllers/ContabilidadController.cs
Normal file
127
RS_system/Controllers/ContabilidadController.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Services;
|
||||
using Rs_system.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Rs_system.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class ContabilidadController : Controller
|
||||
{
|
||||
private readonly IContabilidadService _contabilidadService;
|
||||
private readonly IMiembroService _miembroService;
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public ContabilidadController(IContabilidadService contabilidadService, IMiembroService miembroService, ApplicationDbContext context)
|
||||
{
|
||||
_contabilidadService = contabilidadService;
|
||||
_miembroService = miembroService;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index(long? grupoId)
|
||||
{
|
||||
var grupos = await _miembroService.GetGruposTrabajoAsync();
|
||||
ViewBag.Grupos = new SelectList(grupos.Select(g => new { g.Id, g.Nombre }), "Id", "Nombre", grupoId);
|
||||
|
||||
List<ReporteMensualContable> reportes = new();
|
||||
if (grupoId.HasValue)
|
||||
{
|
||||
reportes = await _contabilidadService.ListarReportesPorGrupoAsync(grupoId.Value);
|
||||
}
|
||||
|
||||
ViewBag.GrupoId = grupoId;
|
||||
return View(reportes);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> AbrirMes(long grupoId, int mes, int anio)
|
||||
{
|
||||
try
|
||||
{
|
||||
var reporte = await _contabilidadService.ObtenerOCrearReporteMensualAsync(grupoId, mes, anio);
|
||||
TempData["Success"] = $"Reporte de {reporte.NombreMes} {anio} abierto correctamente.";
|
||||
return RedirectToAction(nameof(RegistroMensual), new { id = reporte.Id });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = "Error al abrir el mes: " + ex.Message;
|
||||
return RedirectToAction(nameof(Index), new { grupoId });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> RegistroMensual(long id)
|
||||
{
|
||||
var reporte = await _context.ReportesMensualesContables
|
||||
.Include(r => r.GrupoTrabajo)
|
||||
.Include(r => r.Registros)
|
||||
.FirstOrDefaultAsync(r => r.Id == id);
|
||||
|
||||
if (reporte == null) return NotFound();
|
||||
|
||||
ViewBag.SaldoActual = await _contabilidadService.CalcularSaldoActualAsync(id);
|
||||
return View(reporte);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> GuardarBulk([FromBody] BulkSaveRequest request)
|
||||
{
|
||||
if (request == null || request.ReporteId <= 0) return BadRequest("Solicitud inválida.");
|
||||
|
||||
var registros = request.Registros.Select(r => new ContabilidadRegistro
|
||||
{
|
||||
Id = r.Id,
|
||||
Tipo = r.Tipo,
|
||||
Monto = r.Monto,
|
||||
Fecha = DateTime.SpecifyKind(r.Fecha, DateTimeKind.Utc),
|
||||
Descripcion = r.Descripcion ?? ""
|
||||
}).ToList();
|
||||
|
||||
var success = await _contabilidadService.GuardarRegistrosBulkAsync(request.ReporteId, registros);
|
||||
if (success)
|
||||
{
|
||||
var nuevoSaldo = await _contabilidadService.CalcularSaldoActualAsync(request.ReporteId);
|
||||
return Json(new { success = true, saldo = nuevoSaldo });
|
||||
}
|
||||
|
||||
return Json(new { success = false, message = "Error al guardar los registros. Verifique que el mes no esté cerrado." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> CerrarMes(long id)
|
||||
{
|
||||
var success = await _contabilidadService.CerrarReporteAsync(id);
|
||||
if (success)
|
||||
{
|
||||
TempData["Success"] = "El reporte ha sido cerrado. Ya no se pueden realizar cambios.";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["Error"] = "No se pudo cerrar el reporte.";
|
||||
}
|
||||
return RedirectToAction(nameof(RegistroMensual), new { id });
|
||||
}
|
||||
|
||||
// Helper classes for AJAX
|
||||
public class BulkSaveRequest
|
||||
{
|
||||
public long ReporteId { get; set; }
|
||||
public List<RegistroInput> Registros { get; set; } = new();
|
||||
}
|
||||
|
||||
public class RegistroInput
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public TipoMovimientoContable Tipo { get; set; }
|
||||
public decimal Monto { get; set; }
|
||||
public DateTime Fecha { get; set; }
|
||||
public string? Descripcion { get; set; }
|
||||
}
|
||||
}
|
||||
319
RS_system/Controllers/ContabilidadGeneralController.cs
Normal file
319
RS_system/Controllers/ContabilidadGeneralController.cs
Normal file
@@ -0,0 +1,319 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Services;
|
||||
using Rs_system.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Rs_system.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class ContabilidadGeneralController : Controller
|
||||
{
|
||||
private readonly IContabilidadGeneralService _contabilidadService;
|
||||
private readonly ApplicationDbContext _context;
|
||||
private readonly IFileStorageService _fileStorageService;
|
||||
|
||||
public ContabilidadGeneralController(IContabilidadGeneralService contabilidadService, ApplicationDbContext context, IFileStorageService fileStorageService)
|
||||
{
|
||||
_contabilidadService = contabilidadService;
|
||||
_context = context;
|
||||
_fileStorageService = fileStorageService;
|
||||
}
|
||||
|
||||
// ==================== Vista Principal ====================
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Index(int? anio)
|
||||
{
|
||||
var anioActual = anio ?? DateTime.Now.Year;
|
||||
ViewBag.Anio = anioActual;
|
||||
|
||||
// Generar lista de años disponibles
|
||||
var anios = Enumerable.Range(DateTime.Now.Year - 5, 10).Reverse();
|
||||
ViewBag.Anios = new SelectList(anios);
|
||||
|
||||
var reportes = await _contabilidadService.ListarReportesAsync(anioActual);
|
||||
return View(reportes);
|
||||
}
|
||||
|
||||
// ==================== Abrir/Crear Reporte Mensual ====================
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> AbrirMes(int mes, int anio)
|
||||
{
|
||||
try
|
||||
{
|
||||
var reporte = await _contabilidadService.ObtenerOCrearReporteMensualAsync(mes, anio);
|
||||
TempData["Success"] = $"Reporte de {reporte.NombreMes} {anio} abierto correctamente.";
|
||||
return RedirectToAction(nameof(RegistroMensual), new { id = reporte.Id });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = "Error al abrir el mes: " + ex.Message;
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Registro Mensual (Excel-like) ====================
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> RegistroMensual(long id)
|
||||
{
|
||||
var reporte = await _context.ReportesMensualesGenerales
|
||||
.Include(r => r.Movimientos)
|
||||
.ThenInclude(m => m.CategoriaIngreso)
|
||||
.Include(r => r.Movimientos)
|
||||
.ThenInclude(m => m.CategoriaEgreso)
|
||||
.FirstOrDefaultAsync(r => r.Id == id);
|
||||
|
||||
if (reporte == null) return NotFound();
|
||||
|
||||
ViewBag.SaldoActual = await _contabilidadService.CalcularSaldoActualAsync(id);
|
||||
ViewBag.CategoriasIngreso = await _contabilidadService.ObtenerCategoriasIngresoAsync();
|
||||
ViewBag.CategoriasEgreso = await _contabilidadService.ObtenerCategoriasEgresoAsync();
|
||||
|
||||
return View(reporte);
|
||||
}
|
||||
|
||||
// ==================== Guardar Movimientos Bulk (AJAX) ====================
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> GuardarBulk([FromBody] BulkSaveRequest request)
|
||||
{
|
||||
if (request == null || request.ReporteId <= 0)
|
||||
return BadRequest("Solicitud inválida.");
|
||||
|
||||
var movimientos = request.Movimientos.Select(m => new MovimientoGeneral
|
||||
{
|
||||
Id = m.Id,
|
||||
Tipo = m.Tipo,
|
||||
CategoriaIngresoId = m.CategoriaIngresoId,
|
||||
CategoriaEgresoId = m.CategoriaEgresoId,
|
||||
Monto = m.Monto,
|
||||
Fecha = DateTime.SpecifyKind(m.Fecha, DateTimeKind.Utc),
|
||||
Descripcion = m.Descripcion ?? "",
|
||||
NumeroComprobante = m.NumeroComprobante
|
||||
}).ToList();
|
||||
|
||||
var success = await _contabilidadService.GuardarMovimientosBulkAsync(request.ReporteId, movimientos);
|
||||
|
||||
if (success)
|
||||
{
|
||||
var nuevoSaldo = await _contabilidadService.CalcularSaldoActualAsync(request.ReporteId);
|
||||
return Json(new { success = true, saldo = nuevoSaldo });
|
||||
}
|
||||
|
||||
return Json(new { success = false, message = "Error al guardar los movimientos. Verifique que el mes no esté cerrado." });
|
||||
}
|
||||
|
||||
// ==================== Cerrar Mes ====================
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> CerrarMes(long id)
|
||||
{
|
||||
var success = await _contabilidadService.CerrarReporteAsync(id);
|
||||
if (success)
|
||||
{
|
||||
TempData["Success"] = "El reporte ha sido cerrado. Ya no se pueden realizar cambios.";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["Error"] = "No se pudo cerrar el reporte.";
|
||||
}
|
||||
return RedirectToAction(nameof(RegistroMensual), new { id });
|
||||
}
|
||||
|
||||
// ==================== Consolidado Mensual ====================
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> Consolidado(long id)
|
||||
{
|
||||
var reporte = await _context.ReportesMensualesGenerales
|
||||
.FirstOrDefaultAsync(r => r.Id == id);
|
||||
|
||||
if (reporte == null) return NotFound();
|
||||
|
||||
ViewBag.ConsolidadoIngresos = await _contabilidadService.ObtenerConsolidadoIngresosAsync(id);
|
||||
ViewBag.ConsolidadoEgresos = await _contabilidadService.ObtenerConsolidadoEgresosAsync(id);
|
||||
ViewBag.SaldoActual = await _contabilidadService.CalcularSaldoActualAsync(id);
|
||||
|
||||
return View(reporte);
|
||||
}
|
||||
|
||||
// ==================== Gestión de Categorías ====================
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GestionCategorias()
|
||||
{
|
||||
var categoriasIngreso = await _context.CategoriasIngreso
|
||||
.OrderBy(c => c.Nombre)
|
||||
.ToListAsync();
|
||||
|
||||
var categoriasEgreso = await _context.CategoriasEgreso
|
||||
.OrderBy(c => c.Nombre)
|
||||
.ToListAsync();
|
||||
|
||||
ViewBag.CategoriasIngreso = categoriasIngreso;
|
||||
ViewBag.CategoriasEgreso = categoriasEgreso;
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
// ==================== CRUD Categorías Ingreso ====================
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> CrearCategoriaIngreso(CategoriaIngreso categoria)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
await _contabilidadService.CrearCategoriaIngresoAsync(categoria);
|
||||
TempData["Success"] = "Categoría de ingreso creada exitosamente.";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["Error"] = "Error al crear la categoría.";
|
||||
}
|
||||
return RedirectToAction(nameof(GestionCategorias));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> EditarCategoriaIngreso(CategoriaIngreso categoria)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var success = await _contabilidadService.ActualizarCategoriaIngresoAsync(categoria);
|
||||
TempData[success ? "Success" : "Error"] = success
|
||||
? "Categoría actualizada exitosamente."
|
||||
: "Error al actualizar la categoría.";
|
||||
}
|
||||
return RedirectToAction(nameof(GestionCategorias));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> EliminarCategoriaIngreso(long id)
|
||||
{
|
||||
var success = await _contabilidadService.EliminarCategoriaIngresoAsync(id);
|
||||
TempData[success ? "Success" : "Error"] = success
|
||||
? "Categoría eliminada exitosamente."
|
||||
: "Error al eliminar la categoría.";
|
||||
return RedirectToAction(nameof(GestionCategorias));
|
||||
}
|
||||
|
||||
// ==================== CRUD Categorías Egreso ====================
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> CrearCategoriaEgreso(CategoriaEgreso categoria)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
await _contabilidadService.CrearCategoriaEgresoAsync(categoria);
|
||||
TempData["Success"] = "Categoría de egreso creada exitosamente.";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["Error"] = "Error al crear la categoría.";
|
||||
}
|
||||
return RedirectToAction(nameof(GestionCategorias));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> EditarCategoriaEgreso(CategoriaEgreso categoria)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var success = await _contabilidadService.ActualizarCategoriaEgresoAsync(categoria);
|
||||
TempData[success ? "Success" : "Error"] = success
|
||||
? "Categoría actualizada exitosamente."
|
||||
: "Error al actualizar la categoría.";
|
||||
}
|
||||
return RedirectToAction(nameof(GestionCategorias));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> EliminarCategoriaEgreso(long id)
|
||||
{
|
||||
var success = await _contabilidadService.EliminarCategoriaEgresoAsync(id);
|
||||
TempData[success ? "Success" : "Error"] = success
|
||||
? "Categoría eliminada exitosamente."
|
||||
: "Error al eliminar la categoría.";
|
||||
return RedirectToAction(nameof(GestionCategorias));
|
||||
}
|
||||
|
||||
// ==================== Helper Classes for AJAX ====================
|
||||
|
||||
public class BulkSaveRequest
|
||||
{
|
||||
public long ReporteId { get; set; }
|
||||
public List<MovimientoInput> Movimientos { get; set; } = new();
|
||||
}
|
||||
|
||||
public class MovimientoInput
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public int Tipo { get; set; }
|
||||
public long? CategoriaIngresoId { get; set; }
|
||||
public long? CategoriaEgresoId { get; set; }
|
||||
public decimal Monto { get; set; }
|
||||
public DateTime Fecha { get; set; }
|
||||
public string? Descripcion { get; set; }
|
||||
public string? NumeroComprobante { get; set; }
|
||||
}
|
||||
|
||||
// ==================== Adjuntos ====================
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> ObtenerAdjuntos(long movimientoId)
|
||||
{
|
||||
var adjuntos = await _contabilidadService.ObtenerAdjuntosMovimientoAsync(movimientoId);
|
||||
return Json(adjuntos.Select(a => new {
|
||||
id = a.Id,
|
||||
nombre = a.NombreArchivo,
|
||||
url = _fileStorageService.GetFileUrl(a.RutaArchivo),
|
||||
tipo = a.TipoContenido,
|
||||
fecha = a.FechaSubida.ToLocalTime().ToString("g")
|
||||
}));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> SubirAdjunto(long movimientoId, List<IFormFile> archivos)
|
||||
{
|
||||
if (movimientoId <= 0 || archivos == null || !archivos.Any())
|
||||
return BadRequest("Datos inválidos.");
|
||||
|
||||
int count = 0;
|
||||
foreach (var archivo in archivos)
|
||||
{
|
||||
if (archivo.Length > 0)
|
||||
{
|
||||
// El usuario solicitó guardar en uploads/miembros
|
||||
var ruta = await _fileStorageService.SaveFileAsync(archivo, "miembros");
|
||||
if (!string.IsNullOrEmpty(ruta))
|
||||
{
|
||||
await _contabilidadService.CrearAdjuntoAsync(movimientoId, archivo.FileName, ruta, archivo.ContentType);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Json(new { success = true, count = count, message = $"{count} archivos subidos correctamente." });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> EliminarAdjunto(long id)
|
||||
{
|
||||
// Primero obtener para borrar el archivo físico si es necesario (opcional, aquí solo borramos registro BD)
|
||||
// O idealmente el servicio se encarga. Por ahora solo borramos BD.
|
||||
var success = await _contabilidadService.EliminarAdjuntoAsync(id);
|
||||
return Json(new { success = success });
|
||||
}
|
||||
}
|
||||
125
RS_system/Controllers/EstadosController.cs
Normal file
125
RS_system/Controllers/EstadosController.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Services;
|
||||
|
||||
namespace Rs_system.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class EstadosController : Controller
|
||||
{
|
||||
private readonly IEstadoArticuloService _service;
|
||||
|
||||
public EstadosController(IEstadoArticuloService service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
|
||||
// GET: Estados
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var list = await _service.GetAllAsync();
|
||||
return View(list);
|
||||
}
|
||||
|
||||
// GET: Estados/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: Estados/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create([Bind("Nombre,Descripcion,Color,Activo")] EstadoArticulo estado)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(estado.Nombre))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "El nombre es obligatorio.");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (await _service.ExistsAsync(estado.Nombre))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "Ya existe un estado con ese nombre.");
|
||||
return View(estado);
|
||||
}
|
||||
|
||||
estado.CreadoPor = User.Identity?.Name ?? "Sistema";
|
||||
var result = await _service.CreateAsync(estado);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Estado creado exitosamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
ModelState.AddModelError("", "Ocurrió un error al guardar los datos.");
|
||||
}
|
||||
return View(estado);
|
||||
}
|
||||
|
||||
// GET: Estados/Edit/5
|
||||
public async Task<IActionResult> Edit(int? id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
|
||||
var estado = await _service.GetByIdAsync(id.Value);
|
||||
if (estado == null) return NotFound();
|
||||
|
||||
return View(estado);
|
||||
}
|
||||
|
||||
// POST: Estados/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(int id, [Bind("Id,Nombre,Descripcion,Color,Activo")] EstadoArticulo estado)
|
||||
{
|
||||
if (id != estado.Id) return NotFound();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(estado.Nombre))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "El nombre es obligatorio.");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (await _service.ExistsAsync(estado.Nombre, id))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "Ya existe otro estado con ese nombre.");
|
||||
return View(estado);
|
||||
}
|
||||
|
||||
var result = await _service.UpdateAsync(estado);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Estado actualizado exitosamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
ModelState.AddModelError("", "No se pudo actualizar el estado o no fue encontrado.");
|
||||
}
|
||||
|
||||
return View(estado);
|
||||
}
|
||||
|
||||
// POST: Estados/Delete/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var result = await _service.DeleteAsync(id);
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Estado eliminado exitosamente.";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["ErrorMessage"] = "No se pudo eliminar el estado.";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
190
RS_system/Controllers/MovimientosInventarioController.cs
Normal file
190
RS_system/Controllers/MovimientosInventarioController.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Services;
|
||||
|
||||
namespace Rs_system.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class MovimientosInventarioController : Controller
|
||||
{
|
||||
private readonly IMovimientoService _movimientoService;
|
||||
private readonly IArticuloService _articuloService;
|
||||
private readonly IUbicacionService _ubicacionService;
|
||||
private readonly IEstadoArticuloService _estadoService;
|
||||
private readonly IPrestamoService _prestamoService;
|
||||
|
||||
public MovimientosInventarioController(
|
||||
IMovimientoService movimientoService,
|
||||
IArticuloService articuloService,
|
||||
IUbicacionService ubicacionService,
|
||||
IEstadoArticuloService estadoService,
|
||||
IPrestamoService prestamoService)
|
||||
{
|
||||
_movimientoService = movimientoService;
|
||||
_articuloService = articuloService;
|
||||
_ubicacionService = ubicacionService;
|
||||
_estadoService = estadoService;
|
||||
_prestamoService = prestamoService;
|
||||
}
|
||||
|
||||
// GET: MovimientosInventario
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var historial = await _movimientoService.GetHistorialGeneralAsync(50); // Limit 50 for performance
|
||||
return View(historial);
|
||||
}
|
||||
|
||||
// GET: MovimientosInventario/Create
|
||||
// This is the "Wizard" or "Action Selector"
|
||||
public async Task<IActionResult> Create(int? articuloId)
|
||||
{
|
||||
if (articuloId.HasValue)
|
||||
{
|
||||
var articulo = await _articuloService.GetByIdAsync(articuloId.Value);
|
||||
if (articulo == null) return NotFound();
|
||||
|
||||
ViewBag.ArticuloId = articulo.Id;
|
||||
ViewBag.ArticuloNombre = $"{articulo.Codigo} - {articulo.Nombre}";
|
||||
ViewBag.UbicacionActual = articulo.UbicacionNombre;
|
||||
ViewBag.EstadoActual = articulo.EstadoNombre;
|
||||
ViewBag.TipoControl = articulo.TipoControl; // "UNITARIO" or "LOTE"
|
||||
ViewBag.CantidadGlobal = articulo.CantidadGlobal; // For LOTE validation?
|
||||
}
|
||||
|
||||
ViewBag.Articulos = new SelectList((await _articuloService.GetAllAsync()).Select(x => new { x.Id, Nombre = $"{x.Codigo} - {x.Nombre}" }), "Id", "Nombre", articuloId);
|
||||
ViewBag.Ubicaciones = new SelectList(await _ubicacionService.GetAllAsync(), "Id", "Nombre");
|
||||
ViewBag.Estados = new SelectList(await _estadoService.GetAllAsync(), "Id", "Nombre");
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: MovimientosInventario/RegistrarTraslado
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RegistrarTraslado(int articuloId, int nuevaUbicacionId, string observacion, int cantidad = 1)
|
||||
{
|
||||
var usuario = User.Identity?.Name ?? "Sistema";
|
||||
// Use the new Quantity-Aware method
|
||||
var result = await _movimientoService.RegistrarTrasladoCantidadAsync(articuloId, nuevaUbicacionId, cantidad, observacion, usuario);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Traslado registrado correctamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
TempData["ErrorMessage"] = "Error al registrar el traslado. Verifique stock o campos.";
|
||||
return RedirectToAction(nameof(Create), new { articuloId });
|
||||
}
|
||||
|
||||
// POST: MovimientosInventario/RegistrarBaja
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RegistrarBaja(int articuloId, string motivo, int cantidad = 1)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(motivo))
|
||||
{
|
||||
TempData["ErrorMessage"] = "Debe especificar un motivo para la baja.";
|
||||
return RedirectToAction(nameof(Create), new { articuloId });
|
||||
}
|
||||
|
||||
var usuario = User.Identity?.Name ?? "Sistema";
|
||||
var result = await _movimientoService.RegistrarBajaCantidadAsync(articuloId, cantidad, motivo, usuario);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Baja registrada correctamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
TempData["ErrorMessage"] = "Error al registrar la baja.";
|
||||
return RedirectToAction(nameof(Create), new { articuloId });
|
||||
}
|
||||
|
||||
// POST: MovimientosInventario/RegistrarCambioEstado
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RegistrarCambioEstado(int articuloId, int nuevoEstadoId, string observacion)
|
||||
{
|
||||
var usuario = User.Identity?.Name ?? "Sistema";
|
||||
var result = await _movimientoService.RegistrarCambioEstadoAsync(articuloId, nuevoEstadoId, observacion, usuario);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Cambio de estado registrado correctamento.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
TempData["ErrorMessage"] = "Error al registrar el cambio de estado. Verifique que el estado sea diferente al actual.";
|
||||
return RedirectToAction(nameof(Create), new { articuloId });
|
||||
}
|
||||
|
||||
// POST: MovimientosInventario/RegistrarPrestamo
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RegistrarPrestamo(int articuloId, int cantidad, string personaNombre, string personaIdentificacion, DateTime? fechaDevolucionEstimada, string observacion)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(personaNombre))
|
||||
{
|
||||
TempData["ErrorMessage"] = "Debe especificar el nombre de la persona a quien se presta el artículo.";
|
||||
return RedirectToAction(nameof(Create), new { articuloId });
|
||||
}
|
||||
|
||||
var usuario = User.Identity?.Name ?? "Sistema";
|
||||
var result = await _prestamoService.RegistrarPrestamoAsync(articuloId, cantidad, personaNombre, personaIdentificacion, fechaDevolucionEstimada, observacion, usuario);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Préstamo registrado correctamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
TempData["ErrorMessage"] = "Error al registrar el préstamo. Verifique stock disponible.";
|
||||
return RedirectToAction(nameof(Create), new { articuloId });
|
||||
}
|
||||
|
||||
// GET: MovimientosInventario/PrestamosActivos
|
||||
public async Task<IActionResult> PrestamosActivos()
|
||||
{
|
||||
var prestamosActivos = await _prestamoService.GetPrestamosActivosAsync();
|
||||
return View(prestamosActivos);
|
||||
}
|
||||
|
||||
// POST: MovimientosInventario/RegistrarDevolucion
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RegistrarDevolucion(long prestamoId, string observacion)
|
||||
{
|
||||
var usuario = User.Identity?.Name ?? "Sistema";
|
||||
var result = await _prestamoService.RegistrarDevolucionAsync(prestamoId, observacion, usuario);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Devolución registrada correctamente.";
|
||||
return RedirectToAction(nameof(PrestamosActivos));
|
||||
}
|
||||
|
||||
TempData["ErrorMessage"] = "Error al registrar la devolución.";
|
||||
return RedirectToAction(nameof(PrestamosActivos));
|
||||
}
|
||||
|
||||
// POST: MovimientosInventario/RegistrarEntrada
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> RegistrarEntrada(int articuloId, int cantidad, string observacion)
|
||||
{
|
||||
var usuario = User.Identity?.Name ?? "Sistema";
|
||||
var result = await _movimientoService.RegistrarEntradaCantidadAsync(articuloId, cantidad, observacion, usuario);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Entrada de inventario registrada correctamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
TempData["ErrorMessage"] = "Error al registrar la entrada de inventario.";
|
||||
return RedirectToAction(nameof(Create), new { articuloId });
|
||||
}
|
||||
}
|
||||
168
RS_system/Controllers/TipoColaboracionController.cs
Normal file
168
RS_system/Controllers/TipoColaboracionController.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Rs_system.Data;
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Services;
|
||||
|
||||
namespace Rs_system.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class TipoColaboracionController : Controller
|
||||
{
|
||||
private readonly IColaboracionService _colaboracionService;
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
public TipoColaboracionController(IColaboracionService colaboracionService, ApplicationDbContext context)
|
||||
{
|
||||
_colaboracionService = colaboracionService;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// GET: TipoColaboracion
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
try
|
||||
{
|
||||
var tipos = await _context.TiposColaboracion
|
||||
.OrderBy(t => t.Orden)
|
||||
.ToListAsync();
|
||||
return View(tipos);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = $"Error al cargar tipos: {ex.Message}";
|
||||
return View(new List<TipoColaboracion>());
|
||||
}
|
||||
}
|
||||
|
||||
// GET: TipoColaboracion/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
var model = new TipoColaboracion
|
||||
{
|
||||
MontoSugerido = 1.00m,
|
||||
Activo = true
|
||||
};
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// POST: TipoColaboracion/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create(TipoColaboracion model)
|
||||
{
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
model.CreadoEn = DateTime.UtcNow;
|
||||
model.ActualizadoEn = DateTime.UtcNow;
|
||||
|
||||
_context.TiposColaboracion.Add(model);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
TempData["Success"] = "Tipo de colaboración creado exitosamente";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError("", $"Error al crear: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// GET: TipoColaboracion/Edit/5
|
||||
public async Task<IActionResult> Edit(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tipo = await _context.TiposColaboracion.FindAsync(id);
|
||||
if (tipo == null)
|
||||
{
|
||||
TempData["Error"] = "Tipo de colaboración no encontrado";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
return View(tipo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = $"Error al cargar tipo: {ex.Message}";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
|
||||
// POST: TipoColaboracion/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(long id, TipoColaboracion model)
|
||||
{
|
||||
if (id != model.Id)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tipo = await _context.TiposColaboracion.FindAsync(id);
|
||||
if (tipo == null)
|
||||
{
|
||||
TempData["Error"] = "Tipo de colaboración no encontrado";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
tipo.Nombre = model.Nombre;
|
||||
tipo.Descripcion = model.Descripcion;
|
||||
tipo.MontoSugerido = model.MontoSugerido;
|
||||
tipo.Activo = model.Activo;
|
||||
tipo.Orden = model.Orden;
|
||||
tipo.ActualizadoEn = DateTime.UtcNow;
|
||||
_context.TiposColaboracion.Update(tipo);
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
TempData["Success"] = "Tipo de colaboración actualizado exitosamente";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ModelState.AddModelError("", $"Error al actualizar: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// POST: TipoColaboracion/Delete/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(long id)
|
||||
{
|
||||
try
|
||||
{
|
||||
var tipo = await _context.TiposColaboracion.FindAsync(id);
|
||||
if (tipo == null)
|
||||
{
|
||||
TempData["Error"] = "Tipo de colaboración no encontrado";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
// Soft delete - just deactivate
|
||||
tipo.Activo = false;
|
||||
tipo.ActualizadoEn = DateTime.UtcNow;
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
TempData["Success"] = "Tipo de colaboración desactivado exitosamente";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TempData["Error"] = $"Error al desactivar: {ex.Message}";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
125
RS_system/Controllers/UbicacionesController.cs
Normal file
125
RS_system/Controllers/UbicacionesController.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Rs_system.Models;
|
||||
using Rs_system.Services;
|
||||
|
||||
namespace Rs_system.Controllers;
|
||||
|
||||
[Authorize]
|
||||
public class UbicacionesController : Controller
|
||||
{
|
||||
private readonly IUbicacionService _service;
|
||||
|
||||
public UbicacionesController(IUbicacionService service)
|
||||
{
|
||||
_service = service;
|
||||
}
|
||||
|
||||
// GET: Ubicaciones
|
||||
public async Task<IActionResult> Index()
|
||||
{
|
||||
var list = await _service.GetAllAsync();
|
||||
return View(list);
|
||||
}
|
||||
|
||||
// GET: Ubicaciones/Create
|
||||
public IActionResult Create()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
// POST: Ubicaciones/Create
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Create([Bind("Nombre,Descripcion,Responsable,Activo")] Ubicacion ubicacion)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ubicacion.Nombre))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "El nombre es obligatorio.");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (await _service.ExistsAsync(ubicacion.Nombre))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "Ya existe una ubicación con ese nombre.");
|
||||
return View(ubicacion);
|
||||
}
|
||||
|
||||
ubicacion.CreadoPor = User.Identity?.Name ?? "Sistema";
|
||||
var result = await _service.CreateAsync(ubicacion);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Ubicación creada exitosamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
ModelState.AddModelError("", "Ocurrió un error al guardar los datos.");
|
||||
}
|
||||
return View(ubicacion);
|
||||
}
|
||||
|
||||
// GET: Ubicaciones/Edit/5
|
||||
public async Task<IActionResult> Edit(int? id)
|
||||
{
|
||||
if (id == null) return NotFound();
|
||||
|
||||
var ubicacion = await _service.GetByIdAsync(id.Value);
|
||||
if (ubicacion == null) return NotFound();
|
||||
|
||||
return View(ubicacion);
|
||||
}
|
||||
|
||||
// POST: Ubicaciones/Edit/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Edit(int id, [Bind("Id,Nombre,Descripcion,Responsable,Activo")] Ubicacion ubicacion)
|
||||
{
|
||||
if (id != ubicacion.Id) return NotFound();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ubicacion.Nombre))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "El nombre es obligatorio.");
|
||||
}
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
if (await _service.ExistsAsync(ubicacion.Nombre, id))
|
||||
{
|
||||
ModelState.AddModelError("Nombre", "Ya existe otra ubicación con ese nombre.");
|
||||
return View(ubicacion);
|
||||
}
|
||||
|
||||
var result = await _service.UpdateAsync(ubicacion);
|
||||
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Ubicación actualizada exitosamente.";
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
|
||||
ModelState.AddModelError("", "No se pudo actualizar la ubicación o no fue encontrada.");
|
||||
}
|
||||
|
||||
return View(ubicacion);
|
||||
}
|
||||
|
||||
// POST: Ubicaciones/Delete/5
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> Delete(int id)
|
||||
{
|
||||
var result = await _service.DeleteAsync(id);
|
||||
if (result)
|
||||
{
|
||||
TempData["SuccessMessage"] = "Ubicación eliminada exitosamente.";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData["ErrorMessage"] = "No se pudo eliminar la ubicación.";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(Index));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user