DataHistory. SoftLock. Creating your own server method. #

DataHistory #

This mixin has the following properties:

  • mi_data_id - contains a copy of a record with a several version
  • mi_dateFrom - record availability start time
  • mi_dateTo - record availability finish time

It allows to add the ability to create different versions of the same request, which become valid from a certain date.

To use it add the following code in the mixins section in the req_reqList.meta file.

"mixins": { 
    "mStorage" : { "simpleAudit": true },
    "dataHistory" : {}, //add attributes mi_data_id, mi_date_From, mi_date_To to entity
    "rls": {
	    "expression": "'(' + $.currentOwner() + ' OR ' + '[department.roleInSystem] in (' + Session.userRoles + '))'"
    }
}

Execute the following command in the console:

ubcli generateDDL -u admin -p admin -host http://localhost:8881 -autorun

Then go to the Request List section to see new items in the context menu of the entries.

dataHistory_menu

New version suggests specifying a new date (it should be greater than the current date) of the document's relevance and changing the data to new ones - they will not be displayed in the current version.

Changes history allows you to see the versions of documents and edit them.

dataHistory_versions

SoftLock #

Mixin softLock is responsible for the logical blocking of EDITING entity records.
The correct name is Pessimistic Lock (pessimistic lock).

It's useful in situations where you need to lock the document before editing so that other users can't modify it at the same time as you. Then, after the end of editing, the lock is removed.

❗ It should be noted that the server will NOT let perform UPDATE and DELETE on records that are not locked by the user!

There are 2 types of locking:

  • Persist/ltPersist - Permanent blocking. There is an unlimited amount of time until the command comes to remove the lock from the user who supplied it.
  • Temp/ltTemp - Temporary blocking. Valid as long as the user does not remove it or take some time out from the start of the lock. The default lockout timeout is 60 seconds.

Since the request list can be edited by a large number of users at the same time, it is necessary to provide the blocking of the record which is now "occupied" by another user.

Add the following code to the mixins section of the file req_reqList.meta:

    "softLock": {
        "lockIdentifier": "ID",
        "lockEntity": "req_reqList"
    }

and execute the command:

ubcli generateDDL -u admin -p admin -host http://localhost:888 -autorun

Then open any request at the same time as the administrator and user of the department.

This is how the temporary blocking of the record looks for its "owner":

request_lockedOn

This is how the locked record looks for someone who trying to edit it:

record_locked.png

Persist/ltPersist blocking is set by the user in manual mode in the recording settings menu.

persist_lock

This view has blocked entries in the Lock Manager in the Misc / Blocking (SoftLock) admin menu.

softLock_management

The administrator can manually unlock the document.

SafeDelete #

safeDelete is a boolean flag that allows to use the soft delete function of the database.

  • If it false, the entry is permanently deleted from the database table.
  • If it true, then the record is marked as remote, but physically remains in the database table.

The default value is false.

When this flag is enabled, the mixin adds the following service attributes to the entity:

  • mi_deleteDate – Date-time of soft removal of an entity record.
    By default, the value of this attribute is the date 31.12.9999.
    This is the value that indicates that the entry was NOT deleted.
    This field participates in many unique indices of the entity.
  • mi_createUser – The identifier of the user who deleted the entry in an entity.
    The attribute type - 'Entity' refers to the entity uba_user.
    The default value is null.

Let's make it possible to delete SubDepartments so they remain in the memory of the applications in which they were specified.

In the file req_subDepart.meta in mixins section add the following code:

"mixins": { // the mandatory section (can be empty)
    "mStorage": {
	    "safeDelete": true //add attr mi_deleteDate and mi_createUset to entity
    }
 }

and execute the command:

ubcli generateDDL -u admin -p admin -host http://localhost:8881 -autorun

Now, create a new temporary department in the SubDepartments menu.

tempDep_creating

Create a new request with it.
Then, delete that department and re-enter request in the editing mode (the name of our temporary department will be crossed out since it doesn't exist anymore).

safeDeleting_department

Creating your own functionality. #

Create an additional functionality for managing applications for the administrator role.

Suppose that the form related to departments has a table that contains the list of requests for a particular department.
In the toolbar, add the button - Reassign all applications. By clicking on the button a modal form of department selection appears, to which the list of applications from the current one will go. After the selection is sent to the server, which makes the appropriate changes to the database or reports the reasons for the impossibility of changes.

In INTERFACE DEFINITION of req_depart form change the code to the next one:

//@! "do not remove comments below unless you know what you do!"
//@isDefault "false"
//@entity "req_depart"
//@formType "auto"
//@caption "Departments"
//@description "Departments"
exports.formDef = {
    dockedItems: [{ //add method button
		xtype: "toolbar",
		dock: "top",
		items: [{
			orderId: 5,
			actionId: "ActionReassignedAll" 
		}]
	}],
	items: [
			{
		layout: 'hbox',
		items: [{
                items: [
                    {attributeName: "ID" /**/},
                    {attributeName: "name" /*Department Name*/},
                    {attributeName: "roleInSystem", width:300 /*Role in System*/}
                   ]
		     }, {
                items: [
                     { attributeName: "postAddr" /*Department Address*/},
                     { attributeName: "phoneNum" /*Department Phone*/}
                ]
		     }]
	},
         { //SubDepartment Detail Grid
                    xtype: "ubdetailgrid",
            		title:'Subdepartments',
                    entityConfig: {
                        "entity": "req_subDepart", //assotiated entity
                        "method": "select",
                        "fieldList": [ // displayed fields
                            "ID",
                            "name"
                        ]
                    },
                    masterFields: ["ID"], 
                    detailFields: ["department"] //assotiated field
                },
        { //Request List detail grid
                    xtype: "ubdetailgrid",
            		title:'Request List',
            		entityConfig: {
                        "entity": "req_reqList", //entity to view
                        "method": "select",
                        "fieldList": [ // fields to view
                            "ID",
                            "reqDate",
                            "status",
                            "reqText",
                            "answer"
                        ]
                    },
                    masterFields: ["ID"], 
                    detailFields: ["department"] //filter field
                }
	]
};

In METHOD'S DEFINITION add the following code:

 exports.formCode = {
       addBaseActions: function () {
        this.callParent(arguments);
        this.actions.ActionReassignedAll = new Ext.Action({ 
            actionId: "ActionReassignedAll",
            actionText: UB.i18n('Reassigned all requests'),
            handler: reAssignAll.bind(this)
        });
    },
     initUBComponent: function () {
		var me = this; 
         me.getField('ID').readOnly = true;
	}
};
function reAssignAll() {
var form = this;
    $App.showModal({
        formCode: 'req_reqList-reassignAll',
        description: UB.i18n('Reassign to'),
       }).then(function (result) {
         if (result.action === 'ok') {
            if(result.newDep != form.getField('ID').getValue())
            {
             $App.connection.query({
                       entity: 'req_depart',
                       method: 'reassignDep',
		       curDep: form.getField('ID').getValue(),
                       newDep: result.newDep
            }).then(function(){
				$App.dialogInfo('Requests Reassigned Successfully').then();
            });
                }
              else {$App.dialogError('Departments must be different')}
        }
    }); 
}

The reAssignAll () function calls a modal form with the code req_reqList-reassignAll.

Create a new form of the Pure ExtJS form type.

reassignAll_form_creating

Forms of type ExtJS are described only in the INTERFACE DEFINITION section, along with interface and methods. This form is used to select the department from the drop-down list and send the selected value (newDep identifier) to the calling reAssignAll function in the parent window. Add the following code to the section

//@! "do not remove comments below unless you know what you do!"
//@isDefault "false"
//@entity "req_reqList"
//@formType "custom"
//@caption "Reassingn to"
//@description "Reassign to other departments"
exports.formDef = 'requests.reassignAll'
Ext.define('requests.reassignAll', {
    extend: "Ext.form.Panel",
    size: {
		width: 500,
		height: 600
	},

    items: [
        {
  			xtype:'ubcombobox',
            width:400,
            name:'cbxDep',
  			fieldLabel: UB.i18n('department'),
  			allowBlank: false,
  			ubRequest: {
              entity: 'req_depart',
              method: 'select',
              fieldList: ['ID','name']
  				}
 		}
    ],
    buttons: [{
        ubID: 'btnOK',
        text: UB.i18n('ok'),
        iconCls: 'fas fa-save',
        formBind: true
    }, {
        ubID: 'btnCancel',
        text: UB.i18n('cancel'),
        iconCls: 'fas fa-times'
    }],
    
    initComponent: function () {
        var me = this;
        me.callParent(arguments);

        me.down('button[ubID="btnOK"]').handler = function(){
            me.commandConfig.deferred.resolve({
                action: 'ok',
                newDep: me.getForm().findField('cbxDep').getValue()
               });
            me.up('window').close();
        };

        me.down('button[ubID="btnCancel"]').handler = function(){
            me.commandConfig.deferred.resolve({
                action: 'cancel'
            });
            me.up('window').close();
        };
    }
});

If done correctly, the form req_depart will look like:

reassignTo_dialog

At this stage, the form of selection (from the list of departments) checks whether the selected department is equal to the current one only on the client side.
To change the data on the server side, you need to describe the server method "reassignDep", which is specified in the url parameter of the function $App.connection.xhr()

Create the req_depart.js file in the models\requests folder and add there the following code:

const UB = require('@unitybase/ub')
var me= req_depart;
me.entity.addMethod('reassignDep');

/**
 * Reassign all departments form params.curDep to params.newDep
 * @param {ubMethodParams} ctxt
 */
function reassignDep(ctxt){
	"use strict";
	var
        params = ctxt.mParams,
        curDep = params.curDep,
        newDep = params.newDep;

       if(!$.currentUserInGroup(ubm_desktop,'admins')){
	    throw new Error('You don`t nave permission for this action');
        } 
	if (!curDep) {
            throw new Error('curDep parameter is required');
        }
        if (!newDep) {
            throw new Error('newDep parameter is required');
        }
	if (curDep === newDep) {
            throw new Error('curDep and newDep parameters must be different');
        }
	var store = UB.DataStore('req_reqList');
	var updStore = UB.DataStore('req_reqList');
 	UB.Repository('req_reqList').attrs('ID','department','subDepartment','mi_modifyDate').where('department', '=', curDep).select(store);
	while(!store.eof) {
                updStore.run('update', {
            	entity: 'req_reqList',
		lockType: 'Temp',
	        execParams: {
                department: newDep,
		subDepartment: null,
		ID: store.get('ID'),
		mi_modifyDate: store.get('mi_modifyDate')
            	}
        	});
	store.next();
}                
}
me.reassignDep = reassignDep;

Restart the UB server and make sure that created functionality works correctly.
Next step