/// <summary> /// Reset the negotiation to a "in negotiation" state. /// </summary> /// <param name="consumerDebtNegotiations">The the negotiations to reset.</param> internal static void Reject(ConsumerDebtNegotiationInfo[] consumerDebtNegotiations) { // An instance of the shared data model is required to use its methods. DataModel dataModel = new DataModel(); // The business logic requires the current time and the user identifier for auditing. Guid createUserId = TradingSupport.UserId; DateTime createDateTime = DateTime.UtcNow; DateTime modifiedTime = createDateTime; Guid modifiedUserId = createUserId; // This Web Method comes with an implicit transaction that is linked to its execution. DataModelTransaction dataModelTransaction = DataModelTransaction.Current; // This method can handle a batch of updates in a single transaction. foreach (ConsumerDebtNegotiationInfo consumerDebtNegotiationInfo in consumerDebtNegotiations) { // The blotter is not passed in from the client but is used Guid blotterId = Guid.Empty; Status negotiationStatus; DebtNegotiationInfo debtNegotiationInfo = null; // This is the next negotiation in the batch to be updated. ConsumerDebtNegotiationRow consumerDebtNegotiationRow = DataModel.ConsumerDebtNegotiation.ConsumerDebtNegotiationKey.Find(consumerDebtNegotiationInfo.ConsumerDebtNegotiationId); // The payment methods available to this negotiation is a vector. Rather than delete everything and re-add it anytime an update is made, a // list of changes is constructed: new payment methods are added, obsolete payment methods are deleted and the ones that haven't changed are // left alone. These list help to work out the differences. List <ConsumerDebtNegotiationPaymentMethodTypeInfo> counterItems = new List <ConsumerDebtNegotiationPaymentMethodTypeInfo>(); try { // Lock the current negotation record for reading. The data model doesn't support reader lock promotion, so the programming model is to // lock the database, collect the data, release the locks and then write. This model is especially important when iterating through a // large batch to prevent the number of locks from growing to large. consumerDebtNegotiationRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // The blotter identifier is used for access control and is not passed in by the client. blotterId = consumerDebtNegotiationRow.BlotterId; debtNegotiationInfo = new DebtNegotiationInfo(consumerDebtNegotiationRow); negotiationStatus = StatusMap.FromId(consumerDebtNegotiationRow.StatusId); // Determine whether the client has the right to modify this record. if (!TradingSupport.HasAccess(dataModelTransaction, blotterId, AccessRight.Write)) { throw new FaultException <FluidTrade.Core.SecurityFault>(new SecurityFault("You do not have write access to the selected object.")); } MatchRow matchRow = DataModel.Match.MatchKey.Find(debtNegotiationInfo.MatchId); try { matchRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); debtNegotiationInfo.MatchRowVersion = matchRow.RowVersion; debtNegotiationInfo.ContraMatchId = matchRow.ContraMatchId; } finally { matchRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } MatchRow contraMatchRow = DataModel.Match.MatchKey.Find(debtNegotiationInfo.ContraMatchId); try { contraMatchRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); debtNegotiationInfo.ContraMatchRowVersion = contraMatchRow.RowVersion; } finally { contraMatchRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // The payment methods are maintained as a vector associated with the negotiation record. This will lock each of the records and read the // payment methods into a data structure so the locks don't need to be held when it is time to write. foreach (var consumerDebtNegotiationOfferPaymentMethodRow in consumerDebtNegotiationRow.GetConsumerDebtNegotiationCounterPaymentMethodRows()) { try { // Temporarily lock the record containing the payment method. consumerDebtNegotiationOfferPaymentMethodRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // This will construct a mask of items that are already part of this negotiation. The mask is used to prevent an item from being // added if it's already there. // This list is used to delete the payment methods that are no longer part of this negotiation. counterItems.Add( new ConsumerDebtNegotiationPaymentMethodTypeInfo( consumerDebtNegotiationOfferPaymentMethodRow.PaymentMethodTypeId, consumerDebtNegotiationOfferPaymentMethodRow.ConsumerDebtNegotiationCounterPaymentMethodId, consumerDebtNegotiationOfferPaymentMethodRow.RowVersion)); } finally { // At this point the payment method isn't needed. consumerDebtNegotiationOfferPaymentMethodRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } } finally { // At this point, the negotiation record isn't needed. It is critical to release the reader locks before attempting a write. consumerDebtNegotiationRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // At this point, all the data for this operation has been collected and the CRUD operations can be invoked to finish the update. Note that // the counter party information is not modified here, but is done through the Chinese wall. Guid newConsureDebtNegotiationId = Guid.NewGuid(); dataModel.CreateConsumerDebtNegotiation( debtNegotiationInfo.AccountBalance, debtNegotiationInfo.BlotterId, newConsureDebtNegotiationId, debtNegotiationInfo.CounterPaymentLength, debtNegotiationInfo.CounterPaymentStartDateLength, debtNegotiationInfo.CounterPaymentStartDateUnitId, debtNegotiationInfo.CounterSettlementUnitId, debtNegotiationInfo.CounterSettlementValue, modifiedTime, modifiedUserId, debtNegotiationInfo.IsRead, debtNegotiationInfo.IsReply, debtNegotiationInfo.MatchId, modifiedTime, modifiedUserId, consumerDebtNegotiationInfo.PaymentLength, consumerDebtNegotiationInfo.PaymentStartDateLength, consumerDebtNegotiationInfo.PaymentStartDateUnitId, consumerDebtNegotiationInfo.SettlementUnitId, consumerDebtNegotiationInfo.SettlementValue, StatusMap.FromCode(Status.Rejected), out debtNegotiationInfo.Version); // This will add the payment methods to the negotiation that are not already there. foreach (Guid paymentMethodTypeId in consumerDebtNegotiationInfo.PaymentMethodTypes) { dataModel.CreateConsumerDebtNegotiationOfferPaymentMethod( blotterId, newConsureDebtNegotiationId, Guid.NewGuid(), paymentMethodTypeId); } //This will delete those payment methods that are no longer part of the negotiation. foreach (ConsumerDebtNegotiationPaymentMethodTypeInfo consumerDebtNegotiationPaymentMethodTypeInfo in counterItems) { dataModel.UpdateConsumerDebtNegotiationCounterPaymentMethod(blotterId, null, new Object[] { consumerDebtNegotiationPaymentMethodTypeInfo.ConsumerDebtNegotiationOfferPaymentMethodId }, newConsureDebtNegotiationId, null, consumerDebtNegotiationPaymentMethodTypeInfo.RowVersion); } //Reset the Match Status Id. This is required so the match engine will redo the match. The //match engine does not recalculate if it is not in the initial three stages of - Valid, Partial, ValidwithFunds dataModel.UpdateMatch( null, null, null, null, null, null, null, new object[] { debtNegotiationInfo.MatchId }, debtNegotiationInfo.MatchRowVersion, StatusMap.FromCode(Status.ValidMatch), null); //Reset the Contra Match Status Id dataModel.UpdateMatch( null, null, null, null, null, null, null, new object[] { debtNegotiationInfo.ContraMatchId }, debtNegotiationInfo.ContraMatchRowVersion, StatusMap.FromCode(Status.ValidMatch), null); } }