Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: filter fees by date range #780

Merged
merged 7 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions doc/api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2407,6 +2407,18 @@ paths:
required: false
schema:
$ref: '#/components/schemas/FeeStatusEnum'
- name: from_due_datetime
in: query
required: false
schema:
type: string
format: date-time
- name: to_due_datetime
in: query
required: false
schema:
type: string
format: date-time
responses:
'200':
description: List of fees in a xlsx file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toUnmodifiableList;
import static org.springframework.format.annotation.DateTimeFormat.ISO.DATE_TIME;

import java.io.IOException;
import java.time.Instant;
import java.util.List;
import lombok.AllArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand Down Expand Up @@ -87,9 +88,13 @@ public List<Fee> getFeesByStudentId(
}

@GetMapping(value = "/fees/raw", produces = "application/vnd.ms-excel")
public byte[] generateFeesListAsXlsx(@RequestParam(name = "status") FeeStatusEnum status)
throws IOException {
return feeService.generateFeesAsXlsx(status);
public byte[] generateFeesListAsXlsx(
@RequestParam(name = "status", required = false) FeeStatusEnum status,
@RequestParam(name = "from_due_datetime", required = false) @DateTimeFormat(iso = DATE_TIME)
Instant from,
@RequestParam(name = "to_due_datetime", required = false) @DateTimeFormat(iso = DATE_TIME)
Instant to) {
return feeService.generateFeesAsXlsx(status, from, to);
}

@GetMapping("/fees")
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/school/hei/haapi/repository/dao/FeeDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,29 @@ private void addDatePredicates(
predicates.add(builder.lessThanOrEqualTo(root.get("dueDatetime"), monthTo));
}
}

public List<Fee> findAllByStatusAndDueDatetimeBetween(
FeeStatusEnum status, Instant startDate, Instant endDate) {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Fee> query = builder.createQuery(Fee.class);
Root<Fee> root = query.from(Fee.class);

List<Predicate> predicates = new ArrayList<>();

if (status != null) {
predicates.add(builder.equal(root.get("status"), status));
}
if (startDate != null) {
predicates.add(builder.greaterThanOrEqualTo(root.get("dueDatetime"), startDate));
}
if (endDate != null) {
predicates.add(builder.lessThanOrEqualTo(root.get("dueDatetime"), endDate));
}

query.where(predicates.toArray(new Predicate[0]));

query.orderBy(builder.desc(root.get("dueDatetime")));

return entityManager.createQuery(query).getResultList();
}
}
4 changes: 2 additions & 2 deletions src/main/java/school/hei/haapi/service/FeeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ public class FeeService {
private static final String MONTHLY_FEE_TEMPLATE_NAME = "Frais mensuel L1";
private static final String YEARLY_FEE_TEMPLATE_NAME = "Frais annuel L1";

public byte[] generateFeesAsXlsx(FeeStatusEnum feeStatus) {
public byte[] generateFeesAsXlsx(FeeStatusEnum feeStatus, Instant from, Instant to) {
XlsxCellsGenerator<Fee> xlsxCellsGenerator = new XlsxCellsGenerator<>();
List<Fee> feeList = feeRepository.findAllByStatus(feeStatus);
List<Fee> feeList = feeDao.findAllByStatusAndDueDatetimeBetween(feeStatus, from, to);
return xlsxCellsGenerator.apply(
feeList,
List.of(
Expand Down
66 changes: 66 additions & 0 deletions src/test/java/school/hei/haapi/integration/FeeIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static school.hei.haapi.endpoint.rest.model.FeeStatusEnum.LATE;
import static school.hei.haapi.endpoint.rest.model.FeeStatusEnum.PAID;
import static school.hei.haapi.endpoint.rest.model.FeeStatusEnum.PENDING;
import static school.hei.haapi.endpoint.rest.model.FeeTypeEnum.HARDWARE;
import static school.hei.haapi.endpoint.rest.model.FeeTypeEnum.REMEDIAL_COSTS;
import static school.hei.haapi.endpoint.rest.model.FeeTypeEnum.TUITION;
Expand All @@ -29,18 +30,22 @@
import static school.hei.haapi.integration.conf.TestUtils.fee2;
import static school.hei.haapi.integration.conf.TestUtils.fee3;
import static school.hei.haapi.integration.conf.TestUtils.fee4;
import static school.hei.haapi.integration.conf.TestUtils.requestFile;
import static school.hei.haapi.integration.conf.TestUtils.setUpCognito;
import static school.hei.haapi.integration.conf.TestUtils.setUpS3Service;

import jakarta.persistence.EntityManager;
import jakarta.persistence.Query;
import java.io.IOException;
import java.net.URI;
import java.time.Instant;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.http.HttpStatus;
import org.testcontainers.junit.jupiter.Testcontainers;
import school.hei.haapi.endpoint.event.consumer.EventConsumer;
import school.hei.haapi.endpoint.rest.api.PayingApi;
Expand All @@ -52,13 +57,17 @@
import school.hei.haapi.endpoint.rest.model.FeesWithStats;
import school.hei.haapi.integration.conf.FacadeITMockedThirdParties;
import school.hei.haapi.integration.conf.TestUtils;
import school.hei.haapi.repository.FeeRepository;
import school.hei.haapi.repository.dao.FeeDao;

@Testcontainers
@AutoConfigureMockMvc
@Slf4j
class FeeIT extends FacadeITMockedThirdParties {
@Autowired EventConsumer subject;
@Autowired EntityManager entityManager;
@Autowired FeeRepository feeRepository;
@Autowired FeeDao feeDao;

/***
* Get fee by id without jpa, avoiding FILTER isDeleted = true | false
Expand Down Expand Up @@ -479,4 +488,61 @@ void get_fees_statistics_ok() throws ApiException {
assertEquals(2, stats.getPaidFees());
assertEquals(2, stats.getUnpaidFees());
}

@Test
void generate_fees_list_as_xlsx_without_parameters_ok() throws IOException, InterruptedException {
var response = requestFile(URI.create("http://localhost:" + localPort + "/fees/raw"));

assertEquals(HttpStatus.OK.value(), response.statusCode());
assertNotNull(response.body());
assertNotNull(response);
}

@Test
void generate_fees_list_as_xlsx_with_parameters_ok() throws IOException, InterruptedException {
var responseWithStatus =
requestFile(URI.create("http://localhost:" + localPort + "/fees/raw?status=" + PENDING));
assertEquals(HttpStatus.OK.value(), responseWithStatus.statusCode());
assertNotNull(responseWithStatus.body());
assertNotNull(responseWithStatus);

var responseWithDateStart =
requestFile(
URI.create(
"http://localhost:"
+ localPort
+ "/fees/raw?from_due_datetime=2022-01-01T12:00:00.000Z"));
assertEquals(HttpStatus.OK.value(), responseWithDateStart.statusCode());
assertNotNull(responseWithDateStart.body());
assertNotNull(responseWithDateStart);

var responseWithDateRange =
requestFile(
URI.create(
"http://localhost:"
+ localPort
+ "/fees/raw?from_due_datetime=2022-01-01T12:00:00Z&to_due_datetime=2024-01-02T12:00:00Z"));
assertEquals(HttpStatus.OK.value(), responseWithDateRange.statusCode());
assertNotNull(responseWithDateRange.body());
assertNotNull(responseWithDateRange);
}

@Test
void all_fee_without_status_and_dueDatetime_work() {
var real_fees = feeRepository.findAll();
var fees = feeDao.findAllByStatusAndDueDatetimeBetween(null, null, null);

assertEquals(real_fees.size(), fees.size());
}

@Test
void all_fee_by_status_and_dueDatetime_in_date_range_must_contain_some_fee() {
var fees =
feeDao.findAllByStatusAndDueDatetimeBetween(
null,
Instant.parse("2021-11-08T08:25:24.00Z"),
Instant.parse("2022-12-08T08:25:24.00Z"));

assertFalse(fees.isEmpty());
}
}
13 changes: 13 additions & 0 deletions src/test/java/school/hei/haapi/integration/conf/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -1611,6 +1611,19 @@ public static CrupdatePromotion createPromotion4() {
return new CrupdatePromotion().id("promotion4_id").name("Promotion 24").ref("PROM24");
}

public static HttpResponse<byte[]> requestFile(URI request)
throws IOException, InterruptedException {
HttpClient httpClient = HttpClient.newBuilder().build();

return httpClient.send(
HttpRequest.newBuilder()
.uri(request)
.GET()
.header("Authorization", "Bearer " + MANAGER1_TOKEN)
.build(),
HttpResponse.BodyHandlers.ofByteArray());
}

public static boolean isBefore(String a, String b) {
return a.compareTo(b) < 0;
}
Expand Down
Loading