From ca9f3d013a624f6980daf2db01ffe852f046741d Mon Sep 17 00:00:00 2001 From: Bengt Brodersen Date: Sun, 15 Nov 2020 16:19:15 +0100 Subject: [PATCH] fix(mt942): set right amount (0.0) for missing debit floor mark --- .../submessage/field/FloorLimitIndicator.java | 15 ++ .../swift/submessage/mt942/MT942Page.java | 37 +++-- .../submessage/mt942/MT942PageReader.java | 65 ++++++--- .../submessage/mt942/MT942PageReaderTest.java | 134 +++++++++++++++++- 4 files changed, 212 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/qoomon/banking/swift/submessage/field/FloorLimitIndicator.java b/src/main/java/com/qoomon/banking/swift/submessage/field/FloorLimitIndicator.java index a0cca1a..b39408b 100644 --- a/src/main/java/com/qoomon/banking/swift/submessage/field/FloorLimitIndicator.java +++ b/src/main/java/com/qoomon/banking/swift/submessage/field/FloorLimitIndicator.java @@ -11,6 +11,7 @@ import java.math.BigDecimal; import java.util.List; +import java.util.Objects; import java.util.Optional; /** @@ -96,4 +97,18 @@ public String getContent() { throw new IllegalStateException("Invalid field values within " + getClass().getSimpleName() + " instance", e); } } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FloorLimitIndicator that = (FloorLimitIndicator) o; + return debitCreditMark.equals(that.debitCreditMark) && + amount.equals(that.amount); + } + + @Override + public int hashCode() { + return Objects.hash(debitCreditMark, amount); + } } diff --git a/src/main/java/com/qoomon/banking/swift/submessage/mt942/MT942Page.java b/src/main/java/com/qoomon/banking/swift/submessage/mt942/MT942Page.java index 51934e4..d19f9ba 100644 --- a/src/main/java/com/qoomon/banking/swift/submessage/mt942/MT942Page.java +++ b/src/main/java/com/qoomon/banking/swift/submessage/mt942/MT942Page.java @@ -94,18 +94,25 @@ public MT942Page( Preconditions.checkArgument(accountIdentification != null, "accountIdentification can't be null"); Preconditions.checkArgument(statementNumber != null, "statementNumber can't be null"); - Preconditions.checkArgument(floorLimitIndicatorDebit != null, "floorLimitIndicatorDebit can't be null"); - Optional debitMark = floorLimitIndicatorDebit.getDebitCreditMark(); - debitMark.ifPresent(mark -> Preconditions.checkArgument(mark == DEBIT, - "floorLimitIndicatorDebit type can't be " + mark)); - - Preconditions.checkArgument(floorLimitIndicatorCredit != null, "floorLimitIndicatorCredit can't be null"); - Optional creditMark = floorLimitIndicatorCredit.getDebitCreditMark(); - creditMark.ifPresent(mark -> Preconditions.checkArgument(mark == CREDIT, - "floorLimitIndicatorCredit type can't be " + mark)); - - Preconditions.checkArgument(debitMark.isPresent() == creditMark.isPresent(), - "floorLimitIndicatorDebit and floorLimitIndicatorCredit debit credit marks needs to be both empty or DEBIT and CREDIT"); + { + Preconditions.checkArgument(floorLimitIndicatorDebit != null, "floorLimitIndicatorDebit can't be null"); + DebitCreditMark debitMark = floorLimitIndicatorDebit.getDebitCreditMark().orElse(null); + Preconditions.checkArgument(debitMark == null || debitMark == DEBIT, + "floorLimitIndicatorDebit mark can't be " + debitMark); + + Preconditions.checkArgument(floorLimitIndicatorCredit != null, "floorLimitIndicatorCredit can't be null"); + DebitCreditMark creditMark = floorLimitIndicatorCredit.getDebitCreditMark().orElse(null); + Preconditions.checkArgument(creditMark == null || creditMark == CREDIT, + "floorLimitIndicatorCredit mark can't be " + creditMark); + + Preconditions.checkArgument((debitMark == null) == (creditMark == null), + "floorLimitIndicatorDebit and floorLimitIndicatorCredit marks need to be both blank or DEBIT and CREDIT"); + + if (debitMark == null) { + Preconditions.checkArgument(floorLimitIndicatorDebit.getAmount().equals(floorLimitIndicatorCredit.getAmount()), + "floorLimitIndicatorDebit and floorLimitIndicatorCredit amounts needs to be equal, if marks are blank"); + } + } Preconditions.checkArgument(dateTimeIndicator != null, "dateTimeIndicator can't be null"); Preconditions.checkArgument(transactionGroupList != null, "transactionGroupList can't be null"); @@ -113,7 +120,7 @@ public MT942Page( // ensure matching currencies CurrencyUnit statementCurrency = floorLimitIndicatorDebit.getAmount().getCurrencyUnit(); String statementFundsCode = statementCurrency.getCode().substring(2, 3); - + { // check floorLimitIndicatorCredit currency CurrencyUnit currency = floorLimitIndicatorCredit.getAmount().getCurrencyUnit(); @@ -142,7 +149,7 @@ public MT942Page( this.accountIdentification = accountIdentification; this.statementNumber = statementNumber; this.floorLimitIndicatorDebit = floorLimitIndicatorDebit; - this.floorLimitIndicatorCredit = Optional.ofNullable(floorLimitIndicatorCredit).orElse(floorLimitIndicatorDebit); + this.floorLimitIndicatorCredit = floorLimitIndicatorCredit; this.dateTimeIndicator = dateTimeIndicator; this.transactionGroupList = transactionGroupList; this.transactionSummaryDebit = Optional.ofNullable(transactionSummaryDebit); @@ -201,7 +208,7 @@ public String getContent() { contentBuilder.append(swiftTextOf(accountIdentification)).append("\n"); contentBuilder.append(swiftTextOf(statementNumber)).append("\n"); contentBuilder.append(swiftTextOf(floorLimitIndicatorDebit)).append("\n"); - if (floorLimitIndicatorDebit.getDebitCreditMark().isPresent()) { + if (!floorLimitIndicatorCredit.equals(floorLimitIndicatorDebit)) { contentBuilder.append(swiftTextOf(floorLimitIndicatorCredit)).append("\n"); } contentBuilder.append(swiftTextOf(dateTimeIndicator)).append("\n"); diff --git a/src/main/java/com/qoomon/banking/swift/submessage/mt942/MT942PageReader.java b/src/main/java/com/qoomon/banking/swift/submessage/mt942/MT942PageReader.java index 2683517..0f8b1ce 100644 --- a/src/main/java/com/qoomon/banking/swift/submessage/mt942/MT942PageReader.java +++ b/src/main/java/com/qoomon/banking/swift/submessage/mt942/MT942PageReader.java @@ -7,12 +7,17 @@ import com.qoomon.banking.swift.submessage.exception.PageParserException; import com.qoomon.banking.swift.submessage.field.*; import com.qoomon.banking.swift.submessage.field.subfield.DebitCreditMark; +import org.joda.money.BigMoney; +import org.joda.money.CurrencyUnit; import java.io.Reader; import java.util.LinkedList; import java.util.List; import java.util.Set; +import static com.qoomon.banking.swift.submessage.field.subfield.DebitCreditMark.CREDIT; +import static com.qoomon.banking.swift.submessage.field.subfield.DebitCreditMark.DEBIT; + /** * Parser for {@link MT942Page} */ @@ -104,30 +109,46 @@ public MT942Page read() throws SwiftMessageParseException { } case FloorLimitIndicator.FIELD_TAG_34F: { FloorLimitIndicator floorLimitIndicator = FloorLimitIndicator.of(currentField); - if (floorLimitIndicatorDebit == null) { - if(!floorLimitIndicator.getDebitCreditMark().isPresent() - || floorLimitIndicator.getDebitCreditMark().get() == DebitCreditMark.DEBIT) { - floorLimitIndicatorDebit = floorLimitIndicator; - floorLimitIndicatorCredit = new FloorLimitIndicator( - floorLimitIndicator.getDebitCreditMark().isPresent() - ? DebitCreditMark.CREDIT - : null, - floorLimitIndicator.getAmount() - ); - nextValidFieldSet = ImmutableSet.of( - FloorLimitIndicator.FIELD_TAG_34F, - DateTimeIndicator.FIELD_TAG_13D); - } else { - // handle missing debit floor indicator - floorLimitIndicatorDebit = new FloorLimitIndicator( - DebitCreditMark.DEBIT, - floorLimitIndicator.getAmount() - ); - floorLimitIndicatorCredit = floorLimitIndicator; - nextValidFieldSet = ImmutableSet.of( - DateTimeIndicator.FIELD_TAG_13D); + DebitCreditMark debitCreditMark = floorLimitIndicator.getDebitCreditMark().orElse(null); + + // second occurrence of field 34F + if (floorLimitIndicatorDebit != null) { + if (debitCreditMark != CREDIT) { + throw new PageParserException( + "Expected Field '" + DateTimeIndicator.FIELD_TAG_13D + " (second occurrence) with CREDIT mark'," + + " but mark was '" + (debitCreditMark) + "'", fieldReader.getFieldLineNumber()); + } + } + + if (debitCreditMark != null) { + CurrencyUnit currencyUnit = floorLimitIndicator.getAmount().getCurrencyUnit(); + switch (debitCreditMark) { + case DEBIT: { + floorLimitIndicatorDebit = floorLimitIndicator; + // preset optional credit floor indicator + floorLimitIndicatorCredit = new FloorLimitIndicator(CREDIT, + BigMoney.zero(currencyUnit)); + nextValidFieldSet = ImmutableSet.of( + FloorLimitIndicator.FIELD_TAG_34F, + DateTimeIndicator.FIELD_TAG_13D); + break; + } + case CREDIT: { + floorLimitIndicatorCredit = floorLimitIndicator; + // handle missing debit floor indicator + if (floorLimitIndicatorDebit == null) { + floorLimitIndicatorDebit = new FloorLimitIndicator(DEBIT, + BigMoney.zero(currencyUnit)); + } + nextValidFieldSet = ImmutableSet.of( + DateTimeIndicator.FIELD_TAG_13D); + break; + } + default: + throw new IllegalStateException("Unexpected value: " + debitCreditMark); } } else { + floorLimitIndicatorDebit = floorLimitIndicator; floorLimitIndicatorCredit = floorLimitIndicator; nextValidFieldSet = ImmutableSet.of( DateTimeIndicator.FIELD_TAG_13D); diff --git a/src/test/java/com/qoomon/banking/swift/submessage/mt942/MT942PageReaderTest.java b/src/test/java/com/qoomon/banking/swift/submessage/mt942/MT942PageReaderTest.java index f32e020..525a912 100644 --- a/src/test/java/com/qoomon/banking/swift/submessage/mt942/MT942PageReaderTest.java +++ b/src/test/java/com/qoomon/banking/swift/submessage/mt942/MT942PageReaderTest.java @@ -165,14 +165,57 @@ public void parse_WHEN_parse_many_valid_file_RETURN_message() throws Exception { } @Test - public void parse_WHEN_parse_file_with_credit_floor_RETURN_message() throws Exception { + public void parse_WHEN_parse_without_specific_floor_mark_RETURN_message() throws Exception { // Given String mt942MessageText = "" + ":20:02761\n" + ":25:6-9412771\n" + ":28C:1/1\n" + - ":34F:USDC123,\n" + + ":34F:USD123,\n" + + ":13D:0001032359+0500\n" + + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + + ":86:multiline info\n" + + "-info\n" + + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + + ":86:singleline info\n" + + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + + ":90D:75475USD123,\n" + + ":90C:75475USD123,\n" + + ":86:multiline summary\n" + + "summary\n" + + "-"; + + MT942PageReader classUnderTest = new MT942PageReader(new StringReader(mt942MessageText)); + + // When + List pageList = TestUtils.collectUntilNull(classUnderTest::read); + + // Then + assertThat(pageList).hasSize(1); + MT942Page MT942Page = pageList.get(0); + assertThat(MT942Page.getTransactionGroupList()).hasSize(3); + assertThat(MT942Page.getStatementNumber().getStatementNumber()).isEqualTo("1"); + assertThat(MT942Page.getStatementNumber().getSequenceNumber()).contains("1"); + + FloorLimitIndicator debitFloorLimitIndicator = MT942Page.getFloorLimitIndicatorDebit(); + assertThat(debitFloorLimitIndicator.getDebitCreditMark()).isEmpty(); + assertThat(debitFloorLimitIndicator.getAmount()).isEqualTo(BigMoney.of(USD, new BigDecimal("123"))); + + FloorLimitIndicator creditFloorLimitIndicator = MT942Page.getFloorLimitIndicatorCredit(); + assertThat(creditFloorLimitIndicator.getDebitCreditMark()).isEmpty(); + assertThat(creditFloorLimitIndicator.getAmount()).isEqualTo(BigMoney.of(USD, new BigDecimal("123"))); + } + + @Test + public void parse_WHEN_parse_with_debit_floor_but_not_credit_floor_RETURN_message() throws Exception { + + // Given + String mt942MessageText = "" + + ":20:02761\n" + + ":25:6-9412771\n" + + ":28C:1/1\n" + + ":34F:USDD123,\n" + ":13D:0001032359+0500\n" + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + ":86:multiline info\n" + @@ -202,6 +245,93 @@ public void parse_WHEN_parse_file_with_credit_floor_RETURN_message() throws Exce assertThat(debitFloorLimitIndicator.getDebitCreditMark()).hasValue(DEBIT); assertThat(debitFloorLimitIndicator.getAmount()).isEqualTo(BigMoney.of(USD, new BigDecimal("123"))); + FloorLimitIndicator creditFloorLimitIndicator = MT942Page.getFloorLimitIndicatorCredit(); + assertThat(creditFloorLimitIndicator.getDebitCreditMark()).hasValue(CREDIT); + assertThat(creditFloorLimitIndicator.getAmount()).isEqualTo(BigMoney.zero(USD)); + } + + @Test + public void parse_WHEN_parse_with_debit_floor_snd_credit_floor_RETURN_message() throws Exception { + + // Given + String mt942MessageText = "" + + ":20:02761\n" + + ":25:6-9412771\n" + + ":28C:1/1\n" + + ":34F:USDD123,\n" + + ":34F:USDC789,\n" + + ":13D:0001032359+0500\n" + + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + + ":86:multiline info\n" + + "-info\n" + + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + + ":86:singleline info\n" + + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + + ":90D:75475USD123,\n" + + ":90C:75475USD123,\n" + + ":86:multiline summary\n" + + "summary\n" + + "-"; + + MT942PageReader classUnderTest = new MT942PageReader(new StringReader(mt942MessageText)); + + // When + List pageList = TestUtils.collectUntilNull(classUnderTest::read); + + // Then + assertThat(pageList).hasSize(1); + MT942Page MT942Page = pageList.get(0); + assertThat(MT942Page.getTransactionGroupList()).hasSize(3); + assertThat(MT942Page.getStatementNumber().getStatementNumber()).isEqualTo("1"); + assertThat(MT942Page.getStatementNumber().getSequenceNumber()).contains("1"); + + FloorLimitIndicator debitFloorLimitIndicator = MT942Page.getFloorLimitIndicatorDebit(); + assertThat(debitFloorLimitIndicator.getDebitCreditMark()).hasValue(DEBIT); + assertThat(debitFloorLimitIndicator.getAmount()).isEqualTo(BigMoney.of(USD, new BigDecimal("123"))); + + FloorLimitIndicator creditFloorLimitIndicator = MT942Page.getFloorLimitIndicatorCredit(); + assertThat(creditFloorLimitIndicator.getDebitCreditMark()).hasValue(CREDIT); + assertThat(creditFloorLimitIndicator.getAmount()).isEqualTo(BigMoney.of(USD, new BigDecimal("789"))); + } + + @Test + public void parse_WHEN_parse_with_missing_debit_floor_but_credit_floor_RETURN_message() throws Exception { + + // Given + String mt942MessageText = "" + + ":20:02761\n" + + ":25:6-9412771\n" + + ":28C:1/1\n" + + ":34F:USDC123,\n" + + ":13D:0001032359+0500\n" + + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + + ":86:multiline info\n" + + "-info\n" + + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + + ":86:singleline info\n" + + ":61:0312091211D880,FTRFBPHP/081203/0003//59512112915002\n" + + ":90D:75475USD123,\n" + + ":90C:75475USD123,\n" + + ":86:multiline summary\n" + + "summary\n" + + "-"; + + MT942PageReader classUnderTest = new MT942PageReader(new StringReader(mt942MessageText)); + + // When + List pageList = TestUtils.collectUntilNull(classUnderTest::read); + + // Then + assertThat(pageList).hasSize(1); + MT942Page MT942Page = pageList.get(0); + assertThat(MT942Page.getTransactionGroupList()).hasSize(3); + assertThat(MT942Page.getStatementNumber().getStatementNumber()).isEqualTo("1"); + assertThat(MT942Page.getStatementNumber().getSequenceNumber()).contains("1"); + + FloorLimitIndicator debitFloorLimitIndicator = MT942Page.getFloorLimitIndicatorDebit(); + assertThat(debitFloorLimitIndicator.getDebitCreditMark()).hasValue(DEBIT); + assertThat(debitFloorLimitIndicator.getAmount()).isEqualTo(BigMoney.zero(USD)); + FloorLimitIndicator creditFloorLimitIndicator = MT942Page.getFloorLimitIndicatorCredit(); assertThat(creditFloorLimitIndicator.getDebitCreditMark()).hasValue(CREDIT); assertThat(creditFloorLimitIndicator.getAmount()).isEqualTo(BigMoney.of(USD, new BigDecimal("123")));