How can you set custom field e.g. UsrSecondType on Account as readonly in Mobile app with Freedom UI?

Like 0

Like

1 comments

If you go to Configurations and search for the object, open it on the left hand side you see business rules then you put conditions in a way where a field that is required is set to filled in and the field that you want read only select that. 


Here color is the field you want to make read only and Job number is a required filled. 

Show all comments

Hello, i want to ask about how to implement code on life cycle Initialized on Mobile Creatio. On Web version, i can use onInitiliazed function, but on mobile i didnt find any possible way to do so. 

In my case, i need to get current month everytime user open the page. 

Anyone can help? 

Like 0

Like

3 comments

Hello,

 

As was stated here, the analog of the onEntityInitialized method from the desktop version is the onLoadRecord method in the mobile application. It should be overridden in the Controller of the corresponded edit page of the mobile application. 

Oleg Drobina,

Hi Oleg Drobina, thanks for answer my question. 
Can i have example of onLoadRecord code, i still have no idea how to use it. 
Thanks

Yosua Aleksander Thanos, 

Hello! 

Here is an example of the controller for the PharmaMobileActivityEditPage, you may modify it according to your business task:

/* globals Activity: false */
/* globals ActivityCategory: false */
Terrasoft.LastLoadedPageData = {
	controllerName: "Terrasoft.configuration.controller.PharmaMobileActivityEditPage",
	viewXType: "fieldforceactivityeditpageview"
};
 
Ext.define("Terrasoft.configuration.controller.PharmaMobileActivityEditPage", {
	alternateClassName: "PharmaMobileActivityEditPage.Controller",
	extend: "FieldForceActivityEditPage.Controller",
 
	statics: {
		Model: Activity
	},
 
	/**
	 * @private
	 */
	createPromotedProducts: function(config) {
		var record = config.records.pop();
		if (!record) {
			Ext.callback(config.success, config.scope);
			return;
		}
		var product = record.get("Product");
		if (!product) {
			this.createPromotedProducts(config);
			return;
		}
		var failureFn = function(exception) {
			Ext.callback(config.failure, config.scope, [exception]);
		};
		var modelName = "ActivityPromProduct";
		var model = Ext.ClassManager.get(modelName);
		model.createWithDefaultValues(function(newRecord) {
			newRecord.set("Activity", config.activityId);
			newRecord.set("Product", product);
			newRecord.save({
				isCancelable: false,
				queryConfig: Ext.create("Terrasoft.QueryConfig", {
					modelName: modelName,
					columns: ["Activity", "Product"]
				}),
				success: function() {
					this.createPromotedProducts(config);
				},
				failure: failureFn
			}, this);
		}, failureFn, this, {isCancelable: false});
	},
 
	/**
	 * @private
	 */
	getPromotedProducts: function(config) {
		var filters = Ext.create("Terrasoft.Filter", {
			property: "Contact",
			value: config.contactId
		});
		var columns = ["Product"];
		Terrasoft.DataUtils.loadRecords({
			modelName: "PromotedProduct",
			filters: filters,
			columns: columns,
			success: function(records) {
				Ext.callback(config.success, config.scope, [records]);
			},
			failure: function(exception) {
				Ext.callback(config.failure, config.scope, [exception]);
			},
			scope: this
		});
	},
 
	/**
	 * @private
	 */
	createNosologiesByProducts: function(config) {
		var filters = Ext.create("Terrasoft.Filter", {
			property: "Product",
			funcType: Terrasoft.FilterFunctions.In,
			funcArgs: config.productIds
		});
		var failureFn = function(exception) {
			Ext.callback(config.failure, config.scope, [exception]);
		};
		Terrasoft.DataUtils.loadRecords({
			modelName: "NosologyInProduct",
			filters: filters,
			columns: ["Nosology"],
			success: function(records) {
				this.createNosologyInActivity({
					activityId: config.activityId,
					records: records,
					addedNosologies: [],
					success: function() {
						Ext.callback(config.success, config.scope);
					},
					failure: failureFn,
					scope: this
				});
			},
			failure: failureFn,
			scope: this
		});
	},
 
	/**
	 * @private
	 */
	createNosologyInActivity: function(config) {
		var record = config.records.pop();
		if (!record) {
			Ext.callback(config.success, config.scope);
			return;
		}
		var nosology = record.get("Nosology");
		var nosologyId = nosology ? nosology.getId() : null;
		if (nosologyId === null || Ext.Array.contains(config.addedNosologies, nosologyId)) {
			this.createNosologyInActivity(config);
			return;
		}
		var failureFn = function(exception) {
			Ext.callback(config.failure, config.scope, [exception]);
		};
		var modelName = "NosologyInActivity";
		var model = Ext.ClassManager.get(modelName);
		model.createWithDefaultValues(function(newRecord) {
			newRecord.set("Activity", config.activityId);
			newRecord.set("Nosology", nosology);
			newRecord.save({
				isCancelable: false,
				queryConfig: Ext.create("Terrasoft.QueryConfig", {
					modelName: modelName,
					columns: ["Activity", "Nosology"]
				}),
				success: function() {
					config.addedNosologies.push(nosologyId);
					this.createNosologyInActivity(config);
				},
				failure: failureFn
			}, this);
		}, failureFn, this, {isCancelable: false});
	},
 
	/**
	 * @private
	 */
	fillVisitToDoctorDetails: function(config) {
		var failureFn = function(exception) {
			Ext.callback(config.failure, config.scope, [exception]);
		};
		this.getPromotedProducts({
			contactId: config.contactId,
			success: function(promotedProducts) {
				this.createPromotedProducts({
					activityId: config.activityId,
					records: Ext.clone(promotedProducts),
					success: function() {
						var productIds = [];
						for (var i = 0, ln = promotedProducts.length; i < ln; i++) {
							var productId = promotedProducts[i].get("Product.Id");
							if (productId) {
								productIds.push(productId);
							}
						}
						if (productIds.length === 0) {
							Ext.callback(config.success, config.scope);
							return;
						}
						this.createNosologiesByProducts({
							activityId: config.activityId,
							productIds: productIds,
							success: function() {
								Ext.callback(config.success, config.scope);
							},
							failure: failureFn,
							scope: this
						});
					},
					failure: failureFn,
					scope: this
				});
			},
			failure: failureFn,
			scope: this
		});
	},
 
	/**
	 * @inheritdoc
	 * @protected
	 * @overridden
	 */
	copyProductsFromAccount: function(config) {
		var activity = config.activity;
		var categoryId = activity.get("ActivityCategory.Id");
		if (categoryId === Terrasoft.Configuration.ActivityCategory.PharmacyVisit) {
			this.callParent(arguments);
		} else {
			Ext.callback(config.success, config.scope);
		}
	},
 
	/**
	 * @inheritdoc
	 * @protected
	 * @overridden
	 */
	onLoadRecord: function(record) {
		var detailConfig = this.getDetailConfig();
		if (record.phantom && detailConfig && record.get("Type.Id") === Terrasoft.Configuration.ActivityTypes.Visit) {
			var categoryId;
			switch (detailConfig.parentColumnName) {
				case "Account":
					categoryId = Terrasoft.Configuration.ActivityCategory.PharmacyVisit;
					break;
				case "Contact":
					categoryId = Terrasoft.Configuration.ActivityCategory.DoctorVisit;
					break;
				default:
					categoryId = null;
					break;
			}
			if (categoryId) {
				var activityCategory = ActivityCategory.Store.getById(categoryId);
				record.set("ActivityCategory", activityCategory);
			}
		}
		this.callParent(arguments);
		var pageHistoryItem = this.getPageHistoryItem();
		var rawConfig = pageHistoryItem.getRawConfig();
		if (rawConfig.focusColumn) {
			setTimeout(function() {
				var field = this.getFieldByColumnName(rawConfig.focusColumn);
				if (!field) {
					return;
				}
				if ("getPicker" in field) {
					var picker = field.getPicker();
					picker.show();
				} else {
					field.focus();
				}
			}.bind(this), 50);
		}
	},
 
	/**
	 * @inheritdoc
	 * @protected
	 * @overridden
	 */
	completeDataSaving: function() {
		var args = arguments;
		var record = this.record;
		var categoryId = record.get("ActivityCategory.Id");
		var contactId = record.get("Contact.Id");
		if (this.isNewVisit && categoryId === Terrasoft.Configuration.ActivityCategory.DoctorVisit && contactId) {
			var activityId = record.getPrimaryColumnValue();
			this.fillVisitToDoctorDetails({
				activityId: activityId,
				contactId: contactId,
				success: function() {
					var parent = Terrasoft.configuration.controller.PharmaMobileActivityEditPage.superclass;
					parent.completeDataSaving.apply(this, args);
				},
				failure: function(exception) {
					Terrasoft.Mask.hide({force: true});
					Terrasoft.MessageBox.showException(exception);
				},
				scope: this
			});
		} else {
			this.callParent(args);
		}
	},
 
	/**
	 * @inheritdoc
	 * @protected
	 * @overridden
	 */
	getCategoryId: function() {
		var activityRecord = this.record;
		var category = activityRecord.get("ActivityCategory");
		return category ? category.get("Id") : null;
	}
 
});
Show all comments

Hi Community, 

 

I have written a business rule that, when a boolean field has been set to true in the mobile app, stores the current timestamp in another separate date/time field: 

 

Terrasoft.sdk.Model.addBusinessRule("Activity", {
    name: "SetTimestampOnCheckForTriggeredField",
    ruleType: Terrasoft.RuleTypes.Custom,
    triggeredByColumns: [
        "InnEndSiteVisitCheck", "InnEndTravelInCheck", "InnEndTravelOutCheck",
        "InnStartSiteVisitCheck", "InnStartTravelInCheck", "InnStartTravelOutCheck"
    ],
    events: [Terrasoft.BusinessRuleEvents.ValueChanged],
 
    executeFn: function(record, rule, column, customData, callbackConfig) {
        // Mapping of boolean fields to their corresponding date-time fields
        var fieldMapping = {
            "InnEndSiteVisitCheck": "InnEndSiteVisitTime",
            "InnEndTravelInCheck": "InnEndTravelInTime",
            "InnEndTravelOutCheck": "InnEndTravelOutTime",
            "InnStartSiteVisitCheck": "InnStartSiteVisitTime",
            "InnStartTravelInCheck": "InnStartTravelInTime",
            "InnStartTravelOutCheck": "InnStartTravelOutTime"
        };
 
        // Get the boolean value of the triggered field
        var isCheckTrue = record.get(column);
 
        // Check if the boolean field is true
        if (isCheckTrue && fieldMapping[column]) {
            // Set the corresponding date-time field to the current date/time
            var currentDateTime = new Date();
            record.set(fieldMapping[column], currentDateTime);
        }
 
        // Asynchronous callback to indicate the rule has been processed
        Ext.callback(callbackConfig.success, callbackConfig.scope, [true]);
    }
});

 

I now want to be able to show a warning message that gives an option to 'cancel' or 'confirm' when a user attempts to set one of these booleans to false. 

 

The box would need to be shown after an attempt to change the booleans value, halting this action until confirmed  - if the action is not prevented before confirmation and the 'cancel' option merely reverts the boolean to being true, the old timestamp would be lost with the new value being representative of the time of cancellation (which is not what I desire).

 

The following post presents a similar requirement: Pop-up with options in mobile application | Community Creatio

 

However, guidance given points to a push notification (which I can't envision helping as I require an option in the app that temporarily halts the setting of the boolean to false), or to use Terrasoft.MessageBox.showMessage, which from what I can tell can only produce a message box (no option to cancel or confirm). 

 

I have also thought about writing individual rules that set each field to be disabled once it has been checked:

 

// Disable the InnEndSiteVisitCheck field after it is checked
Terrasoft.sdk.Model.addBusinessRule("Activity", {
    name: "DisableInnEndSiteVisitCheck",
    ruleType: Terrasoft.RuleTypes.Activation,
    triggeredByColumns: ["InnEndSiteVisitCheck"],
    events: [Terrasoft.BusinessRuleEvents.Load, Terrasoft.BusinessRuleEvents.ValueChanged],
    conditionalColumns: [
        { name: "InnEndSiteVisitCheck", value: true }
    ],
    dependentColumnNames: ["InnEndSiteVisitCheck"]
});

 

However, this is not as flexible as I would want as users may set field values to true by mistake. Even then, the rule I have written does not seem to work as I get a server error when syncing in the mobile app after adding it - perhaps this is because two rules are initiated by the same event? 

Any help on this would be greatly appreciated, thanks in advance!

Like 1

Like

0 comments
Show all comments

Hi there,

 

I am trying to create a mobile app which has custom buttons which, when pressed, record a timestamp. I have created a new workplace in the mobile application wizard and have added the section where I want to include these buttons. When I generate this section, four client modules are created as shown in the image attached. Can I configure these custom buttons in one of these client modules? Some documentation recommends configuration in MobileFUI... client modules but I have no MobileFUI client modules for my new section.
 

 

Thanks 

Like 1

Like

1 comments

Hello,
The process of adding a new button in the mobile app involves creating your own remote module, basically, the list of things you need to do is following:

  1. 1) Create TypeScript project for remote module.
  2. 2) Create a custom request in remote module.
  3. 3) Create custom request handlers using remote module.
  4. 4) Register handler in remote module.
  5. 5) Build project and upload changes to Creatio.
  6. 6) Add button to the page using metadata.
  7. 7) Check functionality.
  8. Also, you need to add and enable EnableMobileSDK feature in the features section.
    An example of adding a custom module can be found in this article. In your case, the code of the remote module should look like this:
  9. import {
    	BaseRequest,
    	BaseRequestHandler,
    	CrtModule,
    	CrtRequestHandler,
    	Logger,
    	CrtRequest,
    	ModalPresenter,
    	bootstrapCrtModule,
    	DoBootstrap,
    } from '@creatio/mobile-common';
     
    @CrtRequest({
    	type: 'crt.MyButtonRequest',
    })
    export class MyButtonRequest extends BaseRequest {
    	public message?: string;
    }
     
    @CrtRequestHandler({
    	requestType: 'crt.MyButtonRequest',
    	type: 'usr.MyButtonRequest',
    	scopes: ['UsrMobileUsrUserModuleRecordPageSettingsDefaultWorkplace_Root'],
    })
    export class UsrMyButtonRequestHandler extends BaseRequestHandler<MyButtonRequest> {
    	public async handle(request: BaseRequest): Promise<unknown> {
    		var message: string | undefined = (request as MyButtonRequest).message;
    		if (message) {
    			Logger.console('Request message: ' + message);
    			ModalPresenter.showSnackBar('Message title', message);
    		}
    		return this.next?.handle(request);
    	}
    }
     
    @CrtModule({
    	requestHandlers: [
    		UsrMyButtonRequestHandler,
    	],
    })
    export class UsrModule implements DoBootstrap {
    	bootstrap(): void {
    		bootstrapCrtModule('UsrModule', UsrModule);
    	}
    }

The difference with the article above is a way to add a button. In order to do so in one of the schemas you showed (depending on where you want to locate a button) you need to add a code of the button to a viewConfigDiff:

{
	"operation": "insert",
	"name": "settings",
	"values": {
		"entitySchemaName": "UsrUserModule",
		"details": [],
		"columnSets": [],
		"localizableStrings": {
			"SocialMessageDetailCaptionUsrUserModule_caption": "Feed",
			"AttachmentsDetailCaptionUsrUserModule_caption": "Attachments",
			"primaryColumnSetUsrUserModule_caption": "General information"
		},
		"settingsType": "RecordPage",
		"operation": "insert",
        "viewConfigDiff": "[{\"operation\": \"insert\", \"name\": \"MyButton\", \"parentName\": \"UsrUserModule_PrimaryTab_Body_primaryColumnSet\", \"propertyName\":\"items\", \"values\": {\"type\":\"crt.Button\",\"caption\": \"Click here\", \"color\":\"primary\",\"clicked\":{\"request\": \"crt.MyButtonRequest\", \"params\": { \"message\": \"Some test message\" }}}}]"
	}
}
Show all comments

Hello,
I have a slightly urgent question on how to get Address from Account lookup
the code lookslike this
Terrasoft.sdk.Model.addBusinessRule("Activity", {

  name: "LatLongPreFilled",

  ruleType: Terrasoft.RuleTypes.Custom,

  triggeredByColumns: ["Usrcheck_in"],

  events: [

    Terrasoft.BusinessRuleEvents.ValueChanged,

    Terrasoft.BusinessRuleEvents.Save,

  ],

  executeFn: function (model, rule, column, customData, callbackConfig) {

    if (navigator.geolocation) {

      navigator.geolocation.getCurrentPosition(

        function (position) {

          model.set("Usrcheck_in_longitude", position.coords.longitude, true);

          model.set("Usrcheck_in_latitude", position.coords.latitude, true);


 

          var account = model.get("Account").get("Address");


 

          alert(account);

          Ext.callback(callbackConfig.success, callbackConfig.scope);

        },


 

        function (error) {

          alert("Geolocation failed: " + error.message);

          Ext.callback(callbackConfig.success, callbackConfig.scope);

        }

      );

    } else {

      alert("Geolocation is not supported by this browser.");

      Ext.callback(callbackConfig.success, callbackConfig.scope);

    }

  },

});


i use this: var account = model.get("Account").get("Address"); 
but when i test it, displayed on an alert it says "undefined", but this model.get("Account").get("Id"); or model.get("Account").get("Name"); works fine.
Can anyone help me out? 

Thank you.  

Like 1

Like

3 comments

Hello,

 

Please check if the Address column is present in the SyncColumns array of the Account entity in the ModelDataImportConfig array of the mobile application manifest. It seems that the column is not synced so you receive undefined in the debugger.

Oleg Drobina,


Hi Oleg,
thanks for the reply,

should i add manually on Mobile manifest file or via Mobile Application Wizard?
PS: i don't use debugger like console.log, i use javascript's alert function since i use iOS and i have no clue on how to debug mobile apps on a smartphone, especially iOS.

Yanuar Adinagoro Vishnu Saputro,

 

It can be either manually or you can add the Address column to the account edit page and the column should be added to the manifest automatically in this case.

Show all comments

It is possible to change the name of the mobile app?

 

Can be rename with the name of the company instead of "Creatio"?

 

Thanks 

Like 0

Like

2 comments

Hello,

 

While it is not possible to change the name of the mobile app directly, you can use Mobile Branding to customize your mobile application.

 

You can find more information about Mobile app Branding in this article: https://academy.creatio.com/docs/8.x/dev/development-on-creatio-platfor…

Thanks for the tip

Show all comments

Hi Team,

 

We have installed the Field Management (Open Street Map) in our instance map configurations is working as we expected in web app but same thing we configured in the Mobile app, fields are displaying but the map UI is not showing in mobile.

And we have tried to install the Field Sales connector, DB error occurred while installing please help us to achieve the Mobile map configuration.

 

Error log on Filed Sales:

 

2024-09-23 14:09:41,557 Error occurred while installing data "SysModuleInWorkplace" in package "FieldForce". UId 5f69e597-9acb-4852-9f3d-e4d278c7b02b: The UPDATE statement conflicted with the FOREIGN KEY constraint "FKd5jrT2KFY0lXcOnqZCi4dxhomfE". The conflict occurred in database "Creatio8DB_GL_250424", table "dbo.SysModule", column 'Id'.

The statement has been terminated.

 

Thanks,

Prem

Like 0

Like

4 comments
Best reply

Prem Kumar,

Hi!

This is not currently available in the mobile app.
You can inspect the Account address detail as an example of integration with OpenStreetMaps in the system and Field Force application as an example of maps functionality on mobile apps. 

And also, 

You can find information in these posts:

https://community.creatio.com/questions/account-address-map-mobile-application

https://community.creatio.com/questions/maps-mobile-application

 

Best regards,

Anton

You look like to be dealing with two different problems. It can be a platform-specific usability or setup problem with the mobile app map user interface. Examine any settings unique to mobile devices and see if the mobile map feature is properly supported. Regarding the Field Sales connector DB error, there can be an oversight or a problem with database compatibility. In addition to making that the connector is compatible with the version of your system, check the logs for detailed problem messages.

Hi Joshua,

 

We have the requirement to build map functionalities on web and mobile app it is like a (google map) for that we have implemented on web applications using  Field force connector it is working as we expected in web app and same things we configured on the mobile application wizard but we are not able to see the map in mobile as in web app. Please find the snap for the reference for map UI in web app, we are expecting the Open Street Map hyper link in mobile.
 

Web app Map Snap
 

Prem Kumar,

Hi!

This is not currently available in the mobile app.
You can inspect the Account address detail as an example of integration with OpenStreetMaps in the system and Field Force application as an example of maps functionality on mobile apps. 

And also, 

You can find information in these posts:

https://community.creatio.com/questions/account-address-map-mobile-application

https://community.creatio.com/questions/maps-mobile-application

 

Best regards,

Anton

Anton Starikov,

Hi Anton,

 

We have tried above mentioned approaches also but we couldn't get exact UI  as we expected  on mobile app.

We are expecting like a google map in mobile, it is used for delivering the Freight to the customer place. Please help us to achieve this functionalities in mobile.

Thanks,
Prem

Web app Map Snap

 

Show all comments

Hello, I recently started working with Creatio Mobile. I have downloaded the files and was able to create the Creatio APK through the SDKConsole utility. I have a question: how can I add a page within the application? I see that most of the app is based on WebView. My goal is to import a library from Pub.dev (in my case, call_log) and implement it to display the data on a page within this app.

If anyone could help me, I would be very grateful. Thank you.

Like 0

Like

1 comments

Hello,
In the mobile application there is an abillity to open custom JS pages using the handler logic, an example of such handler:

const openCustomPageRequest: OpenCustomWebViewPageRequest = {
   type: 'crt.OpenCustomWebViewPageRequest',
   $context: request.$context,
   controllerName: 'Terrasoft.controller.MobileSyncLogPage',
   viewXClass: 'Terrasoft.view.MobileSyncLogPage',
   parameters: {
       "customParam": 1
   }
};
HandlerChainService.instance.process(openCustomPageRequest);

controllerName - class name of JS controller 
viewXClass - class name of JS view 
parameters - additional parameters of custom page. To get the parameter call this method inside the controller this.getPageHistoryItem().getRawConfig().customParam

This handler should be added via the remote module (see this article) and using the @creatio/mobile-common library enabling the feature EnableMobileSDK in the Features page.

Show all comments

Hi Team,

 

My team and I have been assigned to a project this month, and one of the requirements is to implement a QR code scanner in the mobile app. So far, I've been able to open the camera using `Ext.device.Camera.capture`, but it doesn't scan anything. I also came across the documentation mentioning Cordova, and I tried using the Cordova barcode scanner plugin, but that didn't work either.

 

Could anyone please provide any additional documentation or articles related to this issue? Specifically, it would be helpful if there are resources on how to use the Cordova plugin or how to develop a custom widget using Flutter.

Like 1

Like

2 comments

Also interested, lots of clients are interested in using QR code (for example, event registrations, or orders etc...), currently use of QR code is limited.

Hello,

Our R&D team has a task to implement a QR scanner in the mobile application. The task is still in progress on their end and we expect this functionality to appear in the app in the next application releases. Currently,  we do not have any examples of such implementation. 

You can use all the provided examples of customizations in mobile applications that we have provided on the Academy: https://academy.creatio.com/docs/8.x/dev/development-on-creatio-platform/category/mobile-app-development.

Show all comments

When making changes to the section list and/or section pages for the mobile app, should I create a new packages or is it ok to leave the current package as "custom" and make the changes via the mobile application wizard?

Like 0

Like

1 comments

Hi!

It is normal to leave the current package as "custom". You can change the package only if you want to keep some functionality in a separate package. 

Best regards, 
Anton

Show all comments