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
BobRichards
Posts: 1376
Joined: Wed Jan 15, 2014 3:50 pm
Location: Raleigh, NC
Contact:

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
clr.AddReference('System.Core')
clr.ImportExtensions(System.Linq)
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 handy way to remove messy 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.
    @staticmethod
    def _safeGetProperty(context, property):
        if IOrderItem.HasProperty(context, property) and IOrderItem.GetIsSupported(context, property):
            return IOrderItem.GetProperty(context, property)
        else:
            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
    @staticmethod
    def _createSearchObject(feeScheduleType, state, city, county, documentType):
        
        if feeScheduleType == FeeScheduleType.Recording:
            search = RecordingFeeScheduleSearch()
        elif feeScheduleType == FeeScheduleType.CityCountyTaxStamps:
            search = MunicipalTaxStampsFeeScheduleSearch()
            search.City = city
        else:
            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.
    @staticmethod
    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()

            else:
                # 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.

Enhancements/Thoughts
  • 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
amtech
Posts: 9
Joined: Tue May 24, 2022 5:51 pm

Re: Setting a Fee Schedule for Recording Fees and Transfer Fees

Post by amtech »

Hi Bob.

Thank you so much for posting this! I've butted my head against this a couple of times.

I have the COR working for CDF orders but now I need a version to cover CSS orders as well but I'm having trouble with the function name for. I used Alt+F6 to try to figure it out but I'm not having any luck. I looked at the CDF for reference to reverse engineer it but nothing I've tried is working:
  • CSSChargeFee_FeeSchedule_Value
  • CSSChargeFee_FeeScheduleType_Value
  • CSSLineChargeFee_FeeSchedule_Value
  • StatementChargeFee_FeeSchedule_Value
  • StatementCharge_FeeSchedule_Value
  • CommercialChargeFee_FeeSchedule_Value
  • CommercialStatementChargeFee_FeeSchedule_Value
  • Fee_FeeSchedule_Value
CSS tax and fees.jpg
CSS tax and fees.jpg (53.81 KiB) Viewed 2317 times
Below is the code I'm using. I've removed the import statements and condensed your second code block (class) to concentrate on where I'm having an issue.

Code: Select all

# Imports from your first code block

#def TaxAndGovernmentDetailChargeFee_FeeSchedule_Value(args):
 def CSSChargeFee_FeeSchedule_Value(args):
	chargeFee = args.Context
	root = args.Context.Root
	name = chargeFee.GetType().Name
	
	#rate = TaxAndGovFeeSchedules.GetFeeScheduleVersionObject(args, root, chargeFee, 'CCTS Fee Schedule')
	rate = TaxAndGovFeeSchedules.GetFeeScheduleVersionObject(args, root, chargeFee, 'NY - Mortgage (Albany) Recording')
	args.Value = rate
	
# Your second code block
# Class wrapper to hold utility functions for Taxes and Other Government Fees.
class TaxAndGovFeeSchedules:
BobRichards
Posts: 1376
Joined: Wed Jan 15, 2014 3:50 pm
Location: Raleigh, NC
Contact:

Re: Setting a Fee Schedule for Recording Fees and Transfer Fees

Post by BobRichards »

Sorry that this is way harder than it should be. :(

For CSS orde types, the parent object name is CSSTaxAndGovernmentChargeCalculation.
2022-12-13_15-55-26.png
2022-12-13_15-55-26.png (6.02 KiB) Viewed 2299 times

Code: Select all

def CSSTaxAndGovernmentChargeCalculation_FeeSchedule_Value(args):
    chargeFee = args.Context
    root = args.Context.Root
	
    rate = TaxAndGovFeeSchedules.GetFeeScheduleVersionObject(args, root, chargeFee, 'CCTSTest')
    args.Value = rate
Bob Richards, Senior Software Developer, SoftPro
Post Reply