From ab3485984ece8403df650be38afb62605300c013 Mon Sep 17 00:00:00 2001 From: Dennis Eckerskorn Date: Thu, 15 May 2025 20:20:41 +0200 Subject: [PATCH] Fixed issues with invoice, payments creation and also added validation of amount --- .../libs/memberflow-data-1.0-SNAPSHOT.jar | Bin 260347 -> 260412 bytes .../finance_management/PaymentController.java | 19 +++++-- .../finance_management_dtos/PaymentDTO.java | 18 ++++++- .../finance_services/PaymentService.java | 15 +++++- .../src/components/forms/PaymentForm.jsx | 47 ++++++++++++++---- .../src/components/lists/PaymentList.jsx | 2 +- 6 files changed, 84 insertions(+), 17 deletions(-) diff --git a/memberflow-api/libs/memberflow-data-1.0-SNAPSHOT.jar b/memberflow-api/libs/memberflow-data-1.0-SNAPSHOT.jar index ba35941b8a27ea171ac211181ee867ad5487a24c..ec84b553274bdc6a1a0438d083802a6255584da9 100644 GIT binary patch delta 4714 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 BRek^f delta 4693 zcmZvg2{hDQ8^CAg8jPhfMbA(A~UcEc+~D2?VeQfdzqJMj)aqixS!Q zgKcpn99*G_>U~fQ%5DoXJ5mwKA_g%^Ne{sM7P@VYK;ogf9eE7O-6(x1Poa#UEJi6n zIgL_lw^oTa7>n*qXPRt6TR;AXIfk{u~VuB&AFpK?VE%JeWIlp1IQDC5wk zAcES?C|Y)VE9Bjb5>RR~YC!oHqw%(1c{k%D^bRW6tl1c!eSk(F!gi2vV2@A;Lts~V z7h5qJdPQQ(86l|hG%-P76q2C9;RB4!P;*pvfab%kILQDt$fGY9VQz1!You^S1fpaY zc~>$&`Sc}bvg}bz`KDGnEg}`aMaz*+G^c64S$(8Izr7VLM|zc{*=q&_Vwi>6-+ppD zinH9$Wsp|L%>5GvR#d(u#>la~o2085V%e++MEQOMLXCXPMS-&T39yux1QoGDM~7$> zfZ!6_hJv6N-$euV8>eU{FY-wwG_@x2(o8eAd1!!6#?pZI?nN3TrcKkFgEQ=DFmmr1 z4brm=X)u3Zmj?0={Af^|bBhLgxv@0p&ZC1vK|M_)_UJYZUKDxIK)NIn$-ccyYobGw z3=)BGBb(+PBpWjDQ=Tn=z2*F6$}~r-@^~6dR}|AArSgmb+!L~Um3HLoLq0Tcn>ax` z#Ja^^_@7sv{M`iuPxC{?A&0lr=Qq0xfzTr-BDpD}f=FJ94-1m13J;Lu@KIICvLFX{ zY$vZ`=RxK(!6$En@a}wzK_Cpc$s9s_+hMY#?9`WmXx}`~K z>3X=N+9%zTb$WBTrravH~3RUQ;y2=&vf%Sy{H8hnbLzj}$!Ma9dRU~YUp#aCC^ z>5kJ%l1Ix*m$kB&g!L<;=d0AK`&yf$BfJ*~5x1D{6LB~#&i%-zMUU!p^IHOm$~wyncFe$%&gRJhdp9q@FybuP(R5suBUBKI?*Ja7ink~G9;K8KCkv*qV-X5 z9~*+H)0;NHYW}`6Y644D%B~A&k!W2bN&H>M-9nKTc&qr_u;pq} zVcs5-vI{2)D>AdA-1aJUt!G43FuLm$l1u*Jn-m_j-9+O$V;7VS>lR9L0-Dz=EA)10 zldrm9{&Y>NTGKp@HZ;cMioXnyxinK*|30@~w?St}Z0xcJ)~9dlntOxuM4{V2?{kdt z?%qrx3$4f;A&!#_9+IAY1KBqpn`>8nBBebk%n>^nwK>om^W*1nZa)@RgALo*=9Ijq zyqH``4HdULrkhD_I%@TLB|IOh&v=~8XwAV!KNV2bnZH@W7sQp`ZKo5%nZtR|7Yux~ z?)-`VatxKU$E;fpa!rKVXBCj^ zk5YtRtELvtP-MeIP!f2gA5n8IwX*dupJX4iddc%~ZZ^_$TsOwhK~66*`|8!Oi?7d@ zeYn|(pNz7KVNnb^5JyINsno}EnOjPeqROstBw*fcHf>eO=TO+AHj#zgnd*-&K<4PIjr20kZ{11(9@V?)~%3LmR<~UNQU3b zv0}>-ju_5nTvS=O!8c#~hjAVmu}deq(hP|2ns_fgvHXnWC{A3qIja9$`tj8)g^I0Z zAql?+hlH=J1Y*4F`0xjKl5Y{5!+1Z$TFrbV*g4c%*9Cr*7%-KSl_pHISyVSAp06s^ z@lIT}sErQ{R?)9Dimp-IbQy5d%_KcfNs>?JzUQ6Y^U6Q^s-J=X-X%MQMir!U&H1Rk zFZAkzR@7s&-9&Xgy*db`sI~EfT^;fM(W`e3#$r~H!jHXomA{vdX2m9F&z_0L3cMRo zKH+dpegD`irKvYf?c=wfsLm9c?f85jWwH51>a1H7PVM12sc8<(zDDusjYK~?(sjoS zIifAP_@MWjSm6Jm#^&@l%XfO+ zD45wR8XRMIu6n(r4ljK(wPs2*7=8V{Lh;w?IE{cSf!+A1llC<`Ih}>(zE)pht=8{p z{}ZL?8_esNDlTeUpB*OgrQkzKZ78W)vjiJ>_#bC!yDrw;cL8;gkIp(6U+_#%7@hOi zZQ#VTf8tA$c|FwwF82BC&}y~bVpe^%B9P)?N=XjEaV;4lD4b&?f>6GPa zV_OZjaI9t`!+Y}DqM|GET3*fm#=!AHr?RUV9hq*o*_y}h$s}r6@aNdwJ>IUW}I zq1$#`g2~J%wk7R9d%Q-AmM_BA2=-$&PTh#hzMmGjE4 z6*AiGSt0!W4_64^r+pUF)?Gt#=*%Y**oMhoL#yEMH(DE&<;AJ$-kE9zyD}v&%V%xu zh{p|wRL4#srs5}<+xQGw-mCT)*}V4sNicq6D&u(0GoG5(_xZ@MGG>gR#x zmNupY+i{#9DZKiPrtN+4gfx?IQpfDGZoUofQTzuZ;}MgW%K=Wd4H4+8oJhyW=mX25 z^%r`jqsxM5$v{mK=H|N&Qvn{TvS)=IhihPOBOn7N7R!W;w&H&!r^ErzAPIa&y=o zbN-y2vz;^h)TsXH=@QBeyN9c>ySk^1vS;jSNYXzUjP|T^F>Bw0y_sL zZ4T=F4}m~9d3(y7arUZGlSJCX*`X>!4oRCLs#=wi=isHYN(qNFh4;{^I0EtwyqZ>- zTOc3O?wDJ)$QllK$E@{2#zAYep&yd^K~)+0fwYs`!wqGw6BI#6?E*(2baerZ?Jx>w zH;{qgISBYDj@>{Mu1fo(2aD<@K56(fLR@H+*Er|c+L+xsSt}Yy)4TTiUAaK|o z=P1;!HQ|Oz27wBMg+X8pLE{6khdDAo0NZW2Jwz|#@eBHgfCqHZ8wOqws)y+t;2EJe z7dk@MeEbEPAAvJ;&izQ=3Fjzqg_^KYdep=yJ+s*uy=dJSaECt9pXiYKi5|uD8JvbD zx6kyJTR+oUrbv#{Pc~{CoPj~@8m2%D1e0kH z0-<9X1VA`GLx-kcfSUzj(9}2!LLunSfk;}^9EgBmF;CCaJ5MihdVwDG`4_k>(tRcu z!ATf}TcRK4gC%-Vp=COxErVd_!@oi=nzjObp$7Yf{&XU!00+H)$0*9rFW`*e|NC}P zzhLk$JG@VV!m$eEC@zxJ87*NMu)(CmtAGz>Dz|MCq}*5o+>}W}`#RsJ^!{!~HfSfi$z)&5t@O(Efsd;eR?@GI4>PWa7!?<;^~C#7!N z#ANZCDb)hWN0I$T?^5F%)n01*n_brqx#z!K*1W{M6Q}-2qs>Wsr~GO(JKW3bw;;B4 zAP9FYvrf(T create(@Valid @RequestBody PaymentDTO dto) throws DuplicateEntityException { - Payment saved = paymentService.save(dto.toEntity()); + public ResponseEntity create(@Valid @RequestBody PaymentDTO dto) + throws DuplicateEntityException, EntityNotFoundException { + + Invoice invoice = paymentService.getInvoiceById(dto.getInvoiceId()); + Payment saved = paymentService.save(dto.toEntityWithInvoice(invoice)); + return new ResponseEntity<>(new PaymentDTO(saved), HttpStatus.CREATED); } + @PutMapping("/update") @Operation(summary = "Update an existing payment and adjust invoice status") - public ResponseEntity update(@Valid @RequestBody PaymentDTO dto) throws EntityNotFoundException, InvalidDataException { - Payment updated = paymentService.update(dto.toEntity()); + public ResponseEntity update(@Valid @RequestBody PaymentDTO dto) + throws EntityNotFoundException, InvalidDataException { + + Invoice invoice = paymentService.getInvoiceById(dto.getInvoiceId()); + Payment updated = paymentService.update(dto.toEntityWithInvoice(invoice)); + return new ResponseEntity<>(new PaymentDTO(updated), HttpStatus.OK); } + @GetMapping("/getById/{id}") @Operation(summary = "Get payment by ID") public ResponseEntity getById(@PathVariable Integer id) throws EntityNotFoundException, InvalidDataException { diff --git a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java index f53606a..0690699 100644 --- a/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java +++ b/memberflow-api/src/main/java/com/denniseckerskorn/dtos/finance_management_dtos/PaymentDTO.java @@ -1,5 +1,6 @@ package com.denniseckerskorn.dtos.finance_management_dtos; +import com.denniseckerskorn.entities.finance.Invoice; import com.denniseckerskorn.entities.finance.Payment; import com.denniseckerskorn.enums.PaymentMethodValues; import com.denniseckerskorn.enums.StatusValues; @@ -27,7 +28,8 @@ public class PaymentDTO { @NotNull private StatusValues status; - public PaymentDTO() {} + public PaymentDTO() { + } public PaymentDTO(Payment entity) { this.id = entity.getId(); @@ -44,6 +46,9 @@ public class PaymentDTO { public Payment toEntity() { Payment payment = new Payment(); + if (this.id != null) { + payment.setId(this.id); + } payment.setId(this.id); payment.setPaymentDate(this.paymentDate); payment.setAmount(this.amount); @@ -52,6 +57,17 @@ public class PaymentDTO { 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... 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 e2626be..4fb78fe 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 @@ -47,15 +47,20 @@ public class PaymentService extends AbstractService { } payment.setInvoice(invoice); - invoice.setPayment(payment); + Payment savedPayment = super.save(payment); + + invoice.setPayment(savedPayment); invoice.setStatus(StatusValues.PAID); invoiceService.update(invoice); - return super.save(payment); + + return savedPayment; } + @Override + @Transactional public Payment update(Payment entity) throws EntityNotFoundException, InvalidDataException { logger.info("Updating payment: {}", entity); validate(entity); @@ -97,6 +102,7 @@ public class PaymentService extends AbstractService { } } + @Transactional private void updateInvoiceStatus(Payment payment) { Invoice invoice = payment.getInvoice(); invoice.setStatus(StatusValues.PAID); @@ -116,4 +122,9 @@ public class PaymentService extends AbstractService { return paymentRepository.findByInvoice_User_Id(userId); } + public Invoice getInvoiceById(Integer id) { + return invoiceService.findById(id); + } + + } diff --git a/memberflow-frontend/src/components/forms/PaymentForm.jsx b/memberflow-frontend/src/components/forms/PaymentForm.jsx index a86ec68..3b463d7 100644 --- a/memberflow-frontend/src/components/forms/PaymentForm.jsx +++ b/memberflow-frontend/src/components/forms/PaymentForm.jsx @@ -20,7 +20,9 @@ const PaymentForm = () => { const fetchInvoices = async (userId) => { try { 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); } catch (err) { console.error(err); @@ -48,6 +50,19 @@ const PaymentForm = () => { 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 { await api.post("/payments/create", { invoiceId: parseInt(selectedInvoiceId), @@ -61,7 +76,7 @@ const PaymentForm = () => { setSelectedInvoiceId(""); setAmount(""); setPaymentMethod("CASH"); - fetchInvoices(selectedUserId); // actualizar facturas pendientes + fetchInvoices(selectedUserId); } catch (err) { console.error(err); setError("❌ Error al registrar el pago."); @@ -71,10 +86,14 @@ const PaymentForm = () => { return (
-

Registrar Pago

+

Registrar Pago de una Factura

- {students.map((s) => s.user ? ( @@ -96,13 +115,20 @@ const PaymentForm = () => { {invoices.map((inv) => ( ))} )} + {invoices.length === 0 && selectedUserId && ( +

+ No hay facturas pendientes de pago para este estudiante. +

+ )} + {selectedInvoiceId && ( <> @@ -121,15 +147,18 @@ const PaymentForm = () => { onChange={(e) => setPaymentMethod(e.target.value)} > - - - + + - diff --git a/memberflow-frontend/src/components/lists/PaymentList.jsx b/memberflow-frontend/src/components/lists/PaymentList.jsx index 3168e01..ea1d3a5 100644 --- a/memberflow-frontend/src/components/lists/PaymentList.jsx +++ b/memberflow-frontend/src/components/lists/PaymentList.jsx @@ -48,7 +48,7 @@ const PaymentList = () => { return (
-

Listado de Pagos

+

Listado de Facturas Pagadas por Estudiante