Business Rules/Logic

Labels
AJAX(112) App Studio(7) Apple(1) Application Builder(245) Application Factory(207) ASP.NET(95) ASP.NET 3.5(45) ASP.NET Code Generator(72) ASP.NET Membership(28) Azure(18) Barcode(2) Barcodes(3) BLOB(18) Business Rules(1) Business Rules/Logic(140) BYOD(13) Caching(2) Calendar(5) Charts(29) Cloud(14) Cloud On Time(2) Cloud On Time for Windows 7(2) Code Generator(54) Collaboration(11) command line(1) Conflict Detection(1) Content Management System(12) COT Tools for Excel(26) CRUD(1) Custom Actions(1) Data Aquarium Framework(122) Data Sheet(9) Data Sources(22) Database Lookups(50) Deployment(22) Designer(177) Device(1) DotNetNuke(12) EASE(20) Email(6) Features(101) Firebird(1) Form Builder(14) Globalization and Localization(6) How To(1) Hypermedia(2) Inline Editing(1) Installation(5) JavaScript(20) Kiosk(1) Low Code(3) Mac(1) Many-To-Many(4) Maps(6) Master/Detail(36) Microservices(4) Mobile(63) Mode Builder(3) Model Builder(3) MySQL(10) Native Apps(5) News(18) OAuth(9) OAuth Scopes(1) OAuth2(13) Offline(20) Offline Apps(4) Offline Sync(5) Oracle(11) PKCE(2) Postgre SQL(1) PostgreSQL(2) PWA(2) QR codes(2) Rapid Application Development(5) Reading Pane(2) Release Notes(183) Reports(48) REST(29) RESTful(29) RESTful Workshop(15) RFID tags(1) SaaS(7) Security(81) SharePoint(12) SPA(6) SQL Anywhere(3) SQL Server(26) SSO(1) Stored Procedure(4) Teamwork(15) Tips and Tricks(87) Tools for Excel(2) Touch UI(93) Transactions(5) Tutorials(183) Universal Windows Platform(3) User Interface(338) Video Tutorial(37) Web 2.0(100) Web App Generator(101) Web Application Generator(607) Web Form Builder(40) Web.Config(9) Workflow(28)
Archive
Blog
Business Rules/Logic
Friday, July 20, 2012PrintSubscribe
Code Value

The Code Value property is a calculation performed every time the record is inserted or updated. The calculation expression is written in the programming language of your project – Visual Basic or C#.

For example, let’s keep track of the last time an order detail was modified.

Start SQL Server Management Studio. In the Object Explorer, right-click on Databases / Northwind / Tables / dbo.OrderDetails node and select Design.

Design context menu option for Order Details table of the Northwind database.

Add a column to the table:

Column Name Data Type Allow Nulls
ModifiedOn datetime True

Save the table. Switch back to the application generator and refresh the Order Details controller.

Refresh the OrderDetails controller.

Activate the Project Designer. In the Project Explorer, switch to the Controllers tab. Double-click on OrderDetails / Views / editForm1 / c1 – Order Details / ModifiedOn data field node.

ModifiedOn data field in editForm1 of Order Details controller.

Change the following properties:

Property New Value
Text Mode Static

Press OK to save the data field. Double-click on OrderDetails / Fields / ModifiedOn node.

ModifiedOn field of Order Details controller.

Change the following properties:

Property New Value
Code Value DateTime.Now
Data Format String g

Press OK to save. On the toolbar, press Browse.

Navigate to the Order Details page, and edit a record. The Modified On field has no value.

First editing an order detail will reveal that Modified On field has no value.

Change any field value in the record, and save. Open the detail view for the same record – the Modified On field value has been updated.

Opening the same record will reveal that ModifiedOn field value was updated upon insertion.

The field will update on every insert or update.

The web application generator has placed the Code Value formula in an automatically generated business rule associated with the data controller.

C#:

using System;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using MyCompany.Data;

namespace MyCompany.Rules
{
    public partial class OrderDetailsBusinessRules : MyCompany.Data.BusinessRules
    {
        
        [ControllerAction("OrderDetails", "Insert", ActionPhase.Before)]
        [ControllerAction("OrderDetails", "Update", ActionPhase.Before)]
        public void AssignFieldValuesToOrderDetails(int? orderID, 
            string orderCustomerID, string orderCustomerCompanyName, 
            string orderEmployeeLastName, string orderShipViaCompanyName, 
            int? productID, string productProductName, 
            string productCategoryCategoryName, string productSupplierCompanyName, 
            decimal? unitPrice, short? quantity, float? discount, DateTime? modifiedOn)
        {
            FieldValue ModifiedOnFieldValue = SelectFieldValueObject("ModifiedOn");
            object ModifiedOnCodeValue = DateTime.Now;
            if (ModifiedOnFieldValue == null)
                AddFieldValue("ModifiedOn", ModifiedOnCodeValue);
            else
            {
                ModifiedOnFieldValue.NewValue = ModifiedOnCodeValue;
                ModifiedOnFieldValue.Modified = true;
                ModifiedOnFieldValue.ReadOnly = false;
            }
        }
    }
}

Visual Basic:

Imports MyCompany.Data
Imports System
Imports System.Collections.Generic
Imports System.Data
Imports System.Linq
Imports System.Text.RegularExpressions
Imports System.Web
Imports System.Web.Security

Namespace MyCompany.Rules
    
    Partial Public Class OrderDetailsBusinessRules
        Inherits MyCompany.Data.BusinessRules
        
        <ControllerAction("OrderDetails", "Insert", ActionPhase.Before), _
         ControllerAction("OrderDetails", "Update", ActionPhase.Before)> _
        Public Sub AssignFieldValuesToOrderDetails(ByVal orderID As Nullable(Of Integer),
                                                   ByVal orderCustomerID As String,
                                                   ByVal orderCustomerCompanyName As String,
                                                   ByVal orderEmployeeLastName As String,
                                                   ByVal orderShipViaCompanyName As String,
                                                   ByVal productID As Nullable(Of Integer),
                                                   ByVal productProductName As String,
                                                   ByVal productCategoryCategoryName As String,
                                                   ByVal productSupplierCompanyName As String,
                                                   ByVal unitPrice As Nullable(Of Decimal),
                                                   ByVal quantity As Nullable(Of Short),
                                                   ByVal discount As Nullable(Of Single),
                                                   ByVal modifiedOn As Nullable(Of DateTime))
            Dim ModifiedOnFieldValue As FieldValue = SelectFieldValueObject("ModifiedOn")
            Dim ModifiedOnCodeValue As Object = DateTime.Now
            If (ModifiedOnFieldValue Is Nothing) Then
                AddFieldValue("ModifiedOn", ModifiedOnCodeValue)
            Else
                ModifiedOnFieldValue.NewValue = ModifiedOnCodeValue
                ModifiedOnFieldValue.Modified = True
                ModifiedOnFieldValue.ReadOnly = False
            End If
        End Sub
    End Class
End Namespace

This can also be done with SQL Business Rules.

Monday, July 9, 2012PrintSubscribe
Feature: “Code” Business Rules

The application framework of each project is generated in C# or Visual Basic. The business rules engine of the framework takes care of executing SQL, Email, and JavaScript business rules in response to actions defined in the data controller. Naturally, developers can create business rules written in the programming language of the project to tap in the power of the business rules engine and vast Microsoft.NET library.

'Code' Business Rule defined in the data controller 'Customers'

For example, the “Code” business rule defined in the project configuration will result in the code file generated in the project output folders.

The code file with the implementation stub is automatically generated for each 'Code' Business Rule in the programming language of the project

The business rule file provides a method template linked to the data controller business rule with Rule attribute. The file will not be re-created during subsequent code generation sessions. Any modifications to the file will become a permanent extension of the application framework.

Let’s implement a validation business logic.  Note that the highlighted text indicates the conditional expression inserted in the automatically generated business rule method template.

C#:

using System;
using MyCompany.Data;

namespace MyCompany.Rules
{
    public partial class CustomersBusinessRules : MyCompany.Data.BusinessRules
    {
        /// <summary>
        /// This method will execute in any view before an action
        /// with a command name that matches "Insert|Update|Delete".
        /// </summary>
        [Rule("r100")]
        public void r100Implementation(string customerID, string companyName,
            string contactName, string contactTitle, string address, string city,
            string region, string postalCode, string country, string phone, string fax)
        {
            // This is the placeholder for method implementation.
            if (country == "USA")
            {
                // tell the application framework to skip the execution of update, insert, or delete
                PreventDefault();
                // set the focus to the field "Country" and display an error message
                Result.Focus("Country",
                    "You are not authorized to {0}, " +
                    "if the Country is equal to \"USA\".",
                    Arguments.CommandName.ToLower());
                // show an additional information in the message bar at the top of the page
                Result.ShowMessage("Error trying to execute {0} command",
                    Arguments.CommandName);
            }
        }
    }
}

Visual Basic:

Imports MyCompany.Data
Imports System

Namespace MyCompany.Rules
    Partial Public Class CustomersBusinessRules
        Inherits MyCompany.Data.BusinessRules
        ''' <summary>
        ''' This method will execute in any view before an action
        ''' with a command name that matches "Insert|Update|Delete".
        ''' </summary>
        <Rule("r100")> _
        Public Sub r100Implementation(ByVal customerID As String, ByVal companyName As String,
            ByVal contactName As String, ByVal contactTitle As String, ByVal address As String,
            ByVal city As String, ByVal region As String, ByVal postalCode As String,
            ByVal country As String, ByVal phone As String, ByVal fax As String)
            ' This is the placeholder for method implementation.
            If (country = "USA") Then
                ' tell the application framework to skip the execution of update, insert, or delete
                PreventDefault()
                ' set the focus to the field "Country" and display an error message
                Result.Focus("Country",
                    "You are not authorized to {0}, " &
                    "if the Country is equal to ""USA"".",
                    Arguments.CommandName.ToLower())
                ' show an additional information in the message bar at the top of the page
                Result.ShowMessage("Error trying to execute {0} command",
                    Arguments.CommandName)
            End If
        End Sub
    End Class
End Namespace

The pattern of implementation is following closely the pattern of SQL and JavaScript business rules. Both of these alternatives are actually built on top of the same infrastructure demonstrated in this example.

The screenshot shows the “code” business rule in action.

Validation with C# / Visual Basic Business Rules in a web app created with Code On Time web application generator

Monday, July 9, 2012PrintSubscribe
Validation with JavaScript Business Rules

Data integrity is a primary concern of any database application. Data constraints must exist on the database and application server level, while client-side validation will make a web app more responsive and user-friendly.

For, example, consider the following screenshot of the New Order Details form. End users can select any product and enter any price, quantity, and discount. The database engine will validate the constrains of the table Order Details and raise an exception when invalid data is submitted.

A potenially invalid data can be entered by users in the automatically constructed 'New Order Details' form of the web app created with Code On Time web application generator. It is up to the database engine to be last line of defence when enforcing the data integrity. The client side validation could help a lot here.

 JavaScript Business Rules can help avoiding an unnecessary web server round-trip and report a possibility of error to the users. It is also possible to prevent alteration of pricing once it is entered.

Start Project Designer, activate configuration navigator, enter “order details product id” without quotes, and select the first match.

"Navigate To" quickly locates a project configuration element in Code On Time design environment

This will activate Controllers/ OrderDetails / Fields / ProductID field node on the Controllers tab in Project Explorer.

A data controller field selected in Project Explorer of Code On Time web application generator

Enter the following properties and click OK to save the changes.

Property Value
Copy UnitPrice=UnitPrice

Select UnitPrice field of the same data controller and change its properties as follows.

Property Value
The value of the field is calculated by a business rule expression. Yes
Context Fields ProductID, UnitPrice, Quantity, Discount

The configuration of the fields will cause the client library to copy the unit price form the Products lookup to the UnitPrice field of the Order Details data controller. Any changes to ProductID, UnitPrice, Quantity, or Discount will raise an action with Calculate command.

If the Calculate action has not been processed on the client, then the client library will send the entered data to the server to allow processing by server-side business rules. Any modification of the fields mentioned above will result in webserver round-trip, should the implementation be stopped right at this point. Instead, let’s implement a client-side processing of the field values.

Right-click OrderDetails / Business Rules node in Project Explorer and add a new business rule with the following properties. Save the changes.

Property Value
Type JavaScript
CommandName Calculate|Insert|Update
Phase Before
Script Paste the script shown next.

This is the implementation of the business rule that will perform complex multi-step validation.

// 1. If the collected values are not valid then do not enforce the rule.
//    The client library will instruct the user to correct the input.
var success = this.validateInput();
var commandName = this.arguments().CommandName;
var triggerFieldName = this.arguments().Trigger;

// 2. Reset the base price for calculation of price limits 
//    if the product selection has changed or if an existing
//    data row has been selected for editing
if ([ProductID] != this.property('ProductID')) {
    var basePrice = [UnitPrice];
    if (triggerFieldName != 'ProductID')
        basePrice = [UnitPrice.OldValue];
    this.property('UnitPrice', basePrice);
    this.property('ProductID', [ProductID]);
    if (triggerFieldName == 'ProductID') {
        [Quantity] = 1;
        [Discount] = 0;
        this.result.focus('Quantity');
    }
}
// 3. Adjusting base price for an existing record var originalUnitPrice = this.property('UnitPrice'); if (originalUnitPrice == null) { originalUnitPrice = [UnitPrice.OldValue]; this.property('UnitPrice', originalUnitPrice); } // validate Unit Price field if (success) { // 4. Make sure that the price does not exceed the original price by more than 5% var minPrice = originalUnitPrice; var maxPrice = originalUnitPrice * 1.05; success = [UnitPrice] != null; if (!success) this.result.focus('UnitPrice', 'Please enter the price.'); else { success = minPrice <= [UnitPrice] && [UnitPrice] <= maxPrice; if (!success) this.result.focus('UnitPrice', 'The price must be between {0:c} and {1:c}', minPrice, maxPrice); } } // validate Quantity field if (success) { // 5. The quanity must be greater than zero. success = [Quantity] != null && [Quantity] > 0; if (!success) this.result.focus('Quantity', 'The quantity must be greater than zero.'); } // validate Discount field if (success) { // 6. If the value is greater then 1, then convert the value to a percentage if ([Discount] > 1) [Discount] = [Discount] / 100; // 7. Confirm that the the discount is between 0.00 and success = [Discount] != null && [Discount] >= 0.00 && [Discount] <= 0.99; if (!success) this.result.focus('Discount', 'The discount must be between 0.00 and 0.99 (0% - 99%).'); else { if ([Discount] >= 0.15 && commandName.match(/Insert|Update/)) { // 8. Confirm that the a high discount is desired (> 15%) success = this.result.confirm( 'The discount of {0:p} seems a bit high. Proceed?', [Discount]); if (!success) this.result.focus('Discount'); } } } // 9. Wrapping up if (commandName == 'Calculate' || !success) this.preventDefault();
  
JavaScript business rule in OrderDetails data controller of the Northwind sample created with Code On Time web application generator

The business rule script is written in JavaScript. The scrip includes references to the fields of the data controller enclosed in square brackets. The client library automatically creates a JavaScript class with methods composed of the business rules. The field references are replaced with the internal calls to the client library.

Click Browse button on the toolbar, sign in as admin/admin123% and navigate to Customers / Order Details page.

The client-side business rule will be engaged as soon as a new record record is being entered or an existing one is modified. The business rule will react to changes to the fields specified in the Context Fields property of UnitPrice when Calculate action is raised. The field UnitPrice depends on itself. It does not matter, which application field is used to trigger the Calculate action. The only field that cannot trigger the calculation is the ProductID. The Context Fields of a lookup will provide filtering information for the lookup window. Any field names in the Context Fields of ProductID will make the client library treat them as context filters, which will prevent users from seeing any products in the lookup window.

JavaScript Business Rules provide a true abstraction level and separation from the user interface. The business rule is not actively selecting user interface elements to collect input values, which allows flexible modification of the presentation and the rule itself. The same business rule can service any number of data controller views regardless of the view types.

There are nine distinct steps in the business rule.

1. Initial Input Validation

The client library automatically performs basic validation of the entered values. The scripts declares success variable, which is helping to determine if any special validation shall be applied to the fields.

The command causing the rule to execute is stored in commandName variable. The possible values are Calculate, Insert, or Update.

The script also makes a note of the field that has triggered the business rule. The trigger field name will be known only when Calculate action is raised. The possible triggers are ProductID, UnitPrice, Quantity, and Discount.

var success = this.validateInput();
var commandName = this.arguments().CommandName;
var triggerFieldName = this.arguments().Trigger;

This is an example of a basic validation that does not require custom coding.

An example of basic automatic validation by the client library in web app created with Code On Time web application generator

2. Determination of The “Base” Price

The script will memorize the last ProductID and UnitPrice in the properties of the data view instantiated on the browser page. The base price is used to ensure that users do not enter lesser values and to prevent the price inflation by more than 5%. If the product has just been saved, then the script will reset values of fields Quantity and Discount and set the focus on the Quantity field.

if ([ProductID] != this.property('ProductID')) {
    var basePrice = [UnitPrice];
    if (triggerFieldName != 'ProductID')
        basePrice = [UnitPrice.OldPrice];
    this.property('UnitPrice', basePrice);
    this.property('ProductID', [ProductID]);
    if (triggerFieldName == 'ProductID') {
        [Quantity] = 1;
        [Discount] = 0;
        this.result.focus('Quantity');
    }
}

This screen shot shows the form after the product selection.

The business rule assigns default values to Quantity and Discount and sets the focus on Quantity when a product has been selected

3.  Adjusting Base Price for Existing Records

If the user is editing an existing record then the old (original) value of the UnitPrice is memorized in the data view properties.

var originalUnitPrice = this.property('UnitPrice');
if (originalUnitPrice == null) {
    originalUnitPrice = [UnitPrice.OldValue];
    this.property('UnitPrice', originalUnitPrice);
}

4. Price Validation

The script figures the minimum and maximum price that can be entered by the user. If the price is blank, then the user is instructed to enter a value. If the price is out of range, then the user is forced to correct the problem.

var minPrice = originalUnitPrice;
var maxPrice = originalUnitPrice * 1.05;
success = [UnitPrice] != null;
if (!success)
    this.result.focus('UnitPrice', 'Please enter the price.');
else {
    success = minPrice <= [UnitPrice] && [UnitPrice] <= maxPrice;
    if (!success)
        this.result.focus('UnitPrice',
                'The price must be between {0:c} and {1:c}', minPrice, maxPrice);
}

This is the price validation in the form view.

Price validation by a JavaScript business rule in a form view

This is how the price validation is performed in the data sheet view.

Price validation by a JavaScript business rule in a data sheet view

5. Quantity Validation

The script ensures that a positive Quantity is entered.

success = [Quantity] != null && [Quantity] > 0;
if (!success)
    this.result.focus('Quantity', 'The quantity must be greater than zero.');
  

Quantity validation by a JavaScript business rule in a form view

6.  Automatic Conversion of Discount

The Discount column in Order Details table has the Single type. There is also a database constraint that requires the value to be between zero and one. The business rule will help the user by automatically converting the discount to a fraction of 100.

if ([Discount] > 1)
    [Discount] = [Discount] / 100;

A user has entered  a value greater  than one in this screenshot.

User enters the value of 17 that will be rejected by the database constraint

The value is automatically converted when a user leaves the field.

Business rule automatically adjusts the value when user leaves the field

7. Discount Range Validation

If the end user is trying to enter a particularly high discount that remains out of range even after the automatic conversion, then the validation error is displayed.

success = [Discount] != null && [Discount] >= 0.00 && [Discount] <= 0.99;
if (!success)
    this.result.focus('Discount',
            'The discount must be between 0.00 and 0.99 (0% - 99%).');
 

Discount of 1000% is rejected by the JavaScript business rule

8. High Discount Confirmation

If a user has entered a discount of more than 15%, then the business rule will confirm that only when a record is inserted or updated. The Calculate action will not display a confirmation.

if ([Discount] >= 0.15 && commandName.match(/Insert|Update/)) {
    success = this.result.confirm(
            'The discount of {0:p} seems a bit high. Proceed?', [Discount]);
    if (!success)
        this.result.focus('Discount');
}

A confirmation is displayed when a user tries to Insert or Update a record with a discount higher than 15%

JavaScript code is executed in the browser and offers a luxury of having a conversation with the user. The conversational implementation of server-side business rule is very complex due to asynchronous execution of client and server code.

9. Wrapping Up

The default behavior of the client library is to send the collected values to the server for processing by application. The client-side business rule can prevent that. The JavaScript business rule prevents server-side execution of Calculate action. It is also preventing server-side processing of Insert and Update if the the validation has not been successful.

if (commandName == 'Calculate' || !success)
    this.preventDefault();