Base de datos y JPA

This commit is contained in:
Tatiana Villa Ema 2026-06-14 03:15:12 +02:00
parent 29b899f409
commit 1b0d3035e8
27 changed files with 1379 additions and 1 deletions

100
db-init/01-create-crm.sql Normal file
View File

@ -0,0 +1,100 @@
CREATE DATABASE IF NOT EXISTS crm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE crm;
CREATE TABLE IF NOT EXISTS direcciones (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
calle VARCHAR(255),
numero VARCHAR(50),
piso VARCHAR(50),
ciudad VARCHAR(100),
provincia VARCHAR(100),
codigo_postal VARCHAR(20)
);
CREATE TABLE IF NOT EXISTS propietarios (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
nombre VARCHAR(255) NOT NULL,
telefono VARCHAR(50),
email VARCHAR(255),
direccion_id BIGINT,
FOREIGN KEY (direccion_id) REFERENCES direcciones(id)
);
CREATE TABLE IF NOT EXISTS proveedores (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
nombre VARCHAR(255) NOT NULL,
telefono VARCHAR(50),
email VARCHAR(255),
direccion_id BIGINT,
FOREIGN KEY (direccion_id) REFERENCES direcciones(id)
);
CREATE TABLE IF NOT EXISTS contacto_proveedor (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
proveedor_id BIGINT NOT NULL,
nombre VARCHAR(255) NOT NULL,
telefono VARCHAR(50),
email VARCHAR(255),
FOREIGN KEY (proveedor_id) REFERENCES proveedores(id)
);
CREATE TABLE IF NOT EXISTS servicios (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
nombre VARCHAR(255) NOT NULL,
descripcion TEXT,
proveedor_id BIGINT,
FOREIGN KEY (proveedor_id) REFERENCES proveedores(id)
);
CREATE TABLE IF NOT EXISTS inmuebles (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
referencia_catastral VARCHAR(255) UNIQUE,
descripcion TEXT,
precio DECIMAL(15,2),
estado VARCHAR(100),
direccion_id BIGINT,
propietario_id BIGINT,
FOREIGN KEY (direccion_id) REFERENCES direcciones(id),
FOREIGN KEY (propietario_id) REFERENCES propietarios(id)
);
CREATE TABLE IF NOT EXISTS contratos (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
inmueble_id BIGINT NOT NULL,
propietario_id BIGINT NOT NULL,
tipo VARCHAR(50),
fecha_inicio DATE,
fecha_fin DATE,
importe DECIMAL(15,2),
estado VARCHAR(50),
cliente_nombre VARCHAR(255),
FOREIGN KEY (inmueble_id) REFERENCES inmuebles(id),
FOREIGN KEY (propietario_id) REFERENCES propietarios(id)
);
CREATE TABLE IF NOT EXISTS compran (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
inmueble_id BIGINT NOT NULL,
comprador_nombre VARCHAR(255),
fecha_compra DATE,
precio_compra DECIMAL(15,2),
FOREIGN KEY (inmueble_id) REFERENCES inmuebles(id)
);
CREATE TABLE IF NOT EXISTS alquilados (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
inmueble_id BIGINT NOT NULL,
inquilino_nombre VARCHAR(255),
fecha_inicio DATE,
fecha_fin DATE,
renta_mensual DECIMAL(15,2),
FOREIGN KEY (inmueble_id) REFERENCES inmuebles(id)
);
CREATE TABLE IF NOT EXISTS users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
role VARCHAR(50) NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT TRUE
);

View File

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2023-01-20T13:20:53.659Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.55" etag="7j-pN8mZnZDS76RK0WrE" version="20.8.8" type="device"><diagram name="Página-1" id="toJ7otdHlij8VeJBuyi3">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</diagram></mxfile>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -0,0 +1,358 @@
package es.tatvil.crminmobiliario.controller;
import es.tatvil.crminmobiliario.model.*;
import es.tatvil.crminmobiliario.repository.*;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@RestController
@RequestMapping("/api/crm")
public class CrmController {
private final DireccionRepository direccionRepository;
private final PropietarioRepository propietarioRepository;
private final ProveedorRepository proveedorRepository;
private final ContactoProveedorRepository contactoProveedorRepository;
private final ServicioRepository servicioRepository;
private final InmuebleRepository inmuebleRepository;
private final ContratoRepository contratoRepository;
private final CompraRepository compraRepository;
private final AlquiladoRepository alquiladoRepository;
public CrmController(DireccionRepository direccionRepository,
PropietarioRepository propietarioRepository,
ProveedorRepository proveedorRepository,
ContactoProveedorRepository contactoProveedorRepository,
ServicioRepository servicioRepository,
InmuebleRepository inmuebleRepository,
ContratoRepository contratoRepository,
CompraRepository compraRepository,
AlquiladoRepository alquiladoRepository) {
this.direccionRepository = direccionRepository;
this.propietarioRepository = propietarioRepository;
this.proveedorRepository = proveedorRepository;
this.contactoProveedorRepository = contactoProveedorRepository;
this.servicioRepository = servicioRepository;
this.inmuebleRepository = inmuebleRepository;
this.contratoRepository = contratoRepository;
this.compraRepository = compraRepository;
this.alquiladoRepository = alquiladoRepository;
}
// Direcciones
@GetMapping("/direcciones")
public List<Direccion> listDirecciones() {
return direccionRepository.findAll();
}
@GetMapping("/direcciones/{id}")
public Direccion getDireccion(@PathVariable Long id) {
return direccionRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Dirección no encontrada"));
}
@PostMapping("/direcciones")
@ResponseStatus(HttpStatus.CREATED)
public Direccion createDireccion(@RequestBody Direccion direccion) {
return direccionRepository.save(direccion);
}
@PutMapping("/direcciones/{id}")
public Direccion updateDireccion(@PathVariable Long id, @RequestBody Direccion direccion) {
Direccion existing = getDireccion(id);
existing.setCalle(direccion.getCalle());
existing.setNumero(direccion.getNumero());
existing.setPiso(direccion.getPiso());
existing.setCiudad(direccion.getCiudad());
existing.setProvincia(direccion.getProvincia());
existing.setCodigoPostal(direccion.getCodigoPostal());
return direccionRepository.save(existing);
}
@DeleteMapping("/direcciones/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteDireccion(@PathVariable Long id) {
direccionRepository.delete(getDireccion(id));
}
// Propietarios
@GetMapping("/propietarios")
public List<Propietario> listPropietarios() {
return propietarioRepository.findAll();
}
@GetMapping("/propietarios/{id}")
public Propietario getPropietario(@PathVariable Long id) {
return propietarioRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Propietario no encontrado"));
}
@PostMapping("/propietarios")
@ResponseStatus(HttpStatus.CREATED)
public Propietario createPropietario(@RequestBody Propietario propietario) {
return propietarioRepository.save(propietario);
}
@PutMapping("/propietarios/{id}")
public Propietario updatePropietario(@PathVariable Long id, @RequestBody Propietario propietario) {
Propietario existing = getPropietario(id);
existing.setNombre(propietario.getNombre());
existing.setTelefono(propietario.getTelefono());
existing.setEmail(propietario.getEmail());
existing.setDireccion(propietario.getDireccion());
return propietarioRepository.save(existing);
}
@DeleteMapping("/propietarios/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deletePropietario(@PathVariable Long id) {
propietarioRepository.delete(getPropietario(id));
}
// Proveedores
@GetMapping("/proveedores")
public List<Proveedor> listProveedores() {
return proveedorRepository.findAll();
}
@GetMapping("/proveedores/{id}")
public Proveedor getProveedor(@PathVariable Long id) {
return proveedorRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Proveedor no encontrado"));
}
@PostMapping("/proveedores")
@ResponseStatus(HttpStatus.CREATED)
public Proveedor createProveedor(@RequestBody Proveedor proveedor) {
return proveedorRepository.save(proveedor);
}
@PutMapping("/proveedores/{id}")
public Proveedor updateProveedor(@PathVariable Long id, @RequestBody Proveedor proveedor) {
Proveedor existing = getProveedor(id);
existing.setNombre(proveedor.getNombre());
existing.setTelefono(proveedor.getTelefono());
existing.setEmail(proveedor.getEmail());
existing.setDireccion(proveedor.getDireccion());
return proveedorRepository.save(existing);
}
@DeleteMapping("/proveedores/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteProveedor(@PathVariable Long id) {
proveedorRepository.delete(getProveedor(id));
}
// ContactoProveedor
@GetMapping("/contactos-proveedor")
public List<ContactoProveedor> listContactosProveedor() {
return contactoProveedorRepository.findAll();
}
@GetMapping("/contactos-proveedor/{id}")
public ContactoProveedor getContactoProveedor(@PathVariable Long id) {
return contactoProveedorRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Contacto de proveedor no encontrado"));
}
@PostMapping("/contactos-proveedor")
@ResponseStatus(HttpStatus.CREATED)
public ContactoProveedor createContactoProveedor(@RequestBody ContactoProveedor contactoProveedor) {
return contactoProveedorRepository.save(contactoProveedor);
}
@PutMapping("/contactos-proveedor/{id}")
public ContactoProveedor updateContactoProveedor(@PathVariable Long id, @RequestBody ContactoProveedor contactoProveedor) {
ContactoProveedor existing = getContactoProveedor(id);
existing.setProveedor(contactoProveedor.getProveedor());
existing.setNombre(contactoProveedor.getNombre());
existing.setTelefono(contactoProveedor.getTelefono());
existing.setEmail(contactoProveedor.getEmail());
return contactoProveedorRepository.save(existing);
}
@DeleteMapping("/contactos-proveedor/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteContactoProveedor(@PathVariable Long id) {
contactoProveedorRepository.delete(getContactoProveedor(id));
}
// Servicios
@GetMapping("/servicios")
public List<Servicio> listServicios() {
return servicioRepository.findAll();
}
@GetMapping("/servicios/{id}")
public Servicio getServicio(@PathVariable Long id) {
return servicioRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Servicio no encontrado"));
}
@PostMapping("/servicios")
@ResponseStatus(HttpStatus.CREATED)
public Servicio createServicio(@RequestBody Servicio servicio) {
return servicioRepository.save(servicio);
}
@PutMapping("/servicios/{id}")
public Servicio updateServicio(@PathVariable Long id, @RequestBody Servicio servicio) {
Servicio existing = getServicio(id);
existing.setNombre(servicio.getNombre());
existing.setDescripcion(servicio.getDescripcion());
existing.setProveedor(servicio.getProveedor());
return servicioRepository.save(existing);
}
@DeleteMapping("/servicios/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteServicio(@PathVariable Long id) {
servicioRepository.delete(getServicio(id));
}
// Inmuebles
@GetMapping("/inmuebles")
public List<Inmueble> listInmuebles() {
return inmuebleRepository.findAll();
}
@GetMapping("/inmuebles/{id}")
public Inmueble getInmueble(@PathVariable Long id) {
return inmuebleRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Inmueble no encontrado"));
}
@PostMapping("/inmuebles")
@ResponseStatus(HttpStatus.CREATED)
public Inmueble createInmueble(@RequestBody Inmueble inmueble) {
return inmuebleRepository.save(inmueble);
}
@PutMapping("/inmuebles/{id}")
public Inmueble updateInmueble(@PathVariable Long id, @RequestBody Inmueble inmueble) {
Inmueble existing = getInmueble(id);
existing.setReferenciaCatastral(inmueble.getReferenciaCatastral());
existing.setDescripcion(inmueble.getDescripcion());
existing.setPrecio(inmueble.getPrecio());
existing.setEstado(inmueble.getEstado());
existing.setDireccion(inmueble.getDireccion());
existing.setPropietario(inmueble.getPropietario());
return inmuebleRepository.save(existing);
}
@DeleteMapping("/inmuebles/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteInmueble(@PathVariable Long id) {
inmuebleRepository.delete(getInmueble(id));
}
// Contratos
@GetMapping("/contratos")
public List<Contrato> listContratos() {
return contratoRepository.findAll();
}
@GetMapping("/contratos/{id}")
public Contrato getContrato(@PathVariable Long id) {
return contratoRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Contrato no encontrado"));
}
@PostMapping("/contratos")
@ResponseStatus(HttpStatus.CREATED)
public Contrato createContrato(@RequestBody Contrato contrato) {
return contratoRepository.save(contrato);
}
@PutMapping("/contratos/{id}")
public Contrato updateContrato(@PathVariable Long id, @RequestBody Contrato contrato) {
Contrato existing = getContrato(id);
existing.setInmueble(contrato.getInmueble());
existing.setPropietario(contrato.getPropietario());
existing.setTipo(contrato.getTipo());
existing.setFechaInicio(contrato.getFechaInicio());
existing.setFechaFin(contrato.getFechaFin());
existing.setImporte(contrato.getImporte());
existing.setEstado(contrato.getEstado());
existing.setClienteNombre(contrato.getClienteNombre());
return contratoRepository.save(existing);
}
@DeleteMapping("/contratos/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteContrato(@PathVariable Long id) {
contratoRepository.delete(getContrato(id));
}
// Compras
@GetMapping("/compras")
public List<Compra> listCompras() {
return compraRepository.findAll();
}
@GetMapping("/compras/{id}")
public Compra getCompra(@PathVariable Long id) {
return compraRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Compra no encontrada"));
}
@PostMapping("/compras")
@ResponseStatus(HttpStatus.CREATED)
public Compra createCompra(@RequestBody Compra compra) {
return compraRepository.save(compra);
}
@PutMapping("/compras/{id}")
public Compra updateCompra(@PathVariable Long id, @RequestBody Compra compra) {
Compra existing = getCompra(id);
existing.setInmueble(compra.getInmueble());
existing.setCompradorNombre(compra.getCompradorNombre());
existing.setFechaCompra(compra.getFechaCompra());
existing.setPrecioCompra(compra.getPrecioCompra());
return compraRepository.save(existing);
}
@DeleteMapping("/compras/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteCompra(@PathVariable Long id) {
compraRepository.delete(getCompra(id));
}
// Alquilados
@GetMapping("/alquilados")
public List<Alquilado> listAlquilados() {
return alquiladoRepository.findAll();
}
@GetMapping("/alquilados/{id}")
public Alquilado getAlquilado(@PathVariable Long id) {
return alquiladoRepository.findById(id)
.orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Alquiler no encontrado"));
}
@PostMapping("/alquilados")
@ResponseStatus(HttpStatus.CREATED)
public Alquilado createAlquilado(@RequestBody Alquilado alquilado) {
return alquiladoRepository.save(alquilado);
}
@PutMapping("/alquilados/{id}")
public Alquilado updateAlquilado(@PathVariable Long id, @RequestBody Alquilado alquilado) {
Alquilado existing = getAlquilado(id);
existing.setInmueble(alquilado.getInmueble());
existing.setInquilinoNombre(alquilado.getInquilinoNombre());
existing.setFechaInicio(alquilado.getFechaInicio());
existing.setFechaFin(alquilado.getFechaFin());
existing.setRentaMensual(alquilado.getRentaMensual());
return alquiladoRepository.save(existing);
}
@DeleteMapping("/alquilados/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteAlquilado(@PathVariable Long id) {
alquiladoRepository.delete(getAlquilado(id));
}
}

View File

@ -0,0 +1,19 @@
package es.tatvil.crminmobiliario.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import java.security.Principal;
@Controller
public class CrmPageController {
@GetMapping("/crm")
public String crmPage(Model model, Principal principal) {
if (principal != null) {
model.addAttribute("username", principal.getName());
}
return "crm";
}
}

View File

@ -0,0 +1,76 @@
package es.tatvil.crminmobiliario.model;
import jakarta.persistence.*;
import java.time.LocalDate;
@Entity
@Table(name = "alquilados")
public class Alquilado {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(optional = false)
@JoinColumn(name = "inmueble_id")
private Inmueble inmueble;
@Column(name = "inquilino_nombre")
private String inquilinoNombre;
@Column(name = "fecha_inicio")
private LocalDate fechaInicio;
@Column(name = "fecha_fin")
private LocalDate fechaFin;
@Column(name = "renta_mensual")
private Double rentaMensual;
public Alquilado() {
}
public Long getId() {
return id;
}
public Inmueble getInmueble() {
return inmueble;
}
public void setInmueble(Inmueble inmueble) {
this.inmueble = inmueble;
}
public String getInquilinoNombre() {
return inquilinoNombre;
}
public void setInquilinoNombre(String inquilinoNombre) {
this.inquilinoNombre = inquilinoNombre;
}
public LocalDate getFechaInicio() {
return fechaInicio;
}
public void setFechaInicio(LocalDate fechaInicio) {
this.fechaInicio = fechaInicio;
}
public LocalDate getFechaFin() {
return fechaFin;
}
public void setFechaFin(LocalDate fechaFin) {
this.fechaFin = fechaFin;
}
public Double getRentaMensual() {
return rentaMensual;
}
public void setRentaMensual(Double rentaMensual) {
this.rentaMensual = rentaMensual;
}
}

View File

@ -0,0 +1,65 @@
package es.tatvil.crminmobiliario.model;
import jakarta.persistence.*;
import java.time.LocalDate;
@Entity
@Table(name = "compran")
public class Compra {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(optional = false)
@JoinColumn(name = "inmueble_id")
private Inmueble inmueble;
@Column(name = "comprador_nombre")
private String compradorNombre;
@Column(name = "fecha_compra")
private LocalDate fechaCompra;
@Column(name = "precio_compra")
private Double precioCompra;
public Compra() {
}
public Long getId() {
return id;
}
public Inmueble getInmueble() {
return inmueble;
}
public void setInmueble(Inmueble inmueble) {
this.inmueble = inmueble;
}
public String getCompradorNombre() {
return compradorNombre;
}
public void setCompradorNombre(String compradorNombre) {
this.compradorNombre = compradorNombre;
}
public LocalDate getFechaCompra() {
return fechaCompra;
}
public void setFechaCompra(LocalDate fechaCompra) {
this.fechaCompra = fechaCompra;
}
public Double getPrecioCompra() {
return precioCompra;
}
public void setPrecioCompra(Double precioCompra) {
this.precioCompra = precioCompra;
}
}

View File

@ -0,0 +1,61 @@
package es.tatvil.crminmobiliario.model;
import jakarta.persistence.*;
@Entity
@Table(name = "contacto_proveedor")
public class ContactoProveedor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(optional = false)
@JoinColumn(name = "proveedor_id")
private Proveedor proveedor;
@Column(nullable = false)
private String nombre;
private String telefono;
private String email;
public ContactoProveedor() {
}
public Long getId() {
return id;
}
public Proveedor getProveedor() {
return proveedor;
}
public void setProveedor(Proveedor proveedor) {
this.proveedor = proveedor;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getTelefono() {
return telefono;
}
public void setTelefono(String telefono) {
this.telefono = telefono;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

View File

@ -0,0 +1,101 @@
package es.tatvil.crminmobiliario.model;
import jakarta.persistence.*;
import java.time.LocalDate;
@Entity
@Table(name = "contratos")
public class Contrato {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(optional = false)
@JoinColumn(name = "inmueble_id")
private Inmueble inmueble;
@ManyToOne(optional = false)
@JoinColumn(name = "propietario_id")
private Propietario propietario;
private String tipo;
private LocalDate fechaInicio;
private LocalDate fechaFin;
private Double importe;
private String estado;
@Column(name = "cliente_nombre")
private String clienteNombre;
public Contrato() {
}
public Long getId() {
return id;
}
public Inmueble getInmueble() {
return inmueble;
}
public void setInmueble(Inmueble inmueble) {
this.inmueble = inmueble;
}
public Propietario getPropietario() {
return propietario;
}
public void setPropietario(Propietario propietario) {
this.propietario = propietario;
}
public String getTipo() {
return tipo;
}
public void setTipo(String tipo) {
this.tipo = tipo;
}
public LocalDate getFechaInicio() {
return fechaInicio;
}
public void setFechaInicio(LocalDate fechaInicio) {
this.fechaInicio = fechaInicio;
}
public LocalDate getFechaFin() {
return fechaFin;
}
public void setFechaFin(LocalDate fechaFin) {
this.fechaFin = fechaFin;
}
public Double getImporte() {
return importe;
}
public void setImporte(Double importe) {
this.importe = importe;
}
public String getEstado() {
return estado;
}
public void setEstado(String estado) {
this.estado = estado;
}
public String getClienteNombre() {
return clienteNombre;
}
public void setClienteNombre(String clienteNombre) {
this.clienteNombre = clienteNombre;
}
}

View File

@ -0,0 +1,78 @@
package es.tatvil.crminmobiliario.model;
import jakarta.persistence.*;
@Entity
@Table(name = "direcciones")
public class Direccion {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String calle;
private String numero;
private String piso;
private String ciudad;
@Column(name = "provincia")
private String provincia;
@Column(name = "codigo_postal")
private String codigoPostal;
public Direccion() {
}
public Long getId() {
return id;
}
public String getCalle() {
return calle;
}
public void setCalle(String calle) {
this.calle = calle;
}
public String getNumero() {
return numero;
}
public void setNumero(String numero) {
this.numero = numero;
}
public String getPiso() {
return piso;
}
public void setPiso(String piso) {
this.piso = piso;
}
public String getCiudad() {
return ciudad;
}
public void setCiudad(String ciudad) {
this.ciudad = ciudad;
}
public String getProvincia() {
return provincia;
}
public void setProvincia(String provincia) {
this.provincia = provincia;
}
public String getCodigoPostal() {
return codigoPostal;
}
public void setCodigoPostal(String codigoPostal) {
this.codigoPostal = codigoPostal;
}
}

View File

@ -0,0 +1,84 @@
package es.tatvil.crminmobiliario.model;
import jakarta.persistence.*;
@Entity
@Table(name = "inmuebles")
public class Inmueble {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "referencia_catastral", unique = true)
private String referenciaCatastral;
@Column(columnDefinition = "TEXT")
private String descripcion;
private Double precio;
private String estado;
@ManyToOne
@JoinColumn(name = "direccion_id")
private Direccion direccion;
@ManyToOne
@JoinColumn(name = "propietario_id")
private Propietario propietario;
public Inmueble() {
}
public Long getId() {
return id;
}
public String getReferenciaCatastral() {
return referenciaCatastral;
}
public void setReferenciaCatastral(String referenciaCatastral) {
this.referenciaCatastral = referenciaCatastral;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
public Double getPrecio() {
return precio;
}
public void setPrecio(Double precio) {
this.precio = precio;
}
public String getEstado() {
return estado;
}
public void setEstado(String estado) {
this.estado = estado;
}
public Direccion getDireccion() {
return direccion;
}
public void setDireccion(Direccion direccion) {
this.direccion = direccion;
}
public Propietario getPropietario() {
return propietario;
}
public void setPropietario(Propietario propietario) {
this.propietario = propietario;
}
}

View File

@ -0,0 +1,61 @@
package es.tatvil.crminmobiliario.model;
import jakarta.persistence.*;
@Entity
@Table(name = "propietarios")
public class Propietario {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String nombre;
private String telefono;
private String email;
@ManyToOne
@JoinColumn(name = "direccion_id")
private Direccion direccion;
public Propietario() {
}
public Long getId() {
return id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getTelefono() {
return telefono;
}
public void setTelefono(String telefono) {
this.telefono = telefono;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Direccion getDireccion() {
return direccion;
}
public void setDireccion(Direccion direccion) {
this.direccion = direccion;
}
}

View File

@ -0,0 +1,61 @@
package es.tatvil.crminmobiliario.model;
import jakarta.persistence.*;
@Entity
@Table(name = "proveedores")
public class Proveedor {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String nombre;
private String telefono;
private String email;
@ManyToOne
@JoinColumn(name = "direccion_id")
private Direccion direccion;
public Proveedor() {
}
public Long getId() {
return id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getTelefono() {
return telefono;
}
public void setTelefono(String telefono) {
this.telefono = telefono;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Direccion getDireccion() {
return direccion;
}
public void setDireccion(Direccion direccion) {
this.direccion = direccion;
}
}

View File

@ -0,0 +1,53 @@
package es.tatvil.crminmobiliario.model;
import jakarta.persistence.*;
@Entity
@Table(name = "servicios")
public class Servicio {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String nombre;
@Column(columnDefinition = "TEXT")
private String descripcion;
@ManyToOne
@JoinColumn(name = "proveedor_id")
private Proveedor proveedor;
public Servicio() {
}
public Long getId() {
return id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
public Proveedor getProveedor() {
return proveedor;
}
public void setProveedor(Proveedor proveedor) {
this.proveedor = proveedor;
}
}

View File

@ -8,7 +8,7 @@ import jakarta.persistence.Id;
import jakarta.persistence.Table; import jakarta.persistence.Table;
@Entity @Entity
@Table(name = "usuarios") @Table(name = "users")
public class Usuario { public class Usuario {
@Id @Id

View File

@ -0,0 +1,9 @@
package es.tatvil.crminmobiliario.repository;
import es.tatvil.crminmobiliario.model.Alquilado;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface AlquiladoRepository extends JpaRepository<Alquilado, Long> {
}

View File

@ -0,0 +1,9 @@
package es.tatvil.crminmobiliario.repository;
import es.tatvil.crminmobiliario.model.Compra;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface CompraRepository extends JpaRepository<Compra, Long> {
}

View File

@ -0,0 +1,9 @@
package es.tatvil.crminmobiliario.repository;
import es.tatvil.crminmobiliario.model.ContactoProveedor;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ContactoProveedorRepository extends JpaRepository<ContactoProveedor, Long> {
}

View File

@ -0,0 +1,9 @@
package es.tatvil.crminmobiliario.repository;
import es.tatvil.crminmobiliario.model.Contrato;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ContratoRepository extends JpaRepository<Contrato, Long> {
}

View File

@ -0,0 +1,9 @@
package es.tatvil.crminmobiliario.repository;
import es.tatvil.crminmobiliario.model.Direccion;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface DireccionRepository extends JpaRepository<Direccion, Long> {
}

View File

@ -0,0 +1,9 @@
package es.tatvil.crminmobiliario.repository;
import es.tatvil.crminmobiliario.model.Inmueble;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface InmuebleRepository extends JpaRepository<Inmueble, Long> {
}

View File

@ -0,0 +1,9 @@
package es.tatvil.crminmobiliario.repository;
import es.tatvil.crminmobiliario.model.Propietario;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PropietarioRepository extends JpaRepository<Propietario, Long> {
}

View File

@ -0,0 +1,9 @@
package es.tatvil.crminmobiliario.repository;
import es.tatvil.crminmobiliario.model.Proveedor;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ProveedorRepository extends JpaRepository<Proveedor, Long> {
}

View File

@ -0,0 +1,9 @@
package es.tatvil.crminmobiliario.repository;
import es.tatvil.crminmobiliario.model.Servicio;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface ServicioRepository extends JpaRepository<Servicio, Long> {
}

View File

@ -0,0 +1,109 @@
document.addEventListener('DOMContentLoaded', function () {
const propietariosList = document.getElementById('propietarios-list');
const inmueblesList = document.getElementById('inmuebles-list');
const propietarioForm = document.getElementById('propietario-form');
const inmuebleForm = document.getElementById('inmueble-form');
const propietarioMessage = document.getElementById('propietario-message');
const inmuebleMessage = document.getElementById('inmueble-message');
function showMessage(container, message, success = true) {
container.textContent = message;
container.className = success ? 'alert success' : 'alert error';
}
function fetchPropietarios() {
propietariosList.textContent = 'Cargando propietarios...';
fetch('/api/crm/propietarios')
.then(response => { if (!response.ok) throw new Error('Error al cargar propietarios'); return response.json(); })
.then(data => {
if (data.length === 0) {
propietariosList.innerHTML = '<p>No hay propietarios registrados.</p>';
return;
}
propietariosList.innerHTML = '<ul>' + data.map(prop =>
`<li><strong>${prop.nombre}</strong> | Tel: ${prop.telefono || '-'} | Email: ${prop.email || '-'} | Dirección ID: ${prop.direccion ? prop.direccion.id : 'N/A'}</li>`
).join('') + '</ul>';
})
.catch(error => {
propietariosList.textContent = error.message;
});
}
function fetchInmuebles() {
inmueblesList.textContent = 'Cargando inmuebles...';
fetch('/api/crm/inmuebles')
.then(response => { if (!response.ok) throw new Error('Error al cargar inmuebles'); return response.json(); })
.then(data => {
if (data.length === 0) {
inmueblesList.innerHTML = '<p>No hay inmuebles registrados.</p>';
return;
}
inmueblesList.innerHTML = '<ul>' + data.map(inmueble =>
`<li><strong>${inmueble.referenciaCatastral || 'Sin referencia'}</strong> | Precio: ${inmueble.precio || '-'} | Estado: ${inmueble.estado || '-'} | Propietario ID: ${inmueble.propietario ? inmueble.propietario.id : 'N/A'}</li>`
).join('') + '</ul>';
})
.catch(error => {
inmueblesList.textContent = error.message;
});
}
propietarioForm.addEventListener('submit', function (event) {
event.preventDefault();
const payload = {
nombre: document.getElementById('propietario-nombre').value,
telefono: document.getElementById('propietario-telefono').value,
email: document.getElementById('propietario-email').value,
direccion: document.getElementById('propietario-direccion-id').value ? { id: Number(document.getElementById('propietario-direccion-id').value) } : null
};
fetch('/api/crm/propietarios', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
})
.then(response => {
if (!response.ok) throw new Error('No se pudo guardar el propietario');
return response.json();
})
.then(() => {
showMessage(propietarioMessage, 'Propietario creado correctamente.');
propietarioForm.reset();
fetchPropietarios();
})
.catch(error => showMessage(propietarioMessage, error.message, false));
});
inmuebleForm.addEventListener('submit', function (event) {
event.preventDefault();
const payload = {
referenciaCatastral: document.getElementById('inmueble-referencia').value,
descripcion: document.getElementById('inmueble-descripcion').value,
precio: document.getElementById('inmueble-precio').value ? Number(document.getElementById('inmueble-precio').value) : null,
estado: document.getElementById('inmueble-estado').value,
direccion: document.getElementById('inmueble-direccion-id').value ? { id: Number(document.getElementById('inmueble-direccion-id').value) } : null,
propietario: document.getElementById('inmueble-propietario-id').value ? { id: Number(document.getElementById('inmueble-propietario-id').value) } : null
};
fetch('/api/crm/inmuebles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
})
.then(response => {
if (!response.ok) throw new Error('No se pudo guardar el inmueble');
return response.json();
})
.then(() => {
showMessage(inmuebleMessage, 'Inmueble creado correctamente.');
inmuebleForm.reset();
fetchInmuebles();
})
.catch(error => showMessage(inmuebleMessage, error.message, false));
});
document.getElementById('reload-propietarios').addEventListener('click', fetchPropietarios);
document.getElementById('reload-inmuebles').addEventListener('click', fetchInmuebles);
fetchPropietarios();
fetchInmuebles();
});

View File

@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="es" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CRM Inmobiliario</title>
<link rel="stylesheet" href="/css/style.css" />
<script src="/js/crm.js" defer></script>
</head>
<body>
<div class="titulo">
<h1>CRM Inmobiliario</h1>
<div class="usuario-barra">
<p th:if="${username}">Usuario: <strong th:text="${username}"></strong></p>
<a class="boton" href="/logout">Cerrar sesión</a>
<a class="boton" href="/">Volver al inicio</a>
</div>
</div>
<div class="cuerpo">
<section class="panel">
<h2>Propietarios</h2>
<button id="reload-propietarios" class="boton">Actualizar lista</button>
<div id="propietarios-list">Cargando propietarios...</div>
<h3>Crear propietario</h3>
<form id="propietario-form">
<label>Nombre</label>
<input type="text" id="propietario-nombre" required />
<label>Teléfono</label>
<input type="text" id="propietario-telefono" />
<label>Email</label>
<input type="email" id="propietario-email" />
<label>ID de dirección</label>
<input type="number" id="propietario-direccion-id" />
<button type="submit" class="boton">Guardar propietario</button>
</form>
<div id="propietario-message"></div>
</section>
<section class="panel">
<h2>Inmuebles</h2>
<button id="reload-inmuebles" class="boton">Actualizar lista</button>
<div id="inmuebles-list">Cargando inmuebles...</div>
<h3>Crear inmueble</h3>
<form id="inmueble-form">
<label>Referencia catastral</label>
<input type="text" id="inmueble-referencia" />
<label>Descripción</label>
<textarea id="inmueble-descripcion"></textarea>
<label>Precio</label>
<input type="number" step="0.01" id="inmueble-precio" />
<label>Estado</label>
<input type="text" id="inmueble-estado" />
<label>ID de dirección</label>
<input type="number" id="inmueble-direccion-id" />
<label>ID de propietario</label>
<input type="number" id="inmueble-propietario-id" />
<button type="submit" class="boton">Guardar inmueble</button>
</form>
<div id="inmueble-message"></div>
</section>
</div>
<footer>
<p>Gestión de CRM inmobiliario con Spring Boot.</p>
</footer>
</body>
</html>

View File

@ -22,6 +22,7 @@
<a class="boton" href="/register">Registrarse</a> <a class="boton" href="/register">Registrarse</a>
</div> </div>
<div th:if="${username}"> <div th:if="${username}">
<a class="boton" href="/crm">Ir al CRM</a>
<a class="boton" href="/logout">Cerrar sesión</a> <a class="boton" href="/logout">Cerrar sesión</a>
</div> </div>
</div> </div>