added finance API methods

This commit is contained in:
2023-06-04 22:21:15 -04:00
parent b684b80ea9
commit ffaea04b86
5 changed files with 537 additions and 456 deletions

View File

@@ -15,6 +15,7 @@ import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.WebApplicationException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.SecurityContext;
@@ -48,7 +49,7 @@ public class EventFinanceApi {
)
@Produces(Constants.V1_JSON)
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves the balances of all participants in an event.")
@Operation(summary = "Retrieves the balances of all persons associated with an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@@ -56,8 +57,11 @@ public class EventFinanceApi {
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public List<Map<String, Object>> getBalanceByPersonsAsJson(@Context
SecurityContext securityContext) throws JsonProcessingException {
Map<Long, DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId);
SecurityContext securityContext, @QueryParam("minBalance")
Float minBalance, @QueryParam("maxBalance")
Float maxBalance) throws JsonProcessingException {
Map<Long, DataSet> personsBalances = this.eventFinanceService.getPersonsBalances(this.eventId, minBalance,
maxBalance);
List<Map<String, Object>> personsBalancesJson = new ArrayList<>(personsBalances.size());
for (DataSet personBalance : personsBalances.values()) {
@@ -80,7 +84,7 @@ public class EventFinanceApi {
)
@Produces("text/csv")
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves the balances of all participants in an event.")
@Operation(summary = "Retrieves the balances of all persons associated with an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@@ -115,4 +119,68 @@ public class EventFinanceApi {
};
}
@GET
@Path("/balance/person/{personId}")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "member"
)
@Produces(Constants.V1_JSON)
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves the balance of a person in an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(
responseCode = "404",
description = "An event or person with the specified IDs could not be found"
)
})
public Map<String, Object> getBalanceByPersonsAsJson(@Context
SecurityContext securityContext, @PathParam("personId")
long personId) throws JsonProcessingException {
DataSet personBalance = this.eventFinanceService.getPersonBalance(this.eventId, personId);
Map<String, Object> personBalanceJson = new HashMap<>(5);
personBalanceJson.put("personId", personBalance.getLong("personID"));
personBalanceJson.put("expense", personBalance.getFloat("expense"));
personBalanceJson.put("paid", personBalance.getFloat("paid"));
personBalanceJson.put("balance", personBalance.getFloat("balance"));
return personBalanceJson;
}
@GET
@Path("/series/balance/persons")
@RolesAllowed(
Constants.EVENT_ROLE_PREFIX
+ "member"
)
@Produces(Constants.V1_JSON)
@SecurityRequirement(name = "basic")
@Operation(summary = "Retrieves the cumulative balances of all persons associated with a series prior to an event.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "Success"),
@ApiResponse(responseCode = "401", description = "Not authenticated"),
@ApiResponse(responseCode = "403", description = "Authenticated, but not permitted"),
@ApiResponse(responseCode = "404", description = "An event with the specified ID could not be found")
})
public List<Map<String, Object>> getSeriesBalanceByPersonsAsJson(@Context
SecurityContext securityContext) throws JsonProcessingException {
Map<Long, DataSet> personsBalances = this.eventFinanceService.getSeriesPersonsPreviousBalances(this.eventId);
List<Map<String, Object>> personsBalancesJson = new ArrayList<>(personsBalances.size());
for (DataSet personBalance : personsBalances.values()) {
Map<String, Object> personBalanceJson = new HashMap<>(5);
personBalanceJson.put("personId", personBalance.getLong("personID"));
personBalanceJson.put("expense", personBalance.getFloat("expense"));
personBalanceJson.put("paid", personBalance.getFloat("paid"));
personBalanceJson.put("balance", personBalance.getFloat("balance"));
personsBalancesJson.add(personBalanceJson);
}
return personsBalancesJson;
}
}

View File

@@ -1,8 +1,7 @@
package com.poststats.golf.service;
import java.util.Map;
import com.brianlong.sql.DataSet;
import java.util.Map;
/**
* This service provides financial metadata about the event, series, associated
@@ -31,8 +30,11 @@ public interface EventFinanceService {
* specified event.
*
* @param eventId The unique identifier of an event.
* @param minBalance The minimum balance for returned balances; `0.01` for those that have paid more than they owe; `null` for no constraint.
* @param maxBalance The maximum balance for returned balances; `-0.01` for those that owe; `null` for no constraint.
* @param minBalance The minimum balance for returned balances; `0.01` for those
* that have paid more than they owe; `null` for no
* constraint.
* @param maxBalance The maximum balance for returned balances; `-0.01` for
* those that owe; `null` for no constraint.
* @return A map of `personId` to balance metadata.
*/
Map<Long, DataSet> getPersonsBalances(long eventId, Float minBalance, Float maxBalance);
@@ -48,17 +50,17 @@ public interface EventFinanceService {
DataSet getPersonBalance(long eventId, long personId);
/**
* Computes and retrieves all associated persons' cumulative balance for
* all events in the series before the specified event.
* Computes and retrieves all associated persons' cumulative balance for all
* events in the series before the specified event.
*
* @param eventId The unique identifier of an event.
* @return A map of `personId` to balance metadata.
*/
Map<Long, DataSet> getSeriesPersonsBalances(long eventId);
Map<Long, DataSet> getSeriesPersonsPreviousBalances(long eventId);
/**
* Computes and retrieves the specified person's balances for all events in
* the specified series.
* Computes and retrieves the specified person's balances for all events in the
* specified series.
*
* @param seriesId The unique identifier of a series.
* @param personId The unique identifier of a person.
@@ -67,8 +69,8 @@ public interface EventFinanceService {
Map<Long, DataSet> getSeriesPersonBalances(int seriesId, long personId);
/**
* Computes and retrieves the specified person's balances for all events in
* the series before the specified event.
* Computes and retrieves the specified person's balances for all events in the
* series before the specified event.
*
* @param eventId The unique identifier of an event.
* @param personId The unique identifier of a person.

View File

@@ -1,9 +1,5 @@
package com.poststats.golf.service.db;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Map;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
@@ -14,9 +10,11 @@ import com.poststats.provider.NonTransactionalProvider;
import com.poststats.provider.Statement;
import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.Map;
@ApplicationScoped
public class EventFinanceServiceDAO implements EventFinanceService {
@@ -36,8 +34,8 @@ public class EventFinanceServiceDAO implements EventFinanceService {
try {
for (int i = 1; i <= 5; i++)
fps.setIntegerU(i, eventId);
fps.setFloat(6, minBalance == null ? Float.NEGATIVE_INFINITY : minBalance);
fps.setFloat(7, maxBalance == null ? Float.POSITIVE_INFINITY : maxBalance);
fps.setFloat(6, minBalance == null ? -1e20f : minBalance);
fps.setFloat(7, maxBalance == null ? 1e20f : maxBalance);
return fps.executeQuery().getAllRows("personID", Long.class);
} finally {
fps.close();
@@ -54,7 +52,7 @@ public class EventFinanceServiceDAO implements EventFinanceService {
try {
for (int i = 1; i <= 10; i += 2) {
fps.setIntegerU(i, eventId);
fps.setIntegerU(i+1, personId);
fps.setIntegerU(i + 1, personId);
}
return fps.executeQuery().getNextRow();
} finally {
@@ -66,7 +64,7 @@ public class EventFinanceServiceDAO implements EventFinanceService {
}
@Override
public Map<Long, DataSet> getSeriesPersonsBalances(long eventId) {
public Map<Long, DataSet> getSeriesPersonsPreviousBalances(long eventId) {
FlexMap event = this.eventService.get(eventId);
int seriesId = event.getInteger("seriesID");
LocalDate liveline = event.getDate("liveline");
@@ -76,8 +74,8 @@ public class EventFinanceServiceDAO implements EventFinanceService {
try {
for (int i = 1; i <= 15; i += 3) {
fps.setSmallintU(i, seriesId);
fps.setIntegerU(i+1, eventId);
fps.setDate(i+2, liveline);
fps.setIntegerU(i + 1, eventId);
fps.setDate(i + 2, liveline);
}
return fps.executeQuery().getAllRows("personID", Long.class);
} finally {
@@ -112,9 +110,9 @@ public class EventFinanceServiceDAO implements EventFinanceService {
try {
for (int i = 1; i <= 20; i += 4) {
fps.setSmallintU(i, seriesId);
fps.setIntegerU(i+1, eventId);
fps.setDate(i+2, liveline);
fps.setIntegerU(i+3, personId);
fps.setIntegerU(i + 1, eventId);
fps.setDate(i + 2, liveline);
fps.setIntegerU(i + 3, personId);
}
return fps.executeQuery().getAllRows("personID", Long.class);
} finally {

View File

@@ -1,14 +1,5 @@
package com.poststats.golf.service.db;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.brianlong.sql.DataSet;
import com.brianlong.sql.FlexPreparedStatement;
import com.brianlong.util.FlexMap;
@@ -21,10 +12,17 @@ import com.poststats.provider.StatementProvider;
import com.poststats.service.ServiceException;
import com.poststats.service.db.CacheableServiceDAO;
import com.poststats.util.Contact;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.mail.MessagingException;
import java.math.BigInteger;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ApplicationScoped
public class EventPersonServiceDAO extends CacheableServiceDAO<BigInteger> implements EventPersonService {
@@ -235,7 +233,8 @@ public class EventPersonServiceDAO extends CacheableServiceDAO<BigInteger> imple
}
}
private FlexPreparedStatement getStatementByAutolist(StatementProvider statementProvider, long eventId, String autolist) throws SQLException {
private FlexPreparedStatement getStatementByAutolist(StatementProvider statementProvider, long eventId,
String autolist) throws SQLException {
int weeks = -1;
if (autolist.startsWith("previous") && autolist.length() > "previous".length())
weeks = Integer.parseInt(autolist.substring("previous".length() + 1));
@@ -275,24 +274,29 @@ public class EventPersonServiceDAO extends CacheableServiceDAO<BigInteger> imple
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT personID, epersonID FROM ~g~.EventPerson WHERE eventID=? "
@Statement(
sql = "SELECT personID, epersonID FROM ~g~.EventPerson WHERE eventID=? "
+ "UNION "
+ "SELECT personID, NULL epersonID FROM ~g~.EventPersonContract WHERE eventID=? ")
+ "SELECT personID, NULL epersonID FROM ~g~.EventPersonContract WHERE eventID=? "
)
private StatementProvider sqlSelectEventAllIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT DISTINCT EP.personID, EP.epersonID "
@Statement(
sql = "SELECT DISTINCT EP.personID, EP.epersonID "
+ "FROM ~g~.EventPerson EP "
+ " LEFT JOIN ~g~.EventPerson EP2 ON (EP.eventID<>EP2.eventID AND EP.personID=EP2.personID) "
+ "WHERE EP.eventID=? AND EP2.epersonID IS NULL")
+ "WHERE EP.eventID=? AND EP2.epersonID IS NULL"
)
private StatementProvider sqlSelectEventRookieIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT DISTINCT EP.personID "
@Statement(
sql = "SELECT DISTINCT EP.personID "
+ "FROM ~g~.EventPerson EP "
+ " LEFT JOIN ~g~.EventPerson EP2 ON (EP.eventID<>EP2.eventID AND EP.personID=EP2.personID) "
+ "WHERE EP.eventID=? AND EP2.epersonID IS NULL "
@@ -301,48 +305,56 @@ public class EventPersonServiceDAO extends CacheableServiceDAO<BigInteger> imple
+ "FROM ~g~.EventPerson EP "
+ " LEFT JOIN ~g~.EventPerson EP2 ON (EP.eventID<>EP2.eventID AND EP.personID=EP2.personID) "
+ " INNER JOIN ~p~.Person P ON (EP.personID=P.personID) "
+ "WHERE EP.eventID=? AND EP2.epersonID IS NULL AND P.referralPersonID IS NOT NULL")
+ "WHERE EP.eventID=? AND EP2.epersonID IS NULL AND P.referralPersonID IS NOT NULL"
)
private StatementProvider sqlSelectEventRookieReferralIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT EP.personID, EP.epersonID "
@Statement(
sql = "SELECT EP.personID, EP.epersonID "
+ "FROM ~g~.EventPerson EP "
+ " INNER JOIN ?TT? TT1 ON (EP.personID=TT1.personID) "
+ "WHERE EP.eventID=? "
+ "GROUP BY EP.personID HAVING SUM(TT1.balance) < 0 ")
+ "GROUP BY EP.personID HAVING SUM(TT1.balance) < 0 "
)
private StatementProvider sqlSelectEventParticipantIdsWithBalance;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT DISTINCT EP.personID, EP.epersonID "
@Statement(
sql = "SELECT DISTINCT EP.personID, EP.epersonID "
+ "FROM ~g~.EventOption EO "
+ " INNER JOIN ~g~.EventPerson EP ON (EO.eventID=EP.eventID) "
+ " LEFT JOIN ~g~.EventPersonOption EPO ON (EO.optionID=EPO.optionID AND EP.epersonID=EPO.epersonID) "
+ "WHERE EO.eventID=? "
+ " AND (EO.liveline IS NULL OR EO.liveline<=CURRENT_DATE) "
+ " AND (EO.deadline IS NULL OR EO.deadline>=CURRENT_DATE) "
+ " AND EPO.optionID IS NULL ")
+ " AND EPO.optionID IS NULL "
)
private StatementProvider sqlSelectEventParticipantIdsWithUnansweredOptions;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT DISTINCT EP.personID "
@Statement(
sql = "SELECT DISTINCT EP.personID "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.Event E2 ON (E.seriesID=E2.seriesID) "
+ " INNER JOIN ~g~.EventPerson EP ON (E2.eventID=EP.eventID) "
+ "WHERE E.eventID=? AND E2.eventID<>? "
+ "UNION DISTINCT "
+ "SELECT personID FROM ~g~.EventPersonContract WHERE eventID=? ")
+ "SELECT personID FROM ~g~.EventPersonContract WHERE eventID=? "
)
private StatementProvider sqlSelectSeriesParticipantIds;
@Inject
@NonTransactionalProvider
@GolfProvider
@Statement(sql = "SELECT DISTINCT EP.personID "
@Statement(
sql = "SELECT DISTINCT EP.personID "
+ "FROM ~g~.Event E "
+ " INNER JOIN ~g~.Event E2 ON (E.seriesID=E2.seriesID) "
+ " INNER JOIN ~g~.EventPerson EP ON (E2.eventID=EP.eventID) "
@@ -350,7 +362,8 @@ public class EventPersonServiceDAO extends CacheableServiceDAO<BigInteger> imple
+ " AND ((E2.deadline IS NOT NULL AND DATE_ADD(E2.deadline, INTERVAL ? WEEK)>=CURRENT_DATE) "
+ " OR (E2.liveline IS NOT NULL AND DATE_ADD(E2.liveline, INTERVAL ? WEEK)>=CURRENT_DATE)) "
+ "UNION DISTINCT "
+ "SELECT personID FROM ~g~.EventPersonContract WHERE eventID=? ")
+ "SELECT personID FROM ~g~.EventPersonContract WHERE eventID=? "
)
private StatementProvider sqlSelectRecentSeriesParticipantIds;
@Override