diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/db/UserDatabase.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/db/UserDatabase.java
index 7efd5341e..09aa8c1a6 100644
--- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/db/UserDatabase.java
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/db/UserDatabase.java
@@ -2,6 +2,7 @@
import edu.suffolk.litlab.efsp.db.model.Transaction;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.CodeTableConstants;
+import edu.suffolk.litlab.efsp.model.EmailTemplates;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -84,12 +85,7 @@ public void addToTable(
String caseType,
String courtId,
Timestamp submitted,
- String acceptedTmpl,
- String acceptedSubject,
- String rejectedTmpl,
- String rejectedSubject,
- String neutralTmpl,
- String neutralSubject,
+ EmailTemplates emailTemplates,
String caseTitle,
String envelopeId)
throws SQLException {
@@ -105,12 +101,7 @@ public void addToTable(
caseType,
courtId,
submitted,
- acceptedTmpl,
- acceptedSubject,
- rejectedTmpl,
- rejectedSubject,
- neutralTmpl,
- neutralSubject,
+ emailTemplates,
caseTitle,
envelopeId);
}
@@ -128,12 +119,7 @@ public void addToTable(
String caseType,
String courtId,
Timestamp submitted,
- String acceptedTmpl,
- String acceptedSubject,
- String rejectedTmpl,
- String rejectedSubject,
- String neutralTmpl,
- String neutralSubject,
+ EmailTemplates tmpls,
String caseTitle,
String envelopeId)
throws SQLException {
@@ -173,12 +159,12 @@ INSERT INTO submitted_filings (
insertSt.setString(8, caseType);
insertSt.setString(9, courtId);
insertSt.setTimestamp(10, submitted);
- insertSt.setString(11, acceptedTmpl);
- insertSt.setString(12, acceptedSubject);
- insertSt.setString(13, rejectedTmpl);
- insertSt.setString(14, rejectedSubject);
- insertSt.setString(15, neutralTmpl);
- insertSt.setString(16, neutralSubject);
+ insertSt.setString(11, tmpls.acceptedTemplate());
+ insertSt.setString(12, tmpls.acceptedSubject());
+ insertSt.setString(13, tmpls.rejectedTemplate());
+ insertSt.setString(14, tmpls.rejectedSubject());
+ insertSt.setString(15, tmpls.neutralTemplate());
+ insertSt.setString(16, tmpls.neutralSubject());
insertSt.setString(17, caseTitle);
insertSt.setString(18, envelopeId);
insertSt.executeUpdate();
diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/AddressDocassembleJacksonDeserializer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/AddressDocassembleJacksonDeserializer.java
index fcf28cd0f..321d288a7 100644
--- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/AddressDocassembleJacksonDeserializer.java
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/AddressDocassembleJacksonDeserializer.java
@@ -2,6 +2,7 @@
import com.fasterxml.jackson.databind.JsonNode;
import edu.suffolk.litlab.efsp.model.Address;
+import edu.suffolk.litlab.efsp.server.ecf4.CodesParser;
import edu.suffolk.litlab.efsp.utils.FilingError;
import edu.suffolk.litlab.efsp.utils.InfoCollector;
import edu.suffolk.litlab.efsp.utils.InterviewVariable;
@@ -21,8 +22,8 @@ protected AddressDocassembleJacksonDeserializer() {}
*
* @throws FilingError if @param node isn't a JSON object describing an address
*/
- public static Optional
fromNode(JsonNode node, InfoCollector collector)
- throws FilingError {
+ public static Optional fromNode(
+ JsonNode node, CodesParser parser, InfoCollector collector) throws FilingError {
if (!node.isObject()) {
FilingError err =
FilingError.malformedInterview(
@@ -65,6 +66,7 @@ public static Optional fromNode(JsonNode node, InfoCollector collector)
String zip = (node.has("zip")) ? node.get("zip").asText("") : "";
String country = (node.has("country")) ? node.get("country").asText("US").toUpperCase() : "US";
if (country.length() > 2) {
+ // TODO: consider handling ISO-3166-alpha 3 codes?
log.error("Country {} isn't a valid country (should be 2 letters", country);
InterviewVariable countryOptions =
collector.requestVar(
@@ -73,6 +75,13 @@ public static Optional fromNode(JsonNode node, InfoCollector collector)
collector.error(err);
throw err;
}
+ var stateRes = parser.vetStateCode(state, country);
+ if (stateRes.isErr()) {
+ var stateBuilder = collector.varBuilder().name("state");
+ collector.addCodeError(stateRes.expectErr(""), stateBuilder);
+ } else {
+ state = stateRes.expect("");
+ }
Address addr = new Address(address, unit, city, state, zip, country);
return Optional.of(addr);
}
diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializer.java
index a10c0c766..9c6ff7e52 100644
--- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializer.java
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingDocDocassembleJacksonDeserializer.java
@@ -25,6 +25,7 @@
import edu.suffolk.litlab.efsp.server.ecf4.CodesParser.TextVarError;
import edu.suffolk.litlab.efsp.utils.FilingError;
import edu.suffolk.litlab.efsp.utils.InfoCollector;
+import edu.suffolk.litlab.efsp.utils.InterviewVariable;
import edu.suffolk.litlab.efsp.utils.JsonHelpers;
import fj.data.NonEmptyList;
import fj.data.Option;
@@ -57,6 +58,7 @@ public static Optional fromNode(
int sequenceNum,
List filingOptions,
boolean isInitialFiling,
+ boolean hasServiceContacts,
CodesParser parser,
InfoCollector collector)
throws FilingError {
@@ -161,7 +163,7 @@ public static Optional fromNode(
yield Optional.empty();
}
};
- var actionRes = parser.vetFilingAction(maybeAction, isInitialFiling);
+ var actionRes = parser.vetFilingAction(maybeAction, isInitialFiling, hasServiceContacts);
if (actionRes.isErr()) {
collector.addWrong(
collector
@@ -187,6 +189,16 @@ public static Optional fromNode(
return PartyId.Already(fp);
})
.collect(Collectors.toList());
+ var partiesRes = parser.vetFilingParties(fullParties);
+ if (partiesRes.isErr()) {
+ InterviewVariable partyVar =
+ collector.requestVar(
+ "filing_parties", "The Parties that are filing this document", "text");
+ collector.addRequired(partyVar);
+ } else {
+ fullParties = partiesRes.expect("");
+ }
+
var components = new ArrayList(parser.retrieveFilingComponents(filingType));
fj.data.List attachments = fj.data.List.nil();
if (node.has("tyler_merge_attachments")
@@ -234,7 +246,7 @@ public static Optional fromNode(
var descriptionFromSpec =
parser.getDocumentDescription(
- userDescription, goodAttachments.some().head().getFileName(), filingType);
+ userDescription, goodAttachments.some().head().fileName(), filingType);
return Optional.of(
new FilingDoc(
filingType,
@@ -333,6 +345,15 @@ private static Optional getAttachment(
+ dataUrl
+ ")"));
}
+
+ // TODO(brycew): depends on some DA code, should read in the PDF if possible here. Might be
+ // risky though.
+ // https://stackoverflow.com/questions/6026971/page-count-of-pdf-with-java
+ Optional pageCount = Optional.empty();
+ if (node.has("page_count")) {
+ int count = node.get("page_count").asInt(1);
+ pageCount = Optional.of(count);
+ }
// Difficult to hardcode more SSRF solutions, since deployment can be varied. Can't block local
// network /
// localhost, since things could be on the same server / network. TODO: inject an allow-list
@@ -351,11 +372,12 @@ private static Optional getAttachment(
return Optional.of(
new FilingAttachment(
+ filingComponent,
+ documentDescription,
fileName,
(inStream != null) ? inStream.readAllBytes() : new byte[0],
documentType,
- filingComponent,
- documentDescription));
+ pageCount));
} catch (MalformedURLException ex) {
FilingError err =
serverError(
diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingInformationDocassembleJacksonDeserializer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingInformationDocassembleJacksonDeserializer.java
index 97b428f35..844d17985 100644
--- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingInformationDocassembleJacksonDeserializer.java
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/FilingInformationDocassembleJacksonDeserializer.java
@@ -1,5 +1,7 @@
package edu.suffolk.litlab.efsp.docassemble;
+import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getNumberMember;
+import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getStringDefault;
import static edu.suffolk.litlab.efsp.utils.JsonHelpers.getStringMember;
import com.fasterxml.jackson.core.JsonParser;
@@ -11,6 +13,7 @@
import com.hubspot.algebra.Result;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.FilingCode;
import edu.suffolk.litlab.efsp.model.CaseServiceContact;
+import edu.suffolk.litlab.efsp.model.EmailTemplates;
import edu.suffolk.litlab.efsp.model.FilingDoc;
import edu.suffolk.litlab.efsp.model.FilingInformation;
import edu.suffolk.litlab.efsp.model.LowerCourtInfo;
@@ -319,6 +322,7 @@ public FilingInformation fromNode(JsonNode node, InfoCollector collector) throws
varToPartyId,
filingOptions,
isInitialFiling,
+ entities.getServiceContacts().size() > 0,
parser,
collector));
if (entities.getFilings().isEmpty()) {
@@ -400,7 +404,53 @@ public FilingInformation fromNode(JsonNode node, InfoCollector collector) throws
}
entities.setReturnDate(extractReturnDate(node.get("return_date"), collector));
- entities.setMiscInfo(node);
+
+ var filerTypeRes = parser.vetFilerType(getStringMember(node, "filer_type"));
+ if (filerTypeRes.isErr()) {
+ var filerTypeBuilder = collector.varBuilder().name("filer_type");
+ collector.addCodeError(filerTypeRes.expectErr(""), filerTypeBuilder);
+ } else {
+ entities.setFilerType(filerTypeRes.expect(""));
+ }
+
+ var maybeAmt = getNumberMember(node, "amount_in_controversy");
+ var filingCodes = entities.getFilings().stream().map(d -> d.getFilingCode()).toList();
+ var amtRes = parser.vetAmountInControversy(maybeAmt, filingCodes);
+ if (amtRes.isErr()) {
+ collector.error(
+ FilingError.malformedInterview("ad danum amount, Amount in controversy required"));
+ } else {
+ entities.setAmountInControversy(amtRes.expect(""));
+ }
+
+ var maybeMax = getNumberMember(node, "max_fee_amount");
+ entities.setMaxFeeAmount(parser.vetMaxAmount(maybeMax));
+
+ boolean contestedCase = false;
+ JsonNode jsonContested = node.get("is_contested_case");
+ if (jsonContested != null && jsonContested.isBoolean()) {
+ contestedCase = jsonContested.asBoolean();
+ }
+ entities.setIsContestedCase(contestedCase);
+
+ boolean outOfState = false;
+ if (node.has("out_of_state")) {
+ outOfState = node.get("out_of_state").asBoolean(false);
+ }
+ entities.setOutOfState(outOfState);
+
+ var emailTemplates =
+ new EmailTemplates(
+ getStringDefault(node, "email_confirmation_contents", ""),
+ getStringDefault(node, "email_confirmation_subject", ""),
+ getStringDefault(node, "acceptance_contents", ""),
+ getStringDefault(node, "acceptance_subject", ""),
+ getStringDefault(node, "rejected_contents", ""),
+ getStringDefault(node, "rejected_subject", ""),
+ getStringDefault(node, "neutral_contents", ""),
+ getStringDefault(node, "neutral_subject", ""));
+
+ entities.setEmailTemplates(emailTemplates);
return entities;
}
@@ -517,6 +567,7 @@ private static List extractFilingDocs(
Map varToPartyId,
List filingOptions,
boolean isInitialFiling,
+ boolean hasServiceContacts,
CodesParser parser,
InfoCollector collector)
throws FilingError {
@@ -547,6 +598,7 @@ private static List extractFilingDocs(
filingDocs.size(),
filingOptions,
isInitialFiling,
+ hasServiceContacts,
parser,
collector);
collector.popAttributeStack();
diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializer.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializer.java
index a8c4cbf1e..2d07c0e95 100644
--- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializer.java
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/docassemble/PersonDocassembleJacksonDeserializer.java
@@ -64,7 +64,8 @@ public static Result fromNode(
if (node.has("address") && node.get("address").isObject()) {
collector.pushAttributeStack("address");
try {
- addr = AddressDocassembleJacksonDeserializer.fromNode(node.get("address"), collector);
+ addr =
+ AddressDocassembleJacksonDeserializer.fromNode(node.get("address"), parser, collector);
collector.popAttributeStack();
} catch (FilingError err) {
if (!err.getType().equals(FilingError.Type.MissingRequired)) {
diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/EmailTemplates.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/EmailTemplates.java
new file mode 100644
index 000000000..c6489a1b5
--- /dev/null
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/EmailTemplates.java
@@ -0,0 +1,16 @@
+package edu.suffolk.litlab.efsp.model;
+
+public record EmailTemplates(
+ String confirmationTemplate,
+ String confirmationSubject,
+ String acceptedTemplate,
+ String acceptedSubject,
+ String rejectedTemplate,
+ String rejectedSubject,
+ String neutralTemplate,
+ String neutralSubject) {
+
+ public EmailTemplates() {
+ this("", "", "", "", "", "", "", "");
+ }
+}
diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingAttachment.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingAttachment.java
index 49ffde3c1..2888dfc7b 100644
--- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingAttachment.java
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingAttachment.java
@@ -5,50 +5,15 @@
import java.util.Optional;
/** An individual PDF, all a part of the same "document". The equivalent of an ALDocument. */
-public class FilingAttachment {
- private final FilingComponent filingComponentCode;
- // For the setBinaryDescriptionText
- private final String documentDescription;
- private final String fileName;
- private final byte[] fileContents;
- // This is, "determined via configuration within the EFM for each EFSP"?
- // So, we can just say yes?
- // Provides Document Type code / BinaryFormatStandardName
- private final Optional documentTypeFormatStandardName;
-
- // page count?
-
- public FilingAttachment(
- String fileName,
- byte[] fileStream,
- Optional documentTypeFormatStandardName,
- FilingComponent filingComponentCode,
- String documentDescription) {
- this.filingComponentCode = filingComponentCode;
- this.fileName = fileName;
- this.documentTypeFormatStandardName = documentTypeFormatStandardName;
- this.documentDescription = documentDescription;
- this.fileContents = fileStream;
- }
-
- public byte[] getFileContents() {
- return fileContents;
- }
-
- public String getFileName() {
- return fileName;
- }
-
- public FilingComponent getFilingComponent() {
- return filingComponentCode;
- }
-
- public Optional getDocumentTypeFormatStandardName() {
- return documentTypeFormatStandardName;
- }
-
- /** The description of this document. Goes into BinaryDescriptionText for Tyler. */
- public String getDocumentDescription() {
- return documentDescription;
- }
-}
+public record FilingAttachment(
+ FilingComponent filingComponentCode,
+ /** The description of this document. Goes into BinaryDescriptionText for Tyler. */
+ String documentDescription,
+ String fileName,
+ byte[] fileContents,
+ // This is, "determined via configuration within the EFM for each EFSP"?
+ // So, we can just say yes?
+ // Provides Document Type code / BinaryFormatStandardName
+ Optional documentTypeFormatStandardName,
+ // If present, we've pre-counted the pages, if not, will have to count them later.
+ Optional pageCount) {}
diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingDoc.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingDoc.java
index 816892dc3..24928f747 100644
--- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingDoc.java
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingDoc.java
@@ -10,8 +10,7 @@
import java.util.UUID;
import org.apache.commons.lang3.builder.ToStringBuilder;
-// TODO(brycew-later): this class is a mess. Refactor, considering the same pattern that's in
-// FilingInformation: add a JsonNode / generic container for EFM specific settings
+// TODO(brycew-later): this class is a mess. Refactor
public class FilingDoc {
private final Optional userProvidedDescription;
// Tyler has crazy dumb rules regarding the title of a doc; it's set here
@@ -107,7 +106,7 @@ public FilingDoc(
public int allAttachmentsLength() {
int length = 0;
for (var attachment : filingAttachments) {
- length += attachment.getFileContents().length;
+ length += attachment.fileContents().length;
}
return length;
}
diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingInformation.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingInformation.java
index 716e46a9b..d8da93a9a 100644
--- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingInformation.java
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/model/FilingInformation.java
@@ -1,9 +1,10 @@
package edu.suffolk.litlab.efsp.model;
-import com.fasterxml.jackson.databind.JsonNode;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.CaseCategory;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.CaseType;
+import edu.suffolk.litlab.efsp.ecfcodes.tyler.FilerType;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.NameAndCode;
+import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
@@ -69,7 +70,13 @@ public class FilingInformation {
private Person leadContact;
- private JsonNode miscInfo;
+ private Optional filerType;
+
+ private Optional amountInControversy = Optional.empty();
+ private Optional maxFeeAmount = Optional.empty();
+ private boolean contestedCase = false;
+ private boolean outOfState = false;
+ private EmailTemplates emailTemplates = new EmailTemplates();
/**
* Gets all of the peole who are listed by filer ids in the filing docs. This corresponds to the
@@ -194,14 +201,34 @@ public Optional getReturnDate() {
return returnDate;
}
- public JsonNode getMiscInfo() {
- return miscInfo;
+ public Optional getFilerType() {
+ return filerType;
}
public Optional getLowerCourtInfo() {
return lowerCourtInfo;
}
+ public Optional getAmountInControversy() {
+ return amountInControversy;
+ }
+
+ public Optional getMaxFeeAmount() {
+ return maxFeeAmount;
+ }
+
+ public boolean isContestedCase() {
+ return contestedCase;
+ }
+
+ public boolean getOutOfState() {
+ return outOfState;
+ }
+
+ public EmailTemplates getEmailTemplates() {
+ return emailTemplates;
+ }
+
public void setCourtLocation(String courtLocationId) {
this.courtLocationId = courtLocationId;
}
@@ -274,8 +301,8 @@ public void setReturnDate(Optional returnDate) {
this.returnDate = returnDate;
}
- public void setMiscInfo(JsonNode node) {
- this.miscInfo = node;
+ public void setFilerType(Optional filerType) {
+ this.filerType = filerType;
}
public void setLowerCourtInfo(LowerCourtInfo info) {
@@ -285,4 +312,24 @@ public void setLowerCourtInfo(LowerCourtInfo info) {
public void setLowerCourtInfo(Optional info) {
this.lowerCourtInfo = info;
}
+
+ public void setAmountInControversy(Optional amtInControversy) {
+ this.amountInControversy = amtInControversy;
+ }
+
+ public void setMaxFeeAmount(Optional maxFeeAmount) {
+ this.maxFeeAmount = maxFeeAmount;
+ }
+
+ public void setIsContestedCase(boolean isContestedCase) {
+ this.contestedCase = isContestedCase;
+ }
+
+ public void setOutOfState(boolean outOfState) {
+ this.outOfState = outOfState;
+ }
+
+ public void setEmailTemplates(EmailTemplates emailTemplates) {
+ this.emailTemplates = emailTemplates;
+ }
}
diff --git a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/CodesParser.java b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/CodesParser.java
index 0d44e188f..efff4513a 100644
--- a/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/CodesParser.java
+++ b/proxyserver/src/main/java/edu/suffolk/litlab/efsp/server/ecf4/CodesParser.java
@@ -6,6 +6,7 @@
import edu.suffolk.litlab.efsp.ecfcodes.tyler.CaseType;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.DocumentTypeTableRow;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.FileType;
+import edu.suffolk.litlab.efsp.ecfcodes.tyler.FilerType;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.FilingCode;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.FilingComponent;
import edu.suffolk.litlab.efsp.ecfcodes.tyler.NameAndCode;
@@ -49,6 +50,8 @@ public sealed interface AttorneyError {}
public record NoMultipleAttorneys() implements AttorneyError {}
public record RequiredAttorneys() implements AttorneyError {}
+ public record ThingRequired() {}
+
public record DueDateRequired() {}
public record InvalidFilingAction(String reason) {}
@@ -82,6 +85,8 @@ public Result vetFilingType(
public Result, CodeError> vetLangCode(Optional lang);
+ public Result vetStateCode(String state, String countryString);
+
public Result