** It is not recommended to read Manual on phones and smaller screen tablets, since you may not enjoy full user experience.
BASIC
⇨Input requirements:
• Advanced computer knowledge (basic programming skills)
• Experience in Alchemy as Configuration Administrator (Admin)
Welcome to the Alchemy Script User Manual!
This document will be your personal guide through the Alchemy Script language, a tool that enables you to harness the power of Alchemy Script in order to automate parts of processes and data entry, data visibility, or data calculations reflected in the Alchemy User Portal.
As a Configuration Administrator (Admin), you have the possibility to add Alchemy Script Expressions to the process and record templates to fit the needs of your organization using the Configuration Portal. In the User Portal, configured templates will allow members of your organization (Users) to capture their work in a structured and predetermined way.
This manual will help you understand where and how to write Alchemy Script Expressions, and learn all the best practices.
Throughout the Configuration portal Manual, you had the chance to see different alchemy script expressions in examples and explanations.
Alchemy Script Expressions can be used in the record templates
Alchemy Script Expressions can be used in the process templates
Before we see how to write expressions, we need to define the types of data to be used in expressions:
Elements of alchemy script expressions can be
Possible elements of the formula expressions are graphically represented in Figure 1.
Constants are values (not fields) that are entered directly into the Alchemy Script Expression and do not change when the context is changed.
They can be:
Note that other data types (mainly references) cannot be used as constants, as such data always depends on the context in the user portal.
As variables in Alchemy Script Expressions we can use field identifiers from record templates or system data fields. The system evaluates variables every time the calculation is triggered.
Each field type value results in already defined data types:
To use fields from a record in the same record:
VariableFieldIdentifier
In the record Request, a requestor fills in the number of samples needed in the Lab Work. This is configured using a field named Number of Samples (type: number, identifier: NumberOfSamples). Each sample needs to be tested three times. In the same record template create a field Number of Tests (type: number, identifier: NumberOfTests). In the Formula Expression of this field write
NumberOfSamples * 3
This will pull the number value entered in the field Number of Samples, multiply it by 3 and the resulting value of the field Number of Tests will be automatically calculated.
When you want to use a variable (a field value), from one record, in an expression placed in a different record, you will need to access them through record references. In general, you can access the field using the following expression:
In the Request record template there is a field name Number of Samples (type: number, identifier: NumberOfSamples). A person who fills in the Request sets how many samples are needed. In the record template Test Results, a Chemist who performs the tests and enters the results needs to have the same value of the number of samples needed. You need to pull that value from the record Request to the record Test Results. To achieve this you need to:
- In the record template Test Results, create a record type field (identifier RelatedToRequest), and for record reference choose the record template Request. This field serves as a “door” to enter the record Request data from the record Test Results.
- Create a number type field where you will pull the data. In the Formula Expression of this field write
RelatedToRequest.NumberOfSamples
You can further use this expression in calculation formulas like in the previous example (not just pulling the data, but calculating further with the pulled data).
You can access all records created in the scope of the current process template using Record[RecordTemplateIdentifier] in an expression:
Record[RecordTemplateIdentifier].VariableFieldIdentifier
Record - Keyword representing which concrete record should be accessed. Records are sorted by the time of their creation. By using [RecordTemplateIdentifier] in expression, Alchemy will look for and take the value of the last created record in the same process.
If the field is in the record template that is used in multiple stages, you need to specify which stage you want to pull the data from by adding the Stage[StageName] part to your expression
Stage[StageName].Record[RecordTemplateIdentifier ].VariableFieldIdentifier
In the Characterization stage you made a field for the number of samples that are needed. The name of the field is Number Of Samples, the identifier is NumberOfSamples. This field is a number type. The number of samples will determine how many rows will be available in the characterization table. To do this, make a repeatable row group and put it on top of input fields as already explained in the chapter How to make a table in Alchemy. In the Repeatable Option field of the group advanced settings, select Determined by Formula. In the Formula Expression write the expression NumberOfSamples.
The second possible solution is that you defined the number of samples in the stage Request in the record Request Form. In that case in the Formula Expression for the repeatable group Determined by Formula write
Stage[Request].Record[RequestForm].NumberOfSamples.
To access and perform calculations with fields located in a record in another process you will need to configure a field where the users can select either the record reference directly (record type field) or a process reference from where you want to access the record (process type field).
In case you configure the record field, its identifier will be used as the record reference in your expressions:
RecordFieldIdentifier.VariableFieldIdentifier
To access the value of the pH field(identifier ProductpH) entered in the record QC Profile in the process Register Product (marked with a star in the scheme) you need to configure the following two fields in the record QC Result of the process Quality Control:
1 - record type field (marked with 1) with identifier FromQCProfile
2 - number type field (marked with 2) with calculation FromQCProfile.ProductpH
In case you use a process field in your configuration, you must add its identifier in the expression
ProcessField.Record[RecordTemplateIdentifier].VariableFieldIdentifier
This expression behaves the same as explained earlier, with the difference that the records will be pulled from the process selected in the ProcessField field. See the examples below.
In the Alchemy Lab, Chemists are using the process template Register Product to input all details about lab products they get from their formulation outcomes. The Lab Manager is using the process template Sample to prepare samples of products, finish the administration work and send them out to customers. Inside a Sample process, the Lab Manager needs to pull some data from a Register Product process. Schemes of both process templates are shown in the figure below. In the process template Register Product there are multiple stages and records, but the field from which the Lab Manager needs to pull data from is called pH (Name: pH, Identifier: pH). This field is located in the stage Product Information in the record Chemical Characteristics. Both, the process template and record template need to be referenced in Lab Manager's Sample in order for the system to recognize from where to pull the data, especially because the record template Chemical Characteristics is used across the organization in multiple process templates (NPD, ARM).
Fields that are needed in the Sample process template are
This field needs to have a Formula Expression ChooseProduct.Record[ChemicalCharacteristics].pH
These fields are in the Sample process template, stage Request, record template Request Form as seen in the figure below.
In case there may be multiple records of the same template in the process you are referencing, it is advisable to create a new record field, where the user can choose which record to pull the data from.
With the same example as above, when there may be multiple Chemical Characterization Records in the Register Product Process, it is advisable to have an intermediate field where the user can select which of the characterization records to pull the data from.
Fields that are needed in the Sample process template are
A field which has the same field type as the field from which we are pulling data from. Field pH is a number field type so this field also needs to be a number field type. The field where the data will be pulled has the Name: Product pH and identifier: ProductpH. This field also needs to have a Formula Expression: CharacteristicType.pH
As you have seen already, it is possible to "connect" multiple record and process references in one expression. For example, it is possible to access the system information about the process where the record was created using the expression:
RecordVariable.Process
Where RecordVariable is the identifier of your record field. This can further be expanded to access the process system fields, for example:
RecordVariable.Process.Status
will return the string representing the status of the process in which this record is created. This could be useful filter criteria, i.e. when we only want to be able to select formulation records in ongoing lab work processes.
While the multiple "dot expressions" are useful, they should be used sparingly, as more complex expressions may impact performance.
ThisField makes it possible to reference a field in its own calculation by enabling calculations to check the state of the field before any recalculation is done, giving the user the possibility to calculate values only for newly created rows in tables.
Data generated by the system operation is available to be used in expressions. System data can provide more information about a process, record, stage run or a user. They can be accessed by using specific keywords listed in the next chapters.
You can get some data about the process from the system fields listed below using the keyword Process or the process template identifier. In order to get process data you need to create a field (alphanumeric, number, datetime or user field, based on the type of data from the system field that you want) in which you will write the expression in the Formula Expression field. The process data that you want can be related to the current process or some other process in the system:
1. If you want the system data from the current process, you will write Process.SystemFieldIdentifier in the Formula Expression of the created field, where Process is the keyword for the current process instance.
If you make an alphanumeric field and write in the Formula Expression: Process.Status, as an output you will get one of the four available statuses of the current process (In Progress, On Hold, Completed, Voided) as a string.
2. If you want the system data from another process, you need to create a process field type where the User on the User Portal will choose the exact process. Then you need to create the field (alphanumeric, number, datetime or user field, based on the type of data from the system field that you want) in which you will write ProcessFieldIdentifier.SystemFieldIdentifier in Formula Expression, where ProcessFieldIdentifier is the identifier of the process type field.
To build on the previous example, before the alphanumeric field, you would need to make a process field type (identifier ChooseProduct) where you can choose the other process from which you would like system data information. Then, in the alphanumeric field you have made, in the Calculation formula write: ChooseProduct.Status, as a result you will get the status of the process you have chosen in a string.
System fields that could be placed in the Formula Expression of the alphanumeric field - their output is a string:
System fields that could be placed in the Formula Expression of the number field - their output is a number
System fields that could be placed in the Formula Expression of the datetime field - their output is date and time:
System fields that could be placed in the Formula Expression of the user field - their output is the user's first and last name:
You can get any of the data about the stage run from the system fields listed below using the stage identifier. You need to create a field (alphanumeric, number, date/time or user field, based on the type of data from the system field that you want) in which you will write the expression in the Formula Expression field.
System fields that could be placed in the Formula Expression of the alphanumeric field - their output is a string:
System fields that could be placed in the Formula Expression of the yes/no and checkbox fields, their output is a boolean:
System fields that could be placed in the Formula Expression of the number field - their output is a number:
System fields that could be placed in the Formula Expression of the datetime field - their output is date and time:
System fields that could be placed in the Formula Expression of the user field - their output is user's first and last name:
CompletedBy : user that has completed the stage run
In the Lab Work Process after the Application Testing the flow is continuing to Lab Work Review. In the Lab Work Review you may want to know the effort spent in the previous stage, the Application Testing (identifier: ApplicationTesting). In the Lab Work Review you can make a field, number type, with the Formula Expression: Stage[ApplicationTesting].TotalEffortSpent. This formula will output the total effort spent on the stage in hours.
You can get some data about the record from the system fields listed below using the record field identifier or record template identifier. You need to create a field (alphanumeric, number, date/time or user field, based on the type of the data from the system field that you want) in which you will write the expression in the Formula Expression field. You can access the record data that you want if the record is in the current process or if the record is from another process:
System fields that could be placed in the Formula Expression of the alphanumeric field - their output is a string:
System field that could be placed in the Formula Expression of the datetime field - its output is date and time:
System field that could be placed in the Formula Expression of the user field - its output is the user's first and last name:
System field that could be placed in the Formula Expression of the process field - its output is the process reference:
System field that could be placed in the Formula Expression of the number field - its output is the number:
Id: unique identification number of the record (visible in the URL also)
In the stage Formulation, there are multiple records: Formulation 1, Formulation 2, Formulation 3. Their record template identifier is FormulationRecord, and they are created respectively. In the stage Lab Work Review you may want to access system data for the last Formulation record created in the process. Make a datetime field type in the Lab Work Review stage, in the Formula Expression write:Stage[Formulation].Record[FormulationTemplate].CompletedOn, this gives you the information about the date and time when the last Formulation Template record was completed.
You can get some of the data about the users from the system fields listed below using the user field type identifier. First, create a user field type. In order to get user data, you need to create an alphanumeric field in which you will write in the Formula Expression field: UserFieldTypeIdentifier.SystemFieldIdentifier
System fields that could be placed in the Formula Expression of the alphanumeric field type:
You can combine multiple system data information. In the Request Review stage, you can have a field that will give you the information on who completed the stage Technical Call. Make a user field type (identifier: TechnicalCallResponsible) in the Request Review stage (Approval record), in the Formula Expression write: Stage[TechnicalCall].CompletedBy, according to the information from that stage and the field TechnicalCallResponsible, you can get the information about the user's email. Make an alphanumeric field type in the Request Review stage, in the Approval record and in the Formula Expression write: TechnicalCallResponsible.Email.
Use square brackets when you want to access the record from the current process the user is working in. When you want a user to have a dropdown list in the User Portal and to choose a record that they need from the process where they are at that moment, you need to make a link using the record reference field.
Complex expressions are possible by "dotting" record and process variables together, allowing you to access data from different processes and records.
To summarize - accessing system data can be done by following the logic below:
This approach can be used for all the Process data.
To access a specific stage run:
Record[Approval, last].CreatedOn - will access the last record of type Approval in the whole process
When you are trying to access data from a record that is in the current process you are in, make sure that the record template of that record is added to the process template of the process you are currently in. This is the case when you are using square brackets in the formula expressions.
Operators are symbols or expressions that use two inputs, one on each (left and right) side of the operator. Spaces are allowed between the inputs and the operators. If an operation is defined in an expression on an unsupported type, an error is generated. Below you can find the lists of operators.
AND - operator checks two Boolean expressions and only returns true if both of the expressions are true.
Examples:
(6 > 3) AND (2 < 5)
- The output is true because both of the expressions are true.
(‘text’ == ‘tekst’) AND (3 < 5)
- The output is false because only the second expression is true.
The Lab Manager wants to continue from the Lab Work to the Report stage. In the Lab Work Review stage there are two questions at the end. One question is: More formulations needed? (field type Yes/No, identifier: MoreFormulations) and the other is: Is the review finished? (field type Yes/No, identifier: ReviewFinished). The first question needs to be false and the second question true to transition to Report. The transition condition set up in the When field of the Actions for the stage will be:
MoreFormulations == false AND ReviewFinished == true
OR - operator combines two Boolean expressions and returns true if at least one of the expressions is true.
Examples:
(ProductPrice < ProductRecommendedPrice / 2) OR (ProductPrice > 0)
- The value of field ProductPrice equals 3, and you have another field called ProductRecommendedPrice whose value equals 2. The expression will return true because at least one of the expressions is true (the second one). Note that the expression would still return true if both statements had been true.
('text' == 'nottext') OR ('ciao' == 'stillnotciao')
- The expression will return false, because both expressions are false.
When using the logical operator OR, if the left operand is true, the right operand is not evaluated.
NOT - operator negates the specified Boolean expression. Returns true if the operand is false, false otherwise.
Example:
NOT('text' == 'hallo')
- The expression will return true.
NOT(true)
- The expression will return false.
IN - Checks whether all the values of the left operand are all contained in the right operand. Returns true if all the values present in the left operand are contained (are a subset) of all the values in the right operand. Otherwise, false is returned. The Left and right operand can be a single constant, enumerated constants in brackets, a single field, a single field with multiple values (multiple, column, or multiple columns), or an enumerated field in brackets.
Example:
'Liquid' IN TypeOfSample
- The expression will return true if one of the values for the field TypeOfSample is 'Liquid'
NOT IN - Negation of IN. Returns true if all the values present in the left operand are not contained (are not a subset) of all the values in the right operand. Otherwise, false is returned. Left and right operands can be a single constant, enumerated constants in brackets, a single field, a single field with multiple values (multiple, column, or multiple columns), or an enumerated field in brackets.
In the Formulation stage there is a list of formulations that needs to be made. That list is in the table (repeatable row group). The second column is Status (alphanumeric field with the predefined values, identifier: Status) - status of making a formulation can be ‘Done,’ ‘In Progress’ or ‘Pending’. Below that table, there is a checkbox that says: "All formulations are done". Chemists are able to go to the Characterization stage only when all formulations are done and the checkbox is checked.
You will make that possible by writing this expression in the Formula Expression of the checkbox "All formulations are done":
IFS(('In Progress' NOT IN Status AND 'Pending' NOT IN Status), true, true, false).
This calculation formula will check the checkbox only when In Progress and Pending are not in the column labeled Status, and leave it unchecked if the opposite. IFS function will be explained below.
IS SET - Checks whether a value of a field is set. Returns true if the supplied operand contains at least a single value, false otherwise.
Example:
ModificationsApplied IS SET
- The expression will return true if the value for the field ModificationsApplied is set.
NOT SET - Checks whether a value of a field is set. Returns false if the supplied operand contains at least a single value, true otherwise.
Examples:
Example:
ModificationsApplied NOT SET
- The expression will return true if the value for the field ModificationsApplied is not set.
IS VALID - Checks if there is any value error at the field template. Returns true if there is no value error, false otherwise.
Examples:
FieldIdentifier IS VALID
NOT VALID - Checks if there is any value error at the field template. Returns true if there is a value error, false otherwise.
Examples:
FieldIdentifier NOT VALID
Greater Than ( > ) - This operator determines whether the first value is greater than the second value. Returns true if the left operand is greater than the right operand, false otherwise (null value is treated as 0).
- Examples:
5 > 3
- returns true
pH1 > pH2
- returns false if the value of pH1 field is 2.3 and the value for pH2 field is 2.4
Greater or Equal ( >= ) - Checks if the left operand is greater than or equal to the right operand. Returns true if the left operand is greater or equal to the right operand, false otherwise.
- Examples:
Less Than ( < ) - This operator determines whether the first value is less than the second value. Returns true if the left operand is less than the right operand, false otherwise.
- Examples:
Less Than or Equal To ( <= ) - Checks if the left operand is less than or equal to the right operand. Returns true if the left operand is less than or equal to the right operand, false otherwise.
- Examples:
Is Equal To ( == ) - This operator determines whether the two values are equal. Returns true if the left operand is equal to the right operand, false otherwise.
- Example:
a == b
Is Not Equal To ( != ) - Determines whether the two values are not equal Works like the exact opposite of is equal to operator.
- Example:
a != b
Assign operator ( = ) - it is used to assign the value from the expression on the right side to the variable on the left side
- Examples:
Addition/Concatenation ( + ) - Adds two numbers or concatenates two strings, depending on operand type.
When you concatenate two alphanumeric fields as a result you will see values of those fields glued together. In order to avoid that add empty string in the Calculation formula (i.e. FieldIdentifier1 + ' ' + FieldIdentifier2).
- Examples:
NumberFieldA + NumberFieldB
- returns a number;
TextFieldA + ' : ' + TextFieldB
- returns a string;
RecordIdentifier.NumberFieldIdentifier + 3.5
- returns a number;
Subtraction ( - ) - Subtracts the second operand from the first..
- Examples:
NumberFieldA - NumberFieldB
- returns a number;
NumberFieldA - 1
- returns a number;
EndDate-StartDate
(the subtraction must happen in a number type field) - returns a number;
Multiplication ( * ) - Multiplies two numbers.
- Examples:
NumberFieldA * NumberFieldB
- returns a number;
NumberFieldA * 3
- returns a number;
Division ( / ) - Divides two numbers.
- Examples:
NumberFieldA / NumberFieldB
- returns a number;
NumberFieldA / 3
- returns a number;
Negation ( # ) - Changes the sign of the number. Transfers it from negative to positive or vice versa.
- Examples:
#(-2)
- the result is 2;
#NumberFieldA
- returns a number;
MOD - Calculates the remainder of the division of one number by another. The result has the same sign as the divisor. Number a is the dividend, and number b is the divisor.
Example:
MOD(23%5)
- the result is 3;
Please note: all the Alchemy functions that will be listed in the following pages are available, but not applicable in all cases. Some of the functions are generated in order to help solve specific problems and use cases, and their application at the moment is not wide.
Functions are expressions in Alchemy that can use one or more inputs, given in brackets (), and perform complex calculations before returning the output.
IFS (condition, value...)- Checks whether one or more conditions are met and returns the value that corresponds to the first true condition. Accepts parameters in pairs, the first parameter in pair is a condition and must evaluate to a boolean or an error is generated. The second parameter can be a constant, variable, or another expression that must result in the same type as the field for which we are using the IFS function. The first parameter is considered an expression before the comma, and the second one is after the comma.
- Examples:
IFS ( a < b, a, a> b, b)
- returns a if a<b is true, if a<b is false it will check if a>b, and if it is true and it will return b;
IFS ( f <=5, d * 3, (f +2 < c), IFS (f < c, 4, e>=d, d), a = b, c, f >= 250, d + 1)
- example of nested IFS functions, if the first condition (f<=5) is false and the second condition (f +2 < c) is true then it will start evaluating second IFS function conditions.
In the Configuration Portal Administrator Manual chapter 4.6.3 Formulas in Calculations, there is an example of function IFS.
SQRT ()- A function that calculates the square root of the number. When performed on an operand, it finds the square root of the number.
- Examples:
SQRT(NumberField)
- returns a number;
SQRT(NumberFieldA + NumberFieldB)
- returns a number;
SQRT(144)
- the result is 12;
POW ()- A function that calculates the result of a number raised to a power. Returns the value of the first operand (before the comma) being raised to the power of the second operand (after the comma).
- Examples:
POW(NumberFieldA, 2)
- returns a number;
POW(3*NumberFieldA, 3)
- returns a number;
POW(10, 2)
- the result is 100;
ABS () - Returns the absolute value of a number or the operand (value without the sign).
ABS(NumberFieldA)
- returns a number;
ABS(-15)
- the result is 15;
SUM () - Returns the sum of all the values of a multiple value field or all the values in a column. Adds all the values of the specified field or constant.
- Examples:
SUM(15)
- the result is 15;
SUM(NumberFieldA)
- returns a number;
LOG () - calculates the logarithm (base 10) of the value in the argument.
- Examples:
LOG(100)
- the result is 2;
LOG(NumberFieldA)
- returns a number;
LOG(NumberFieldA/NumberFieldB)
- returns a number;
COUNT () - Counts all the values of a multiple value field or all the values in a column. This function counts all the values of the specified field or constant.
- Examples:
COUNT(15)
- the result is 1;
COUNT(FieldIdentifier)
- returns a number;
ROW_COUNT () - Counts rows or columns of a certain field.
- If it is used on a constant, the result is 1. If it is used on a single value field (multiple=false) that belongs to a group that is a single type, a warning is issued during configuration. It will return 1 if there is a value, 0 otherwise.
- If it is used on a field that is marked as multiple (multiple=true, groupType=SINGLE) the result is 1.
- If it is used on a single value field in a table (multiple=false, group type != SINGLE) the result is the number of rows or columns.
- If used on a multiple value field in a table (multiple=true, group type != SINGLE) the result is the number of rows or columns.
- Examples:
ROW_COUNT(FieldIdentifier)
- returns a number;
AVERAGE ()- Calculates the average (arithmetic mean) of all the values of a multiple value property or all the values in a column. This function works by adding together all the number values and then dividing them by the number of values. Calculates the average of the specified number field or constant.
- Examples:
AVERAGE(15)
- the result is 15;
AVERAGE(NumberField)
- returns a number;
TODAY () - returns the current date, in UTC, can be parameterized optionally.
- Examples:
TODAY ()
- returns current UTC date and time, i.e. 2022-04-06T20:40:50Z;
TODAY(2)
- returns a day after tomorrow's UTC date and time, i.e 2022-04-08T20:40:50Z;
TODAY(0, -1, 0)
- returns a UTC date and time that is one hour in the past, i.e. 2022-04-06T19:40:50Z;
Whenever you need a date&time type of field to be filled in by today's date you can use this function. Write TODAY in the Formula Expression of the field type date&time. You should check the Editable checkbox “Editable” in the Properties tab of that field, so if needed, the user in the User Portal can change the date of today to some other date.
DATE FORMAT () - returns a text representation of a date input based on the supplied format.
- Examples:
DATE_FORMAT(ProjectStartDate, 'YY-MM-DD')
- the result is a text representation of the date value of ProjectStartDate field, i.e. 22-04-06
DATE_FORMAT(Process.CreatedOn, 'EEEE, MMMM d, y')
- the result is a text representation of the date value of Process.CreatedOn system data, i.e. Wednesday, April 6, 2022;
ROUND () - Rounds the number to a given precision. It is rounded using half-up mode (i.e. everything above 12.5 will be shown as 13, and everything below 12.5 will be shown as 12. If the number that is rounded is 12.5, 13 will be shown).
- Examples:
ROUND(12, 2)
- the result is 12.00;
ROUND(12.5, 0)
- the result is 13;
ROUND(NumberField, 4)
- returns a number with 4 decimal places;
ROUND(NumberFieldA, NumberFieldB)
- returns a number with the number of decimals determined by the value of NumberFieldB;
TRANSITION_FROM () - Checks whether the activation path is coming from the selected stage. Returns true if stage activation is coming from the specified stage, false otherwise. Can only be used in process template rules (i.e., in conditions for actions), an error is generated if used in other types of expressions.
- Example:
TRANSITION_FROM(Stage[Requirements])
- the result is true if the stage is activated from the stage Requirements, otherwise it will return false;
Request Review in the Lab Work can be done after Request stage or after Technical Call stage. The Lab Manager needs to get the task with a different subject for review. One subject should be when the task is created after the Request stage and the other subject when the task is created after Technical Call.
In order to set up those tasks, you need to add two Create Task actions on the Request Review stage. In the first one in the When you will write TRANSITION_FROM(Stage[Request]) and the Task Subject you will fill in with the 'Review the Lab Work Request for ' + Process.Name. In the second one in the When you will write TRANSITION_FROM(Stage[TechnicalCall]) and the Task Subject you will fill in with the 'Review the Lab Work Request for ' + Process.Name + ' - technical call is done'.
HAS_ANY_TAG () - Returns a list of users that have any of the supplied tags.
- Example:
HAS_ANY_TAG(Tag[SalesDirector], Tag[President])
- returns a list of users who have the tag SalesDirector or the tag President;
HAS_ALL_TAGS () - Returns a list of users that have all of the supplied tags.
- Example:
HAS_ALL_TAGS(Tag[SalesDirector], Tag[VPMarketing])
- returns a list of users who have the tag SalesDirector and the tag VPMarketing;
INTERSECT () - finds the intersection between multiple lists of the same type. Only the values that are found in all the supplied lists are returned.
- Examples:
INTERSECT(HAS_ALL_TAGS(Tag[Thermosets], Tag[TechnicalService]), HAS_ANY_TAG(Tag[Manager], Tag[Representative]))
- returns a list of users who have the tags Thermosets and TechnicalService, and have the tag Manager or Representative;
UNION () - Calculates the union between multiple lists of the same type. All the values that are found in all the supplied lists are returned. List members are not repeated if found multiple times.
- Examples:
UNION(HAS_ANY_TAG(Tag[Chemist], Tag[SynthesisLab]), HAS_ALL_TAGS(Tag[LabManager]))
- returns a list of users who have the tag Chemist or SynthesisLab or LabManager;
CREATED_BY () - Determines if the process has been created by supplied users. Returns true if one of the supplied users has created the process, false otherwise.
- Example:
CREATED_BY(Tag[SalesDirector]) OR CREATED_BY(Tag[LabManager])
- returns true if the process was created by a user with tag SalesDirector or LabManager, otherwise it returns false;
PATTERN () - Returns an auto-incremented pattern based on the supplied pattern in the operand. A defined pattern supports letters (acbd, etc.), digits (1234, etc.), and date and time. The maximum length of this field is 255 characters. We support single pattern definition; if multiple patterns are defined in a formula, an error is generated. If you need a pattern to start from a specific number you can use the following expression: PATTERN('NN','41'), where 41 can be any two digits number.
Each time you reach the "end" of the pattern, the pattern will restart from the beginning. So if you use N for single digits, when N reaches 9, it will start from the 1 again; the same thing applies for letters.
This does not apply to the time-based pattern values, since their values are dependent on the current date and time, but changes in these values influence the resetting of the values from there (i.e., if you are counting Lab Works per date, the pattern would start from the beginning with the new day). Date values are calculated in the time zone from which the request is coming from.
Also, it does not apply when using n as an infinite digit pattern. Small n will start from 1 (or any other value if it’s specified as the second parameter of the PATTERN function and third parameter of the KEY_PATTERN function) and will be increased by 1 up to max value of 2,147,483,647. It doesn’t have leading zeros.
- Examples:
'Sample ' + PATTERN('ANN')
- the first instance returns text ‘Sample A01’;
'Shipment ' + PATTERN('YYYY-MM-DD-NN')
- the first instance returns text, i.e. 'Shipment 2022-04-06-01’;
'Lab Trial ' + PATTERN('n')
- the first instance returns text, i.e. 'Lab Trial 1’; The 15th instance returns text, i.e. 'Lab Trial 15’.
In Alchemy Laboratory each Lab Work process is registered by a unique predefined pattern. It is done by defining a calculation formula in the naming field (a field that gives a name to the process started, located in the first record of the first stage of the process; Identifier: Name) with the following expression:
‘LW-’ + LWNumber + ' ' + LWDescription
LWNumber is the identifier of an alphanumeric field where the pattern function is used under the calculation formula: PATTERN('YYYY-MM-NNN').
LWDescription is the identifier of an alphanumeric field where the user can enter a short description of the process started.
When creating a new process, it will automatically be titled “LW-2021-09-001 ‘description entered’ “ (for the first process in September). The following one will be named “LW-2021-09-002 ‘description entered’ “, etc. If by the end of September there are 999 Lab Works, counting will start again from 001. The first Lab Work in October will be titled “LW-2021-10-001 ‘description entered’ “.
You cannot have more than one PATTERN() formula in one expression. i.e. PATTERN('YYYY') + PATTERN('NNN') is not a valid expression. If you need to join two patterns, write the expression in two separate fields, and concatenate the strings in a third.
KEY_PATTERN () - Returns an auto-incremented pattern based on the supplied pattern in the operand
- Examples:
The value of KEY_PATTERN will reset with the change of KEY. If the configurator decides to return the previous KEY in the calculation, the pattern will continue to calculate from the last value that KEY returned before the change is made.
TextField + KEY_PATTERN(Textfield, ’NNN’)
- returns the pattern value together with the KEY
KEY_PATTERN(TextField, ’N’)
- returns only the pattern value
KEY_PATTERN('Text', 'NN', '23')
- returns pattern value starting from 23
One formulation can be tested multiple times. In order to distinguish between these different test results, you can use the KEY_PATTERN function to name different Characterization Tests records. In the Characterization Tests record template there is a field Related To Formulation (identifier ROwulation), where formulations displayed are filtered only to those created in the current process. You need to add an alphanumeric field, identifier CharacterizationKeyNumber.This field should be hidden because it helps you to name Characterization Tests records in the process and serves only for that. Calculation of this field should be:
RelatedToFormulation + KEY_PATTERN(RelatedToFormulation + ' ', 'N.N')
Under Lab Work Stage, under the Records tab, choose the Characterization Tests and in the Naming Pattern use the value of the field CharacterizationKeyNumber. This way, if you have more Characterization Tests records created for the same RelatedToFormulation field value, they will have different names. The difference will be in the number given by the KEY_PATTERN function: 0.1, 0.2, 0.3, etc.
KEY_PATTERN function is a powerful tool. You can change the KEY, and reset the counter. If you change the KEY back to the previous value, it will not reset the counter again, but continue from the value when it was changed for the first time. See the following example.
In Alchemy Lab final Products receive a number based on the Product Type. Product Number is an alphanumeric field with the calculation ProductType + KEY_PATTERN(ProductType, 'NNN'). However, the new R&D director decided that the Product should receive a number based on the Industry Type where it is applied. In that case, the new calculation in the Product Number field will be IndustryType + KEY_PATTERN(IndustryType, 'NNN'). The first new product will have the number 001 for a certain Industry Type no matter if it is from the Product Type where the next number would be 029. The reason is that KEY is changed.
Getting back the previous KEY in calculation
After a while the new R&D director realized that the previous product naming convention was more efficient. He asked a configurator to change Product Numbers so they again depend on the Product Type. After a calculation in Product Number is returned to ProductType + KEY_PATTERN(ProductType, 'NNN') numbers will count from the number where they stopped before the change was made (the product from the certain type will have the number 030).
STAGE_COMPLETED_BY () - Determines if the stage has been completed by supplied users. Returns true if one of the supplied users has completed the stage, false otherwise.
- Example:
STAGE_COMPLETED_BY(Tag[SalesDirector])
- returns true if the stage was completed by a user with tag SalesDirector;
RUN_COUNT () - Returns the number of runs the given Stage has
- Examples:
RUN_COUNT(Stage[Review])
- returns the number of runs for the stage Review;
CONVERT UNIT - Convert provided number value in the desired unit
var a = { “unitType”: { “value”: unitTypeValueVariable, “unit”:
unitTypeUnitVariable } }
CONVERT_UNIT (ValueToConvertField, UnitField, UnitFieldToConvert, a);
- Examples:
CONVERT_UNIT(ValueToConvertField, UnitField, UnitFieldToConvert);
var a = { "density": { "value": DensityValueVariable, "unit": DensityUnitVariable } }
CONVERT_UNIT(100, ‘g’,’ml’, a);
The accepted abbreviations for some of the units are given below. The details on the units’ abbreviations were based on this Source.
Abbreviations:'mg','g','kg','gr','lb','dr','oz','t','ton','ml','dl','cl','l','m3','gal','hl','tsp','tbsp','fl oz','cup','pt','qt','g/ml','g/dl','g/cl','g/l','g/m3','kg/ml','kg/dl','kg/cl','kg/l','kg/m3','lb/gal','lb/pt','lb/qt','oz/gal','oz/pt','oz/qt',
The following functions are used only in writing Alchemy scripts (in buttons, in stage execute script and in On Change action scripts), not in formula or visibility expressions:
SIGNED_IN_USER () - Returns the REFERENCED_USER which is signed in.
- Examples:
UserField = SIGNED_IN_USER()
- returns the user who is signed in;
CREATE_PROCESS () - Function that creates a new process or processes of the provided process template. The first parameter is the Process Template Identifier. The second parameter is the number of processes to be created.
- Examples:
CREATE_PROCESS('LabWork',1)
- creates a new process from the Lab Work process template and returns the created process;
CREATE_RECORD () - Function that creates a new record or records of the provided Record Template. Record Templates configured to generate records outside of the process (solo records) can also be created by this function. In that case, second and third parameters should not be added to the expression.
- Examples:
CREATE_RECORD(‘Formulation’, ‘LabWork’, Process,1)
- creates a new Formulation record in the Lab Work stage of the current process and returns the created record;
CREATE_RECORD(Material, 1)
- creates a new Material solo record and returns the created record;
ADD_ROW () - Function for adding a row in a group.
- Examples:
ADD_ROW(GroupIdentifier, 1)
- adds one row to the group determined by the GroupIdentifier;
ADD_ROW(GroupIdentifier, Group1.RowCount)
- adds the number of rows determined by the number of rows in Group1 to the group determined by the GroupIdentifier;
GET_RECORD () - Function returns referenced record value, depending on passed record ID
- Examples:
GET_RECORD(IdOfRecord)
- returns the record with ID determined by the field IdOfRecord;
GET_RECORD(5)
- returns the record with ID 5;
CREATE_TASK () - Creates a new task on the defined stage. Function receives next parameters comma separated:
Order of parameters defined must be as it is listed above.
The function will be executed only if all requirements are met (Stage is active, assignees have Can Edit privilege, etc)
- Examples:
CREATE_TASK(Process.Stage[LabWork], 'Title for the Task From a Script', 'This is a description for the task from a script', TODAY(), DateTimeField, 1, HAS_ANY_TAG(Tag[SalesDirector]))
- returns a new Task;
CREATE_COMPARE () - Creates a new Compare Records. Function receives next parameters, comma separated:
- Examples:
CREATE_COMPARE(Process.Stage[LabWork], ‘LabRecord’, [‘NumberField’, ‘TextField’], [‘TextField’], Record[LabRecord])
- returns a Compare Records;
CREATE_COMPARE(Process.Stage[LabWork], ‘LabRecord’, [‘NumberField’, ‘TextField’], [‘TextField’], RecordField)
- returns a Compare Records;
CREATE_VIEW () - Creates a new View in a stage, from the View Template with predefined fields and filters. Function receives next parameters, comma separated:
Note that all parameters can be calculated using an expression, it is not necessary to put an explicit value for numbers, strings, etc. Calculation rules apply.
Standard field keyword table:
Filter comparison type list:
- Example:
var filterParam = [
{
'node' : '1',
'fields' : [
{
'name' : 'PRIORITY',
'visible' : true,
'filters' : [
{
'type' : 'IS_BETWEEN',
'from' : 5 , 'to' : 15
}
]
}
]
} , {
'node' : '3',
'fields' : [
{
'name' : 'RECORD_ID'
'visible' : false,
'filters' : [
{
'type' : 'IS_ANY_OF',
'values' : [Record[LabWork], ReferenceRecordInRR, Record]
}
]
} , {
'name' : 'NumberField' ← note that this is an identifier string, so it has ‘ ’
'visible' : true,
'precision' : 3,
'filters' : [
{
'type' : 'IS_GREATER_THAN_OR_EQUAL',
'values' : [SomeOtherNumberField] ← number calculation, no ‘ ’
}
]
}
]
}
];
CREATE_VIEW(Stage[LabWork], 'View1', 'View From Button', filterParam);
SEND_EMAIL () - This function sends email to the specified user(s). CAUTION: Usage of this function needs to be carefully tested since sending email cannot be undone.
- param1 is the email recipient. Can be email address as string, or any expression returning user reference
- param2 is the subject of email (string)
- param3 is the body of email (string)
- Examples:
SEND_EMAIL(‘const1@mail.com’, ‘mail subject’, ‘mail body’);
SEND_EMAIL(‘const1@mail.com, const2@mail.com’, ‘mail subject2’, ‘mail body2’);
SEND_EMAIL([‘const3@mail.com, const4@mail.com’], ‘mail subject3’, ‘mail body3’);
SEND_EMAIL(UserField.Email, ‘mail subject4’, ‘mail body4’);SEND_EMAIL(UserField, RecordName + ‘ send as user 5’, RecordName + ‘ 5’);
SEND_EMAIL(UserFieldInGroup, RecordName + ‘ send as a user in group 6’, RecordName + ‘ 6’);
SEND_EMAIL(HAS_ANY_TAG(Tag[SalesDirector]), ‘mail subject 7’, ‘mail body7’);
If(){} else {} - If expressions can be used to define conditional actions in expressions. The correct syntax is the following: If(condition){ block of code } else { block of code}
- Examples:
if(CustomerType == ‘Prospect’){ NewCallDate = TODAY (4); }
- only if the value of the CustomerType field is Prospect, then it will set the value of the NewCallDate to the 4 days from now;
if(NumberA > 3){ CalcNumber = NumberA*3.14; }
else { CalcNumber = NumberA/3.14; }
- if the value of the NumberA field is bigger than 3, then the value of the CalcNumber will be calculated from the value NumberA * 3.14, else the value of the CalcNumber will be calculated from the value NumberA / 3.14
for() {} - A For loop is used to define number of executions (iterations) of defined code block (calculations/commands). It is used to access values of fields with multiple values or fields in repeatable groups.
The correct syntax is the following: for(var i=n; i<=m; i=i+k){code bloc}
- Example:
• for(var i=NumberField; i<=5;i=i+2){code bloc}
• for(var i=1; i<=Group1.RowCount;i=i+2){code bloc}
RECALCULATE_PRIVILEGES() - this function can be used in action script when it needs to recalculate privileges when you need to change them (for example when you need to mark a record as “confidential”)
- Examples:
RECALCULATE_PRIVILEGES(Process)
RECALCULATE_PRIVILEGES(Record)
- initiates access rights recalculation for the active process;
- initiates access rights recalculation for the standalone records;
CREATE_LINK() - Function is used in calculation of the link type field to generate a new link.
- Examples:
CREATE_LINK('https://www.alchemy.cloud/alchemy-knowledge-center/home', 'Alchemy Knowledge Center')
CREATE_LINK('https://pubchem.ncbi.nlm.nih.gov/#query=' + CasNumber , 'PubChem Link')
- where CASNumber is an identifier of an alphanumeric field in the same record template where the link field is. The output link is a dynamic value that depends on the CASNumber entry.
CREATE_RECORD_ASYNC - Function is used to create records asynchronously. Asynchronous creation of records enables better performance.
- Examples:
Creating solo record:
var myParams = {
"newRecordName":"new record",
"newText":RecordField.AphanumericField
};
var myScript = "RecordName = newRecordName;
AphanumericField = newText;";
CREATE_RECORD_ASYNC("RecordB", myParams, myScript);
Creating record in a process:
CREATE_RECORD_ASYNC(“RecordA”,Process.Stage[Stage_1],{ "a":Record[RecordB].NumberField}, “AlphanumericField = a;”);
CREATE_PROCESS_ASYNC - Function is used to create new processes asynchronously.
var myParams = {
"recordId": Record.Id
}
var myScript = "
Process.Record[RecordA].RefRecordField = GET_RECORD(recordId);
Process.Record[RecordA].RefRecordField.ProcessNameField = Process.Name;"
CREATE_PROCESS_ASYNC("ProcessA", myParams, myScript);
CREATE_SAMPLE_RECORD_ FROM_MATERIAL() - Function is used to create a standalone Sample from the passed Material record.
CREATE_SAMPLE_RECORD_FROM_MATERIAL(Material)
CREATE_MATERIAL_RECORD() - Function is used to create a Material record from the ELN & LIMS Material record template.
SIN() - calculates the SINE of the given angle
- Examples:
SIN(FieldIdentifier)
- returns a number;
COS() - calculates the COSINE of the given angle
- Examples:
COS(1.047)
- the result is 0.500171;
COS(RADIANS(60))
- the result is 0.5
TAN() - calculates the TANGENT of the given angle
- Example:
TAN(0.785)
- the result is 0.99920;
TAN(RADIANS(45))
- the result is 1;
COT() - calculates the COTANGENT of the given angle
- Example:
COT(30)
- the result is -0.156119952161659;
ASIN() - calculates the ARCSINE of the given number (inverse SINE)
- Example:
ASIN(-0.5)
- the result is -0.523598776;
DEGREES(ASIN(-0.5))
- the result is -30;
ACOS() - calculates the ARCCOSINE of the given number (inverse COSINE)
- Example:
ACOS(-0.5)
- the result is 2.094395102;
DEGREES(ACOS(-0.5))
- the result is 120;
ATAN() - calculates the ARCTANGENT of the given number (inverse TANGENT)
- Example:
ATAN(1)
- the result is 0.785398163;
DEGREES(ATAN(1))
- the result is 45;
ATAN2() from X- and Y-coordinates - calculates the ARCTANGENT of the specified x- and y-coordinates (inverse TANGENT).
- Example:
ATAN2(1, 1)
DEGREES(ATAN2(-1, -1))
ACOT() - calculates the principal value of the ARCCOTANGENT of a number (inverse COTANGENT)
- Example:
ACOT(2)
DEGREES() - converts angle given in radians to angle in degrees
- Example:
DEGREES(0.785398163)
- the result is 45;
RADIANS() - converts angle given in degrees to angle in radians
- Example:
RADIANS(270)
- the result is 4.712389;
LN() - calculates the natural logarithm of a number. The natural logarithm of a number x is the power to which constant e (e=2.718) should be raised to equal x.
- Example:
LN(86)
EXP() - calculates constant e (e=2.718) raised to the power of a given number. It is an inverse function of LN.
- Example:
EXP(1)
CEILING() - rounds a number up, to the nearest integer
FLOOR() - rounds a number down, to the nearest integer
IS_DIVISIBLE() - checks if two numbers are divisible without the remainder
Example:
IS_DIVISIBLE(8,2)
- returns true;
GEOMEAN() - calculates geometric mean of the given numbers (not arithmetic mean, for that you can use function AVG)
ENCODEURL() - Transforms given string in URL code
- Example:
ENCODEURL(‘string’)
LEFT() - Gives the leftmost characters from a text value
- Example:
RIGHT() - Gives the rightmost characters from a text value
- Example:
RIGHT(FieldIdentifier,4)
SEARCH() - finds a location of one text value within another text value and returns a number of the character where starts searched part of the text
- Example:
SEARCH(AlphanumericField, AlphanumericSerchField)
AlphanumericField has the value "Lorem ipsum dolor sit amet, consectetur adipiscing elit" and AlphanumericSearchField has the value "dol". The result is 13 which is the position of “d” from “dolor”
REPLACE() - Replaces characters within the text.
SUBSTITUTE() - Substitutes new text for old text in a string
LOWER() - converts each uppercase letter in a text string to a lower case letter
- Example:
LOWER(FieldIdentifier)
- returns a text all in lower case;
LOWER(‘Yield’)
- the result is ‘yield’;
UPPER() - converts a text string to uppercase
- Example:
UPPER(FieldIdentifier)
- returns a text all in lower case;
UPPER(‘total amount’)
- the result is ‘TOTAL AMOUNT’;
FORMAT_STRING() - Gives a formatted string made from a string with placeholders and values passed to fit in the placeholders. The number of placeholders in a string must be equal to the number of values to be placed in the string
STRING_FORMAT(Instruction, Ingredient, Phase)
- This calculation is placed on an alphanumeric field with identifier Procedure. Instruction, Ingredient, and Phase are alphanumeric fields. User must enter a value for field Instruction in the following format: ‘Mix {1} than add {2}’. Number 1 in the curly brackets is where the value of field Ingredient will be placed in the text. Number 2 in the curly brackets is where the value of field Phase will be placed in the text. There should be a single number in the curly brackets without any other characters.
MID() - Trims the given number of characters from the original string from the left and from the right side
RANDOMIZE - function generates random string according to the provided string length, it returns array of characters
Input 1 is the length, number type, which defines how many characters need to be returned.
Input 2 is isNumber, boolean value, which defines whether the returned array of characters is only numbers.
RANDOMIZE(length, isNumber)
P_CORREL() - calculates the Pearson correlation coefficient, dimensionless index that ranges from -1.0 to 1.0 inclusive. It is a measure of linear correlation between two sets of data.
S_CORREL() - calculates the Spearman correlation coefficient.
MAX() - Gives the largest value in a set of values
MIN() - Gives the smallest value in a set of values
MEDIAN() - Gives the median of the given numbers. The median is in the middle of a set of numbers.
MODE() - Gives the most frequently occurring single value in a data set
MODE_M() - Gives the most frequently occurring multiple values in a data set. Only the values that are most frequently repeated and that repeat exactly the same number of times are returned
PERCENTILE() - Calculates the k-th percentile of values in a data set; where k is in the range from 0 to 1, excluded
STDEV() - Calculates standard deviation based on the sample of population
DATE_DIF() - calculates the date difference, or the number of days, months, years, etc. between two dates.
DATE_ADD() - calculates the resulting date and time by adding the amount specified to the given date
- Example:
DATE_ADD(DateTimeFieldIdentifier, 3, 'MONTH')
- returns a date & time 3 months from the date provided in the field DateTimeFieldIdentifier;
DATE_GET() - returns a year, a month, a day of a month, or a day of a year of the given date.
- Example:
DATE_GET(DateFieldIdentifier, 'YEAR')
- returns a year for the date provided in the field DateFieldIdentifier;
DATE_GET(CreatedOn, 'MONTH')
- if the value of the field Created On is 25th September 2021, the result will be 9.
DATE_GET(CreatedOn, 'MONTHDAY')
- if the value of the field Created On is 25th September 2021, the result will be 25;
DATE_GET(CreatedOn, 'DAY')
- if the value of the field Created On is 25th September 2021, the result will be 268;
WEEKDAY - returns a day of the week as a number from 1 for Sunday to 7 for Monday
HOUR - returns an hour of the given date&time value as a number from 0 (for 12:00 AM) to 23 (for 11:00 PM)
MINUTE - returns a minute of the given date&time value as a number from 0 to 59
SECOND - returns a second of the given date&time value as a number from 0 to 59
MILLISECOND - returns a millisecond of the given date&time value as a number from 0 to 999
NUMBER OF THE WEEK - returns a weak number of the given date&time value as a number from 1 to 52. Numbering is determined by aligned-week-of-the-year and depends on the starting day of the year. The first aligned week of the year starts on day-of-year 1, the second aligned week starts on day-of-year 8, and so on...
QUARTER OF THE YEAR - returns a quarter of the year for the given date&time value as a number from 1 to 4
END_OF() - returns a date value which is at the end of the specified time unit for the given date. As for the DATE_GET function, the week starts on Sunday.
Functions listed below are to be used with arrays. Elements of the array can be numbers, text, another array, or struct. Different types can be elements of the same array.
APPEND() - Adds an element at the last place in the array. Array size will be increased by one. If the added element is another array, it will be added at the last place of the main array as a subarray.
- param1 is the local variable of the type array
- param2 is an element to be added to the array defined in param1
- Example:
var a = [1, 2, 3, 4];
APPEND(a, 5); - the result is array a = [1, 2, 3, 4, 5]
var a = [1, 2, 3, 4];
APPEND(a, [5, 6]); - the result is array a = [1, 2, 3, 4, [5, 6]]
APPEND_ALL() - Adds element(s) at the last place in the array. Array size will be increased by one (or more elements depending on the type of added element). If the element added is another array, APPEND_ALL will merge the main array with the array being added. The size of the main array will be increased by the size of the added array. It is good practice to use this function instead of APPEND in Predefined Values Expressions.
- param1 is the local variable of the type array
- param2 is the element to be added to the array defined in param1
- Example:
var a = [1, 2, 3, 4];
APPEND_ALL(a, 5); - the result is array a = [1, 2, 3, 4, 5]
var a = [1, 2, 3, 4];
APPEND_ALL(a, [5, 6]); - the result is array a = [1, 2, 3, 4, 5, 6]
INSERT() - Inserts an element to the exact position in the array. The existing element on the position of insertion will be shifted to the next position. Array size will be increased by one.
- param1 is the local variable of the type array
- param2 is the position where a new element will be inserted
- param3 is the element inserted on the position defined by param2 in the array defined by param1
- Example:
var a = [1, 2, 3, 4];
INSERT(a, 2, 11]); - the result is array a = [1, 11, 2, 3, 4]
var a = [1, 2, 3, 4];
INSERT(a, 2, [5, 6]); - the result is array a = [1, [5, 6], 2, 3, 4]
INSERT_ALL() - Inserts element(s) to the exact position in the array.
- When the element being inserted is not an array, the array size will be increased by one.
- If the element being inserted is another array, the array size will be increased by the number of the elements in the inserted array.
- param1 is the local variable of the type array
- param2 is the position where a new element will be inserted
- param3 is the element inserted on the position defined by param2 in the array defined by param1
- Example:
var a = [1, 2, 3, 4];
INSERT_ALL(a, 2, 13]); - the result is array a = [1, 13, 2, 3, 4]
var a = [1, 2, 3, 4];
INSERT_ALL(a, 2, [5, 6]); - the result is array a = [1, 5, 6, 2, 3, 4]
REMOVE() - Removes the element(s) from the array on the exact position(s). The array size will be decreased by the number of removed elements.
- param1 is the local variable of type array
- param2 is the position where a single element will be removed or array of positions of elements to be removed
- Example:
var a = [1, 2, 3, 4];
REMOVE(a, 2); - the result is array a = [1, 3, 4]
var a = [1, 2, 3, 4];
REMOVE(a, [2, 4]); - the result is array a = [1, 3]
RESIZE() - Resizes the array to the defined size.
- If the size is decreasing, elements on positions outside of the new size will be removed from the resized array.
- If the size is increasing, the number of elements in the new array will increase to the new size. The new elements will be populated by null values.
- param1 is local variable of type array
- param2 defines the new size of array (number of elements in a resized array)
- Example:
var a = [1, 2, 3, 4];
RESIZE(a, 3]); - the result is array a = [1, 2, 3]
var a = [1, 2, 3, 4];
RESIZE(a, 6); - the result is array a = [1, 2, 3, 4, null, null]
SUBARRAY() - Creates a new array that will be the subarray of the main array. The new array contains elements between from and to position of the main array. Elements on from position and to position are included in the subarray. If to position is greater than the size of the main array, returned subarray will be between from position and the last element of the main array.
- param1 is local variable of type array
- param2 defines the start of the subarray (position of element in main array that will be first element in subarray)
- param3 defines the end of the subarray (position of element in main array that will be last element in subarray)
- Example:
var a = [1, 2, 3, 4];
var b = SUBARRAY(a, 2, 3); - the result is array b = [2, 3]
var a = [1, 2, 3, 4];
var b = SUBARRAY(a, 2, 8); - the result is array b = [2, 3, 4]
CONTAINS() - checks if an array contains a specific element. If struct is used, checks if struct contains a specific key.
- param1 is local variable of type array or type struct
- param2 has two options based on type of param1:
1) if the param1 is an array - the param2 can be text, number, struct or another array.
2) if the param1 is struct - the param2 can be only text
- Example:
var a = [1, 2, 3, 4];
CONTAINS(a, 2); // returns true
var a = [1, 2, 3, 4];
CONTAINS(a, 7); // returns false
var a = [1, 2, 3, 4];
CONTAINS(a, [1, 2]); // returns false
var a = [1, 2, 3, 4];
a[3] = [4, 5, 6]; // array a now has on position 3 array [4, 5, 6]
CONTAINS(a, [4, 5, 6]]); // returns true
var s = {'key1': 'description1', 'key2': 'description2'};
CONTAINS(s, 'key1'); // returns true
CONTAINS(s, 'key5'); // returns false
var s = {'key1': 'description1', 'key2': 'description2'};
s[‘key3’] = ‘new description’; // struct is now updated {'key1': 'description1', 'key2': 'description2', 'key3': 'new description'}
var s = {'key1': 'description1', 'key2': 'description2'};
s[‘key1’] = ‘demo’; // struct is now updated {'key1': demo, 'key2': 'description2'}
CONTAINS_ALL() - checks if the main array contains all of the specified elements. Elements to check are defined in another array.
- param1 is the local variable of type array
- param2 specifies elements to be checked, defined in another array
- Example:
var a = [1, 2, 3, 4];
CONTAINS_ALL(a, [1, 2]); // returns true
var a = [1, 2, 3, 4];
CONTAINS_ALL(a, [4, 5, 6]); // returns false
CONTAINS_ANY() - checks if the main array contains any of the specified elements. Elements to check are defined in another array.
- param1 is local variable of type array
- param2 specifies elements to be checked, defined in another array
- Example:
CONTAINS_ANY(a, [1, 2]); // returns true
CONTAINS_ANY(a, [4, 5, 6]); // returns true
var a = [1, 2, 3, 4];
CONTAINS_ANY(a, [1, 2]); // returns true
var a = [1, 2, 3, 4];
CONTAINS_ANY(a, [4, 5, 6]); // returns true
COUNT_OF() - Counts how many times a specified element (or elements in another array) is repeated in the main array.
- param1 is local variable of type array
- param2 specifies elements to be checked, defined in another array
- Example:
var a = [1, 2, 3, 4];
COUNT_OF(a, 2); // returns 1
var a = [1, 2, 3, 4];
COUNT_OF(a, 5); // returns 0
var a = [1, 2, 3, 2, 4, 2];
COUNT_OF(a, 2); // returns 3
var a = [1, 2, [3, 4], 3, 4];
COUNT_OF(a, [3, 4]); // returns 1
FIND_AND_REMOVE - find and remove the first (or all) equal item(s) in the array. The function won't return any value, what is forwarded is changed.
It can be used to configure a repeatable row table where already used predefined values need to be removed after previously used.
param1 is local variable of type array
param2 specifies value which will be removed from param1
var a = [1, 2, 3, 4];
FIND_AND_REMOVE(a, 2);
NumberField=a; //NumberField = [1, 3, 4]
var a = [1, [1, 2], 2, 3];
FIND_AND_REMOVE(a, [1, 2]);
NumberField = a; //NumberField = [1, 2, 3]
INDEX_OF - finds the equal item(s) and returns their index as an array of number(s).
param1 is local variable of type array
param2 specifies value which will be found in param1
var a = [10, 20, 30, 40];
INDEX_OF(a, 20); //returns [2]
var a = [10, 20, 30, 40, 30];
INDEX_OF(a, 30); // returns [3,5]
var a = [10, 20, 30, 40];
INDEX_OF(a, 20); //returns [2]
var a = [10, 20, 30, 40, 30];
INDEX_OF(a, 30); // returns [3,5]
AS_ARRAY - converts input parameters to an array
Since it is returning the array, the field to which the value is returned needs to support multiple values.
var a = 20;
var index = AS_ARRAY(a); // index = [20]
someField is single number field out of group with predefined values 10, 20, 30 and value 10 is set
var index = AS_ARRAY(someField); // index = [10]
someField is multi number field out of group with predefined values 10, 20, 30 and values 10 and 20 are set
var index = AS_ARRAY(someField); // index = [10, 20]
someField is single number field in a group with predefined values 10, 20, 30 and values are set:
var index = AS_ARRAY(someField); // index = [10, 20, 30]
someField is multi number field in a group with predefined values 10, 20, 30 and values are set:
var index = AS_ARRAY(someField); // index = [[10, 20], [20], [30, 10, 20]]
RANDOMIZE - function generates random string according to the provided string length, it returns array of characters
Input 1 is the length, number type, which defines how many characters need to be returned.
Input 2 is isNumber, boolean value, which defines whether the returned array of characters is only numbers.
RANDOMIZE(length, isNumber)
Item keyword is used to access the specific item from the resultset used in expressions in repeatable rows or columns determined by record reference or determined by filter (Item.FieldIdentifier). Namely, they are enabling the creation of bound tables, as explained in Chapter Repeatable Options of the Configuration Portal Administrator Manual.
Item keyword is used as a record reference variable, to access a specific item from the result set of field values in repeatable rows or columns. Item is only used in the Formula Expression for fields in repeatable rows or columns where the repeatable group option is set to Determined by Record Reference or Determined by Filter.
In our example in the Configuration Portal Administrator Manual on how to make a bound table in Alchemy we configured a bound table in the record Work Plan that is determined by the Literature Search record reference. In this case, the Item keyword will act as a record reference variable, for the Literature Search record.
Since the Item is a record variable, all other options explained in the Chapter Variables are available, as long as they are used inside the repeatable row group. Some use cases are
Item.CreatedBy - must use in formula expression for a user type field. Returns a user who created a certain record of the table
In this section, you will see some functionalities that can be utilized to write a proper script.
As already described in chapter 4.4.2 Alphanumeric, alphanumeric fields can have predefined values. In some cases, you will need to configure the first alphanumeric field with predefined values. Then, depending on the predefined value selected in this field, a specific set of predefined values should be available for selection in the second alphanumeric field. This is known as Conditional Predefined Values and it can be realized by writing a proper script. Here’s an example.
You need to configure the Test Instruction Record Template. This template will be used for the creation of different records - a variety of test instructions in your organization. First, you need to set the type of your Test Instruction. Make an alphanumeric field (name Test Type, identifier TestType) and give it three predefined values: Characterization, Application, Microbiology.
Make the second alphanumeric field (name: Test, identifier Test), where different tests will appear in a dropdown, depending on the predefined value selected in the Test Type field. Let’s say these are the values you need to configure:
First, in the VALUES tab of the field Test, add all predefined values to the field Test (pH, Viscosity, Density, Scrub Resistance, Adhesion, Tensile Elongation, Fungal, Bacterial).
Second, write the Expression for conditional predefined values like in figure 3 where the Values Tab of the field Test is shown with the Expression entered.
Correct Expression is the following:
var a=[];
if(TestType=='Characterization')
{a=['pH','Viscosity', 'Density'];}
else if(TestType=='Application')
{a=['Scrub Resistance', 'Adhesion', 'Tensile Elongation'];}
else if(TestType=='Microbiology')
{a=['Fungal', 'Bacterial'];}a;
First line var a=[]; defines and initializes a variable in the code. Variable a is initialized as an empty array [].
When the first if condition if(TestType=='Characterization') is satisfied (i.e., field with identifier TestType has a value 'Characterization'), variable a can have only these values {a=['pH','Viscosity', 'Density'];}. This means that when the user in the User Portal selects 'Characterization' in the Test Type, in the dropdown of the field Type will appear only ‘pH’, ‘Viscosity’, and ‘Density’.
It goes the same for the second if condition if(TestType==’Application’), when variable a can have only one of these values {a=['Scrub Resistance', 'Adhesion', 'Tensile Elongation'];}. The same principle is for the third if condition. Finally, there is the last part of the expression: a; that will return the value of variable a to the field Test Type and filter down the list of predefined values based on the satisfied condition.
Another important and powerful functionality that you can use in every field type is Action - On Change. Each time the value of the field is changed, this functionality will execute a specific action. The action is added to the field which will be changed.
In the Menu of the field, under tab ACTIONS, you can add a new action by clicking on the button.
Upon adding new action you have to set On* to “Changed”. Under the Term you write an Expression of action that should be executed when a user changes the value of the field. Here’s an example.
In chapter 4.64. Button of the Configuration Portal Administrator Manual, there is an Example 1 - Button recalculates all rows in the table. In this example, you learned how the user can compose the formulation, and how (s)he can change the formulation and easily recalculate all amounts in the table with the press of a button “Normalize”.
Thanks to the On Change action functionality, you can make this recalculation completely automatic - there is no need for a user to press the button “Normalize”. Each time user changes Target Batch Size L, all rows in the table (all amounts of materials added to the formulation) will be recalculated automatically.
In the Actions tab of the field Target Batch Size L add a New Action. Set the On* to “Changed” and under Term write the same calculation that is in the action script of a button “Normalize”:
AddedMaterialg = AddedMaterialL / TotalVolume * TargetBatchSizeL * MaterialDensity
See how it’s done in Figure 4.
It is important where are you setting this functionality. Try to take into account all possible scenarios that can happen from the User’s perspective regarding changing the field’s value because each time the value of the field is changed, action will happen.
As already explained, Action can be added to the field. Each time a field's value is changed, an action will be executed.
This action can also be performed on a field in a specific row of the repeatable row. Specifying a row is done by using the keyword CurrentRow. Example 3 - Button with Helper Field will be upgraded to explain this functionality.
In the Record Template Work Plan, there is a table for defining new formulations: users can add rows, set names, and target properties for pH, Density, and Viscosity. With a click of a button, Formulation Records are created with the names defined in the table. The Formulation Record created on the button will have a name defined in an alphanumeric field Experimental Formula Name (identifier ExperimentalFormulaName). Let’s say you want to configure that the user can change the name of the Formulation record by changing the field Experimental Formula Name in the table.
It was already explained that the Create Formulations button requires an additional record reference helper field. In Example 3 - Button with Helper Field, the helper field is located outside of the repeatable row group. Now, in order to establish a link between each row in the table with the formulation created, you need to place a helper field inside a repeatable row.
Given the fact that the helper field is now inside the repeatable row, each line of the button script should be updated to access value for each row using FormulaHelper[i], therefore the button script needs to be modified, like this:
for(var i = 1; i <= ExperimentalFormulaRRGroup.RowCount; i = i + 1)
{
if(FormulaCreated[i] == false)
{
FormulaHelper[i] = CREATE_RECORD('FormulationDesign','LabWork',Process,1);
FormulaHelper[i].FormulationName = LabWorkNumber+' ' + ExperimentalFormulaName[i];
FormulaHelper[i].TargetpHfrom=TargetpHfrom[i];
FormulaHelper[i].TargetpHto=TargetpHto[i];
FormulaHelper[i].TargetViscosityFrom=TargetViscosityFrom[i];
FormulaHelper[i].TargetViscosityTo=TargetViscosityTo[i];
FormulaHelper[i].TargetDensityFrom=TargetDensityFrom[i];
FormulaHelper[i].TargetDensityTo=TargetDensityTo[i];
FormulaCreated[i] = true;
Changing the name of the specific Formulation Record is done by changing the field Experimental Formula Name in that specific row.
In the field Experimental Formula Name (identifier ExperimentalFormulaName), in the tab Actions, add New Action - On Change with the following script:
if(FormulaHelper[CurrentRow] IS SET)
{
FormulaHelper[CurrentRow].FormulationName = LabWorkNumber + ' ' + ExperimentalFormulaName[CurrentRow];
}
Condition if(FormulaHelper[CurrentRow] IS SET) must be placed in order to apply On Change Action only for the already created Formulations.
This powerful feature enables you to change the property of the field using the appropriate Alchemy script expression. It can be used on all field types except for Label, Image, and Button.The general form of Conditional Formatting Expression is
FieldIdentifier.FieldProperty = PropertyValue;
Where FieldProperty is the keyword to access a specific property of the field defined by the FieldIdentifier. Specific property is getting the value defined by PropertyValue.
Conditional Formatting Expression can be placed on Formula Expression, Button Action Expression, Field Action - On Change, and Stage Action - Execute Script.
When the Action is triggered, the script will be executed and the field property will be updated. If you change the Conditional Formatting Expression in the Configuration Portal and backpropagate - field property will not be affected. It will remain as defined by the previous Conditional Formatting Expression.
Field Dimensions (Width, Height, and Margins) CANNOT be changed using Conditional Formatting.
Here is the list of all Field Properties that CAN be changed using Conditional Formatting. In the list of available field properties below, for each property there is an output type and possible values in parentheses:
Field property Hidden should be used only for a field that is not inside a repeatable row or repeatable column. Other properties can be used if the field is inside a repeatable row/column. If you need to target a specific row inside a table, use the Current Row system data in the form:
FieldIdentifier[CurrentRow].FieldProperty = PropertyValue;
Examples
Let’s build on an already existing example - IFS conditions. In this example we already have explained how an alphanumeric field can show text IN RANGE or OUT OF RANGE depending on the entered value for pH, Density or Viscosity and defined target values (i.e. range). To make it more visually appealing, let’s say you need to configure the same alphanumeric field to be green when the entered value is in range and red when it is out of range. In the Formula Expression of alphanumeric field (identifier pHRange) there should be this expression:
var a = ' ';
if(pH>=TargetpHfrom AND pH<=TargetpHto)
{pHRange.InputBackgroundColor='#93C480';
a='IN RANGE';}
else
{pHRange.InputBackgroundColor='#DF6969';
a='OUT OF RANGE';}
a;
Let’s read and explain this script. In the first condition we are checking if entered number for pH value (in the field with identifier pH) is greater than defined start range limit (value defined in the field with identifier TargetpHfrom) and lower than defined end range limit (value defined in the field with identifier TargetpHto). If that condition is satisfied, Conditional Formatting Expression will be executed by changing the field property InputBackgroundColor to green ('#93C480'). Also, the field will show the provided text: 'IN RANGE'. In the case that this condition is not met, field property will be red ('#DF6969') and the field will get the value 'OUT OF RANGE'.
The Hex Code can be used as a property value by providing it as a string (under single quotes). The list of Hex Codes for the default Alchemy Colors available in any color Picker is:
Additional hex codes are available by selecting “Custom” in the Color Picker. Choosing the desired color will display its Hex Code. You can copy and paste it from there in the Conditional Formatting Expression.