From f5e3c4aacd3809dded796defbcd7ce1b4b5dae53 Mon Sep 17 00:00:00 2001 From: fede Date: Sun, 19 Jan 2025 01:15:06 -0300 Subject: [PATCH] falta soporte para el menu de propietario de mostrar los archivos --- .gitignore | 1 + Aspnet/AlquilaFacil.csproj | 1 + Aspnet/Controllers/ContratoController.cs | 50 +++++++++- Aspnet/Facade/DocumentoFacade.cs | 24 +++++ Aspnet/Facade/DocumentoGeneradorHtml.cs | 45 +++++++++ Aspnet/Facade/DocumentoGeneradorPdf.cs | 67 +++++++++++++ Front/src/Componentes/ModalPedirDoc.svelte | 44 +++++++++ Front/src/paginas/ContratoInquilino.svelte | 96 ++++++++++++++++--- Front/src/paginas/ContratosPropietario.svelte | 72 +++++++++++++- Modelo/RepositorioCanons.cs | 29 +++--- 10 files changed, 400 insertions(+), 29 deletions(-) create mode 100644 Aspnet/Facade/DocumentoFacade.cs create mode 100644 Aspnet/Facade/DocumentoGeneradorHtml.cs create mode 100644 Aspnet/Facade/DocumentoGeneradorPdf.cs create mode 100644 Front/src/Componentes/ModalPedirDoc.svelte diff --git a/.gitignore b/.gitignore index baab4fc..0a0f362 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /Entidades/obj/ /Aspnet/obj/ /Modelo/bin/ +Aspnet/bin/ diff --git a/Aspnet/AlquilaFacil.csproj b/Aspnet/AlquilaFacil.csproj index a513915..91ae823 100644 --- a/Aspnet/AlquilaFacil.csproj +++ b/Aspnet/AlquilaFacil.csproj @@ -10,6 +10,7 @@ + diff --git a/Aspnet/Controllers/ContratoController.cs b/Aspnet/Controllers/ContratoController.cs index 46bf234..fa6c146 100644 --- a/Aspnet/Controllers/ContratoController.cs +++ b/Aspnet/Controllers/ContratoController.cs @@ -2,6 +2,7 @@ using System.Net; using System.Text.Json; using AlquilaFacil.Builder; using AlquilaFacil.Config; +using AlquilaFacil.Facade; using Entidades; using Entidades.Dto; using Microsoft.AspNetCore.Mvc; @@ -16,6 +17,51 @@ namespace AlquilaFacil.Controllers; [ApiController] public class ContratoController: ControllerBase { + [HttpPost("api/contrato/GenerarRecibo")] + public ActionResult GenerarRecibo([FromHeader(Name="Auth")]string Auth, MarcarPagoDto dto, bool html= true) { + if (String.IsNullOrEmpty(Auth)) return Unauthorized(); + var validacion1 = RepositorioGrupos.Singleton.CheckGrupos(Auth, "Propietario"); + if (validacion1 == false){ + validacion1 = RepositorioGrupos.Singleton.CheckGrupos(Auth, "Inquilino"); + if (validacion1 == false) { + return Unauthorized(); + } + } + + if (dto.Idcontrato <= 0) return BadRequest(new { message = "No puede tener un contrato con id 0 o menor"}); + + Contrato? cont = RepositorioContratos.Singleton.ObtenerContratoPorId(dto.Idcontrato); + if (cont == null) return BadRequest(new { message = "No hay un contrato por ese id"}); + + if (cont.DniinquilinoNavigation == null || cont.DnipropietarioNavigation == null || cont.IdpropiedadNavigation == null || + cont.IdpropiedadNavigation.IdtipropiedadNavigation == null) return BadRequest(new { message = "comunicate con el admin esta mal cargado el contratod de alguina forma"}); + + Canon? can = RepositorioCanons.Singleton.ObtenerCanonContrato(dto.fecha, dto.Idcontrato); + if (can == null) return BadRequest(new { message = "no hay un canon para ese contrato con esa fecha"}); + if (can.IdreciboNavigation == null) return BadRequest(new { message = "No hay un recibo para ese canon"}); + + var cdb = new ContratoDtoBuilder() + .SetInquilino($"{cont.DniinquilinoNavigation.Nombre} {cont.DniinquilinoNavigation.Apellido}") + .SetUbicacion(cont.IdpropiedadNavigation.Ubicacion) + .SetPropietario($"{cont.DnipropietarioNavigation.Nombre} {cont.DnipropietarioNavigation.Apellido}") + .SetId(cont.Id) + .SetTipo(cont.IdpropiedadNavigation.IdtipropiedadNavigation.Descripcion) + .SetFechaInicio(cont.Fechainicio) + .SetEstado(cont.Habilitado, cont.Cancelado) + .Build(); + + var dof = new DocumentoFacade(); + MemoryStream? memstr = new(); + if (html){ + dof.GenerarHtml(cdb, can.IdreciboNavigation, memstr); + return File(memstr, "text/html", "Recibo.html"); + } else { + dof.GenerarPdf (cdb, can.IdreciboNavigation, memstr); + return File(memstr, "application/pdf", "Recibo.pdf"); + } + + } + [HttpPost("api/contratos/marcarPago")] public IActionResult marcarPago([FromHeader(Name="Auth")]string Auth, MarcarPagoDto dto) { if (String.IsNullOrEmpty(Auth)) return Unauthorized(); @@ -73,7 +119,7 @@ public class ContratoController: ControllerBase { Ok(new { message = "Se guardo correctamente"}):BadRequest(new { message = "No se pudo guardar"}); } - [HttpPost("api/contratos/crearcanons")] //WIP + [HttpPost("api/contratos/crearcanons")] public IActionResult crearCanons([FromHeader(Name="Auth")]string Auth, CrearCanonsDto dto){ if (String.IsNullOrEmpty(Auth)) return Unauthorized(); var validacion1 = RepositorioGrupos.Singleton.CheckGrupos(Auth, "Propietario"); @@ -81,7 +127,7 @@ public class ContratoController: ControllerBase { Cliente? cli = RepositorioUsuarios.Singleton.ObtenerClientePorToken(Auth); if (cli == null) return Unauthorized(); - if (dto.aumento <=Decimal.Zero || dto.idcontrato <=0) return BadRequest(new { message ="estan mal cargados los datos"}); + if (dto.idcontrato <=0) return BadRequest(new { message ="estan mal cargados los datos"}); Contrato? cont = RepositorioContratos.Singleton.ObtenerContratoPorId(dto.idcontrato); if (cont == null) return BadRequest(new { message = "no hay un contrato por esa id"}); diff --git a/Aspnet/Facade/DocumentoFacade.cs b/Aspnet/Facade/DocumentoFacade.cs new file mode 100644 index 0000000..71f3d64 --- /dev/null +++ b/Aspnet/Facade/DocumentoFacade.cs @@ -0,0 +1,24 @@ +using System.Runtime; +using System.Text; +using Entidades; +using Entidades.Dto; + +namespace AlquilaFacil.Facade; + +public class DocumentoFacade { + private readonly DocumentoGeneradorHtml d1 = new(); + private readonly DocumentoGeneradorPdf d2 = new(); + + public void GenerarHtml(ContratoDto cd, Recibo r, MemoryStream memoryStream) { + string str = d1.GenerarHTML(cd, r); + StreamWriter writer = new StreamWriter(memoryStream, Encoding.UTF8); + writer.WriteAsync(str).Wait(); + writer.FlushAsync().Wait(); + memoryStream.Position = 0; + } + public void GenerarPdf(ContratoDto cd, Recibo r, MemoryStream memoryStream) { + var mem = d2.GenerarPdf(cd, r); + mem.CopyToAsync(memoryStream).Wait(); + memoryStream.Position = 0; + } +} \ No newline at end of file diff --git a/Aspnet/Facade/DocumentoGeneradorHtml.cs b/Aspnet/Facade/DocumentoGeneradorHtml.cs new file mode 100644 index 0000000..f6ff529 --- /dev/null +++ b/Aspnet/Facade/DocumentoGeneradorHtml.cs @@ -0,0 +1,45 @@ +using Entidades; +using Entidades.Dto; +using Microsoft.AspNetCore.Routing.Template; + +namespace AlquilaFacil.Facade; +public class DocumentoGeneradorHtml { + public string GenerarHTML(ContratoDto cd, Recibo r) { + string tmpl =$""" + + + + + + +
+
+
+ AlquilaFácil +
+
+
Detalles
+

+ ID: {cd.id}
+ Ubicación: {cd.Ubicacion}
+ Tipo de Propiedad: {cd.TipoPropiedad}
+ Fecha de Inicio: {cd.Fechainicio}
+ Inquilino: {cd.Inquilino}
+ Propietario: {cd.Propietario}
+

+
+
Detalles del Recibo
+

+ ID del Recibo: {r.Id}
+ Fecha: {r.Fecha}
+ Monto: {r.Monto}
+

PAGO

+

+
+
+
+ + + """; return tmpl; + } + } diff --git a/Aspnet/Facade/DocumentoGeneradorPdf.cs b/Aspnet/Facade/DocumentoGeneradorPdf.cs new file mode 100644 index 0000000..e1e8279 --- /dev/null +++ b/Aspnet/Facade/DocumentoGeneradorPdf.cs @@ -0,0 +1,67 @@ +using Entidades; +using Entidades.Dto; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; +using System.IO; + +namespace AlquilaFacil.Facade; +public class DocumentoGeneradorPdf { + public MemoryStream GenerarPdf(ContratoDto cd, Recibo r) + { + var pdfStream = new MemoryStream(); + QuestPDF.Settings.License = LicenseType.Community; + Document.Create(container => + { + container.Page(page => + { + page.Size(PageSizes.A4); + page.Margin(2, Unit.Centimetre); + page.Header().Text("AlquilaFácil").FontSize(20).SemiBold().FontColor(Colors.White); + page.Content().Column(column => + { + column.Spacing(10); + + column.Item().Border(1).Padding(10).Column(card => + { + card.Item().Row(row => + { + row.RelativeItem().Text("Detalles").FontSize(16).SemiBold(); + }); + + card.Item().Column(body => + { + body.Spacing(5); + body.Item().Text($"ID: {cd.id}").FontSize(12).Bold(); + body.Item().Text($"Ubicación: {cd.Ubicacion}").FontSize(12); + body.Item().Text($"Tipo de Propiedad: {cd.TipoPropiedad}").FontSize(12); + body.Item().Text($"Fecha de Inicio: {cd.Fechainicio}").FontSize(12); + body.Item().Text($"Inquilino: {cd.Inquilino}").FontSize(12); + body.Item().Text($"Propietario: {cd.Propietario}").FontSize(12); + }); + }); + + column.Item().Border(1).Padding(10).Column(card => + { + card.Item().Row(row => + { + row.RelativeItem().Text("Detalles del Recibo").FontSize(16).SemiBold(); + }); + + card.Item().Column(body => + { + body.Spacing(5); + body.Item().Text($"ID del Recibo: {r.Id}").FontSize(12).Bold(); + body.Item().Text($"Fecha: {r.Fecha}").FontSize(12); + body.Item().Text($"Monto: {r.Monto}").FontSize(12); + body.Item().AlignCenter().Text("PAGO").FontSize(20).Bold(); + }); + }); + }); + }); + }).GeneratePdf(pdfStream); + + pdfStream.Position = 0; + return pdfStream; + } +} \ No newline at end of file diff --git a/Front/src/Componentes/ModalPedirDoc.svelte b/Front/src/Componentes/ModalPedirDoc.svelte new file mode 100644 index 0000000..1615f74 --- /dev/null +++ b/Front/src/Componentes/ModalPedirDoc.svelte @@ -0,0 +1,44 @@ + + + + \ No newline at end of file diff --git a/Front/src/paginas/ContratoInquilino.svelte b/Front/src/paginas/ContratoInquilino.svelte index 9c6cac3..7e70dfc 100644 --- a/Front/src/paginas/ContratoInquilino.svelte +++ b/Front/src/paginas/ContratoInquilino.svelte @@ -4,12 +4,12 @@ import ModalEstatico from "../Componentes/ModalEstatico.svelte"; import {urlG} from "../stores/urlStore"; import type { CanonDto, ContratoDto, ContratoPropiedadDto, GaranteDto2 } from "../types"; - + import ModalPedirDoc from "../Componentes/ModalPedirDoc.svelte"; let token:string = sessionStorage.getItem("token")||""; - let interes:number = $state(0); - + let selMod:any =$state(); + let showmodal:boolean = $state(false); let canons:CanonDto[] = $state([]); let garantes: GaranteDto2[] = $state([]); let prop:ContratoPropiedadDto = $state({ @@ -72,13 +72,30 @@ const qs = window.location.search; const par = new URLSearchParams(qs); contratoid = par.get("id")||""; - } - - - function submitnuevosCanones() { + } - } - async function verContrato() { + async function refreshCanon() { + try { + const ret = await fetch($urlG+"/api/contratos/canon?id="+contratoid, { + method: "GET", + headers: { + "Auth": String(token), + } + }); + if (!ret.ok){ + let data = await ret.json(); + modaldata = data.message; + return; + } + let data = await ret.json(); + canons = data; + return; + } catch { + modaldata = "No se pudo obtener la lista de canones actualizada"; + } + } + + async function verContrato() { if (prop.id <= 0) { modaldata = "no hay contratos con id 0 o menor"; return; @@ -105,6 +122,58 @@ modaldata= "fallo intentar hacer la request"; } } + + async function realizarpago(mes: Date) { + try { + const ret = await fetch($urlG+"/api/contratos/realizarPago", { + method: "POST", + headers: { + "Auth": String(token), + "Content-Type": "application/json", + }, + body: JSON.stringify({idcontrato:contratoid, fecha:mes}), + }); + let data = await ret.json(); + modaldata = data.message; + if (ret.ok){ + refreshCanon() + return; + } + } catch { + modaldata = "Fallo al intentar hacer la request"; + } + } + + + function generarTiket(mod) { + selMod = mod; + showmodal =true; + } + + async function pedirdocumento(op:boolean) { + try { + const ret = await fetch($urlG+"/api/contrato/GenerarRecibo?html="+op, { + method: "POST", + headers: { + "Auth": String(token), + "Content-Type": "application/json", + }, + + body: JSON.stringify({fecha: selMod.mes, idcontrato: contratoid}) + }); + if (!ret.ok) { + let blob = await ret.json(); + modaldata=blob.message; + return; + } + let blob = await ret.blob(); + const blobUrl = URL.createObjectURL(blob); + window.open(blobUrl, '_blank'); + setTimeout(() => URL.revokeObjectURL(blobUrl), 100000); + } catch { + modaldata = "Fallo al intentar hacer la request"; + } + } @@ -112,6 +181,11 @@ {#if modaldata} !!(modaldata = "")}/> {/if} + +{#if showmodal} + showmodal=false} onSubmit={pedirdocumento} /> +{/if} +
@@ -219,10 +293,10 @@

Pago: {canon.pago ? "Sí" : "No"}

diff --git a/Front/src/paginas/ContratosPropietario.svelte b/Front/src/paginas/ContratosPropietario.svelte index 0ecff7d..68d5b39 100644 --- a/Front/src/paginas/ContratosPropietario.svelte +++ b/Front/src/paginas/ContratosPropietario.svelte @@ -75,8 +75,24 @@ contratoid = par.get("id")||""; } - function submitnuevosCanones() { - + async function submitnuevosCanones() { + try { + const ret = await fetch($urlG+"/api/contratos/crearcanons",{ + method: "POST", + headers: { + "Auth" : String(token), + }, + body: JSON.stringify({idcontrato: contratoid, aumento: interes}) + }); + + let data = await ret.json(); + modaldata = data.message; + if (ret.ok) { + refreshCanon(); + } + } catch { + modaldata = "Fallo al intentar alcanzar el servidor"; + } } async function verContrato() { @@ -106,6 +122,53 @@ modaldata= "fallo intentar hacer la request"; } } + + async function refreshCanon() { + try { + const ret = await fetch($urlG+"/api/contratos/canon?id="+contratoid, { + method: "GET", + headers: { + "Auth": String(token), + } + }); + if (!ret.ok){ + let data = await ret.json(); + modaldata = data.message; + return; + } + let data = await ret.json(); + canons = data; + return; + } catch { + modaldata = "No se pudo obtener la lista de canones actualizada"; + } + } + + + async function marcarPago(mes: Date) { + try { + const ret = await fetch($urlG+"/api/contratos/marcarPago", { + method: "POST", + headers: { + "Auth": String(token), + "Content-Type": "application/json", + }, + body: JSON.stringify({idcontrato:contratoid, fecha:mes}), + }); + let data = await ret.json(); + modaldata = data.message; + if (ret.ok){ + refreshCanon() + return; + } + } catch { + modaldata = "Fallo al intentar hacer la request"; + } + } + + async function generarTiket(mes: Date) { //WIP + + } @@ -114,7 +177,6 @@ !!(modaldata = "")}/> {/if} -
@@ -222,10 +284,10 @@

Pago: {canon.pago ? "Sí" : "No"}

diff --git a/Modelo/RepositorioCanons.cs b/Modelo/RepositorioCanons.cs index 1660cdb..0a1699c 100644 --- a/Modelo/RepositorioCanons.cs +++ b/Modelo/RepositorioCanons.cs @@ -1,5 +1,6 @@ using Entidades; using Microsoft.EntityFrameworkCore; +using Org.BouncyCastle.Math.EC.Rfc7748; namespace Modelo; @@ -14,17 +15,13 @@ public class RepositorioCanons: RepositorioBase { var con = Context; Canon? cc = null; - var c = con.Canons.FirstOrDefault(x => x.Idrecibo == null && - x.Fecha == f ); + var c = con.Contratos.Include(x=>x.Idcanons).ThenInclude(x=>x.IdreciboNavigation).FirstOrDefault(x => x.Id == idcont); if (c == null) return null; - //no deberia de tener que usar un foreach pero entity por algun motivo mapeo - //la entidad como muchos a muchos. no hay forma de que un canon tenga multiples - //contratos por como lo codifique pero igualmente - foreach (var i in c.Idcontratos) { - foreach (var j in i.Idcanons) { - if (j.Fecha == f) { - cc = j; - } + + foreach (var j in c.Idcanons) { + if (j.Fecha == f) { + cc = j; + break; } } return cc; @@ -52,6 +49,10 @@ public class RepositorioCanons: RepositorioBase { if (cont == null) return false; int exist = cont.Idcanons.Count(); + Canon? d = cont.Idcanons.OrderByDescending(x=>x.Fecha).FirstOrDefault(); + if (d == null) return false; + + if (exist+cont.MesesHastaAumento >= cont.MesesDurationContrato){ exist = cont.MesesDurationContrato-exist; } else{ @@ -62,8 +63,14 @@ public class RepositorioCanons: RepositorioBase { for (int i = 0; i < exist; i++){ Canon c = new Canon{ - Fecha + Fecha = d.Fecha.AddMonths(i==0?1:i+1), + Id = con.Canons.Count()+i+1, + Monto = cont.Monto, + Pagado = 0, }; + con.Canons.Add(c); + cont.Idcanons.Add(c); } + return Guardar(con); } } \ No newline at end of file