From ea319c652a156935d7807ae0331c75f77d463997 Mon Sep 17 00:00:00 2001
From: IzabellaDTS <84338278+IzabellaDTS@users.noreply.github.com>
Date: Thu, 16 Jan 2025 16:54:44 -0600
Subject: [PATCH 1/4] Adding New Bank
Added Cass Commercial Bank and a x937 file type
---
.../App.config | 30 +-
.../X937/CassCommercialBank.cs | 263 ++++++++++++++++++
...com.bemaservices.RemoteCheckDeposit.csproj | 35 ++-
.../packages.config | 11 +-
4 files changed, 316 insertions(+), 23 deletions(-)
create mode 100644 com.bemaservices.RemoteCheckDeposit/FileFormatTypes/X937/CassCommercialBank.cs
diff --git a/com.bemaservices.RemoteCheckDeposit/App.config b/com.bemaservices.RemoteCheckDeposit/App.config
index 19a4a79..7c18f22 100644
--- a/com.bemaservices.RemoteCheckDeposit/App.config
+++ b/com.bemaservices.RemoteCheckDeposit/App.config
@@ -1,34 +1,36 @@
-
+
-
-
-
+
+
+
-
+
-
+
-
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
\ No newline at end of file
diff --git a/com.bemaservices.RemoteCheckDeposit/FileFormatTypes/X937/CassCommercialBank.cs b/com.bemaservices.RemoteCheckDeposit/FileFormatTypes/X937/CassCommercialBank.cs
new file mode 100644
index 0000000..91714b6
--- /dev/null
+++ b/com.bemaservices.RemoteCheckDeposit/FileFormatTypes/X937/CassCommercialBank.cs
@@ -0,0 +1,263 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.ComponentModel.Composition;
+using System.IO;
+using System.Linq;
+
+using Rock;
+using Rock.Attribute;
+using Rock.Model;
+
+using com.bemaservices.RemoteCheckDeposit.Model;
+using com.bemaservices.RemoteCheckDeposit.Records.X937;
+using com.bemaservices.RemoteCheckDeposit;
+using System.Text;
+
+namespace com.bemaservices.RemoteCheckDeposit.FileFormatTypes
+{
+
+ ///
+ /// Defines the x937 File export for Cass Commercial Bank
+ ///
+ [Description("Processes a batch export for Cass Commercial Bank.")]
+ [Export(typeof(FileFormatTypeComponent))]
+ [ExportMetadata("ComponentName", "Cass Commercial Bank")]
+ //[EncryptedTextField("Origin Routing Number", "Used on Type 10 Record 3 for Account Routing", true, key: "OriginRoutingNumber")]
+ //[EncryptedTextField("Destination Routing Number", "", true, "072000096", key: "DestinationRoutingNumber")]
+ public class CassCommercialBank : X937DSTU
+ {
+ #region System Setting Keys
+ ///
+ /// The system setting for the next cash header identifier. These should never be
+ /// repeated. Ever.
+ ///
+ protected const string SystemSettingNextCashHeaderId = "CassCommercial.NextCashHeaderId";
+
+ ///
+ /// The system setting that contains the last file modifier we used.
+ ///
+ protected const string SystemSettingLastFileModifier = "CassCommercial.LastFileModifier";
+
+ ///
+ /// The last item sequence number used for items.
+ ///
+ protected const string LastItemSequenceNumberKey = "CassCommercial.LastItemSequenceNumber";
+ #endregion
+
+ protected override FileHeader GetFileHeaderRecord(ExportOptions options)
+ {
+ var header = base.GetFileHeaderRecord(options);
+
+ var originRoutingNumber = Rock.Security.Encryption.DecryptString(GetAttributeValue(options.FileFormat, "InstitutionRoutingNumber"));
+
+ // Override account number with institution routing number for Field 5
+ header.ImmediateOriginRoutingNumber = originRoutingNumber;
+
+ return header;
+ }
+
+
+ protected override CashLetterHeader GetCashLetterHeaderRecord(ExportOptions options)
+ {
+ int cashHeaderId = GetSystemSetting(SystemSettingNextCashHeaderId).AsIntegerOrNull() ?? 0;
+ var originRoutingNumber = Rock.Security.Encryption.DecryptString(GetAttributeValue(options.FileFormat, "InstitutionRoutingNumber"));
+
+ var header = base.GetCashLetterHeaderRecord(options);
+ header.ID = cashHeaderId.ToString("D8");
+ SetSystemSetting(SystemSettingNextCashHeaderId, (cashHeaderId + 1).ToString());
+
+ // Override routing number with institution routing number for Field 4
+ header.ClientInstitutionRoutingNumber = originRoutingNumber;
+
+ return header;
+ }
+
+ protected override BundleHeader GetBundleHeader(ExportOptions options, int bundleIndex)
+ {
+ var header = base.GetBundleHeader(options, bundleIndex);
+ var originRoutingNumber = Rock.Security.Encryption.DecryptString(GetAttributeValue(options.FileFormat, "InstitutionRoutingNumber"));
+
+ // Override routing number with institution routing number for Field 4
+ header.ClientInstitutionRoutingNumber = originRoutingNumber;
+
+ // Set Bundle ID (should be same as Bundle Sequence Number )
+ header.ID = (bundleIndex + 1).ToString();
+
+ return header;
+ }
+
+ //
+ /// Gets the item detail records (type 25)
+ ///
+ /// Export options to be used by the component.
+ /// The transaction being deposited.
+ /// A collection of records.
+ protected override List GetItemDetailRecords(ExportOptions options, FinancialTransaction transaction)
+ {
+ //var accountNumber = Rock.Security.Encryption.DecryptString(GetAttributeValue(options.FileFormat, "AccountNumber"));
+ //var routingNumber = Rock.Security.Encryption.DecryptString(GetAttributeValue(options.FileFormat, "RoutingNumber"));
+
+ //
+ // Parse the MICR data from the transaction.
+ //
+ var micr = GetMicrInstance(transaction.CheckMicrEncrypted);
+
+ var transactionRoutingNumber = micr.GetRoutingNumber();
+
+ // Check Detail Record Type (25)
+ var detail = new Records.X937.CheckDetail
+ {
+ AuxiliaryOnUs = micr.GetAuxOnUs(),
+ ExternalProcessingCode = micr.GetExternalProcessingCode(),
+ PayorBankRoutingNumber = transactionRoutingNumber.Substring(0, 8),
+ PayorBankRoutingNumberCheckDigit = transactionRoutingNumber.Substring(8, 1),
+ OnUs = string.Format("{0}/{1}", micr.GetAccountNumber(), micr.GetCheckNumber()),
+ ItemAmount = transaction.TotalAmount,
+ ClientInstitutionItemSequenceNumber = transaction.Id.ToString(),
+ DocumentationTypeIndicator = "G", // Field Value must be "G" - Meaning there are 2 images present
+ ElectronicReturnAcceptanceIndicator = string.Empty,
+ MICRValidIndicator = null,
+ BankOfFirstDepositIndicator = "Y",
+ CheckDetailRecordAddendumCount = 00, // From Veronica
+ CorrectionIndicator = string.Empty,
+ ArchiveTypeIndicator = string.Empty
+
+ };
+
+ return new List { detail };
+ }
+
+ //
+ /// Gets the image record for a specific transaction image (type 50 and 52).
+ ///
+ /// Export options to be used by the component.
+ /// The transaction being deposited.
+ /// The check image scanned by the scanning application.
+ /// if set to true [is front].
+ /// A collection of records.
+ protected override List GetImageRecords(ExportOptions options, FinancialTransaction transaction, FinancialTransactionImage image, bool isFront)
+ {
+ var institutionRoutingNumber = Rock.Security.Encryption.DecryptString(GetAttributeValue(options.FileFormat, "InstitutionRoutingNumber"));
+ var records = base.GetImageRecords(options, transaction, image, isFront);
+
+ foreach (var imageData in records.Where(r => r.RecordType == 52).Cast())
+ {
+ imageData.InstitutionRoutingNumber = institutionRoutingNumber;
+ }
+
+ foreach (var imageData in records.Where(r => r.RecordType == 50).Cast())
+ {
+ imageData.ImageCreatorRoutingNumber = institutionRoutingNumber;
+ }
+
+ return records;
+ }
+
+ ///
+ /// Gets the bundle control record (type 70).
+ ///
+ /// Export options to be used by the component.
+ /// The existing records in the bundle.
+ /// A BundleControl record.
+ protected override Records.X937.BundleControl GetBundleControl(ExportOptions options, List records)
+ {
+ var itemRecords = records.Where(r => r.RecordType == 25);
+
+ // If we are including the credit items then we need to count those as well.
+ /*if (this.countDepositSlip)
+ {
+ itemRecords = records.Where(r => r.RecordType == 25 && r.RecordType == 61); // Just Overwrite
+ }*/
+
+ var checkDetailRecords = records.Where(r => r.RecordType == 25).Cast(); // Only count checks and not the credit detail
+ var imageDetailRecords = records.Where(r => r.RecordType == 52);
+
+ // Record Type 70
+ var control = new Records.X937.BundleControl
+ {
+ ItemCount = itemRecords.Count(),
+ TotalAmount = checkDetailRecords.Sum(r => (decimal)r.ItemAmount),
+ MICRValidTotalAmount = 000000000000, //checkDetailRecords.Sum(r => (decimal)r.ItemAmount),
+ ImageCount = imageDetailRecords.Count()
+ };
+
+ return control;
+ }
+
+ ///
+ /// Gets the cash letter control record (type 90).
+ ///
+ /// Export options to be used by the component.
+ /// Existing records in the cash letter.
+ /// A CashLetterControl record.
+ protected override Records.X937.CashLetterControl GetCashLetterControlRecord(ExportOptions options, List records)
+ {
+ var bundleHeaderRecords = records.Where(r => r.RecordType == 20);
+ var checkDetailRecords = records.Where(r => r.RecordType == 25).Cast();
+ var itemRecords = records.Where(r => r.RecordType == 25); // Only count record types 25.
+ var imageDetailRecords = records.Where(r => r.RecordType == 52);
+ var organizationName = GetAttributeValue(options.FileFormat, "OriginName");
+
+ // Some banks *might* include the deposit slip
+ /*if (this.countDepositSlip)
+ {
+ itemRecords = records.Where(r => r.RecordType == 25 && r.RecordType == 61); // Just Overwrite.
+ }*/
+
+ // Record Type 90
+ var control = new Records.X937.CashLetterControl
+ {
+ BundleCount = bundleHeaderRecords.Count(),
+ ItemCount = itemRecords.Count(),
+ //TotalAmount = 000000000000,//checkDetailRecords.Sum(c => (decimal)c.ItemAmount), // Must be 0's According to Veronica, This should now be the total amount
+ TotalAmount = checkDetailRecords.Sum(c => (decimal)c.ItemAmount), // According to Veronica 09-28-2018
+ ImageCount = imageDetailRecords.Count(),
+ ECEInstitutionName = organizationName
+ };
+
+ return control;
+ }
+
+ protected override FileControl GetFileControlRecord(ExportOptions options, List records)
+ {
+ var fileControl = base.GetFileControlRecord(options, records);
+
+ fileControl.ImmediateOriginContactName = "".PadRight(14, ' ');
+ fileControl.ImmediateOriginContactPhoneNumber = "0".PadRight(10, ' ');
+
+ return fileControl;
+ }
+
+ /*protected int GetNextItemSequenceNumber()
+ {
+ int lastSequence = GetSystemSetting(LastItemSequenceNumberKey).AsIntegerOrNull() ?? 0;
+ int nextSequence = lastSequence + 1;
+
+ SetSystemSetting(LastItemSequenceNumberKey, nextSequence.ToString());
+
+ return nextSequence;
+ }*/
+
+
+
+ ///
+ /// Hashes the string with SHA256.
+ ///
+ /// The contents to be hashed.
+ /// A hex representation of the hash.
+ protected string HashString(string contents)
+ {
+ byte[] byteContents = Encoding.Unicode.GetBytes(contents);
+
+ var hash = new System.Security.Cryptography.SHA256CryptoServiceProvider().ComputeHash(byteContents);
+
+ return string.Join("", hash.Select(b => b.ToString("x2")).ToArray());
+ }
+ }
+}
diff --git a/com.bemaservices.RemoteCheckDeposit/com.bemaservices.RemoteCheckDeposit.csproj b/com.bemaservices.RemoteCheckDeposit/com.bemaservices.RemoteCheckDeposit.csproj
index e43f91d..fa47600 100644
--- a/com.bemaservices.RemoteCheckDeposit/com.bemaservices.RemoteCheckDeposit.csproj
+++ b/com.bemaservices.RemoteCheckDeposit/com.bemaservices.RemoteCheckDeposit.csproj
@@ -1,5 +1,6 @@
+
@@ -14,6 +15,8 @@
512
true
+
+
true
@@ -36,11 +39,25 @@
$(SolutionDir)RockWeb\Bin\DotLiquid.dll
-
- $(SolutionDir)packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll
+
+ ..\packages\EntityFramework.6.5.1\lib\net45\EntityFramework.dll
+ True
-
- $(SolutionDir)packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll
+
+ ..\packages\EntityFramework.6.5.1\lib\net45\EntityFramework.SqlServer.dll
+ True
+
+
+ ..\packages\Microsoft.Data.Edm.5.8.4\lib\net40\Microsoft.Data.Edm.dll
+ True
+
+
+ ..\packages\Microsoft.Data.OData.5.8.4\lib\net40\Microsoft.Data.OData.dll
+ True
+
+
+ ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll
+ True
@@ -60,6 +77,10 @@
+
+ ..\packages\System.Spatial.5.8.4\lib\net40\System.Spatial.dll
+ True
+
@@ -92,6 +113,7 @@
+
@@ -230,6 +252,9 @@
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+