This is the end user documentation for the script language. The script language becomes available in Microbizz in the start of 2022.

Overview

Microbizz provides a script language which can be used for generating the content of custom fields and for writing plugins.

Previously Microbizz has used patches to achieve things like updating custom fields with special values, f.ex. a patch could copy a value from a company every time a task was saved. This can now be achieved without patches, indeed without involving Ventu. Patches are obsolete and should be replaced with plugins, but plugins can still require a lot of work just to achieve things like copying a few fields.

Scripts can be written by anyone with programming experience. Scripts are written within Microbizz, there is no need for an external server to host a plugin.

This documentation isn't intended to teach you programming, it only describes the usage, syntax and features of the Microbizz script language. We assume you are familiar with the basics of programming. If you don't know how to program, we suggest that you start by reading an absolute beginnners introduction to Python. The Microbizz script syntax looks a lot like Python, but it is not Python.

F.ex. you may want to have a custom field which displays the average of the values in 2 other custom fields, but only include the values that are larger than 1000. This can be done with the following script:

n = 0
sum = 0
if this.custom200 > 1000
  n++
  sum += this.custom200
if this.custom201 > 1000
  n++
  sum += this.custom201
if n == 0
  return ""
return sum / n

Example 1 - average of custom fields with values > 1000

The script functionality is only available if it has been enabled by Ventu. Currently only tasks, companies, users, invoices, persons, projects, quotations and equipment provide script custom fields.

Limitations

You may have multiple script custom fields in the same module, but they cannot read each others values. F.ex. you may have two script custom fields for tasks, lets call them "Total hours" and "Total usage", but you cannot read the value of "Total hours" from the "Total usage" script.

Scripts custom fields are quite slow, don't use them for extensive calculations, it will slow down your system, in particular when doing searches where many objects are displayed at the same time. Performance is usually only a problem when using script custom fields, not when using script plugins. The slow speed is mainly due to the speed of accessing objects in  Microbizz.

Custom field

When you create a custom field of type "text" or "number", you can select a subtype. Select the subtype "Script" and a field for editing the script appears.

The script is run every time the custom field is read. The field cannot be shown in the app, or be read by the API, as the value can be very dynamic  and can thus be outdated when the app displays it. The app cannot run the script.

Notice that not all objects that support custom fields also support script custom fields.

Plugins

A script may be run as a plugin, i.e. whenever Microbizz would normally call a plugin, the script will be run instead.

The plugins are edited in Microbizz, in the System module, in the ADMIN > SCRIPT PLUGIN SETUP menu.

Plugins are called with the variable this set to the object that caused the plugin to be called. The variable argv holds the variables that are usually passed to plugins, see Making a Microbizz app.

Tabs

A script may be set up to run in a tab on a details page for an object, eg. for tasks or customers. The script will then be run in a <iframe>. This is still experimental.

Writing nice code

We recommend that you use exactly one SPACE in these situations, however these are just recommendations:

  • on either side of = or += etc in assignments, e.g.:  x += 4 
  • after a keyword, unless it is the last thing on a line, e.g.:  for i in [4, 5, 6]
  • on either side of operators in expressions, e.g. :  res = x + 4 - (4 * y + 2)
  • after the comma in arrays and dicts and argument lists, and after the : in dicts , but not after left brackets or before right brackets  

Obviously there are places where it is necessary to have at least 1 SPACE to prevent ambiguity.

We recommend using lowercase letters for variables and function names. Some built-in objects may have names in UPPERCASE.

Syntax

Blocks

Blocks are defined by being indented by 2 SPACEs. Other languages use curly brackets { } to define blocks. A block ends at the next line with less indentation, or at the end of the script:

if this.custom200 > 1000
  n++
if this.custom201 > 1000
if this.custom202 > 1000
n++
else
n--

Example 2 - indentation

Functions

Functions are defined by the keyword def, followed by the function name, followed by the arguments in brackets; the function body must be indented. A function may return a value, using the keyword return.

def positive_sum(a,b,c)
  sum = 0
  if a > 0
    sum += a
  if b > 0
    sum += b
  if c > 0
    sum += c
  return sum

Example 3 - simple function

Names

Variables and functions must have names that begin with A-Z, a-z or _ (underscore), and may only include characters A-Z, a-z, 0-9 and _ (underscore).

Names should not be longer than 32 chars.

The following keywords are reserved and may not be used for variable names or function names:

def, for, in, while, return, if, else, elseif, break, continue, float, int, array, string, object, const, dict,var, global, class, new, include, import.

Comments

Lines starting with //, or #, are comments and are ignored; comments may be indented by any multiple of 2 SPACE.

Also,  // or # after an expression also indicates that the rest of the line is a comment.

// example of comments
a = 1  // this is a comment
b = 2 # this is also a comment

Example 4 - comments 

Variable types

The following types are supported:

TypeExplanationExamples
stringText is always in UTF-8, use double quotes "  "headline = "Microbizz script"
numberThe decimal point is . (full stop)price = 12.34
arrayThis is a comma seperated list of values, types may be mixed

myarray = [1, 2, 3,"Some text", [4, 5, 6]]

sometext = myarray[4]

dictA list of key:value pairs, the syntax is much like the Javascript object syntax, or JSON

peter = {name: "Peter", age: 15, height: 160}

age = peter.age

age = peter["age"]

object

title = this.title

today = Date.date()

data

Expressions

Expression syntax is pretty standard. The operators are described below. 

Variables are assigned using =. Variables can be updated using += or -= etc.

Operators


* / %  ^Arithmetic; notice that it is valid to divide by 0
+ -

Arithmetic, but + also works for strings and arrays;

a+b means string concatenation if either a or b is a string

||  &&  |  & ^Boolean and bitwise
==  !=  <  <=  >=  >  ===  !==Comparisons, the result is 0 or 1
.Object access
(bool) (number) (array) (string) (int)Cast, convert a value to another type of value
!Logical negation
= += -= *= /= &= |= ^=Assignment

Controls structures

The if-elseif-else and while control structures are supported. while supports break and continue.

res = 0
if this.custom200 < 1000
  res = this.custom200
elseif this.custom201 < 1000
  res = this.custom201
else
  res = 1

while res > 2
  res /= 2

Example 5 - control structures

Loops can be made using the for keyword.  This iterates through the values in some type of iterable value, which can either be a the result of the range() function, or a string or array or dict. for loops support continue and break.

for i in range(1, 100)
  do_something(i)
for letter in "ABCDEFG"
  do_something(letter)
for prime in [1, 2, 3, 5, 7, 11, 13]
  do_something(prime)
dictionary = {a: 1, b: 2, c: 3}
for key in dictionary
  do_something(key, dictionary[key])

Example 6 - for loops

Variable scope

Variables that are created outside a function are global. To use a global variable from inside a function, you need to use the global keyword to specify the variable:

myname = "Microbizz"
mytitle = "Mr."
def getmyname()
  global myname, mytitle
  return "My name is "+myname

Example 7 - global variables

Variables that are created inside a function only exist until the program leaves the function. You cannot have variables that are local to a block.

Pass by reference

When you pass variables as arguments to a function, scalar/primitive variables (strings and numbers) are passed by value, whereas arrays/dicts/objects are passed by reference. This means that the function may modify arrays/dicts/objects that are passed, but not strings and numbers.

myname = {name:"Microbizz"}
mytitle = "Mr."
changename(myname, mytitle)
// myname.name may now have changed, but mytitle is the same

Example 8 - pass by reference

Escaping

Strings are in UTF-8, unicode character codes may be inserted using \uXXXX.

The following character sequences are also recognised and have the usual meaning: \r, \n, \t, \b, \"

Built-in functions

The built-in functions are described here: Microbizz script - functions

Many of the functions are accessed through objects like the Date object, which provides functions like Date.time() and Date.date(), or the Math object which provides functions like Math.max() and Math.round(). Other functions are accessed as properties of variables, like   "this is a string".length() .

Context

The script will be called with a context, the context being the Microbizz object that is relevant for the script.

F.ex. if the script is run to generate the value for a task custom field, the context is the task. The context is accessed through the object named this. Refer to the API documentation for details about the various objects. Some objects allow you to easily access other objects that are somehow connected, e.g. if this is a ask, then this.user is the responsible user for the task.

Microbizz objectDocumentationList of connected objects
CustomerMicropedia - the "customer" object

this.person

this.user

EdiInvoiceMicropedia - the "edi" object

this.todo

this.user

this.customer

EquipmentMicropedia - the "tool" object

this.placeofhome

this.user

InvoiceMicropedia - the "invoice" object

this.user

this.customer

InvoiceLineMicropedia - the "invoiceline" object

this.todo

this.customer

PersonMicropedia - the "person" object
ProjectMicropedia - the "project" objectthis.user
RegistrationMicropedia - the "registrationentry" object

this.todo

this.user

this.customer

TaskMicropedia - the "todo" API object

this.customer

this.user

this.project

UserMicropedia - the "user" object
ProductMicropedia - the "product" object
PlanningMicropedia - the "prodplanentry" object
QuotationMicropedia - the "salescontract" object

this.customer

this.user

AppointmentMicropedia - the "event" object

this.user

this.customer

Script return value

When the script is run to generate the value for a custom field, the return value is specified by the keyword return outside of a function, this will also stop execution of the script; the return value is what is displayed in Microbizz.

area = "nowhere"
if this.postcode >= 1000 && this.postcode <= 2999
  area = "capital"
elseif this.postcode < 9999
  area = "countryside"
return area

Example 9 - return value

The return value may either be text, as in the example above, or it may be HTML generated by the HTML object.

The return value is not used when the script is run as a plugin, or when the script is displayed in a tab.

How to edit the script

Depending on the context (custom field or plugin, task, company or equipment) different functions may be available. The editor provides a list of the available functions.

Plugins and tabs are setup and edited in the System module. We assume that you are already familiar with the basics of plugins. Custom fields are edited where you usually create custom fields, just select the type Text and the subtype Script.

Debugging

When testing/debugging a script where are to ways to output information. You can either use the echo() function, which only works from the script testing page; or you can use the function Microbizz.Log() which will write stuff to the System log; the log may then be viewed in the System module.

Microbizz logs various script related things in the System log.

Sandbox

You may run test the plugins/scripts by running them in a simple "sandbox", which prevent certain functions for doing anything; there is a Run in sandbox checkbox in the script editor for this.

This only applies when the script is run by pressing the Run button in the script editor.

The sandbox prevents the this.save() function from doing anything, it also prevents the functions in the API object from doing anything, and it prevents the Microbizz.SaveProperty() function from working,

Extending the script

If a customer requires special functionality, then this may be developed by Ventu and added to the script as a patch that is available only to selected customers.

F.ex. there may be complicated pricing calculations that depend on the date and postcode of a task. This might be developed by Ventu and could then become available as a new script object providing access to the calcuations. In the unrealistic example below, a new Tax object provides two new functions:

taxrule = Tax.getRule(this.zip, this.createdate)
return Tax.calculate(this.price, taxrule)

Example 10 - unrealistic extension

Real world examples

Copy a custom field from the company to the task whenever a task is saved

This requires that the custom fields are marked as "show in app". We assume that the company custom field has id=321 and the task custom field has id=123.

cus = this.customer
if cus.indb()
  this.sv("custom123", cus.custom321)
  this.save()

Example 11 - copy custom field

Copy a custom field from a project task to all subtasks whenever the project task is saved

This requires that the custom field is marked as "show in app". We assume that the custom field has id=321.

if this.tasktype != 4 && this.parenttodo == 0
subtodos = this.subtodos
if isarray(subtodos)
for id in subtodos
todo = Microbizz.GetTodoByID(id)
todo.sv("custom321", this.custom321)
todo.save()

Example 12 - copy custom field

Known problems

1)

If an array holds a dict as an element, then accessing an element in the dict using eg.

  value = arrayname[0].dictelement

will not work ; you should use something like

  dictname = arrayname[0]

  value = dictname.dictelement

2)

Some syntax errors are not detected/reported.