From 57d214eef40f91b55b833a557131b29cd92b9d6c Mon Sep 17 00:00:00 2001 From: Ryan Andriamahery Date: Thu, 17 Mar 2022 13:18:35 +0300 Subject: [PATCH] fixup: connections and tests between fees and payments --- doc/api.yml | 11 +-- .../rest/controller/PaymentController.java | 15 ++- .../haapi/endpoint/rest/mapper/FeeMapper.java | 9 +- .../endpoint/rest/mapper/PaymentMapper.java | 22 +++-- .../endpoint/rest/security/SecurityConf.java | 6 +- src/main/java/school/hei/haapi/model/Fee.java | 18 +++- .../java/school/hei/haapi/model/Payment.java | 8 +- .../haapi/model/validator/FeeValidator.java | 2 +- .../model/validator/PaymentValidator.java | 36 ++++++++ .../school/hei/haapi/service/FeeService.java | 57 ++++++++---- .../hei/haapi/service/PaymentService.java | 30 +++++- .../db/migration/V0_3__Create_fee_table.sql | 5 - .../db/testdata/V99_3__Test_create_fees.sql | 12 ++- .../testdata/V99_4__Test_create_payments.sql | 8 +- .../school/hei/haapi/integration/FeeIT.java | 60 ++++++++---- .../hei/haapi/integration/PaymentIT.java | 91 ++++++++++++++----- .../hei/haapi/integration/conf/TestUtils.java | 1 + 17 files changed, 280 insertions(+), 111 deletions(-) create mode 100644 src/main/java/school/hei/haapi/model/validator/PaymentValidator.java diff --git a/doc/api.yml b/doc/api.yml index 60fe4ddab..e464b06e2 100644 --- a/doc/api.yml +++ b/doc/api.yml @@ -686,7 +686,6 @@ paths: $ref: '#/components/responses/429' '500': $ref: '#/components/responses/500' - /students/{student_id}/fees/{fee_id}/payments: get: tags: @@ -723,16 +722,12 @@ paths: $ref: '#/components/responses/429' '500': $ref: '#/components/responses/500' + /fees/{fee_id}/payments: post: tags: - Paying summary: Create student payments parameters: - - name: student_id - in: path - required: true - schema: - type: string - name: fee_id in: path required: true @@ -740,7 +735,7 @@ paths: type: string operationId: createStudentPayments requestBody: - description: Student fees to create + description: Student payments to create required: true content: application/json: @@ -767,7 +762,7 @@ paths: $ref: '#/components/responses/429' '500': $ref: '#/components/responses/500' - /students/{student_id}/fees/payments: + /students/{student_id}/payments: get: tags: - Paying diff --git a/src/main/java/school/hei/haapi/endpoint/rest/controller/PaymentController.java b/src/main/java/school/hei/haapi/endpoint/rest/controller/PaymentController.java index 25e864404..156939921 100644 --- a/src/main/java/school/hei/haapi/endpoint/rest/controller/PaymentController.java +++ b/src/main/java/school/hei/haapi/endpoint/rest/controller/PaymentController.java @@ -21,13 +21,11 @@ public class PaymentController { private final PaymentService paymentService; private final PaymentMapper paymentMapper; - @PostMapping("/students/{studentId}/fees/{feeId}/payments") - public List createPayments( - @PathVariable String studentId, - @PathVariable String feeId, + @PostMapping("/fees/{feeId}/payments") + public List createPayments(@PathVariable String feeId, @RequestBody List toCreate) { return paymentService - .saveAll(studentId, feeId, toCreate.stream() + .saveAll(feeId, toCreate.stream() .map(paymentMapper::toDomainPayment) .collect(toUnmodifiableList())) .stream() @@ -36,14 +34,13 @@ public List createPayments( } @GetMapping("/students/{studentId}/fees/{feeId}/payments") - public List getFeePaymentsByStudentId( - @PathVariable String studentId, @PathVariable String feeId) { - return paymentService.getByStudentIdAndFeeId(studentId, feeId).stream() + public List getFeePaymentsByStudentId(@PathVariable String feeId) { + return paymentService.getByFeeId(feeId).stream() .map(paymentMapper::toRestPayment) .collect(toUnmodifiableList()); } - @GetMapping("/students/{studentId}/fees/payments") + @GetMapping("/students/{studentId}/payments") public List getPaymentsByStudentId(@PathVariable String studentId) { return paymentService.getByStudentId(studentId).stream() .map(paymentMapper::toRestPayment) diff --git a/src/main/java/school/hei/haapi/endpoint/rest/mapper/FeeMapper.java b/src/main/java/school/hei/haapi/endpoint/rest/mapper/FeeMapper.java index 852ef1b72..39719961f 100644 --- a/src/main/java/school/hei/haapi/endpoint/rest/mapper/FeeMapper.java +++ b/src/main/java/school/hei/haapi/endpoint/rest/mapper/FeeMapper.java @@ -1,17 +1,22 @@ package school.hei.haapi.endpoint.rest.mapper; import java.util.Objects; +import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import school.hei.haapi.endpoint.rest.model.CreateFee; import school.hei.haapi.endpoint.rest.model.Fee; import school.hei.haapi.model.exception.BadRequestException; +import school.hei.haapi.service.UserService; @Component +@AllArgsConstructor public class FeeMapper { + private final UserService userService; + public Fee toRestFee(school.hei.haapi.model.Fee fee) { return new Fee() .id(fee.getId()) - .studentId(fee.getStudentId()) + .studentId(fee.getStudent().getId()) .status(fee.getStatus()) .type(fee.getType()) .totalAmount(fee.getTotalAmount()) @@ -26,7 +31,7 @@ public school.hei.haapi.model.Fee toDomainFee(CreateFee createFee) { throw new BadRequestException("Total amount is mandatory"); } return school.hei.haapi.model.Fee.builder() - .studentId(createFee.getStudentId()) + .student(userService.getById(createFee.getStudentId())) .type(toDomainFeeType(Objects.requireNonNull(createFee.getType()))) .totalAmount(createFee.getTotalAmount()) .comment(createFee.getComment()) diff --git a/src/main/java/school/hei/haapi/endpoint/rest/mapper/PaymentMapper.java b/src/main/java/school/hei/haapi/endpoint/rest/mapper/PaymentMapper.java index b2a4caaf0..b6c5b2c7b 100644 --- a/src/main/java/school/hei/haapi/endpoint/rest/mapper/PaymentMapper.java +++ b/src/main/java/school/hei/haapi/endpoint/rest/mapper/PaymentMapper.java @@ -1,16 +1,21 @@ package school.hei.haapi.endpoint.rest.mapper; +import lombok.AllArgsConstructor; import org.springframework.stereotype.Component; import school.hei.haapi.endpoint.rest.model.CreatePayment; import school.hei.haapi.endpoint.rest.model.Payment; import school.hei.haapi.model.exception.BadRequestException; +import school.hei.haapi.service.FeeService; @Component +@AllArgsConstructor public class PaymentMapper { + private final FeeService feeService; + public Payment toRestPayment(school.hei.haapi.model.Payment payment) { return new Payment() .id(payment.getId()) - .feeId(payment.getFeeId()) + .feeId(payment.getFee().getId()) .type(payment.getType()) .amount(payment.getAmount()) .comment(payment.getComment()) @@ -18,8 +23,11 @@ public Payment toRestPayment(school.hei.haapi.model.Payment payment) { } public school.hei.haapi.model.Payment toDomainPayment(CreatePayment createPayment) { + if (createPayment.getAmount() == null) { + throw new BadRequestException("Amount is mandatory"); + } return school.hei.haapi.model.Payment.builder() - .feeId(createPayment.getFeeId()) + .fee(feeService.getById(createPayment.getFeeId())) .type(toDomainPaymentType(createPayment.getType())) .amount(createPayment.getAmount()) .comment(createPayment.getComment()) @@ -27,14 +35,14 @@ public school.hei.haapi.model.Payment toDomainPayment(CreatePayment createPaymen } private Payment.TypeEnum toDomainPaymentType(CreatePayment.TypeEnum createPaymentType) { - String feeType = createPaymentType.toString(); - if (feeType.equals(Payment.TypeEnum.CASH.toString())) { + String paymentType = createPaymentType.toString(); + if (paymentType.equals(Payment.TypeEnum.CASH.toString())) { return Payment.TypeEnum.CASH; - } else if (feeType.equals(Payment.TypeEnum.SCOLARSHIP.toString())) { + } else if (paymentType.equals(Payment.TypeEnum.SCOLARSHIP.toString())) { return Payment.TypeEnum.SCOLARSHIP; - } else if (feeType.equals(Payment.TypeEnum.FIX.toString())) { + } else if (paymentType.equals(Payment.TypeEnum.FIX.toString())) { return Payment.TypeEnum.FIX; } - throw new BadRequestException("Payment type must be valid"); + throw new BadRequestException("Unexpected paymentFee: " + paymentType); } } diff --git a/src/main/java/school/hei/haapi/endpoint/rest/security/SecurityConf.java b/src/main/java/school/hei/haapi/endpoint/rest/security/SecurityConf.java index d86785a3b..50ada335b 100644 --- a/src/main/java/school/hei/haapi/endpoint/rest/security/SecurityConf.java +++ b/src/main/java/school/hei/haapi/endpoint/rest/security/SecurityConf.java @@ -80,10 +80,12 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers(GET, "/students/*/fees/*").hasAnyRole(MANAGER.getRole()) .antMatchers(POST, "/students/*/fees").hasAnyRole(MANAGER.getRole()) .requestMatchers(new SelfMatcher(GET, "/students/*/fees")).hasAnyRole(STUDENT.getRole()) + .antMatchers(GET, "/students/*/fees").hasAnyRole(MANAGER.getRole()) .requestMatchers(new SelfMatcher(GET, "/students/*/fees/*/payments")).hasAnyRole(STUDENT.getRole()) .antMatchers(GET, "/students/*/fees/*/payments").hasAnyRole(MANAGER.getRole()) - .antMatchers(POST, "/students/*/fees/*/payments").hasAnyRole(MANAGER.getRole()) - .antMatchers(GET, "/students/*/fees").hasAnyRole(MANAGER.getRole()) + .antMatchers(POST, "/fees/*/payments").hasAnyRole(MANAGER.getRole()) + .requestMatchers(new SelfMatcher(GET, "/students/*/payments")).hasAnyRole(STUDENT.getRole()) + .antMatchers(GET, "/students/*/payments").hasAnyRole(MANAGER.getRole()) .requestMatchers(new SelfMatcher(GET, "/students/*")).hasAnyRole(STUDENT.getRole()) .antMatchers(GET, "/students/*").hasAnyRole(TEACHER.getRole(), MANAGER.getRole()) .antMatchers(PUT, "/students/**").hasAnyRole(MANAGER.getRole()) diff --git a/src/main/java/school/hei/haapi/model/Fee.java b/src/main/java/school/hei/haapi/model/Fee.java index 395172e53..9b3d317ea 100644 --- a/src/main/java/school/hei/haapi/model/Fee.java +++ b/src/main/java/school/hei/haapi/model/Fee.java @@ -2,14 +2,18 @@ import java.io.Serializable; import java.time.Instant; +import java.util.List; import java.util.Objects; -import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Enumerated; import javax.persistence.EnumType; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; import javax.persistence.Table; +import javax.persistence.Transient; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -38,11 +42,13 @@ public class Fee implements Serializable { @GeneratedValue(strategy = IDENTITY) private String id; - @Column(name = "user_id") - private String studentId; + @ManyToOne + @JoinColumn(name = "user_id", nullable = false) + private User student; @Type(type = "pgsql_enum") @Enumerated(EnumType.STRING) + @Transient private school.hei.haapi.endpoint.rest.model.Fee.StatusEnum status; @Type(type = "pgsql_enum") @@ -51,6 +57,7 @@ public class Fee implements Serializable { private int totalAmount; + @Transient private int remainingAmount; private String comment; @@ -58,6 +65,9 @@ public class Fee implements Serializable { private Instant dueDatetime; + @OneToMany(mappedBy = "fee") + private List paymentList; + @Override public boolean equals(Object o) { if (this == o) { @@ -70,7 +80,7 @@ public boolean equals(Object o) { return totalAmount == fee.totalAmount && remainingAmount == fee.remainingAmount && Objects.equals(id, fee.id) - && Objects.equals(studentId, fee.studentId) + && Objects.equals(student.getId(), fee.student.getId()) && status == fee.status && type == fee.type && Objects.equals(creationDatetime, fee.creationDatetime) diff --git a/src/main/java/school/hei/haapi/model/Payment.java b/src/main/java/school/hei/haapi/model/Payment.java index 67ff7dcd2..4a93c7df2 100644 --- a/src/main/java/school/hei/haapi/model/Payment.java +++ b/src/main/java/school/hei/haapi/model/Payment.java @@ -8,6 +8,8 @@ import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; import javax.persistence.Table; import lombok.AllArgsConstructor; import lombok.Builder; @@ -37,7 +39,9 @@ public class Payment implements Serializable { @GeneratedValue(strategy = IDENTITY) private String id; - private String feeId; + @ManyToOne + @JoinColumn(name = "fee_id", nullable = false) + private Fee fee; @Type(type = "pgsql_enum") @Enumerated(EnumType.STRING) @@ -58,7 +62,7 @@ public boolean equals(Object o) { Payment payment = (Payment) o; return amount == payment.amount && Objects.equals(id, payment.id) - && Objects.equals(feeId, payment.feeId) + && Objects.equals(fee.getId(), payment.getFee().getId()) && type == payment.type && creationDatetime.compareTo(payment.creationDatetime) == 0; } diff --git a/src/main/java/school/hei/haapi/model/validator/FeeValidator.java b/src/main/java/school/hei/haapi/model/validator/FeeValidator.java index 2ef8b895b..286ca168a 100644 --- a/src/main/java/school/hei/haapi/model/validator/FeeValidator.java +++ b/src/main/java/school/hei/haapi/model/validator/FeeValidator.java @@ -18,7 +18,7 @@ public void accept(List fees) { @Override public void accept(Fee fee) { Set violationMessages = new HashSet<>(); - if (fee.getStudentId() == null) { + if (fee.getStudent() == null) { violationMessages.add("Student is mandatory"); } if (fee.getDueDatetime() == null) { diff --git a/src/main/java/school/hei/haapi/model/validator/PaymentValidator.java b/src/main/java/school/hei/haapi/model/validator/PaymentValidator.java new file mode 100644 index 000000000..626af4454 --- /dev/null +++ b/src/main/java/school/hei/haapi/model/validator/PaymentValidator.java @@ -0,0 +1,36 @@ +package school.hei.haapi.model.validator; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Component; +import school.hei.haapi.model.Payment; +import school.hei.haapi.model.exception.BadRequestException; + +@Component +@AllArgsConstructor +public class PaymentValidator implements Consumer { + + public void accept(List payments) { + payments.forEach(this::accept); + } + + @Override public void accept(Payment payment) { + Set violationMessages = new HashSet<>(); + if (payment.getFee() == null) { + violationMessages.add("Fee is mandatory"); + } + if (payment.getAmount() < 0) { + violationMessages.add("Amount must be positive"); + } + if (!violationMessages.isEmpty()) { + String formattedViolationMessages = violationMessages.stream() + .map(String::toString) + .collect(Collectors.joining(". ")); + throw new BadRequestException(formattedViolationMessages); + } + } +} diff --git a/src/main/java/school/hei/haapi/service/FeeService.java b/src/main/java/school/hei/haapi/service/FeeService.java index 8d0dd99cb..9034d1002 100644 --- a/src/main/java/school/hei/haapi/service/FeeService.java +++ b/src/main/java/school/hei/haapi/service/FeeService.java @@ -1,14 +1,16 @@ package school.hei.haapi.service; import java.util.List; -import javax.transaction.Transactional; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import school.hei.haapi.model.Fee; +import school.hei.haapi.model.Payment; import school.hei.haapi.model.exception.BadRequestException; import school.hei.haapi.model.validator.FeeValidator; import school.hei.haapi.repository.FeeRepository; +import static java.util.stream.Collectors.toUnmodifiableList; + @Service @AllArgsConstructor public class FeeService { @@ -16,39 +18,58 @@ public class FeeService { private final FeeRepository feeRepository; private final FeeValidator feeValidator; - public Fee getByStudentIdAndFeeId(String userId, String feeId) { - return feeRepository.getByStudentIdAndId(userId, feeId); + public Fee getById(String id) { + return refreshFees(feeRepository.getById(id)); + } + + public Fee getByStudentIdAndFeeId(String studentId, String feeId) { + return refreshFees(feeRepository.getByStudentIdAndId(studentId, feeId)); } - @Transactional public List saveAll(String studentId, List fees) { checkStudentFees(studentId, fees); - for (Fee f : fees) { - f.setRemainingAmount(computeRemainingAmount(f)); - f.setStatus(getFeeStatus(f)); - } - return feeRepository.saveAll(fees); + return refreshFees(feeRepository.saveAll(fees)); } public List getFeesByStudentId(String studentId) { - return feeRepository.getByStudentId(studentId); + return refreshFees(feeRepository.getByStudentId(studentId)); + } + + private school.hei.haapi.endpoint.rest.model.Fee.StatusEnum getFeeStatus(Fee fee) { + if (fee.getRemainingAmount() == 0) { + return school.hei.haapi.endpoint.rest.model.Fee.StatusEnum.PAID; + } + return school.hei.haapi.endpoint.rest.model.Fee.StatusEnum.UNPAID; } private int computeRemainingAmount(Fee fee) { - //TODO : remaining amount is computed from payment status associated to the fee - return fee.getTotalAmount(); //By default return total amount + List payments = fee.getPaymentList(); + int amount = 0; + if (payments != null) { + for (Payment payment : payments) { + amount += payment.getAmount(); + } + } + return fee.getTotalAmount() - amount; } - private school.hei.haapi.endpoint.rest.model.Fee.StatusEnum getFeeStatus(Fee fee) { - //TODO : remaining amount is computed from payment status associated to the fee - return school.hei.haapi.endpoint.rest.model.Fee.StatusEnum.UNPAID; //By default return UNPAID + private Fee refreshFees(Fee fee) { + fee.setRemainingAmount(computeRemainingAmount(fee)); + fee.setStatus(getFeeStatus(fee)); + return fee; + } + + private List refreshFees(List fees) { + return fees.stream() + .map(this::refreshFees) + .collect(toUnmodifiableList()); } private void checkStudentFees(String studentId, Fee toCheck) { - if (!studentId.equals(toCheck.getStudentId())) { + if (!studentId.equals(toCheck.getStudent().getId())) { throw new BadRequestException( - "Fee must be associated to student." + toCheck.getStudentId() + " instead of student." - + studentId); + "Fee must be associated to student." + toCheck.getStudent().getId() + + " instead of student." + studentId); } } diff --git a/src/main/java/school/hei/haapi/service/PaymentService.java b/src/main/java/school/hei/haapi/service/PaymentService.java index 9af88ac13..3b637dbd6 100644 --- a/src/main/java/school/hei/haapi/service/PaymentService.java +++ b/src/main/java/school/hei/haapi/service/PaymentService.java @@ -1,9 +1,13 @@ package school.hei.haapi.service; import java.util.List; +import javax.transaction.Transactional; import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; +import school.hei.haapi.model.Fee; import school.hei.haapi.model.Payment; +import school.hei.haapi.model.exception.BadRequestException; +import school.hei.haapi.model.validator.PaymentValidator; import school.hei.haapi.repository.PaymentRepository; @Service @@ -11,8 +15,9 @@ public class PaymentService { private final PaymentRepository paymentRepository; + private final PaymentValidator paymentValidator; - public List getByStudentIdAndFeeId(String studentId, String feeId) { + public List getByFeeId(String feeId) { return paymentRepository.getByFeeId(feeId); } @@ -20,7 +25,28 @@ public List getByStudentId(String studentId) { return paymentRepository.getByStudentId(studentId); } - public List saveAll(String studentId, String feeId, List toCreate) { + @Transactional + public List saveAll(String feeId, List toCreate) { + checkStudentPayments(feeId, toCreate); return paymentRepository.saveAll(toCreate); } + + private void checkStudentPayments(String feeId, Payment toCheck) { + Fee associatedFee = toCheck.getFee(); + if (!feeId.equals(associatedFee.getId())) { + throw new BadRequestException( + "Payment must be associated to fee." + associatedFee.getId() + + " instead of fee." + feeId); + } + if (associatedFee.getRemainingAmount() < toCheck.getAmount()) { + throw new BadRequestException( + "Fee remaining amount is " + associatedFee.getRemainingAmount() + + ". Actual payment amount is " + toCheck.getAmount()); + } + } + + private void checkStudentPayments(String feeId, List payments) { + paymentValidator.accept(payments); + payments.forEach(payment -> checkStudentPayments(feeId, payment)); + } } diff --git a/src/main/resources/db/migration/V0_3__Create_fee_table.sql b/src/main/resources/db/migration/V0_3__Create_fee_table.sql index e191839d5..be5fae195 100644 --- a/src/main/resources/db/migration/V0_3__Create_fee_table.sql +++ b/src/main/resources/db/migration/V0_3__Create_fee_table.sql @@ -1,9 +1,6 @@ do $$ begin - if not exists(select from pg_type where typname = 'fee_status') then - create type "fee_status" as enum ('UNPAID', 'PAID', 'LATE'); - end if; if not exists(select from pg_type where typname = 'fee_type') then create type "fee_type" as enum ('TUITION', 'HARDWARE'); end if; @@ -16,11 +13,9 @@ create table if not exists "fee" constraint fee_pk primary key default uuid_generate_v4(), user_id varchar not null constraint fee_user_id_fk references "user"(id), - status fee_status not null, type fee_type not null, total_amount integer not null, comment varchar not null, - remaining_amount integer not null, creation_datetime timestamp with time zone not null default now(), due_datetime timestamp with time zone not null ); diff --git a/src/main/resources/db/testdata/V99_3__Test_create_fees.sql b/src/main/resources/db/testdata/V99_3__Test_create_fees.sql index e3439088c..e3fd75a41 100644 --- a/src/main/resources/db/testdata/V99_3__Test_create_fees.sql +++ b/src/main/resources/db/testdata/V99_3__Test_create_fees.sql @@ -1,8 +1,12 @@ insert into "fee" - (id, user_id, status, type, comment,total_amount, remaining_amount, creation_datetime, due_datetime) + (id, user_id, type, comment,total_amount, creation_datetime, due_datetime) values - ('fee1_id', 'student1_id', 'UNPAID', 'TUITION','Comment', 5000, 5000, '2021-11-08T08:25:24.00Z', '2021-12-08T08:25:24.00Z'); + ('fee1_id', 'student1_id', 'TUITION','Comment', 5000, '2021-11-08T08:25:24.00Z', '2021-12-08T08:25:24.00Z'); insert into "fee" - (id, user_id, status, type, comment,total_amount, remaining_amount, creation_datetime, due_datetime) + (id, user_id, type, comment,total_amount, creation_datetime, due_datetime) values - ('fee2_id', 'student2_id', 'UNPAID', 'TUITION','Comment', 5000, 5000, '2022-12-08T08:25:24.00Z', '2021-12-31T08:25:24.00Z'); \ No newline at end of file + ('fee2_id', 'student2_id', 'TUITION','Comment', 5000, '2021-11-08T08:25:24.00Z', '2021-12-08T08:25:24.00Z'); +insert into "fee" + (id, user_id, type, comment,total_amount, creation_datetime, due_datetime) +values + ('fee3_id', 'student2_id', 'TUITION','Comment', 5000, '2022-12-08T08:25:24.00Z', '2021-12-31T08:25:24.00Z'); \ No newline at end of file diff --git a/src/main/resources/db/testdata/V99_4__Test_create_payments.sql b/src/main/resources/db/testdata/V99_4__Test_create_payments.sql index df8d799dc..72696b009 100644 --- a/src/main/resources/db/testdata/V99_4__Test_create_payments.sql +++ b/src/main/resources/db/testdata/V99_4__Test_create_payments.sql @@ -1,8 +1,12 @@ insert into "payment" (id, fee_id, type, comment,amount, creation_datetime) values - ('payment1_id','fee1_id', 'CASH', 'Comment', 2000, '2021-11-08T08:25:24.00Z'); + ('payment1_id','fee1_id', 'CASH', 'Comment', 2000, '2022-11-08T08:25:24.00Z'); insert into "payment" (id, fee_id, type, comment,amount, creation_datetime) values - ('payment2_id','fee2_id', 'CASH', 'Comment', 2000, '2021-11-08T08:25:24.00Z'); \ No newline at end of file + ('payment2_id','fee1_id', 'CASH', 'Comment', 3000, '2022-11-08T08:25:25.00Z'); +insert into "payment" + (id, fee_id, type, comment,amount, creation_datetime) +values + ('payment3_id','fee2_id', 'CASH', 'Comment', 3000, '2022-11-08T08:25:26.00Z'); \ No newline at end of file diff --git a/src/test/java/school/hei/haapi/integration/FeeIT.java b/src/test/java/school/hei/haapi/integration/FeeIT.java index ed8192cb4..fa53f1572 100644 --- a/src/test/java/school/hei/haapi/integration/FeeIT.java +++ b/src/test/java/school/hei/haapi/integration/FeeIT.java @@ -1,7 +1,6 @@ package school.hei.haapi.integration; import java.time.Instant; -import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -15,6 +14,7 @@ import school.hei.haapi.endpoint.rest.client.ApiClient; import school.hei.haapi.endpoint.rest.client.ApiException; import school.hei.haapi.endpoint.rest.model.CreateFee; +import school.hei.haapi.endpoint.rest.model.CreatePayment; import school.hei.haapi.endpoint.rest.model.Fee; import school.hei.haapi.endpoint.rest.security.cognito.CognitoComponent; import school.hei.haapi.integration.conf.AbstractContextInitializer; @@ -65,16 +65,38 @@ static Fee fee1() { Fee fee = new Fee(); fee.setId(FEE1_ID); fee.setStudentId(STUDENT1_ID); + fee.setStatus(Fee.StatusEnum.PAID); + fee.setType(Fee.TypeEnum.TUITION); + fee.setTotalAmount(5000); + fee.setRemainingAmount(0); + fee.setComment("Comment"); + fee.creationDatetime(Instant.parse("2021-11-08T08:25:24.00Z")); + fee.setDueDatetime(Instant.parse("2021-12-08T08:25:24.00Z")); + return fee; + } + + static Fee fee2() { + Fee fee = new Fee(); + fee.setId(FEE2_ID); + fee.setStudentId(STUDENT2_ID); fee.setStatus(Fee.StatusEnum.UNPAID); fee.setType(Fee.TypeEnum.TUITION); fee.setTotalAmount(5000); - fee.setRemainingAmount(5000); + fee.setRemainingAmount(2000); fee.setComment("Comment"); fee.creationDatetime(Instant.parse("2021-11-08T08:25:24.00Z")); fee.setDueDatetime(Instant.parse("2021-12-08T08:25:24.00Z")); return fee; } + static CreatePayment creatablePayment2() { + return new CreatePayment() + .feeId("fee2_id") + .type(CreatePayment.TypeEnum.CASH) + .amount(2000) + .comment("Comment"); + } + static CreateFee creatableFee1() { return new CreateFee() .studentId(STUDENT1_ID) @@ -88,19 +110,11 @@ static CreateFee creatableFee2() { return new CreateFee() .studentId(STUDENT2_ID) .type(CreateFee.TypeEnum.TUITION) - .totalAmount(5000) + .totalAmount(2000) .comment("Comment") .dueDatetime(Instant.parse("2021-12-08T08:25:24.00Z")); } - static List someCreatableFeeList(int number) { - List toCreate = new ArrayList<>(); - for (int i = 0; i < number; i++) { - toCreate.add(creatableFee1()); - } - return toCreate; - } - @Test void teacher_read_ko() { ApiClient teacher1Client = anApiClient(TEACHER1_TOKEN); @@ -116,9 +130,16 @@ void manager_read_ok() throws ApiException { ApiClient manager1Client = anApiClient(MANAGER1_TOKEN); PayingApi api = new PayingApi(manager1Client); - List actual = api.getStudentFees(STUDENT1_ID); + List actual = api.getStudentFees(STUDENT2_ID); + Fee actualFee1 = api.getStudentFeeById(STUDENT2_ID, FEE2_ID); + api.createStudentPayments(FEE2_ID, List.of(creatablePayment2())); + Fee actualFee2 = api.getStudentFeeById(STUDENT2_ID, FEE2_ID); - assertTrue(actual.contains(fee1())); + assertTrue(actual.contains(fee2())); + assertEquals(Fee.StatusEnum.UNPAID, actualFee1.getStatus()); + assertEquals(Fee.StatusEnum.PAID, actualFee2.getStatus()); + assertEquals(2000, actualFee1.getRemainingAmount()); + assertEquals(0, actualFee2.getRemainingAmount()); } @Test @@ -140,7 +161,9 @@ void student_read_own_ok() throws ApiException { Fee actualFee = api.getStudentFeeById(STUDENT1_ID, FEE1_ID); assertTrue(actual.contains(fee1())); - assertEquals(actualFee, fee1()); + assertEquals(fee1(), actualFee); + assertEquals(Fee.StatusEnum.PAID, actualFee.getStatus()); + assertEquals(0, actualFee.getRemainingAmount()); } @Test @@ -150,8 +173,8 @@ void manager_write_ok() throws ApiException { List actual = api.createStudentFees(STUDENT1_ID, List.of(creatableFee1())); - List expectedFees = api.getStudentFees(STUDENT1_ID); - assertTrue(expectedFees.containsAll(actual)); + List expected = api.getStudentFees(STUDENT1_ID); + assertTrue(expected.containsAll(actual)); } @Test @@ -191,9 +214,7 @@ void manager_write_with_some_bad_fields_ko() { PayingApi api = new PayingApi(manager1Client); CreateFee toCreate1 = creatableFee1().totalAmount(null); CreateFee toCreate2 = creatableFee1().totalAmount(-1); - CreateFee toCreate3 = creatableFee1() - .studentId(null) - .dueDatetime(null); + CreateFee toCreate3 = creatableFee1().dueDatetime(null); ApiException exception1 = assertThrows(ApiException.class, () -> api.createStudentFees(STUDENT1_ID, List.of(toCreate1))); @@ -208,6 +229,5 @@ void manager_write_with_some_bad_fields_ko() { assertTrue(exceptionMessage1.contains("Total amount is mandatory")); assertTrue(exceptionMessage2.contains("Total amount must be positive")); assertTrue(exceptionMessage3.contains("Due datetime is mandatory")); - assertTrue(exceptionMessage3.contains("Student is mandatory")); } } diff --git a/src/test/java/school/hei/haapi/integration/PaymentIT.java b/src/test/java/school/hei/haapi/integration/PaymentIT.java index f08af3855..884f70273 100644 --- a/src/test/java/school/hei/haapi/integration/PaymentIT.java +++ b/src/test/java/school/hei/haapi/integration/PaymentIT.java @@ -19,10 +19,11 @@ import school.hei.haapi.integration.conf.AbstractContextInitializer; import school.hei.haapi.integration.conf.TestUtils; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import static school.hei.haapi.integration.conf.TestUtils.FEE1_ID; -import static school.hei.haapi.integration.conf.TestUtils.FEE2_ID; +import static school.hei.haapi.integration.conf.TestUtils.FEE3_ID; import static school.hei.haapi.integration.conf.TestUtils.MANAGER1_TOKEN; import static school.hei.haapi.integration.conf.TestUtils.STUDENT1_ID; import static school.hei.haapi.integration.conf.TestUtils.STUDENT1_TOKEN; @@ -54,28 +55,18 @@ private static ApiClient anApiClient(String token) { } @BeforeEach - public void setUp() { + void setUp() { setUpCognito(cognitoComponentMock); } static Payment payment1() { return new Payment() .id("payment1_id") - .feeId("fee1_id") + .feeId(FEE1_ID) .type(Payment.TypeEnum.CASH) .amount(2000) .comment("Comment") - .creationDatetime(Instant.parse("2021-11-08T08:25:24.00Z")); - } - - static Payment payment2() { - return new Payment() - .id("payment2_id") - .feeId("fee2_id") - .type(Payment.TypeEnum.CASH) - .amount(2000) - .comment("Comment") - .creationDatetime(Instant.parse("2021-11-08T08:25:24.00Z")); + .creationDatetime(Instant.parse("2022-11-08T08:25:24.00Z")); } static CreatePayment creatablePayment1() { @@ -86,6 +77,14 @@ static CreatePayment creatablePayment1() { .comment("Comment"); } + static CreatePayment creatablePayment3() { + return new CreatePayment() + .feeId("fee3_id") + .type(CreatePayment.TypeEnum.CASH) + .amount(6000) + .comment("Comment"); + } + @Test void student_read_own_ok() throws ApiException { ApiClient student1Client = anApiClient(STUDENT1_TOKEN); @@ -97,13 +96,23 @@ void student_read_own_ok() throws ApiException { } @Test - void student1_read_ko() { + void student_read_all_own_ok() throws ApiException { + ApiClient student2Client = anApiClient(STUDENT1_TOKEN); + PayingApi api = new PayingApi(student2Client); + + List actual = api.getStudentPayments(STUDENT1_ID); + + assertTrue(actual.contains(payment1())); + } + + @Test + void student1_read_all_ko() { ApiClient student1Client = anApiClient(STUDENT1_TOKEN); PayingApi api = new PayingApi(student1Client); assertThrowsApiException( "{\"type\":\"403 FORBIDDEN\",\"message\":\"Access is denied\"}", - () -> api.getStudentFeePayments(STUDENT2_ID, FEE2_ID)); + () -> api.getStudentPayments(STUDENT2_ID)); } @Test @@ -112,10 +121,8 @@ void manager_read_ok() throws ApiException { PayingApi api = new PayingApi(manager1Client); List student1Payments = api.getStudentFeePayments(STUDENT1_ID, FEE1_ID); - List student2Payments = api.getStudentFeePayments(STUDENT2_ID, FEE2_ID); assertTrue(student1Payments.contains(payment1())); - assertTrue(student2Payments.contains(payment2())); } @Test @@ -125,19 +132,53 @@ void teacher_read_ko() { assertThrowsApiException( "{\"type\":\"403 FORBIDDEN\",\"message\":\"Access is denied\"}", - () -> api.getStudentFeePayments(STUDENT1_ID, FEE1_ID)); + () -> api.getStudentFeePayments(STUDENT2_ID, FEE1_ID)); } @Test void manager_write_ok() throws ApiException { ApiClient manager1Client = anApiClient(MANAGER1_TOKEN); PayingApi api = new PayingApi(manager1Client); + CreatePayment toCreate = creatablePayment3().amount(5000); - List newPayments = api.createStudentPayments(STUDENT1_ID, FEE1_ID, - List.of(creatablePayment1())); + List actualPayments = api.createStudentPayments(FEE3_ID, + List.of(toCreate)); - List actual = api.getStudentPayments(STUDENT1_ID); - assertTrue(actual.containsAll(newPayments)); + List expectedPayments = api.getStudentPayments(STUDENT2_ID); + assertTrue(expectedPayments.containsAll(actualPayments)); + } + + @Test + void manager_write_ko() { + ApiClient manager1Client = anApiClient(MANAGER1_TOKEN); + PayingApi api = new PayingApi(manager1Client); + + assertThrowsApiException( + "{\"type\":\"400 BAD_REQUEST\",\"message\":\"Payment must be associated to fee.fee1_id" + + " instead of fee.fee3_id\"}", + () -> api.createStudentPayments(FEE3_ID, List.of(creatablePayment1()))); + assertThrowsApiException( + "{\"type\":\"400 BAD_REQUEST\",\"message\":\"Fee remaining amount is 5000" + + ". Actual payment amount is 6000\"}", + () -> api.createStudentPayments(FEE3_ID, List.of(creatablePayment3()))); + } + + @Test + void manager_write_with_some_bad_fields_ko() { + ApiClient manager1Client = anApiClient(MANAGER1_TOKEN); + PayingApi api = new PayingApi(manager1Client); + CreatePayment toCreate1 = creatablePayment1().amount(null); + CreatePayment toCreate2 = creatablePayment1().amount(-1); + + ApiException exception1 = assertThrows(ApiException.class, + () -> api.createStudentPayments(FEE1_ID, List.of(toCreate1))); + ApiException exception2 = assertThrows(ApiException.class, + () -> api.createStudentPayments(FEE1_ID, List.of(toCreate2))); + + String exceptionMessage1 = exception1.getMessage(); + String exceptionMessage2 = exception2.getMessage(); + assertTrue(exceptionMessage1.contains("Amount is mandatory")); + assertTrue(exceptionMessage2.contains("Amount must be positive")); } @Test @@ -147,7 +188,7 @@ void teacher_write_ko() { assertThrowsApiException( "{\"type\":\"403 FORBIDDEN\",\"message\":\"Access is denied\"}", - () -> api.createStudentPayments(STUDENT1_ID, FEE1_ID, List.of())); + () -> api.createStudentPayments(FEE1_ID, List.of())); } @@ -158,6 +199,6 @@ void student_write_ko() { assertThrowsApiException( "{\"type\":\"403 FORBIDDEN\",\"message\":\"Access is denied\"}", - () -> api.createStudentPayments(STUDENT2_ID, FEE1_ID, List.of())); + () -> api.createStudentPayments(FEE1_ID, List.of())); } } diff --git a/src/test/java/school/hei/haapi/integration/conf/TestUtils.java b/src/test/java/school/hei/haapi/integration/conf/TestUtils.java index 33ddc8456..d44c40f08 100644 --- a/src/test/java/school/hei/haapi/integration/conf/TestUtils.java +++ b/src/test/java/school/hei/haapi/integration/conf/TestUtils.java @@ -26,6 +26,7 @@ public class TestUtils { public static final String GROUP1_ID = "group1_id"; public static final String FEE1_ID = "fee1_id"; public static final String FEE2_ID = "fee2_id"; + public static final String FEE3_ID = "fee3_id"; public static final String BAD_TOKEN = "bad_token"; public static final String STUDENT1_TOKEN = "student1_token";