Setting a Fee Schedule for Recording Fees and Transfer Fees

Questions about and code samples for custom order rules and validation within Select.
Post Reply
Posts: 1221
Joined: Wed Jan 15, 2014 3:50 pm
Location: Raleigh, NC

Setting a Fee Schedule for Recording Fees and Transfer Fees

Post by BobRichards »

The fees that fall under this type are created in SPAdmin in the Recording Fees and Transfer Taxes section. On the settlement grid, they show up in CDF section E and HUD section 1200. (For this example, I will focus on the CDF but the search mechanism is the same.) I have already created a City/Count Tax/Stamps fee schedule for the state of TX and a type of Deed named "CCTS Fee Schedule". It has a single entry for a Flat Fee of $100, to keep things as simple as possible.

Create a new CDF order, go to an available line in section E and open it up to view the charge. So that our policy is available to be found in the Fees tab, we have to set Fee Schedule Type to "City/County tax/stamps" and Document to "Deed". At this point, the Fee Schedule drop down list should have our fee schedule ("CCTS Fee Schedule") visible but it may not be selected. Now we add the code that sets the Fee Schedule value.

The code to perform this activity has been lifted directly from the Select base rules and converted to Python. As always, it is up to you to test the code and verify it works in the situations you need. The code resides in a single Python class so it will be treated as a block to drop directly in your code. Your custom code will set up a few variables then pass search information into the "GetFeeScheduleVersionObject(...)" method of the class.

You can see the COR portion is rather simple. For any section E fee, it will pass each fee object (chargeFee displayed in Fees tab) and the desired Fee Schedule name to the search class. The return value from the search is either None (if the name can't be found) or the desired Fee Schedule. Note that it will always return the current Fee Schedule.

Code: Select all

from System import *
from SoftPro.ClientModel import *
from SoftPro.OrderTracking.Client import *
from SoftPro.OrderTracking.Client.Orders import *
from SoftPro.OrderTracking.Client.Rates import *

# Support LINQ for searching support.
import clr
import System
from SoftPro.ClientModel.Linq import Searchable	# Define SoftPro criteria searches.

#  This is the COR definition that you need to use as an example for how to search for a fee schedule.
def TaxAndGovernmentDetailChargeFee_FeeSchedule_Value(args):
	chargeFee = args.Context
	root = args.Context.Root
	name = chargeFee.GetType().Name
	rate = TaxAndGovFeeSchedules.GetFeeScheduleVersionObject(args, root, chargeFee, 'CCTS Fee Schedule')
	args.Value = rate
Now we reach the Fee Schedule search class. You will see several methods that begin with an underscore ("_") - do not call those methods. By Python convention, appending an underscore serves as a marker to others that the method is used inside the class and to leave it alone. It also is a handly way to remove messing class members from consideration by external callers (this means you).

The only class method you need to use is "GetFeeScheduleVersionObject(...)". It is passed four values...
  • a way to get the IRateSchedules service,
  • order root so can find first property to get the state and other address information
  • the chargeFee with Fee Schedule Type and Document input values
  • and the name of the fee schedule
The next step is to build a search object suitable for the IRateSchedules service to find the desired item. Depending on the inputs, we will build one of three different search objects then do the actual search. The result will either be None or the current Fee Schedule.

Code: Select all

# Class wrapper to hold utility functions for Taxes and Other Government Fees.
#   To set Fee Schedule property (CDF Section E and other locations),
#       call GetFeeScheduleVersionObject(...).
class TaxAndGovFeeSchedules:
    # Get IOrderItem property value if supported.  Otherwise return None.
    def _safeGetProperty(context, property):
        if IOrderItem.HasProperty(context, property) and IOrderItem.GetIsSupported(context, property):
            return IOrderItem.GetProperty(context, property)
            return None
    # Create search object appropriate for FeeScheduleType (Deed/Mortgage/Release/Other)
    #   feeScheduleType : FeeScheduleType
    #   state   : IState
    #   city    : String
    #   county  : String
    #   documentType    : FeeScheduleVersionType
    # Returns: Criteria search object
    def _createSearchObject(feeScheduleType, state, city, county, documentType):
        if feeScheduleType == FeeScheduleType.Recording:
            search = RecordingFeeScheduleSearch()
        elif feeScheduleType == FeeScheduleType.CityCountyTaxStamps:
            search = MunicipalTaxStampsFeeScheduleSearch()
            search.City = city
            search = StateTaxStampsFeeScheduleSearch()
        search.County = county
        search.State = state
        search.VersionType = Enum.Parse(FeeScheduleVersionType, str(documentType))
        return search

    # Return FeeScheduleVersion or None if it can't be found.  Returns current version.
    #   services    : Object with GetService(...) method to get Select services
    #   root        : Top level order object
    #   chargeFee   : TaxAndGovernmentDetailChargeFee object.  Used to get fee schedule type, etc.
    #                   Function uses value of DocumentType ("Document" column on ChargeFee object.)
    #                   "Fee Schedule Type" and "Document" columms should be set before calling this routine.
    #   rateName    : String. Name of desired fee schedule.
    def GetFeeScheduleVersionObject(services, root, chargeFee, rateName):
        match = None
        # Do nothing if DocumentType not specified. 
        if chargeFee.DocumentType != DocumentType.None:
            county = None
            state = None
            city = None
            feeScheduleType = chargeFee.FeeScheduleType
            # Get city/county/state if present in first property.
            if root.Properties.Count > 0:
                firstProperty = root.Properties[0]
                county = firstProperty.County
                if firstProperty.Address is not None:
                    state = TaxAndGovFeeSchedules._safeGetProperty(firstProperty.Address, 'State')
                    city = TaxAndGovFeeSchedules._safeGetProperty(firstProperty.Address, 'City')
            # Create a search object.
            search = TaxAndGovFeeSchedules._createSearchObject(feeScheduleType, state, city, county, chargeFee.DocumentType)
            # Search based on FeeScheduleType criteria object.
            rateSchedules = services.GetService(IRateSchedules)
            if feeScheduleType == FeeScheduleType.Recording:
                # Get all RecordingFeeScheduleVersions then pick one with correct name.
                matches = (Searchable.Search(
                        IRateSchedules.RecordingFeeScheduleVersions.GetValue(rateSchedules), search))
                match = matches.Where(lambda t: t.RateScheduleName == rateName).FirstOrDefault()
            elif feeScheduleType == FeeScheduleType.CityCountyTaxStamps:
                # Get all MunicipalTaxStampsFeeScheduleVersions then pick one with correct name.
                matches = (Searchable.Search(
                        IRateSchedules.MunicipalTaxStampsFeeScheduleVersions.GetValue(rateSchedules), search))
                match = matches.Where(lambda t: t.RateScheduleName == rateName).FirstOrDefault()

                # Get all StateTaxStampsFeeScheduleVersions then pick one with correct name.
                matches = (Searchable.Search(
                        IRateSchedules.StateTaxStampsFeeScheduleVersions.GetValue(rateSchedules), search))
                match = matches.Where(lambda t: t.RateScheduleName == rateName).FirstOrDefault()
        # Return what we found or None.
        return match
To execute the COR code, just create a new COR module and insert the first code block (above) into the file and overwrite any pre-existing code. Finally, copy the second code block (above) after the first inserted code block and start the module.

  • If you already know the type of Fee Schedule you will need, you can shorten the code so you don't have to test as many combinations. In a perfect world, you should not do this since once you test it, you will have a piece of code that works for ANY section E fee schedule.
  • Investigate the other search criteria available to the search objects (such as RecordingFeeScheduleSearch). You might find something handy and Select uses search objects in many places so some familiar with the topic is good.
  • Automation snippets can use this code by modifying the variables and service calls used by the COR caller. (Of course, this is an exercise to the reader at this point.)
Bob Richards, Senior Software Developer, SoftPro
Post Reply