DataHistory. SoftLock. Creating your own server method.

Mixin DataHistory

Add the ability to create different versions of the same request, which become valid from a certain date.
To do this, in the file req_reqList.meta in the mixins section add the dataHistory mixin {}

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
  "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 + '))'"

       }   
 }

After executing the command in the console
ubcli generateDDL -u admin -p admin -host http://localhost:8881 -autorun
Go to the Request List section and see the 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 UPDATE and DELETE perform 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. Currently, the lockout timeout is 60 seconds.

Because the request list can be at the same time edited by a large number of users, it is necessary to provide for 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 and execute the commandubcli generateDDL -u admin -p admin -host http://localhost:888 -autorun

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

Then open any request at the same time as the administrator and user of the department.
This is how the temporary blocking of record of its "owner"

request_lockedOn

This is the record of someone trying to edit a locked record

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 to use the soft delete function of the database. If false, the entry is permanently deleted from the database table. If true, then the record is marked remote but physically remains in the database table. The default value is false. In the enabled state of this property, the mixin adds the 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 is null.

Make it possible to delete subdepartments so that they remain in the memory of the applications in which they were specified during their action.
In the file req_subDepart.meta in mixins section add the following code and execute the command
npx ubcli generateDDL -u admin -p admin -host http://localhost:8881 -autorun

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

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 on the form related departments will table - 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'),
       }).done(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
            }).done(function(){
				$App.dialogInfo('Requests Reassigned Successfully').done();
            });
                }
              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 = 'RequestList.reassignAll'
Ext.define('RequestList.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'),
        glyph: UB.core.UBUtil.glyphs.faSave,
        formBind: true
    }, {
        ubID: 'btnCancel',
        text: UB.i18n('cancel'),
        glyph: UB.core.UBUtil.glyphs.faTimes
    }],
    
    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