/// <summary> /// Determine whether this debt class has a parent of the same type. /// </summary> /// <param name="transaction">The curren transaction.</param> /// <param name="entityRow">The entity row of the debt class.</param> /// <returns></returns> public Boolean HasParent(DataModelTransaction transaction, EntityRow entityRow) { Boolean has = false; EntityTreeRow[] entityTreeRows; Guid typeId; entityRow.AcquireReaderLock(transaction); typeId = entityRow.TypeId; entityTreeRows = entityRow.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId(); entityRow.ReleaseLock(transaction.TransactionId); foreach (EntityTreeRow entityTreeRow in entityTreeRows) { EntityRow parentRow; entityTreeRow.AcquireReaderLock(transaction); parentRow = entityTreeRow.EntityRowByFK_Entity_EntityTree_ParentId; entityTreeRow.ReleaseLock(transaction.TransactionId); parentRow.AcquireReaderLock(transaction); has = parentRow.TypeId == typeId; parentRow.ReleaseLock(transaction.TransactionId); if (has) { break; } } return(has); }
/// <summary> /// Get the list of blotters we should be pulling debt rules from. /// </summary> /// <returns>The BlotterIds of the affected blotters.</returns> private List <Guid> GetBlotterList() { List <Guid> blotters = new List <Guid>(); // This lock is mostly redundant, but needed for the initial call from the constructor. lock (DataModel.SyncRoot) { EntityRow blotter = DataModel.Entity.EntityKey.Find(this.blotterId); Guid type = blotter.TypeId; while (blotter.TypeId == type) { EntityTreeRow[] entityTree = blotter.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId(); blotters.Add(blotter.EntityId); if (entityTree.Length > 0) { blotter = entityTree[0].EntityRowByFK_Entity_EntityTree_ParentId; } } } return(blotters); }
/// <summary> /// Find the nearest ancestor DebtClass that has a value for a particular field. /// </summary> /// <param name="debtClassId">The DebtClassId of the debt class to start with.</param> /// <param name="field">The column to look at.</param> /// <returns>The row of the first debt class found to have a value for the indicated field (including the initial debt class).</returns> private static DebtHolderRow FindParentWithField(Guid debtClassId, DataColumn field) { DebtHolderRow parent = null; DebtHolderRow debtHolder = DataModel.DebtHolder.DebtHolderKey.Find(debtClassId); Guid typeId = DataModel.Entity.EntityKey.Find(debtClassId).TypeId; while (debtHolder != null && parent == null) { EntityRow child = DataModel.Entity.EntityKey.Find(debtHolder.DebtHolderId); EntityTreeRow[] parents = child.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId(); if (!debtHolder.IsNull(field)) { parent = debtHolder; } if (parents.Length != 0) { debtHolder = DataModel.DebtHolder.DebtHolderKey.Find(parents[0].ParentId); } else { debtHolder = null; } } return(parent); }
/// <summary> /// See if the ChildId is a ParentId to stop circular reference. /// </summary> /// <param name="transaction"></param> /// <param name="parent"></param> /// <param name="childId"></param> /// <returns></returns> private bool IsParentEntity(DataModelTransaction transaction, EntityRow parent, Guid childId) { parent.AcquireReaderLock(transaction.TransactionId, DataModel.LockTimeout); try { //Make sure we are not adding a child element that is also a parent. foreach (EntityTreeRow entityRow in parent.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId()) { entityRow.AcquireWriterLock(transaction.TransactionId, DataModel.LockTimeout); try { if (entityRow.ParentId == childId) { return(true); } if (IsParentEntity(transaction, entityRow.EntityRowByFK_Entity_EntityTree_ParentId, childId) == true) { return(true); } } finally { entityRow.ReleaseLock(transaction.TransactionId); } } } finally { parent.ReleaseReaderLock(transaction.TransactionId); } return(false); }
/// <summary> /// Check for a parent parent of same entity type as this entity. /// </summary> /// <returns>True if a parent was found - false otherwise.</returns> private DebtClassRow RetrieveParent(Guid entityId) { EntityRow entityRow = DataModel.Entity.EntityKey.Find(entityId); EntityTreeRow[] entityTreeRows = entityRow.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId(); foreach (EntityTreeRow entityTreeRow in entityTreeRows) { if (entityTreeRow.EntityRowByFK_Entity_EntityTree_ParentId.TypeId == entityRow.TypeId) { return(DataModel.DebtClass.DebtClassKey.Find(entityTreeRows[0].ParentId)); } } return(null); }
public ConsumerTrustMatchInfo(DataModelTransaction dataModelTransaction, WorkingOrderRow workingOrderRow) { // Initialize the object this.PaymentMethodTypes = new List <Guid>(); // These rows are required for navigating through the asset. Locks are acquired temporarily for them and released as soon as the Consumer Debt // information is collected. BlotterRow blotterRow = null; SecurityRow securityRow = null; ConsumerTrustRow consumerTrustRow = null; // The working order row is where everything starts. This is the asset that is to be matched against another. workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { // The working order identifier is used when creating matches. this.WorkingOrderId = workingOrderRow.WorkingOrderId; // The underlying security is a Consumer Debt record. securityRow = workingOrderRow.SecurityRowByFK_Security_WorkingOrder_SecurityId; // The blotter row needs to be examined for this cross. blotterRow = workingOrderRow.BlotterRow; } finally { workingOrderRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } securityRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { consumerTrustRow = securityRow.GetConsumerTrustRows()[0]; } finally { securityRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } DebtRuleRow debtRuleRow = null; // This row contains the actual asset that is to be matched. consumerTrustRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { // The savings balance is used when calculating the status of the match (whether there is enough funds in the account). this.SavingsBalance = consumerTrustRow.SavingsBalance; if (consumerTrustRow.IsDebtRuleIdNull() == false) { // At this point a rule override was found on an asset and there's no need to search the Entity hierarchy. debtRuleRow = consumerTrustRow.DebtRuleRow; } } finally { consumerTrustRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // This will attempt to find the debt rule associated with this credit card. If there is no rule explicitly associated with the asset, // then the entity hierarchy is searched until a debt class is found that contains a rule. When the rule is found, the values in that rule // will become the opening bid. DebtClassRow[] blotterRowDebtClassRows = null; blotterRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { // The blotter is required for adding matches to the data model once they are found. this.BlotterId = blotterRow.BlotterId; if (debtRuleRow == null) { blotterRowDebtClassRows = blotterRow.GetDebtClassRows(); } } finally { blotterRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } if (debtRuleRow == null) { // At this point, the asset hasn't got an explicit rule, so the hierarchy will need to be searched. There is only going to be one Debt // Class element associated with this blotter, but the iteration is an easier construct to work with than the equivalent array logic // for a single element. foreach (DebtClassRow debtClassRow in blotterRowDebtClassRows) { // This variable will keep track of our current location as we crawl up the hierarchy. DebtClassRow currentDebtClassRow = debtClassRow; DebtClassRow nextDebtClassRow = null; // This will crawl up the hierarchy until a Debt Class is found with a rule. This rule will provide the opening values for the // bid on this negotiation. do { // This will lock the current item in the hierarchy so it can be examined for a rule or, failing that, a parent element. currentDebtClassRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); Guid currentDebtClassRowDebtClassId; try { currentDebtClassRowDebtClassId = currentDebtClassRow.DebtClassId; if (currentDebtClassRow.IsDebtRuleIdNull() == false) { // At this point we have finally found a debt rule that can be used to start the negotiations. debtRuleRow = currentDebtClassRow.DebtRuleRow; } } finally { // The current Debt Class can be released. At this point, every record that was locked to read the hiearchy has been // released and the loop can either exit (when a rule is found) or move on to the parent Debt Class. currentDebtClassRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // If the current Debt Class has no rule then the Entity Hierarchy is used to find the parent element. if (debtRuleRow == null) { EntityTreeRow[] entityTreeRows; // The entity is the root of all objects in the hierarchy. From this object the path to the parent Debt Class can be // navigated. EntityRow entityRow = DataModel.Entity.EntityKey.Find(currentDebtClassRowDebtClassId); // Each entity needs to be locked before the relation can be used. entityRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { entityTreeRows = entityRow.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId(); } finally { // Finaly, the current entity is released. This allows us to finally move on to the next level of the hierarchy // without having to hold the locks for the entire transaction. entityRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // This will find each relation in the hierarchy which uses the current node as a child. foreach (EntityTreeRow entityTreeRow in entityTreeRows) { EntityRow parentEntityRow; // Lock the relation down before navigating to the parent. entityTreeRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { // This is the parent entity of the current entity in the crawl up the hierarchy. parentEntityRow = entityTreeRow.EntityRowByFK_Entity_EntityTree_ParentId; } finally { // The relationship record is released after each level of the hierarchy is examined for a parent. entityTreeRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } BlotterRow[] blotterRows; // The parent entity must be locked befor it can be checked for blotters and then, in turn, debt // classes. parentEntityRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { blotterRows = parentEntityRow.GetBlotterRows(); } finally { // The parent Entity record is released after each level of the hiearchy is examined for a parent. parentEntityRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // In practice, there will be zero or one blotter rows. The iteration makes it easier to check both // conditions. foreach (BlotterRow parentBlotterRow in blotterRows) { DebtClassRow[] parentBlotterRowDebtClassRows; // The blotter must be locked before iterating through the Debt Classes that may be associated // with the blotter. parentBlotterRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { parentBlotterRowDebtClassRows = parentBlotterRow.GetDebtClassRows(); } finally { // The locks are released after the each level of the hierarchy is checked. parentBlotterRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // Each blotter can have zero or one Debt Classes associated with it. This is a long an // tortuous way to finally get to the parent Debt Class. foreach (DebtClassRow parentDebtClassRow in parentBlotterRowDebtClassRows) { // Now that we've finally found the parent Debt Class, it will become the parent on // the next pass through the hierarchy. Note that the locks are released each time we // pass through a level of the hierarchy. nextDebtClassRow = parentDebtClassRow; } } } } // Now that all the locks are released, the parent Debt Class becomes the current one for the next level up in the hierarchy. // This algorithm will keep on climbing through the levels until a rule is found or the hierarchy is exhausted. currentDebtClassRow = nextDebtClassRow; } while(debtRuleRow == null && currentDebtClassRow != null); } } // The data is copied out of the Debt Rule when one is found in the Entity hierarchy. if (debtRuleRow != null) { DebtRulePaymentMethodRow[] debtRuleRowDebtRulePaymentMethodRows; // At this point we found a rule that can be used for the opening bid of a settlement. debtRuleRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { // These values are used in the opening bid of a negotiated settlement. this.PaymentLength = debtRuleRow.PaymentLength; this.PaymentStartDateLength = debtRuleRow.PaymentStartDateLength; this.PaymentStartDateUnitId = debtRuleRow.PaymentStartDateUnitId; this.SettlementValue = debtRuleRow.SettlementValue; this.SettlementUnitId = debtRuleRow.SettlementUnitId; debtRuleRowDebtRulePaymentMethodRows = debtRuleRow.GetDebtRulePaymentMethodRows(); } finally { // A lock on the Debt Rule is no longer required. debtRuleRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // This will copy each of the payment methods that are available for this settlement. Note that the table rows are locked only long // enough to acquire the item, then released. foreach (DebtRulePaymentMethodRow debtRulePaymentMethodRow in debtRuleRowDebtRulePaymentMethodRows) { debtRulePaymentMethodRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { this.PaymentMethodTypes.Add(debtRulePaymentMethodRow.PaymentMethodTypeId); } finally { debtRulePaymentMethodRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } } }
/// <summary> /// Evaluates whether a given working order is eligible for a cross with another order. /// </summary> /// <param name="key">The key of the object to be handled.</param> /// <param name="parameters">A generic list of paraneters to the handler.</param> public static void MergeDocument(Object[] key, params Object[] parameters) { // Extract the strongly typed variables from the generic parameters. Guid consumerDebtSettlementId = (Guid)key[0]; // This structure will collect the information required for the merge operation. MergeInfo mergeInfo = new MergeInfo(); mergeInfo.ConsumerDebtSettlementId = consumerDebtSettlementId; // An instance of the data model is required for CRUD operations. DataModel dataModel = new DataModel(); // If two counterparties agree on a transaction then a settlement report is generated from the Word Template associated with the Consumer Debt // Entity. using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew, TimeSpan.FromHours(1))) { // This provides a context for any transactions. DataModelTransaction dataModelTransaction = DataModelTransaction.Current; // It is important to minimize the locking for these transactions since they will drag the system performance down and create deadlock // sitiations if too many are held for too long. BlotterRow blotterRow = null; ConsumerDebtSettlementRow consumerDebtSettlementRow = null; ConsumerDebtNegotiationRow consumerDebtNegotiationRow = null; MatchRow matchRow = null; CreditCardRow creditCardRow = null; WorkingOrderRow workingOrderRow = null; try { // The ConsumerDebtSettlement row is where the search for the Settlement Information begins. consumerDebtSettlementRow = DataModel.ConsumerDebtSettlement.ConsumerDebtSettlementKey.Find(consumerDebtSettlementId); consumerDebtSettlementRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // There is no need to generate a report on a settlement that isn't new. This will momentarily lock the status table so we // can see if the settlement is new. try { consumerDebtSettlementRow.StatusRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); if (consumerDebtSettlementRow.StatusRow.StatusCode != Status.New) { return; } } finally { consumerDebtSettlementRow.StatusRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // The RowVersion is needed to update the record with the new PDF report. mergeInfo.RowVersion = consumerDebtSettlementRow.RowVersion; // The negotiation row contains the link to the base matching row. consumerDebtNegotiationRow = consumerDebtSettlementRow.ConsumerDebtNegotiationRow; consumerDebtNegotiationRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // The base matching row is where we'll find the working order. matchRow = consumerDebtNegotiationRow.MatchRow; matchRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // The working order row is where the blotter can be found. workingOrderRow = matchRow.WorkingOrderRow; workingOrderRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // And the blotter will lead us to the Entity hierarchy which is where we'll find the rules. blotterRow = workingOrderRow.BlotterRow; blotterRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // The 'Pending' status is applied to the Settlement after the letter has been generated. The status needs to be picked up while we're // still locking and reading tables. StatusRow statusRow = DataModel.Status.StatusKeyStatusCode.Find(Status.Pending); try { statusRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); mergeInfo.StatusId = statusRow.StatusId; } finally { statusRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } //Find the consumerDebt to find the credit Card to get the origianl creditor ConsumerTrustNegotiationRow trustNegotiation = null; MatchRow contraWorMatchRow = DataModel.Match.MatchKey.Find(matchRow.ContraMatchId); contraWorMatchRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { trustNegotiation = contraWorMatchRow.GetConsumerTrustNegotiationRows()[0]; } finally { if (contraWorMatchRow != null) { contraWorMatchRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } //Consumer Debt Row trustNegotiation.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); try { creditCardRow = trustNegotiation.CreditCardRow; } finally { if (trustNegotiation != null) { trustNegotiation.ReleaseReaderLock(dataModelTransaction.TransactionId); } } creditCardRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // There is only going to be one Debt Class record associated with this blotter, but the iteration is an easier construct to work with than // the equivalent array logic for a single element. foreach (DebtClassRow debtClassRow in blotterRow.GetDebtClassRows()) { // This variable will keep track of our current location as we crawl up the hierarchy. It is important to release the records as soon // as possible to reduce the likelyhood of a deadlock. DebtClassRow currentDebtClassRow = debtClassRow; DebtClassRow nextDebtClassRow = null; // This flag will be set when a Debt Class in the hierarchy contains a Debt Rule. Boolean isFound = false; // This will crawl up the hierarchy until a Debt Class is found with a rule. This rule will provide the opening values for the bid on // this negotiation. do { try { // This will lock the current item in the hierarchy so it can be examined for a rule or, failing that, a parent element. currentDebtClassRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // If the current Debt Class has no rule then the Entity Hierarchy is used to find the parent element. if (currentDebtClassRow.IsSettlementTemplateNull()) { // The entity is the root of all objects in the hierarchy. From this object the path to the parent Debt Class can be // navigated. EntityRow entityRow = DataModel.Entity.EntityKey.Find(currentDebtClassRow.DebtClassId); try { // Each entity needs to be locked before the relation can be used. entityRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // This will find each relation in the hierarchy which uses the current node as a child. foreach (EntityTreeRow entityTreeRow in entityRow.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId()) { try { // Lock the relation down before navigating to the parent. entityTreeRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // This is the parent entity of the current entity in the crawl up the hierarchy. EntityRow parentEntityRow = entityTreeRow.EntityRowByFK_Entity_EntityTree_ParentId; try { // The parent entity must be locked befor it can be checked for blotters and then, in turn, debt // classes. parentEntityRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // In practice, there will be zero or one blotter rows. The iteration makes it easier to check both // conditions. foreach (BlotterRow parentBlotterRow in parentEntityRow.GetBlotterRows()) { try { // The blotter must be locked before iterating through the Debt Classes that may be associated // with the blotter. parentBlotterRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); // Each blotter can have zero or one Debt Classes associated with it. This is a long an // tortuous way to finally get to the parent Debt Class. foreach (DebtClassRow parentDebtClassRow in parentBlotterRow.GetDebtClassRows()) { try { // Now that we've finally found the parent Debt Class, it will become the parent on the // next pass through the hierarchy. Note that the locks are released each time we pass // through a level of the hierarchy. parentDebtClassRow.AcquireReaderLock( dataModelTransaction.TransactionId, DataModel.LockTimeout); nextDebtClassRow = parentDebtClassRow; } finally { // The locks are released after the parent Debt Class is found. parentDebtClassRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } } finally { // The locks are released after the each level of the hierarchy is checked. parentBlotterRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } } finally { // The parent Entity record is released after each level of the hiearchy is examined for a parent. parentEntityRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } finally { // The relationship record is released after each level of the hierarchy is examined for a parent. entityTreeRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } } finally { // Finaly, the current entity is released. This allows us to finally move on to the next level of the hierarchy // without having to hold the locks for the entire transaction. entityRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } else { // The template has been found and converted back to a Microsoft Word template. mergeInfo.SourceDocument = Convert.FromBase64String(currentDebtClassRow.SettlementTemplate); // This will cause the loop to exit. isFound = true; } } finally { // The current Debt Class can be released. At this point, every record that was locked to read the hiearchy has been // released and the loop can either exit (when a rule is found) or move on to the parent Debt Class. currentDebtClassRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // Now that all the locks are released, the parent Debt Class becomes the current one for the next level up in the hierarchy. // This algorithm will keep on climbing through the levels until a rule is found or the hierarchy is exhausted. currentDebtClassRow = nextDebtClassRow; } while (isFound == false && currentDebtClassRow != null); } // AccountBalance mergeInfo.Dictionary.Add( "AccountBalance", String.Format("{0:$#,##0.00}", consumerDebtSettlementRow.AccountBalance)); // CreatedDate mergeInfo.Dictionary.Add("CreatedDate", consumerDebtSettlementRow.CreatedTime); // Original Creditor mergeInfo.Dictionary.Add( "DebtHolder", creditCardRow.IsDebtHolderNull() ? null : creditCardRow.DebtHolder); // DebtorAccountNumber mergeInfo.Dictionary.Add( "DebtorAccountNumber", consumerDebtSettlementRow.IsDebtorAccountNumberNull() ? null : consumerDebtSettlementRow.DebtorAccountNumber); // DebtorBankAccountNumber mergeInfo.Dictionary.Add( "DebtorBankAccountNumber", consumerDebtSettlementRow.IsDebtorBankAccountNumberNull() ? null : consumerDebtSettlementRow.DebtorBankAccountNumber); // DebtorBankRoutingNumber mergeInfo.Dictionary.Add( "DebtorBankRoutingNumber", consumerDebtSettlementRow.IsDebtorBankRoutingNumberNull() ? null : consumerDebtSettlementRow.DebtorBankRoutingNumber); // DebtorAddress1 mergeInfo.Dictionary.Add( "DebtorAddress1", consumerDebtSettlementRow.IsDebtorAddress1Null() ? null : consumerDebtSettlementRow.DebtorAddress1); // DebtorAddress2 mergeInfo.Dictionary.Add( "DebtorAddress2", consumerDebtSettlementRow.IsDebtorAddress2Null() ? null : consumerDebtSettlementRow.DebtorAddress2); // DebtorCity mergeInfo.Dictionary.Add( "DebtorCity", consumerDebtSettlementRow.IsDebtorCityNull() ? null : consumerDebtSettlementRow.DebtorCity); // DebtorFirstName mergeInfo.Dictionary.Add( "DebtorFirstName", consumerDebtSettlementRow.IsDebtorFirstNameNull() ? null : consumerDebtSettlementRow.DebtorFirstName); // DebtorLastName mergeInfo.Dictionary.Add( "DebtorLastName", consumerDebtSettlementRow.IsDebtorLastNameNull() ? null : consumerDebtSettlementRow.DebtorLastName); // DebtorMiddleName mergeInfo.Dictionary.Add( "DebtorMiddleName", consumerDebtSettlementRow.IsDebtorMiddleNameNull() ? null : consumerDebtSettlementRow.DebtorMiddleName); // DebtorOriginalAccountNumber mergeInfo.Dictionary.Add( "DebtorOriginalAccountNumber", consumerDebtSettlementRow.DebtorOriginalAccountNumber); // DebtorPostalCode mergeInfo.Dictionary.Add( "DebtorPostalCode", consumerDebtSettlementRow.IsDebtorPostalCodeNull() ? null : consumerDebtSettlementRow.DebtorPostalCode); // DebtorProvince String debtorProvince = null; if (!consumerDebtSettlementRow.IsDebtorProvinceIdNull()) { ProvinceRow provinceRow = consumerDebtSettlementRow.ProvinceRowByFK_Province_ConsumerDebtSettlement_DebtorProvinceId; try { provinceRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); debtorProvince = provinceRow.Abbreviation; } finally { provinceRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } mergeInfo.Dictionary.Add("DebtorProvinceAbbreviation", debtorProvince); // DebtorSalutation mergeInfo.Dictionary.Add( "DebtorSalutation", consumerDebtSettlementRow.IsDebtorSalutationNull() ? null : consumerDebtSettlementRow.DebtorSalutation); // DebtorSuffix mergeInfo.Dictionary.Add( "DebtorSuffix", consumerDebtSettlementRow.IsDebtorSuffixNull() ? null : consumerDebtSettlementRow.DebtorSuffix); // PayeeAddress1 mergeInfo.Dictionary.Add( "PayeeAddress1", consumerDebtSettlementRow.IsPayeeAddress1Null() ? null : consumerDebtSettlementRow.PayeeAddress1); // PayeeAddress2 mergeInfo.Dictionary.Add( "PayeeAddress2", consumerDebtSettlementRow.IsPayeeAddress2Null() ? null : consumerDebtSettlementRow.PayeeAddress2); // PayeeCity mergeInfo.Dictionary.Add( "PayeeCity", consumerDebtSettlementRow.IsPayeeCityNull() ? null : consumerDebtSettlementRow.PayeeCity); // PayeeCompanyName mergeInfo.Dictionary.Add( "PayeeCompanyName", consumerDebtSettlementRow.IsPayeeCompanyNameNull() ? null : consumerDebtSettlementRow.PayeeCompanyName); // PayeeContactName mergeInfo.Dictionary.Add( "PayeeContactName", consumerDebtSettlementRow.IsPayeeContactNameNull() ? null : consumerDebtSettlementRow.PayeeContactName); // PayeeDepartment mergeInfo.Dictionary.Add( "PayeeDepartment", consumerDebtSettlementRow.IsPayeeDepartmentNull() ? null : consumerDebtSettlementRow.PayeeDepartment); // PayeeEmail mergeInfo.Dictionary.Add( "PayeeEmail", consumerDebtSettlementRow.IsPayeeEmailNull() ? null : consumerDebtSettlementRow.PayeeEmail); // PayeeFax mergeInfo.Dictionary.Add( "PayeeFax", consumerDebtSettlementRow.IsPayeeFaxNull() ? null : consumerDebtSettlementRow.PayeeFax); // PayeeForBenefitOf mergeInfo.Dictionary.Add( "PayeeForBenefitOf", consumerDebtSettlementRow.IsPayeeForBenefitOfNull() ? null : consumerDebtSettlementRow.PayeeForBenefitOf); // PayeePhone mergeInfo.Dictionary.Add( "PayeePhone", consumerDebtSettlementRow.IsPayeePhoneNull() ? null : consumerDebtSettlementRow.PayeePhone); // PayeePostalCode mergeInfo.Dictionary.Add( "PayeePostalCode", consumerDebtSettlementRow.IsPayeePostalCodeNull() ? null : consumerDebtSettlementRow.PayeePostalCode); // PayeeProvince String payeeProvince = null; if (!consumerDebtSettlementRow.IsPayeeProvinceIdNull()) { ProvinceRow provinceRow = consumerDebtSettlementRow.ProvinceRowByFK_Province_ConsumerDebtSettlement_PayeeProvinceId; try { provinceRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); payeeProvince = provinceRow.Abbreviation; } finally { provinceRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } mergeInfo.Dictionary.Add("PayeeProvinceAbbreviation", payeeProvince); // PaymentLength mergeInfo.Dictionary.Add("PaymentLength", consumerDebtSettlementRow.PaymentLength); // PaymentStartDate mergeInfo.Dictionary.Add("PaymentStartDate", consumerDebtSettlementRow.PaymentStartDate.ToLocalTime().ToLongDateString()); // PayeeBankAccountNumber mergeInfo.Dictionary.Add( "PayeeBankAccountNumber", consumerDebtSettlementRow.IsPayeeBankAccountNumberNull() ? null : consumerDebtSettlementRow.PayeeBankAccountNumber); // PayeeBankRoutingNumber mergeInfo.Dictionary.Add( "PayeeBankRoutingNumber", consumerDebtSettlementRow.IsPayeeBankRoutingNumberNull() ? null : consumerDebtSettlementRow.PayeeBankRoutingNumber); // SettlementAmount mergeInfo.Dictionary.Add("SettlementAmount", consumerDebtSettlementRow.SettlementAmount); // SettlementPercent mergeInfo.Dictionary.Add("SettlementPercent", consumerDebtSettlementRow.SettlementAmount / consumerDebtSettlementRow.AccountBalance); // TermPaymentAmount mergeInfo.Dictionary.Add("TermPaymentAmount", consumerDebtSettlementRow.SettlementAmount / consumerDebtSettlementRow.PaymentLength); // The payment methods is modeled as a vector which makes it difficult to add as a single merge field. To work around this, each of the // possible payment methods are described in the data dictionary using the form 'Is{PaymentMethodName}'. The Word Merge process should look for // the presence of these fields to generate a block of text for the instructions for each of the payment methods. This iteration will // collect all the possible payment method types in an array and assume that they don't exist (i.e. set them to a Boolean value of 'false') // until they're found in the settlement instructions. foreach (PaymentMethodTypeRow paymentMethodTypeRow in DataModel.PaymentMethodType) { try { paymentMethodTypeRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); mergeInfo.Dictionary.Add(String.Format("Is{0}", paymentMethodTypeRow.Name.Replace(" ", String.Empty)), false); } finally { paymentMethodTypeRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } // This iteration will cycle through all the payment methods in the settlement and set them to be true. The result is a dictionary of all // possible payment methods with the ones included in this settlement set to be the Boolean value of 'true'. foreach (ConsumerDebtSettlementPaymentMethodRow consumerDebtSettlementPaymentMethodRow in consumerDebtSettlementRow.GetConsumerDebtSettlementPaymentMethodRows()) { try { // Each of the payment methods in the settlement are modeled as a list that is associated with the settlement. This will lock each // of the items in the list in turn and examine the parent 'PaymentMethodType' record to construct a mail-merge tag. consumerDebtSettlementPaymentMethodRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); PaymentMethodTypeRow paymentMethodTypeRow = DataModel.PaymentMethodType.PaymentMethodTypeKey.Find( consumerDebtSettlementPaymentMethodRow.PaymentMethodTypeId); try { // Once the parent MethodType is found a tag is added to the dictionary. The presence of the 'Is<PaymentMethodType>' item in // the dictionary means that the given payment method is acceptable for this settlement. paymentMethodTypeRow.AcquireReaderLock(dataModelTransaction.TransactionId, DataModel.LockTimeout); mergeInfo.Dictionary[String.Format("Is{0}", paymentMethodTypeRow.Name.Replace(" ", String.Empty))] = true; } finally { // The parent payment method type row is not needed any longer. paymentMethodTypeRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } finally { // Release the payment method row. consumerDebtSettlementPaymentMethodRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } } finally { // The CreditCardRow is no longer needed. if (creditCardRow != null && creditCardRow.IsReaderLockHeld(dataModelTransaction.TransactionId)) { creditCardRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // The ConsumerDebtSettlementRow is no longer needed. if (consumerDebtSettlementRow != null) { consumerDebtSettlementRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // The ConsumerDebtNegotiation Row is no longer needed. if (consumerDebtNegotiationRow != null) { consumerDebtNegotiationRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // The MatchRow is no longer needed. if (matchRow != null) { matchRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // The WorkingOrderRow is no longer needed. if (workingOrderRow != null) { workingOrderRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } // The BlotterRow is no longer needed. if (blotterRow != null) { blotterRow.ReleaseReaderLock(dataModelTransaction.TransactionId); } } MemoryStream memoryStream = null; try { // At this point, all the data has been collected and the record locks released. It is time to merge the document. memoryStream = SettlementDocumentFactory.iMailMerge.CreateDocument(mergeInfo.SourceDocument, mergeInfo.Dictionary); } catch (Exception exception) { EventLog.Error("There was a problem creating the settlement letter. \n Details: {0}, {1}", exception.Message, exception.StackTrace); } if (memoryStream != null) { // Update the settlement with the newly generated PFD file. dataModel.UpdateConsumerDebtSettlement( null, null, null, null, new Object[] { consumerDebtSettlementId }, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, mergeInfo.RowVersion, null, Convert.ToBase64String(memoryStream.ToArray()), mergeInfo.StatusId); } // If we reached here the transaction was successful. transactionScope.Complete(); } }
/// <summary> /// /// </summary> /// <returns></returns> public override void Update(Entity record) { DataModel dataModel = new DataModel(); DataModelTransaction transaction = DataModelTransaction.Current; DateTime modifiedTime = DateTime.UtcNow; EntityRow entityRow = DataModel.Entity.EntityKey.Find(record.RowId); Dictionary <String, EntityRow> parents = new Dictionary <String, EntityRow>(); List <String> parentNames = new List <String>(); EntityTreeRow[] entityTreeRows; String oldName = null; Int64 rowVersion; if (!TradingSupport.HasAccess(transaction, record.RowId, AccessRight.Write)) { throw new SecurityException("Current user does not have write access to the selected Entity"); } if (record.Name != null && !EntityPersistence.IsNameValid(record.Name as String)) { throw new FaultException <ArgumentFault>(new ArgumentFault("Names cannot contain a backslash"), "Names cannot contain a backslash"); } entityRow.AcquireReaderLock(transaction); entityTreeRows = entityRow.GetEntityTreeRowsByFK_Entity_EntityTree_ChildId(); oldName = entityRow.Name; rowVersion = entityRow.RowVersion; entityRow.ReleaseLock(transaction.TransactionId); if (!oldName.Equals(record.Name)) { foreach (EntityTreeRow entityTreeRow in entityTreeRows) { EntityRow parentEntityRow; entityTreeRow.AcquireReaderLock(transaction); parentEntityRow = entityTreeRow.EntityRowByFK_Entity_EntityTree_ParentId; entityTreeRow.ReleaseLock(transaction.TransactionId); parentEntityRow.AcquireReaderLock(transaction); parents.Add(parentEntityRow.Name, parentEntityRow); parentEntityRow.ReleaseLock(transaction.TransactionId); } parentNames = parents.Keys.ToList(); parentNames.Sort(); foreach (String parentName in parentNames) { parents[parentName].AcquireWriterLock(transaction); } foreach (EntityRow parentEntityRow in parents.Values) { if (!EntityPersistence.IsNameUnique(transaction, parentEntityRow, record.Name as String)) { throw new FaultException <RecordExistsFault>( new RecordExistsFault("Entity", new object[] { record.Name }), "An entity with this name already exists"); } } } dataModel.UpdateEntity( null, record.Description, null, new object[] { record.RowId }, record.ExternalId0, record.ExternalId1, record.ExternalId2, record.ExternalId3, record.ExternalId4, record.ExternalId5, record.ExternalId6, record.ExternalId7, record.ImageId, record.IsHidden, record.IsReadOnly, modifiedTime, record.Name, rowVersion, record.TenantId, null); }