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}. + + +