From 0aadef56ed5c1b53bcdc5aab5107e63f31443f8a Mon Sep 17 00:00:00 2001 From: Dennis Eckerskorn Date: Wed, 14 May 2025 23:50:04 +0200 Subject: [PATCH] Added payment options but it doesnt work correctly --- .../libs/memberflow-data-1.0-SNAPSHOT.jar | Bin 260039 -> 260347 bytes .../finance_management/PaymentController.java | 9 ++ .../PaymentRepository.java | 4 + .../finance_services/PaymentService.java | 30 +++- .../PaymentServiceTest.java | 64 ++++---- .../src/components/forms/PaymentForm.jsx | 142 ++++++++++++++++++ .../src/components/layout/MainLayout.jsx | 6 + .../src/components/layout/SidebarAdmin.jsx | 3 +- .../src/components/lists/PaymentList.jsx | 106 +++++++++++++ 9 files changed, 325 insertions(+), 39 deletions(-) create mode 100644 memberflow-frontend/src/components/forms/PaymentForm.jsx create mode 100644 memberflow-frontend/src/components/lists/PaymentList.jsx diff --git a/memberflow-api/libs/memberflow-data-1.0-SNAPSHOT.jar b/memberflow-api/libs/memberflow-data-1.0-SNAPSHOT.jar index afe7e7ae80851c9fe5135ada6897d6a89b519f0b..ba35941b8a27ea171ac211181ee867ad5487a24c 100644 GIT binary patch delta 7421 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 YQR->J(W?f5n4tes*&q&UAKNXA-9smFU delta 7117 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 diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java index 6a83586..a88a92b 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/controllers/finance_management/PaymentController.java @@ -62,4 +62,13 @@ public class PaymentController { paymentService.removePayment(id); return new ResponseEntity<>(HttpStatus.NO_CONTENT); } + + @GetMapping("/getAllByUserId/{userId}") + @Operation(summary = "Get all payments by user ID") + public ResponseEntity> getAllByUserId(@PathVariable Integer userId) { + List dtos = paymentService.findAllByUserId(userId) + .stream().map(PaymentDTO::new).collect(Collectors.toList()); + return new ResponseEntity<>(dtos, HttpStatus.OK); + } + } diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/PaymentRepository.java b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/PaymentRepository.java index 5ffd2f7..f49de6d 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/PaymentRepository.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/repositories/finance_repositories/PaymentRepository.java @@ -1,8 +1,12 @@ package com.denniseckerskorn.repositories.finance_repositories; +import com.denniseckerskorn.entities.finance.Invoice; import com.denniseckerskorn.entities.finance.Payment; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; public interface PaymentRepository extends JpaRepository { boolean existsByInvoiceId(Integer invoiceId); + + List findByInvoice_User_Id(Integer userId); } diff --git a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/PaymentService.java b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/PaymentService.java index dbfd7dd..e2626be 100644 --- a/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/PaymentService.java +++ b/memberflow-data/src/main/java/com/denniseckerskorn/services/finance_services/PaymentService.java @@ -8,7 +8,6 @@ import com.denniseckerskorn.exceptions.EntityNotFoundException; import com.denniseckerskorn.exceptions.InvalidDataException; import com.denniseckerskorn.repositories.finance_repositories.PaymentRepository; import com.denniseckerskorn.services.AbstractService; -import com.denniseckerskorn.services.finance_services.InvoiceService; import jakarta.transaction.Transactional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,16 +32,29 @@ public class PaymentService extends AbstractService { @Override @Transactional - public Payment save(Payment entity) throws DuplicateEntityException { - logger.info("Saving payment: {}", entity); - validate(entity); - if (paymentRepository.existsByInvoiceId(entity.getInvoice().getId())) { + public Payment save(Payment payment) throws DuplicateEntityException { + logger.info("Saving payment: {}", payment); + validate(payment); + + Integer invoiceId = payment.getInvoice().getId(); + if (paymentRepository.existsByInvoiceId(invoiceId)) { throw new DuplicateEntityException("A payment already exists for this invoice"); } - updateInvoiceStatus(entity); - return super.save(entity); + + Invoice invoice = invoiceService.findById(invoiceId); + if (invoice == null) { + throw new EntityNotFoundException("Invoice not found"); + } + + payment.setInvoice(invoice); + invoice.setPayment(payment); + + invoice.setStatus(StatusValues.PAID); + invoiceService.update(invoice); + return super.save(payment); } + @Override public Payment update(Payment entity) throws EntityNotFoundException, InvalidDataException { logger.info("Updating payment: {}", entity); @@ -100,4 +112,8 @@ public class PaymentService extends AbstractService { invoiceService.update(invoice); } + public List findAllByUserId(Integer userId) { + return paymentRepository.findByInvoice_User_Id(userId); + } + } diff --git a/memberflow-data/src/test/java/com/denniseckerskorn/services/finance_management_services/PaymentServiceTest.java b/memberflow-data/src/test/java/com/denniseckerskorn/services/finance_management_services/PaymentServiceTest.java index e286e3f..987de4e 100644 --- a/memberflow-data/src/test/java/com/denniseckerskorn/services/finance_management_services/PaymentServiceTest.java +++ b/memberflow-data/src/test/java/com/denniseckerskorn/services/finance_management_services/PaymentServiceTest.java @@ -29,9 +29,6 @@ class PaymentServiceTest { @Mock private InvoiceService invoiceService; - @Mock - private EntityManager entityManager; - @InjectMocks private PaymentService paymentService; @@ -44,59 +41,64 @@ class PaymentServiceTest { invoice = new Invoice(); invoice.setId(1); - invoice.setTotal(new BigDecimal("59.99")); + invoice.setTotal(new BigDecimal("140.00")); invoice.setStatus(StatusValues.NOT_PAID); payment = new Payment(); payment.setId(101); payment.setInvoice(invoice); - payment.setAmount(new BigDecimal("59.99")); + payment.setAmount(new BigDecimal("140.00")); payment.setPaymentDate(LocalDateTime.now().minusDays(1)); - payment.setPaymentMethod(PaymentMethodValues.CREDIT_CARD); + payment.setPaymentMethod(PaymentMethodValues.CASH); payment.setStatus(StatusValues.PAID); - - Field emField = PaymentService.class.getSuperclass().getDeclaredField("entityManager"); - emField.setAccessible(true); - emField.set(paymentService, entityManager); } @Test void save_ShouldUpdateInvoiceStatusAndSavePayment() { when(paymentRepository.existsByInvoiceId(1)).thenReturn(false); + when(invoiceService.findById(1)).thenReturn(invoice); when(paymentRepository.save(any())).thenReturn(payment); Payment saved = paymentService.save(payment); - assertEquals(StatusValues.PAID, payment.getInvoice().getStatus()); + + assertEquals(StatusValues.PAID, saved.getInvoice().getStatus()); verify(invoiceService).update(invoice); + verify(paymentRepository).save(payment); } @Test - void save_DuplicatePayment_ShouldThrow() { + void save_WhenDuplicate_ShouldThrow() { when(paymentRepository.existsByInvoiceId(1)).thenReturn(true); + assertThrows(DuplicateEntityException.class, () -> paymentService.save(payment)); + verify(invoiceService, never()).update(any()); } @Test - void save_InvalidAmount_ShouldThrow() { - payment.setAmount(BigDecimal.ZERO); - assertThrows(InvalidDataException.class, () -> paymentService.save(payment)); - } - - @Test - void save_InvalidPaymentDate_ShouldThrow() { - payment.setPaymentDate(LocalDateTime.now().plusDays(1)); - assertThrows(InvalidDataException.class, () -> paymentService.save(payment)); - } - - @Test - void save_InvalidMethod_ShouldThrow() { - payment.setPaymentMethod(null); - assertThrows(InvalidDataException.class, () -> paymentService.save(payment)); - } - - @Test - void save_InvoiceNull_ShouldThrow() { + void save_WhenInvoiceNull_ShouldThrow() { payment.setInvoice(null); + + assertThrows(InvalidDataException.class, () -> paymentService.save(payment)); + } + + @Test + void save_WhenInvalidAmount_ShouldThrow() { + payment.setAmount(BigDecimal.ZERO); + + assertThrows(InvalidDataException.class, () -> paymentService.save(payment)); + } + + @Test + void save_WhenFutureDate_ShouldThrow() { + payment.setPaymentDate(LocalDateTime.now().plusDays(1)); + + assertThrows(InvalidDataException.class, () -> paymentService.save(payment)); + } + + @Test + void save_WhenMissingMethod_ShouldThrow() { + payment.setPaymentMethod(null); + assertThrows(InvalidDataException.class, () -> paymentService.save(payment)); } } diff --git a/memberflow-frontend/src/components/forms/PaymentForm.jsx b/memberflow-frontend/src/components/forms/PaymentForm.jsx new file mode 100644 index 0000000..a86ec68 --- /dev/null +++ b/memberflow-frontend/src/components/forms/PaymentForm.jsx @@ -0,0 +1,142 @@ +import React, { useEffect, useState } from "react"; +import api from "../../api/axiosConfig"; +import ErrorMessage from "../common/ErrorMessage"; +import "../styles/ContentArea.css"; + +const PaymentForm = () => { + const [students, setStudents] = useState([]); + const [selectedUserId, setSelectedUserId] = useState(""); + const [invoices, setInvoices] = useState([]); + const [selectedInvoiceId, setSelectedInvoiceId] = useState(""); + const [amount, setAmount] = useState(""); + const [paymentMethod, setPaymentMethod] = useState("CASH"); + const [error, setError] = useState(""); + const [success, setSuccess] = useState(""); + + useEffect(() => { + api.get("/students/getAll").then((res) => setStudents(res.data)); + }, []); + + const fetchInvoices = async (userId) => { + try { + const res = await api.get(`/invoices/getAllInvoicesByUserId/${userId}`); + const notPaid = res.data.filter((invoice) => invoice.status === "NOT_PAID"); + setInvoices(notPaid); + } catch (err) { + console.error(err); + setError("Error al cargar facturas."); + } + }; + + const handleStudentChange = (e) => { + const userId = e.target.value; + setSelectedUserId(userId); + setSelectedInvoiceId(""); + setInvoices([]); + if (userId) { + fetchInvoices(userId); + } + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setError(""); + setSuccess(""); + + if (!selectedInvoiceId || !amount || !paymentMethod) { + setError("Completa todos los campos."); + return; + } + + try { + await api.post("/payments/create", { + invoiceId: parseInt(selectedInvoiceId), + paymentDate: new Date().toISOString(), + amount: parseFloat(amount), + paymentMethod, + status: "PAID", + }); + + setSuccess("✅ Pago registrado correctamente."); + setSelectedInvoiceId(""); + setAmount(""); + setPaymentMethod("CASH"); + fetchInvoices(selectedUserId); // actualizar facturas pendientes + } catch (err) { + console.error(err); + setError("❌ Error al registrar el pago."); + } + }; + + return ( +
+
+

Registrar Pago

+ + + + + {invoices.length > 0 && ( + <> + + + + )} + + {selectedInvoiceId && ( + <> + + setAmount(e.target.value)} + /> + + + + + + + + + + )} +
+
+ ); +}; + +export default PaymentForm; diff --git a/memberflow-frontend/src/components/layout/MainLayout.jsx b/memberflow-frontend/src/components/layout/MainLayout.jsx index feda04b..93b6872 100644 --- a/memberflow-frontend/src/components/layout/MainLayout.jsx +++ b/memberflow-frontend/src/components/layout/MainLayout.jsx @@ -24,6 +24,8 @@ import MembershipForm from '../forms/MembershipCreateForm'; import MembershipList from '../lists/MembershipList'; import InvoiceForm from '../forms/InvoiceForm'; import InvoiceList from '../lists/InvoiceList'; +import PaymentForm from '../forms/PaymentForm'; +import PaymentList from '../lists/PaymentList'; import '../styles/MainLayout.css'; @@ -69,6 +71,10 @@ const MainLayout = () => { } /> } /> + } /> + } /> + + {/* Profile Page*/} } /> diff --git a/memberflow-frontend/src/components/layout/SidebarAdmin.jsx b/memberflow-frontend/src/components/layout/SidebarAdmin.jsx index 2dda034..b36588b 100644 --- a/memberflow-frontend/src/components/layout/SidebarAdmin.jsx +++ b/memberflow-frontend/src/components/layout/SidebarAdmin.jsx @@ -39,7 +39,8 @@ const SidebarAdmin = () => {

💵 Finanzas

- + + diff --git a/memberflow-frontend/src/components/lists/PaymentList.jsx b/memberflow-frontend/src/components/lists/PaymentList.jsx new file mode 100644 index 0000000..3168e01 --- /dev/null +++ b/memberflow-frontend/src/components/lists/PaymentList.jsx @@ -0,0 +1,106 @@ +import React, { useState, useEffect } from "react"; +import api from "../../api/axiosConfig"; +import ErrorMessage from "../common/ErrorMessage"; +import '../styles/ContentArea.css'; + +const PaymentList = () => { + const [students, setStudents] = useState([]); + const [selectedUserId, setSelectedUserId] = useState(""); + const [payments, setPayments] = useState([]); + const [error, setError] = useState(""); + + useEffect(() => { + api.get("/students/getAll").then((res) => setStudents(res.data)); + }, []); + + const fetchPayments = async (userId) => { + try { + const res = await api.get(`/payments/getAllByUserId/${userId}`); + setPayments(res.data); + } catch (err) { + console.error(err); + setError("Error al cargar pagos."); + } + }; + + const handleStudentChange = (e) => { + const userId = e.target.value; + setSelectedUserId(userId); + if (userId) { + fetchPayments(userId); + } else { + setPayments([]); + } + }; + + const handleDelete = async (id) => { + if (window.confirm("¿Seguro que quieres eliminar este pago?")) { + try { + await api.delete(`/payments/deleteById/${id}`); + fetchPayments(selectedUserId); + } catch (err) { + console.error(err); + setError("Error al eliminar el pago."); + } + } + }; + + return ( +
+
+

Listado de Pagos

+ + + + + + + {payments.length > 0 && ( + + + + + + + + + + + + + + {payments.map((p) => ( + + + + + + + + + + ))} + +
IDFacturaFechaMonto (€)MétodoEstadoAcciones
{p.id}#{p.invoiceId}{new Date(p.paymentDate).toLocaleDateString()}{p.amount.toFixed(2)}{p.paymentMethod}{p.status} + {/* Botón editar lo montamos luego */} + {/* */} + +
+ )} +
+
+ ); +}; + +export default PaymentList;