Fixed issues with invoice, payments creation and also added validation of amount

This commit is contained in:
Dennis Eckerskorn 2025-05-15 20:20:41 +02:00
parent 0aadef56ed
commit ab3485984e
6 changed files with 84 additions and 17 deletions

View File

@ -1,6 +1,7 @@
package com.denniseckerskorn.controllers.finance_management; package com.denniseckerskorn.controllers.finance_management;
import com.denniseckerskorn.dtos.finance_management_dtos.PaymentDTO; import com.denniseckerskorn.dtos.finance_management_dtos.PaymentDTO;
import com.denniseckerskorn.entities.finance.Invoice;
import com.denniseckerskorn.entities.finance.Payment; import com.denniseckerskorn.entities.finance.Payment;
import com.denniseckerskorn.exceptions.DuplicateEntityException; import com.denniseckerskorn.exceptions.DuplicateEntityException;
import com.denniseckerskorn.exceptions.EntityNotFoundException; import com.denniseckerskorn.exceptions.EntityNotFoundException;
@ -29,18 +30,28 @@ public class PaymentController {
@PostMapping("/create") @PostMapping("/create")
@Operation(summary = "Create a new payment and mark invoice as PAID") @Operation(summary = "Create a new payment and mark invoice as PAID")
public ResponseEntity<PaymentDTO> create(@Valid @RequestBody PaymentDTO dto) throws DuplicateEntityException { public ResponseEntity<PaymentDTO> create(@Valid @RequestBody PaymentDTO dto)
Payment saved = paymentService.save(dto.toEntity()); throws DuplicateEntityException, EntityNotFoundException {
Invoice invoice = paymentService.getInvoiceById(dto.getInvoiceId());
Payment saved = paymentService.save(dto.toEntityWithInvoice(invoice));
return new ResponseEntity<>(new PaymentDTO(saved), HttpStatus.CREATED); return new ResponseEntity<>(new PaymentDTO(saved), HttpStatus.CREATED);
} }
@PutMapping("/update") @PutMapping("/update")
@Operation(summary = "Update an existing payment and adjust invoice status") @Operation(summary = "Update an existing payment and adjust invoice status")
public ResponseEntity<PaymentDTO> update(@Valid @RequestBody PaymentDTO dto) throws EntityNotFoundException, InvalidDataException { public ResponseEntity<PaymentDTO> update(@Valid @RequestBody PaymentDTO dto)
Payment updated = paymentService.update(dto.toEntity()); throws EntityNotFoundException, InvalidDataException {
Invoice invoice = paymentService.getInvoiceById(dto.getInvoiceId());
Payment updated = paymentService.update(dto.toEntityWithInvoice(invoice));
return new ResponseEntity<>(new PaymentDTO(updated), HttpStatus.OK); return new ResponseEntity<>(new PaymentDTO(updated), HttpStatus.OK);
} }
@GetMapping("/getById/{id}") @GetMapping("/getById/{id}")
@Operation(summary = "Get payment by ID") @Operation(summary = "Get payment by ID")
public ResponseEntity<PaymentDTO> getById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { public ResponseEntity<PaymentDTO> getById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException {

View File

@ -1,5 +1,6 @@
package com.denniseckerskorn.dtos.finance_management_dtos; package com.denniseckerskorn.dtos.finance_management_dtos;
import com.denniseckerskorn.entities.finance.Invoice;
import com.denniseckerskorn.entities.finance.Payment; import com.denniseckerskorn.entities.finance.Payment;
import com.denniseckerskorn.enums.PaymentMethodValues; import com.denniseckerskorn.enums.PaymentMethodValues;
import com.denniseckerskorn.enums.StatusValues; import com.denniseckerskorn.enums.StatusValues;
@ -27,7 +28,8 @@ public class PaymentDTO {
@NotNull @NotNull
private StatusValues status; private StatusValues status;
public PaymentDTO() {} public PaymentDTO() {
}
public PaymentDTO(Payment entity) { public PaymentDTO(Payment entity) {
this.id = entity.getId(); this.id = entity.getId();
@ -44,6 +46,9 @@ public class PaymentDTO {
public Payment toEntity() { public Payment toEntity() {
Payment payment = new Payment(); Payment payment = new Payment();
if (this.id != null) {
payment.setId(this.id);
}
payment.setId(this.id); payment.setId(this.id);
payment.setPaymentDate(this.paymentDate); payment.setPaymentDate(this.paymentDate);
payment.setAmount(this.amount); payment.setAmount(this.amount);
@ -52,6 +57,17 @@ public class PaymentDTO {
return payment; return payment;
} }
public Payment toEntityWithInvoice(Invoice invoice) {
Payment payment = new Payment();
payment.setPaymentDate(this.paymentDate);
payment.setAmount(this.amount);
payment.setPaymentMethod(this.paymentMethod);
payment.setStatus(this.status);
payment.setInvoice(invoice);
return payment;
}
// Getters y setters... // Getters y setters...

View File

@ -47,15 +47,20 @@ public class PaymentService extends AbstractService<Payment, Integer> {
} }
payment.setInvoice(invoice); payment.setInvoice(invoice);
invoice.setPayment(payment);
Payment savedPayment = super.save(payment);
invoice.setPayment(savedPayment);
invoice.setStatus(StatusValues.PAID); invoice.setStatus(StatusValues.PAID);
invoiceService.update(invoice); invoiceService.update(invoice);
return super.save(payment);
return savedPayment;
} }
@Override @Override
@Transactional
public Payment update(Payment entity) throws EntityNotFoundException, InvalidDataException { public Payment update(Payment entity) throws EntityNotFoundException, InvalidDataException {
logger.info("Updating payment: {}", entity); logger.info("Updating payment: {}", entity);
validate(entity); validate(entity);
@ -97,6 +102,7 @@ public class PaymentService extends AbstractService<Payment, Integer> {
} }
} }
@Transactional
private void updateInvoiceStatus(Payment payment) { private void updateInvoiceStatus(Payment payment) {
Invoice invoice = payment.getInvoice(); Invoice invoice = payment.getInvoice();
invoice.setStatus(StatusValues.PAID); invoice.setStatus(StatusValues.PAID);
@ -116,4 +122,9 @@ public class PaymentService extends AbstractService<Payment, Integer> {
return paymentRepository.findByInvoice_User_Id(userId); return paymentRepository.findByInvoice_User_Id(userId);
} }
public Invoice getInvoiceById(Integer id) {
return invoiceService.findById(id);
}
} }

View File

@ -20,7 +20,9 @@ const PaymentForm = () => {
const fetchInvoices = async (userId) => { const fetchInvoices = async (userId) => {
try { try {
const res = await api.get(`/invoices/getAllInvoicesByUserId/${userId}`); const res = await api.get(`/invoices/getAllInvoicesByUserId/${userId}`);
const notPaid = res.data.filter((invoice) => invoice.status === "NOT_PAID"); const notPaid = res.data.filter(
(invoice) => invoice.status === "NOT_PAID"
);
setInvoices(notPaid); setInvoices(notPaid);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
@ -48,6 +50,19 @@ const PaymentForm = () => {
return; return;
} }
const selectedInvoice = invoices.find(
(inv) => inv.id === parseInt(selectedInvoiceId)
);
if (!selectedInvoice) {
setError("Factura no encontrada.");
return;
}
if (parseFloat(amount) < selectedInvoice.total) {
setError("El importe pagado no puede ser menor al total de la factura.");
return;
}
try { try {
await api.post("/payments/create", { await api.post("/payments/create", {
invoiceId: parseInt(selectedInvoiceId), invoiceId: parseInt(selectedInvoiceId),
@ -61,7 +76,7 @@ const PaymentForm = () => {
setSelectedInvoiceId(""); setSelectedInvoiceId("");
setAmount(""); setAmount("");
setPaymentMethod("CASH"); setPaymentMethod("CASH");
fetchInvoices(selectedUserId); // actualizar facturas pendientes fetchInvoices(selectedUserId);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
setError("❌ Error al registrar el pago."); setError("❌ Error al registrar el pago.");
@ -71,10 +86,14 @@ const PaymentForm = () => {
return ( return (
<div className="content-area"> <div className="content-area">
<div className="card"> <div className="card">
<h2>Registrar Pago</h2> <h2>Registrar Pago de una Factura</h2>
<label>Seleccionar estudiante:</label> <label>Seleccionar estudiante:</label>
<select className="form-select" value={selectedUserId} onChange={handleStudentChange}> <select
className="form-select"
value={selectedUserId}
onChange={handleStudentChange}
>
<option value="">-- Selecciona un estudiante --</option> <option value="">-- Selecciona un estudiante --</option>
{students.map((s) => {students.map((s) =>
s.user ? ( s.user ? (
@ -96,13 +115,20 @@ const PaymentForm = () => {
<option value="">-- Selecciona una factura --</option> <option value="">-- Selecciona una factura --</option>
{invoices.map((inv) => ( {invoices.map((inv) => (
<option key={inv.id} value={inv.id}> <option key={inv.id} value={inv.id}>
#{inv.id} - {new Date(inv.date).toLocaleDateString()} - Total: {inv.total.toFixed(2)} #{inv.id} - {new Date(inv.date).toLocaleDateString()} - Total:{" "}
{inv.total.toFixed(2)}
</option> </option>
))} ))}
</select> </select>
</> </>
)} )}
{invoices.length === 0 && selectedUserId && (
<p style={{ marginTop: "1rem", color: "gray" }}>
No hay facturas pendientes de pago para este estudiante.
</p>
)}
{selectedInvoiceId && ( {selectedInvoiceId && (
<> <>
<label>Importe pagado ():</label> <label>Importe pagado ():</label>
@ -121,15 +147,18 @@ const PaymentForm = () => {
onChange={(e) => setPaymentMethod(e.target.value)} onChange={(e) => setPaymentMethod(e.target.value)}
> >
<option value="CASH">Efectivo</option> <option value="CASH">Efectivo</option>
<option value="CARD">Tarjeta</option> <option value="CREDIT_CARD">Tarjeta</option>
<option value="TRANSFER">Transferencia</option> <option value="BANK_TRANSFER">Transferencia</option>
<option value="BIZUM">Bizum</option>
</select> </select>
<ErrorMessage message={error} type="error" /> <ErrorMessage message={error} type="error" />
<ErrorMessage message={success} type="success" /> <ErrorMessage message={success} type="success" />
<button className="btn btn-primary" style={{ marginTop: "1rem" }} onClick={handleSubmit}> <button
className="btn btn-primary"
style={{ marginTop: "1rem" }}
onClick={handleSubmit}
>
💳 Confirmar Pago 💳 Confirmar Pago
</button> </button>
</> </>

View File

@ -48,7 +48,7 @@ const PaymentList = () => {
return ( return (
<div className="content-area"> <div className="content-area">
<div className="card"> <div className="card">
<h2>Listado de Pagos</h2> <h2>Listado de Facturas Pagadas por Estudiante</h2>
<label>Seleccionar estudiante:</label> <label>Seleccionar estudiante:</label>
<select className="form-select" value={selectedUserId} onChange={handleStudentChange}> <select className="form-select" value={selectedUserId} onChange={handleStudentChange}>