public void Test_DipsStatus1Bitmasks() { //const int ExtraAuxDom = 16; //const int AuxDom = 32; //const int BsbNumber = 64; //const int AccountNumber = 128; //const int TransactionCode = 256; //const int Amount = 512; // NOTE: False == Invalid Assert.IsFalse(DipsStatus1Bitmask.GetAccountNumberStatusMasked(128)); Assert.IsFalse(DipsStatus1Bitmask.GetAmountStatusMasked(512)); Assert.IsFalse(DipsStatus1Bitmask.GetAuxDomStatusMasked(32)); Assert.IsFalse(DipsStatus1Bitmask.GetBsbNumberStatusMasked(64)); Assert.IsFalse(DipsStatus1Bitmask.GetExtraAuxDomStatusMasked(16)); Assert.IsFalse(DipsStatus1Bitmask.GetTransactionCodeStatusMasked(256)); // NOTE: True == Valid Assert.IsTrue(DipsStatus1Bitmask.GetAccountNumberStatusMasked(0)); Assert.IsTrue(DipsStatus1Bitmask.GetAmountStatusMasked(0)); Assert.IsTrue(DipsStatus1Bitmask.GetAuxDomStatusMasked(0)); Assert.IsTrue(DipsStatus1Bitmask.GetBsbNumberStatusMasked(0)); Assert.IsTrue(DipsStatus1Bitmask.GetExtraAuxDomStatusMasked(0)); Assert.IsTrue(DipsStatus1Bitmask.GetTransactionCodeStatusMasked(0)); // NOTE: Redundant tests! Assert.IsFalse(DipsStatus1Bitmask.GetAccountNumberStatusMasked(1 << 7)); Assert.IsFalse(DipsStatus1Bitmask.GetAmountStatusMasked(1 << 9)); Assert.IsFalse(DipsStatus1Bitmask.GetAuxDomStatusMasked(1 << 5)); Assert.IsFalse(DipsStatus1Bitmask.GetBsbNumberStatusMasked(1 << 6)); Assert.IsFalse(DipsStatus1Bitmask.GetExtraAuxDomStatusMasked(1 << 4)); Assert.IsFalse(DipsStatus1Bitmask.GetTransactionCodeStatusMasked(1 << 8)); }
public override void ExecuteJob() { using (var dbConnection = new SqlConnection(Configuration.SqlConnectionString)) { using (var dbContext = new DipsDbContext(dbConnection)) { Log.Information("Scanning database for completed codeline validation batches"); //Get all the potential batches that have been completed var completedBatches = dbContext.Queues .Where(q => !q.ResponseCompleted && q.S_LOCATION == DipsLocationType.CodelineValidationDone.Value && q.S_LOCK.Trim() == "0") .ToList(); Serilog.Log.Information("Found {0} completed codeline validation batches", completedBatches.Count()); foreach (var completedBatch in completedBatches) { string routingKey; Serilog.Log.Debug("Creating response for batch {@batch}", completedBatch.S_BATCH); //only commit the transaction if // a) we were the first application to mark this batch row as ValidateCodelineCompleted (DipsQueue uses optimistic concurrency) // b) we were able to place a response message on the bus using (var tx = dbContext.BeginTransaction()) { try { //mark the batch as completed so it gets 'locked' completedBatch.ResponseCompleted = true; routingKey = completedBatch.RoutingKey; dbContext.SaveChanges(); var batchNumber = completedBatch.S_BATCH; //get the vouchers, generate and send the response var vouchers = dbContext.NabChqPods .Where(v => v.S_BATCH == batchNumber) .AsEnumerable() .Select(v => new { S_STATUS1 = int.Parse(string.IsNullOrWhiteSpace(v.S_STATUS1) ? "0" : v.S_STATUS1), voucher = v }).ToList(); //use bitmasks to map the values from S_STATUS1 field var batchResponse = new ValidateBatchCodelineResponse { voucherBatch = new VoucherBatch { scannedBatchNumber = completedBatch.S_BATCH, workType = ResponseHelper.ParseWorkType( ResponseHelper.TrimString(completedBatch.S_JOB_ID)), batchAccountNumber = vouchers.First().voucher.batchAccountNumber, captureBsb = vouchers.First().voucher.captureBSB, processingState = ResponseHelper.ParseState( ResponseHelper.TrimString(vouchers.First().voucher.processing_state)), collectingBank = vouchers.First().voucher.collecting_bank, unitID = vouchers.First().voucher.unit_id, batchType = ResponseHelper.TrimString(vouchers.First().voucher.batch_type), subBatchType = ResponseHelper.TrimString(vouchers.First().voucher.sub_batch_type), }, voucher = vouchers.Select(v => new ValidateCodelineResponse { extraAuxDomStatus = DipsStatus1Bitmask.GetExtraAuxDomStatusMasked(v.S_STATUS1), auxDomStatus = DipsStatus1Bitmask.GetAuxDomStatusMasked(v.S_STATUS1), accountNumberStatus = DipsStatus1Bitmask.GetAccountNumberStatusMasked(v.S_STATUS1), amountStatus = DipsStatus1Bitmask.GetAmountStatusMasked(v.S_STATUS1), bsbNumberStatus = DipsStatus1Bitmask.GetBsbNumberStatusMasked(v.S_STATUS1), transactionCodeStatus = DipsStatus1Bitmask.GetTransactionCodeStatusMasked(v.S_STATUS1), documentReferenceNumber = ResponseHelper.ResolveDocumentReferenceNumber( ResponseHelper.TrimString(v.voucher.doc_ref_num)), targetEndPoint = ResponseHelper.TrimString(v.voucher.ie_endPoint), documentType = ResponseHelper.ParseDocumentType(v.voucher.doc_type), processingDate = DateTime.ParseExact(string.Format("{0}", v.voucher.proc_date), "yyyyMMdd", CultureInfo.InvariantCulture), }).ToArray() }; if (Configuration.DeleteDatabaseRows) { ResponseHelper.CleanupBatchData(batchNumber, dbContext); } if (string.IsNullOrEmpty(routingKey)) { routingKey = string.Empty; } //Task.WaitAll(responseExchange.PublishAsync(batchResponse, completedBatch.CorrelationId, routingKey.Trim())); tx.Commit(); Serilog.Log.Debug( "Codeline validation batch '{@batch}' has been completed and a response has been placed on the queue", completedBatch.S_BATCH); Serilog.Log.Information("Batch '{@batch}' response sent: {@response}", completedBatch.S_BATCH, batchResponse); } catch (OptimisticConcurrencyException) { //this is to handle the race condition where more than instance of this service is running at the same time and tries to update the row. //basically ignore the message by loggin a warning and roll back. //if this row was not included by mistake (e.g. it should be included), it will just come in in the next batch run. Serilog.Log.Warning( "Could not create a codeline validation response for batch '{@batch}' because the DIPS database row was updated by another connection", completedBatch.S_BATCH); tx.Rollback(); } catch (Exception ex) { Serilog.Log.Error( ex, "Could not complete and create a codeline validation response for batch '{@batch}'", completedBatch.S_BATCH); tx.Rollback(); } } } } } }
public override void ExecuteJob() { Log.Information("Scanning database for completed transaction validation batches"); using (var dbConnection = new SqlConnection(Configuration.SqlConnectionString)) { using (var dbContext = new DipsDbContext(dbConnection)) { //Get all the potential batches that have been completed var completedBatches = dbContext.Queues .Where(q => !q.ResponseCompleted && q.S_LOCATION == DipsLocationType.TransactionValidationDone.Value && q.S_LOCK.Trim() == "0") .ToList(); Log.Information("Found {0} completed transaction validation batches", completedBatches.Count()); foreach (var completedBatch in completedBatches) { Log.Debug("Creating response for batch {@batch}", completedBatch.S_BATCH); //only commit the transaction if // a) we were the first application to mark this batch row as ValidateTransactionCompleted (DipsQueue uses optimistic concurrency) // b) we were able to place a response message on the bus using (var tx = dbContext.BeginTransaction()) { try { //mark the batch as completed so it gets 'locked' completedBatch.ResponseCompleted = true; var routingKey = completedBatch.RoutingKey; dbContext.SaveChanges(); var batchNumber = completedBatch.S_BATCH; //get the vouchers, generate and send the response var vouchers = dbContext.NabChqPods .Where(v => v.S_BATCH == batchNumber && v.S_DEL_IND != " 255") .ToList() .Select(v => new { S_STATUS1 = int.Parse(string.IsNullOrWhiteSpace(v.S_STATUS1) ? "0" : v.S_STATUS1), voucher = v }).ToList(); if (vouchers.Count > 0) { var firstVoucher = vouchers.First(v => v.voucher.isGeneratedVoucher != "1"); //use bitmasks to map the values from S_STATUS1 field var batchResponse = new ValidateBatchTransactionResponse { voucherBatch = new VoucherBatch { scannedBatchNumber = completedBatch.S_BATCH, workType = ResponseHelper.ParseWorkType(ResponseHelper.TrimString(completedBatch.S_JOB_ID)), batchAccountNumber = firstVoucher.voucher.batchAccountNumber, captureBsb = firstVoucher.voucher.captureBSB, processingState = ResponseHelper.ParseState(ResponseHelper.TrimString(firstVoucher.voucher.processing_state)), collectingBank = firstVoucher.voucher.collecting_bank, unitID = firstVoucher.voucher.unit_id, batchType = ResponseHelper.TrimString(firstVoucher.voucher.batch_type), subBatchType = ResponseHelper.TrimString(firstVoucher.voucher.sub_batch_type), }, voucher = vouchers.Select(v => new ValidateTransactionResponse { documentReferenceNumber = v.voucher.S_TRACE, surplusItemFlag = ResponseHelper.ParseStringAsBool(v.voucher.surplusItemFlag), suspectFraudFlag = ResponseHelper.ParseStringAsBool(v.voucher.micr_suspect_fraud_flag), isGeneratedVoucher = ResponseHelper.ParseStringAsBool(v.voucher.isGeneratedVoucher), codelineFieldsStatus = new CodelineStatus { extraAuxDomStatus = DipsStatus1Bitmask.GetExtraAuxDomStatusMasked(v.S_STATUS1), auxDomStatus = DipsStatus1Bitmask.GetAuxDomStatusMasked(v.S_STATUS1), accountNumberStatus = DipsStatus1Bitmask.GetAccountNumberStatusMasked(v.S_STATUS1), amountStatus = DipsStatus1Bitmask.GetAmountStatusMasked(v.S_STATUS1), bsbNumberStatus = DipsStatus1Bitmask.GetBsbNumberStatusMasked(v.S_STATUS1), transactionCodeStatus = DipsStatus1Bitmask.GetTransactionCodeStatusMasked(v.S_STATUS1), }, reasonCode = ResponseHelper.GetExpertBalanceReason(v.voucher.balanceReason), transactionLinkNumber = ResponseHelper.TrimString(v.voucher.transactionLinkNumber), unprocessable = ResponseHelper.ParseStringAsBool(v.voucher.unproc_flag), forValueIndicator = ResponseHelper.TrimString(v.voucher.fv_ind), dips_override = ResponseHelper.TrimString(v.voucher.@override), thirdPartyCheckRequired = ResponseHelper.ParseYNStringAsBool(v.voucher.tpcRequired), unencodedECDReturnFlag = ResponseHelper.ParseStringAsBool(v.voucher.fxa_unencoded_ECD_return), thirdPartyMixedDepositReturnFlag = ResponseHelper.ParseStringAsBool(v.voucher.tpcMixedDepRet), postTransmissionQaAmountFlag = ResponseHelper.ParseStringAsBool(v.voucher.fxaPtQAAmtFlag), postTransmissionQaCodelineFlag = ResponseHelper.ParseStringAsBool(v.voucher.fxaPtQACodelineFlag), voucher = new Voucher { accountNumber = ResponseHelper.TrimString(v.voucher.acc_num), amount = ResponseHelper.ParseAmountField(v.voucher.amount), auxDom = ResponseHelper.TrimString(v.voucher.ser_num), bsbNumber = v.voucher.bsb_num, //DIPS overwrites doc_ref_num with empty values during auto balancing .. //documentReferenceNumber = ResponseHelper.ResolveDocumentReferenceNumber(ResponseHelper.TrimString(v.voucher.doc_ref_num)), documentReferenceNumber = ResponseHelper.ResolveDocumentReferenceNumber(v.voucher.S_TRACE.Trim()), extraAuxDom = ResponseHelper.TrimString(v.voucher.ead), transactionCode = ResponseHelper.TrimString(v.voucher.trancode), documentType = ResponseHelper.ParseDocumentType(v.voucher.doc_type), processingDate = DateTime.ParseExact(string.Format("{0}{1}", completedBatch.S_SDATE, completedBatch.S_STIME), "dd/MM/yyHH:mm:ss", CultureInfo.InvariantCulture), } }).ToArray() }; if (Configuration.DeleteDatabaseRows) { ResponseHelper.CleanupBatchData(batchNumber, dbContext); } if (string.IsNullOrEmpty(routingKey)) { routingKey = string.Empty; } Exchange.SendMessage(CustomJsonSerializer.MessageToBytes(batchResponse), routingKey.Trim(), completedBatch.CorrelationId); //Task.WaitAll(responseExchange.PublishAsync(batchResponse, completedBatch.CorrelationId, routingKey.Trim())); tx.Commit(); Log.Debug("Transaction validation batch '{@batch}' has been completed and a response has been placed on the queue", completedBatch.S_BATCH); Log.Information("Batch '{@batch}' response sent: {@response}", completedBatch.S_BATCH, batchResponse); } else { Log.Error( "Could not create a transaction validation response for batch '{@batch}' because the vouchers cannot be queried", completedBatch.S_BATCH); } } catch (OptimisticConcurrencyException) { //this is to handle the race condition where more than instance of this service is running at the same time and tries to update the row. //basically ignore the message by loggin a warning and roll back. //if this row was not included by mistake (e.g. it should be included), it will just come in in the next batch run. Log.Warning( "Could not create a transaction validation response for batch '{@batch}' because the DIPS database row was updated by another connection", completedBatch.S_BATCH); tx.Rollback(); } catch (Exception ex) { Log.Error( ex, "Could not complete and create an transaction validation response for batch '{@batch}'", completedBatch.S_BATCH); tx.Rollback(); } } } } } Log.Information("Finished processing completed transaction validation batches"); }