# Standard Integrations

System integration options including REST services

# Docusign Integration

## <span class="mw-headline" id="bkmrk-prereq-1">Prereq</span>

To set up DocuSign integration an account that meets one of the following criteria is needed.

- A DocuSign-partner or
- Advanced developer" license

To use the integration, the uploading user only needs a regular eSignature subscription.

The integration will initially be created in demo mode and has to be approved, before it can be moved to production.  
Approval requires 20 consecutive successful uploads performed within the last 30 days.  
After that, an account with the proper access permissions has to sign in and the integration will be transferred to their account.

## <span class="mw-headline" id="bkmrk-setup-in-docusign-1">Setup in DocuSign</span>

First: Sign in with an admin account and create a new "Integration" under "Settings" -&gt; "Integrations" -&gt; "Apps and Keys". Give it a descriptive name. This name will be shown to all who upload documents via the integration.

Second: Add a "Secret Key", save it. This is needed for *docuSignSecretKey*.

Third: Under "Redirect URIs" provide a URL using the following syntax:  
https://\[domain\]/\[TS-app-name\]/\[docuSignAuthCallbackUrl\]

URL Example: [https://omega.tempusserva.dk/TempusServa/main?command=dk.tempusserva.signing.docusign.PageLoginCallback](https://omega.tempusserva.dk/TempusServa/main?command=dk.tempusserva.signing.docusign.PageLoginCallback)

## <span class="mw-headline" id="bkmrk-setup-in-ts-1">Setup in TS</span>

There are 11 parameters available. Seven of these (marked with an asterisk below) must be set for the integration to work:

- docuSignActive\*
- docuSignLog
- docuSignApiHost\*
- docuSignAuthHost\*
- docuSignHost\*
- docuSignAccountId\*
- docuSignIntegrationKey\*
- docuSignSecretKey\*
- docuSignAuthCallbackUrl
- docuSignEventCallbackUrl
- docuSignBrandId

The Field Type Component for which DocuSign upload should be available must be changed from "[Files: Documents](https://wiki.tsnocode.com/index.php?title=FieldFiles "FieldFiles")" to "[Files: Documents with signing](https://docs.tsnocode.com/books/field-type-reference/page/documents-with-signing "FieldFilesSigning")".

### <span class="mw-headline" id="bkmrk-docusignactive-1">docuSignActive</span>

To enable the DocuSign functionality, this has to be set to "true".

### <span class="mw-headline" id="bkmrk-docusignlog-1">docuSignLog</span>

This is used for debugging and should **not** be enabled in production.  
It enables debuggin-logs on all uploads, both in TS and in DocuSign, thus breaking confidentiality, because all parameters will be saved to a log somewhere.

### <span class="mw-headline" id="bkmrk-docusignapihost-1">docuSignApiHost</span>

This parameter is account-dependant.  
It can be found on the "Apps and Keys" page in DocuSign, labeled "Account Base URI".

### <span class="mw-headline" id="bkmrk-docusignauthhost-1">docuSignAuthHost</span>

This parameter should always be "[https://account-d.docusign.com/oauth/](https://account-d.docusign.com/oauth/)" when testing the integration and "[https://account.docusign.com/oauth/](https://account.docusign.com/oauth/)", when the integration is in production.

### <span class="mw-headline" id="bkmrk-docusignhost-1">docuSignHost</span>

This parameter is account-dependant. It is used to redirect the user, after a successful upload.  
It's value should be "[https://appdemo.docusign.com](https://appdemo.docusign.com/)" during testing.  
The production value is found by signing in with an account, that will be using the integration, and then copying the URL from the browser. Remember to only copy from beginning to end of *docusign.com*, don't include the trailing /.

### <span class="mw-headline" id="bkmrk-docusignaccountid-1">docuSignAccountId</span>

This attribute is account-dependant.  
It can be found on the "Apps and Keys" page in DocuSign, labeled "API Account ID".  
It's a GUID, formatted like this: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

### <span class="mw-headline" id="bkmrk-docusignintegrationk-1">docuSignIntegrationKey</span>

This parameter is integration-dependant.  
It can be found on the "Apps and Keys" page in DocuSign, or when viewing the details about the integration.  
It's a GUID, formatted like this: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

### <span class="mw-headline" id="bkmrk-docusignsecretkey-1">docuSignSecretKey</span>

This parameter is integration-dependant.  
It can be found when viewing the details about the integration, in DocuSign.  
You will only have access to the entire key when creating it, but you can just add a new one if the key is lost. Remember to delete the old one.  
It's a GUID, formatted like this: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

### <span class="mw-headline" id="bkmrk-docusignauthcallback-1">docuSignAuthCallbackUrl</span>

This parameter I static and should always be  
"main?command=dk.tempusserva.signing.docusign.PageLoginCallback".

### <span class="mw-headline" id="bkmrk-docusigneventcallbac-1">docuSignEventCallbackUrl</span>

This parameter is static and should always be  
"docusign-event-callback".

### <span class="mw-headline" id="bkmrk-docusignbrandid-1">docuSignBrandId</span>

This parameter is optional.  
In DocuSign it is possible to create "Brands" ("Settings" -&gt; "Account" -&gt; "Brands").  
All brands have a unique ID, it doesn't have to be the owner of the integration that creates the brand.  
To enable custom branding in email, the signing formula, etc., input the brandID here.  
If this parameter is left is empty, the default DocuSign branding will be used.  
It's a GUID, formatted like this: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

## <span class="mw-headline" id="bkmrk-moving-to-production-1">Moving to production</span>

When moving the integration to production, the following should be taken into account.

Make sure to complete 20 successful uploads in a row within 30 days.  
Uploads don't have to be full featured documents, empty ones will do, and they don't have to be different documents, you can upload the same document 20 times.

If the production account is different from the test account, the following parameters should be updated:

- docuSignAccountId
- docuSignApiHost

Make sure to change the following parameters:

- docuSignHost
- docuSignAuthHost

If the TS Application for test and production are different (different base URL), remember to add the "Redirect URIs" in DocuSign. And make sure that docuSignLog is set to false.

# E-Boks doc2mail

# Install components

Before you start ensure Java 8 is the default

### <span class="mw-headline" id="bkmrk-certificate-1">Certificate</span>

Ensure you have a valid certificate

```bash
sudo nano /mnt/sda/certs/doc2mail.pke
```

Copy paste contents of the certificate

```bash
sudo chmod 777 /mnt/sda/certs/doc2mail.pke
```

### <span class="mw-headline" id="bkmrk-binaries-1">Binaries</span>

Download OneTooX implementation

```bash
cd /mnt/sda/deploy
sudo wget https://www.tempusserva.dk/install/doc2mail/doc2mail.zip
unzip doc2mail.zip
sudo mv lib doc2mail
cd doc2mail
sudo chmod 777 *
```

# Test commandline setup

In this step you will configure and test the configuration for the commandline

Update the parameters below

- i: Location of a test PDF
- t: Name of the document type
- r: A real CPR number

Execute the following command

```bash
java -cp .:/mnt/sda/deploy/doc2mail/* dk.doc2mail.client.FileUploaderClient --address "https://privat.doc2mail.dk/delivery/FileUploader.asmx" --certificate "/mnt/sda/certs/doc2mail.pke" -i "/tmp/eboks_test.pdf" --title "Oplysningsskemaet" -d ebokskmdprint -u "domainX\userY" -t "tempus serva" --ss "Oplysningsskema" --testmode 3 -r 1234561234 --rt CPR
```

A success message will look something like

```xml
 <uploadStatus>
   <deliveryInformation>
       <destination>5</destination>
       <errorCode>0</errorCode>
       <id>148069122</id>
       <mailPriority>B</mailPriority>
       <responseInfo>No response</responseInfo>
       <status>OK</status>
       <succeeded>true</succeeded>
   </deliveryInformation>
   <message>OK</message>
   <success>true</success>
   3379
 </uploadStatus>
```

# Server configuration: Linux

In this step you will deploy the commandline configuration

Make the following substitutions

- i: $DOCUMENT
- title: $TITLE
- r: $CPR
- testmode: 0

The connection string should look something like

```bash
java -cp .:/mnt/sda/deploy/doc2mail/* dk.doc2mail.client.FileUploaderClient --address "https://privat.doc2mail.dk/delivery/FileUploader.asmx" --certificate "/mnt/sda/certs/doc2mail.pke" -i "$DOCUMENT" --title "$TITLE" -d ebokskmdprint -u "domainX\userY" -t "tempus serva" --ss "Oplysningsskema" --testmode 0 -r $CPR --rt CPR
```

### <span class="mw-headline" id="bkmrk-create-execution-scr-1">Create execution script</span>

```bash
cd /mnt/sda/deploy/doc2mail
sudo nano send.sh
```

Paste the execution script (above)

```bash
DOCUMENT=$1
CPR=$2
TITLE="$3 $4 $5 $6 $7"
```

```bash
java -cp .:/mnt/sda/deploy/doc2mail/* dk.doc2mail.client.FileUploaderClient --address "https://privat.doc2mail.dk/delivery/FileUploader.asmx" --certificate "/mnt/sda/certs/doc2mail.pke" -i "$DOCUMENT" --title "$TITLE" -d ebokskmdprint -u "domainX\userY" -t "tempus serva" --ss "Oplysningsskema" --testmode 0 -r $CPR --rt CPR
```

The set up permissions for the script

```bash
sudo chmod 777 send.sh
```

### <span class="mw-headline" id="bkmrk-copy-the-value-to-th-1">Copy the value to the server configuration parameter</span>

1. Modules &gt; Static content &gt; Add
2. Set title = **eboksIntegrationTemplate**

```bash
/mnt/sda/deploy/doc2mail/send.sh @@DOCUMENT@@ @@CPR@@ @@TITLE@@
```

### <span class="mw-headline" id="bkmrk-set-doc2mail-as-ebox-1">Set doc2mail as ebox provider</span>

1. Edit the setting **eboksProvider** (url: ServerSettingSingle.do?PolicyName=eboksProvider&amp;DataType=3)
2. Set the value to: **doc2mail**

### <span class="mw-headline" id="bkmrk-activate-token-rewri-1">Activate token rewrites</span>

1. Edit the setting **interfaceTokenRewrite** (url: ServerSettingSingle.do?PolicyName=interfaceTokenRewrite&amp;DataType=1)
2. Set the value to **true**
3. Edit the setting **interfaceTokenRewriteUrl** (url: ServerSettingSingle.do?PolicyName=interfaceTokenRewriteUrl&amp;DataType=3)
4. Set the value to **[https://svar.link/FOOBAR/](https://svar.link/FOOBAR/)**
    - FOOBAR is the name of your application as registered in svar.link

### <span class="mw-headline" id="bkmrk-reboot-the-server-1">Reboot the server</span>

# Server configuration : Windows

In this step you will deploy the commandline configuration

Make the following substitutions

- i: @@DOCUMENT@@
- title: @@TITLE@@
- r:@@CPR@@
- testmode: 0

The connection string should look something like

```powershell
java -cp .:/mnt/sda/deploy/doc2mail/* dk.doc2mail.client.FileUploaderClient --address "https://privat.doc2mail.dk/delivery/FileUploader.asmx" --certificate "/mnt/sda/certs/doc2mail.pke" -i "@@DOCUMENT@@" --title "@@TITLE@@" -d ebokskmdprint -u "domainX\userY" -t "tempus serva" --ss "Oplysningsskema" --testmode 0 -r @@CPR@@ --rt CPR
```

### <span class="mw-headline" id="bkmrk-copy-the-value-to-th-1">Copy the value to the server configuration parameter</span>

1. Modules &gt; Static content &gt; Add
2. Set title = **eboksIntegrationTemplate**
3. Set value *the string above*

### <span class="mw-headline" id="bkmrk-set-doc2mail-as-ebox-1">Set doc2mail as ebox provider</span>

1. Edit the setting **eboksProvider** (url: ServerSettingSingle.do?PolicyName=eboksProvider&amp;DataType=3)
2. Set the value to: **doc2mail**

### <span class="mw-headline" id="bkmrk-activate-token-rewri-1">Activate token rewrites</span>

1. Edit the setting **interfaceTokenRewrite** (url: ServerSettingSingle.do?PolicyName=interfaceTokenRewrite&amp;DataType=1)
2. Set the value to **true**
3. Edit the setting **interfaceTokenRewriteUrl** (url: ServerSettingSingle.do?PolicyName=interfaceTokenRewriteUrl&amp;DataType=3)
4. Set the value to **[https://svar.link/FOOBAR/](https://svar.link/FOOBAR/)**
    - FOOBAR is the name of your application as registered in svar.link

### <span class="mw-headline" id="bkmrk-finally-reboot-the-s-1">Finally reboot the server</span>

# Sending emails

## <span class="mw-headline" id="bkmrk-preparations-1">Preparations</span>

Get the information of the outbound server

- Server address
- Mail account

Note that the SMTP server may be the same as the IMAP or POP server

## <span class="mw-headline" id="bkmrk-setup-in-tempus-serv-1">Setup in Tempus Serva backend</span>

Go to: **Designer &gt; Modules &gt; Configuration**

In the 'Text' search field, search for Server settings containing **smtp**

[Policy\_reference#Mail\_server](https://docs.tsnocode.com/books/policy-reference "Policy reference")

<table id="bkmrk-alias-empty-meaning-"><tbody><tr><th>Alias</th><th>Empty</th><th>Meaning</th><th>Example</th></tr><tr><td>**smtpServer**</td><td>No</td><td>URL for the outgoing SMTP server</td><td>send.one.com</td></tr><tr><td>**smtpSystemEmail**</td><td>No</td><td>Sender address on email sent from the server</td><td>noreply@acme.com</td></tr><tr><td>**smtpUsername**</td><td>Yes</td><td>Account name for sending emails</td><td>noreply@acme.com</td></tr><tr><td>**smtpPassword**</td><td>Yes</td><td>Password for the above account</td><td>secret1234</td></tr></tbody></table>

Communication with the SMTP server can be tweaked using

<table id="bkmrk-alias-meaning-exampl"><tbody><tr><th>Alias</th><th>Meaning</th><th>Example</th></tr><tr><td>**smtpUseTLS**</td><td>Use TLS communication</td><td>true</td></tr><tr><td>**smtpMailDelay**</td><td>Throtling between each mail (seconds)</td><td>500</td></tr><tr><td>**smtpSendRetries**</td><td>Number of attemts to send each mail</td><td>3</td></tr></tbody></table>

  
In order to test applications a testmode can be activated

<table id="bkmrk-alias-meaning-exampl-1"><tbody><tr><th>Alias</th><th>Meaning</th><th>Example</th></tr><tr><td>**smtpTestMode**</td><td>Activates the test mode</td><td>true</td></tr><tr><td>**smtpTestEmail**</td><td>Email that receives all emails when in testmode</td><td>someone@acme.com</td></tr></tbody></table>

# Embed TS in other sites

To embed the output of a public codeunit, on another website, the following code can be used.

```html
<div id="tsContent"></div>
<script type="text/javascript">
    $.ajax({
        url: 'https://[SYSTEM-DOMAIN]/[APP-NAME]/mainpublic?command=[CODEUNIT-NAME]]',
        success: function(data) {
            $('#tsContent').html($(data).find(".mainContent").html());
        },
    });
</script>
```

It requires jQuery to be loaded beforehand.

# MitID Integration

## <span class="mw-headline" id="bkmrk-what-it-does-1">What it does</span>

We have a generic integration to Criipto (a MitID, and other eID, broker) and an account, allowing for sign in and document signing with MitID/nemID.

### <span class="mw-headline" id="bkmrk-sign-in-1">Sign in</span>

Sign in can be setup as a webinterface or as SSO from the login-page (not fully supported yet), the authentication method isn't available in the dropdown for the webinterface, as of writing, it has to be set through the database (the live database).

```mysql
UPDATE forminterface SET AuthenticationType = 5 WHERE InterfaceID = [ID];
```

### <span class="mw-headline" id="bkmrk-document-signing-1">Document signing</span>

One or multiple documents can be send for signature, one or more people can sign the batch and it is possible to enforce CVR or CPR. This process can be initiated from a StatusAction or be accessing a url.

A page is appended to the signed documents showing who signed it and when, in the order they signed it.

## <span class="mw-headline" id="bkmrk-prereq-1">Prereq</span>

To setup this an "Application" has to be set up in Criipto, one for sign in and one for signatures, and a CNAME dns-record has to be created.

The ID's and secrets created here will be needed later.

## <span class="mw-headline" id="bkmrk-setup-in-criipto-1">Setup in Criipto</span>

First, a domain has to be added.

Haed to the Criipto Dashboard, select "Domains" in the menu, make sure that you are in "Production", click "Add production domain".

Name it `[customer]-eid.tsnocode.com` and head to CloudFlare and add a CNAME record that points to `idp.criipto.id`.

Once the domain is active you can progress.

### <span class="mw-headline" id="bkmrk-sign-in-3">Sign in</span>

Coming

### <span class="mw-headline" id="bkmrk-document-signing-3">Document signing</span>

Requires version 7336 or newer.

Head to "Application" in the Criipto menu, click "Add signatures application" (if it exits), otherwise click "Add login application" and add `?tags=signatures` to the end of the url.

Name the application `[customer] eSign`, select their domain and check the eID's that should be available, select `java` as technology.

A secret might pop up or be shown, take note. It is possible to add more after the fact and re-issue them.

## <span class="mw-headline" id="bkmrk-setup-in-ts-1">Setup in TS</span>

There are a lot of parameters available. Some of these must be set for the integration to work, depending on the integration.

### <span class="mw-headline" id="bkmrk-sign-in-5">Sign in</span>

Coming

##### <span class="mw-headline" id="bkmrk-policies-1">Policies</span>

- oauthCriiptoAllow
- oauthCriiptoHost
- oauthCriiptoClient
- oauthCriiptoSecret

### <span class="mw-headline" id="bkmrk-document-signing-5">Document signing</span>

To start the signing process, setup the configuration and execute a Status Action that executes the codeunit `dk.tempusserva.signing.criipto.CriiptoStatusAction` or `dk.tempusserva.signing.criipto.CriiptoStatusActionGenerator` or `dk.tempusserva.signing.criipto.CriiptoStatusActionGeneratorAlt` or access a url with `command=dk.tempusserva.signing.criipto.CriiptoPage&SagID=[SagID]&DataID=[DataID]` (not ready).

This will try to lookup and send the document(s) out for signing.

#### <span class="mw-headline" id="bkmrk-configurations-1">Configurations</span>

<table class="wikitable" id="bkmrk-configuration-descri"><tbody><tr><th>Configuration</th><th>Description</th></tr><tr><td>Signer.MultipleSigners</td><td>Whether multiple signers is allowed (true/false)</td></tr><tr><td>Signer.MaximumSigners</td><td>How many signatures are needed. Defaults to "COUNT", which counts the number of signers, when using MultipleSigners, otherwise 1. Can otherwise be set to a number.</td></tr><tr><td>Signer.ExpiresInDays</td><td>Number of days the recipient has to sign the document. Default is 30.</td></tr><tr><td>Signer.FieldMaximum</td><td>Not implemented</td></tr><tr><td>Signer.FieldFil \*</td><td>System name for the field with documents that should be send, all documents found here will be send. Defaults to "FILES". Also used to store generated files.</td></tr><tr><td>Signer.FieldCPR</td><td>System name for the field with a CPR, that the signer has to have to sign the document. Also used with MultipleSigners.</td></tr><tr><td>Signer.FieldCVR</td><td>System name for the field with a CVR, that the signer has to have to sign the document. Also used with MultipleSigners.</td></tr><tr><td>Signer.FieldEmail \*</td><td>System name for the field with an email, that will be notified about the signature request. Also used with MultipleSigners. Defaults to "EMAIL".</td></tr><tr><td>Signer.FieldSigners</td><td>System name for a list-of-children-field. All records found here will be required to sign the document. Required when using MultipleSigners.</td></tr><tr><td>Signer.StatusError</td><td>Status that the record should enter if the signature request failed (was rejected or timed out). Defaults to 0.</td></tr><tr><td>Signer.StatusSigned</td><td>Status that the record should enter when all signatures are collected. Defaults to 0.</td></tr><tr><td>Signer.StatusUploaded</td><td>Status that the record should enter when it has been uploaded to signing service. Defaults to 0.</td></tr><tr><td>Signer.StatusDisableCodeunits</td><td>Whether codeunits should be executed or not, when the signing completes or fails. Defaults to false (do execute).</td></tr><tr><td>Signer.EmailSubject</td><td>The subject of the email send to the signer. Defaults to "Dokument til signering".</td></tr><tr><td>Signer.EmailBody</td><td>The email-body of the email send to the signer. Defaults to "Du kan underskrive her: {LINK}".</td></tr><tr><td>Signer.NotificationSubject</td><td>The subject of the email send to EmailWarner, when all signatures have been collected. Defaults to "Dokument til signering er blevet underskrevet"</td></tr><tr><td>Signer.NotificationBody</td><td>The body of the email send to EmailWarner, when all signatures have been collected. Defaults to "Alle parter har nu underskrevet dokumentet. {LINK}". Links to the record.

</td></tr><tr><td>Signer.NotificationBodyExt</td><td>The body of the email send to all signers of a document, when all have signed. Defaults to "Alle parter har nu underskrevet dokumentet. Du kan downloade det underskrevne dokument her: {LINK}". Links to the signed document at Criipto.

</td></tr><tr><td>Signer.EmailWarner</td><td>Can be an email or the system name of a field containing an email. The email found here will be notified when a signature fails and completes. If an email is not found, the email of the current user will be used, if not a status action. Required for status action.

</td></tr><tr><td>Signer.WarningSubject</td><td>The subject of the email send to EmailWarner, when a signature request fails. Defaults to "Dokument til signering blev afvist".</td></tr><tr><td>Signer.WarningBody</td><td>The body of the email send to EmailWarner, when a signature request fails. Defaults to "En underskrift blev afvist. {LINK}". Links to the record.</td></tr><tr><td>Signer.CriiptoClientID \*</td><td>The client ID from Criipto application.</td></tr><tr><td>Signer.CriiptoClientSecret \*</td><td>The client secret from Criipto application.</td></tr><tr><td>Signer.OverwriteOnReupload</td><td>true/false, default false. If enabled the system allows re-sending a record for signing, overwriting the old one.</td></tr><tr><td>Signer.FileName</td><td>Name of generated file, defaults to kontrakt.docx, used by CriiptoStatusActionGenerator</td></tr><tr><td>Signer.TemplateID</td><td>ID of template to be rendered and saved before sending it of to be signed, used by CriiptoStatusActionGenerator</td></tr><tr><td>Signer.FileNameAlt</td><td>Name of generated file, defaults to kontrakt.docx, used by CriiptoStatusActionGeneratorAlt</td></tr><tr><td>Signer.TemplateIDAlt</td><td>ID of template to be rendered and saved before sending it of to be signed, used by CriiptoStatusActionGeneratorAlt</td></tr></tbody></table>

# OLAP

# OLAP example

We have a solution

- Change management \[changemanagement\]

The solution contains the following lookup fields

- Scope \[TYPE\]
- Type \[TASKTYPE\]

It also has some decimal values

- Estimate \[ESTIMATE\]
- Realized \[REALIZED\]

In our reporting we want to see distributions by

- A period hierarchy: Year / Quarter / Month
- Categories by Scope, Type and Status

## <span class="mw-headline" id="bkmrk-setting-up-the-cube-1">Setting up the cube</span>

A new OLAP cube is set up in: "Integration" &gt; "OLAP cubes" &gt; "Add"

### <span class="mw-headline" id="bkmrk-basic-information-1">Basic information</span>

- Name display: sample1
- Cube header: Report sample1

### <span id="bkmrk-"></span><span class="mw-headline" id="bkmrk-cube-definition-%28%22cu-1">Cube definition ("Cube Schema XML")</span>

For further help to designing schemas please refer to the following ressources

- [http://mondrian.pentaho.com/documentation/schema.php](http://mondrian.pentaho.com/documentation/schema.php)

```xml
<Cube name="sample1cube">
  <Table name="data_changemanagement"/>
  <Dimension name="Scope" foreignKey="TASKTYPE">
    <Hierarchy hasAll="true" primaryKey="LookupID">
    <Table name="formfieldlookup"/>
      <Level name="Type" column="Value" uniqueMembers="true"/>
    </Hierarchy>
  </Dimension>
  <Dimension name="Type" foreignKey="TYPE">
    <Hierarchy hasAll="true" primaryKey="LookupID">
    <Table name="formfieldlookup"/>
      <Level name="Type" column="Value" uniqueMembers="true"/>
    </Hierarchy>
  </Dimension>
  <Dimension name="Status" foreignKey="StatusID">
    <Hierarchy hasAll="true" primaryKey="StatusID">
    <Table name="formstatus"/>
      <Level name="Type" column="Status" uniqueMembers="true"/>
    </Hierarchy>
  </Dimension>
  <Dimension name="Period" type="TimeDimension">
    <Hierarchy name="Periode" hasAll="true" allMemberName="All period">
      <Table name="data_changemanagement"/>
      <Level name="Aar" levelType="TimeYears" uniqueMembers="true">
        <KeyExpression>
          <SQL dialect="mysql">Year(CreatedAt)</SQL>
          <SQL dialect="generic">YEAR</SQL>
        </KeyExpression>
      </Level>
      <Level name="Kvartal" uniqueMembers="false" levelType="TimeQuarters">
        <KeyExpression>
          <SQL dialect="mysql">Quarter(CreatedAt)</SQL>
          <SQL dialect="generic">Quarter</SQL>
        </KeyExpression>
      </Level>
      <Level name="Maaned" uniqueMembers="false" levelType="TimeMonths">
        <KeyExpression>
          <SQL dialect="mysql">Month(CreatedAt)</SQL>
          <SQL dialect="generic">Month</SQL>
        </KeyExpression>
      </Level>
    </Hierarchy>
  </Dimension>
  <Measure name="Revision" column="Revision" aggregator="sum" formatString="Standard"/>   
  <Measure name="Estimate" column="ESTIMATTIMER" aggregator="sum" formatString="Standard"/>   
  <Measure name="Realized" column="REALISERETTIMER" aggregator="sum" formatString="Standard"/>   
</Cube>
```

### <span id="bkmrk--1"></span><span class="mw-headline" id="bkmrk-mdx-query-%28%22cube-que-1">MDX query ("Cube Query MDX")</span>

For further help to designing queries please refer to the following ressources

- [http://technet.microsoft.com/en-us/library/ms145971.aspx](http://technet.microsoft.com/en-us/library/ms145971.aspx)

```mysql
SELECT
  NON EMPTY {([Measures].[Estimate],[Measures].[Realized])} ON COLUMNS,
  NON EMPTY {([Period],[Type],[Status])} ON ROWS
FROM [sample1cube]
```

## <span class="mw-headline" id="bkmrk-results-1">Results</span>

The cube is acccesed via this URL (or press "test" form the OLAP overview) [http://YOUR\_DOMAIN/TempusServa/olapview.jsp?query=sample1](http://your_domain/TempusServa/olapview.jsp?query=sample1)

[![image.png](https://docs.tsnocode.com/uploads/images/gallery/2025-04/scaled-1680-/DT1uGurX8iq4UTEA-image.png)](https://docs.tsnocode.com/uploads/images/gallery/2025-04/DT1uGurX8iq4UTEA-image.png)

[![image.png](https://docs.tsnocode.com/uploads/images/gallery/2025-04/scaled-1680-/RdSDIs4HYtgV3pWL-image.png)](https://docs.tsnocode.com/uploads/images/gallery/2025-04/RdSDIs4HYtgV3pWL-image.png)

# Schema cheatsheet

## <span class="mw-headline" id="bkmrk-introduction-1">Introduction</span>

The below paragraphs contains sample snippets for your OLAP schema.

Precise documentation on how to define a schema, can be found at: [http://mondrian.pentaho.com/documentation/schema.php](http://mondrian.pentaho.com/documentation/schema.php)

## <span class="mw-headline" id="bkmrk-schema-structure-ove-1">Schema structure overview</span>

- Table (one): 
    - Structure: The centre table containing the measures (FACT table)
    - OLAP cube: *Not displayed*

- Dimensions (many) 
    - Structure: Related tables or groupable values (FACT table relations)
    - OLAP cube: Columns/row "headers" in the cube

- Measures (many) 
    - Structure: Values found in the centre table (FACT table values)
    - OLAP cube: Numbers to be displayed in the table cells

## <span class="mw-headline" id="bkmrk-defining-dimensions--1">Defining dimensions for related values</span>

### <span class="mw-headline" id="bkmrk-standard-lookup-valu-1">Standard lookup value</span>

The cube schema below only needs adjustment for the system name of the lookup field.

**Structure**

<table id="bkmrk-solution-as-displaye"><tbody><tr><td>Solution as displayed</td><td>"Sample solution" : "Category" = xxx</td></tr><tr><td>Solution system names</td><td>sample : CATEGORY = xxx</td></tr><tr><td>Database table names</td><td>data\_sample : CATEGORY = xxx</td></tr></tbody></table>

**Data model**

- data\_sample 
    - CATEGORY: Field containing the lookup value

**Cube schema**

```xml
...
<Dimension name="Example" foreignKey="CATEGORY">
   <Hierarchy hasAll="true" primaryKey="LookupID">
      <Level name="Category" column="Value" uniqueMembers="true" />
   </Hierarchy>
</Dimension>
...
```

### <span class="mw-headline" id="bkmrk-simple-choice-value-1">Simple choice value</span>

The cube schema below only needs adjustment for the system name of the lookup field.

**Structure**

<table id="bkmrk-solution-as-displaye-1"><tbody><tr><td>Solution as displayed</td><td>"Sample solution" : "My choice" = xxx</td></tr><tr><td>Solution system names</td><td>sample : CHOICE = xxx</td></tr><tr><td>Database table names</td><td>data\_sample : CHOICE = xxx</td></tr></tbody></table>

**Data model**

- data\_sample 
    - CHOICE: Field containing the lookup value

**Cube schema**

```xml
...
<Dimension name="Example" foreignKey="CHOICE">
   <Hierarchy hasAll="true" primaryKey="ChoiceID">
      <Level name="Answer" column="Value" uniqueMembers="true" />
   </Hierarchy>
</Dimension>
...
```

### <span class="mw-headline" id="bkmrk-standard-record-stat-1">Standard record Status</span>

The cube schema below can be copied directly without modification: The status properties and tablenames are allways the same.

**Structure**

<table id="bkmrk-solution-as-displaye-2"><tbody><tr><td>Solution as displayed</td><td>"Sample solution" : "Status" = xxx</td></tr><tr><td>Solution system names</td><td>sample : "StatusID" = xxx</td></tr><tr><td>Database table names</td><td>data\_sample "StatusID" = xxx</td></tr></tbody></table>

**Data model**

- data\_sample 
    - StatusID: Field containing status reference (allways the same)

**Cube schema**

```xml
...
<Dimension name="Example" foreignKey="StatusID">
   <Hierarchy hasAll="true" primaryKey="StatusID">
      <Level name="Status" column="Status" uniqueMembers="true" />
   </Hierarchy>
</Dimension>
...
```

## <span class="mw-headline" id="bkmrk-defining-dimensions--3">Defining dimensions for related records</span>

### <span class="mw-headline" id="bkmrk-related-solution-one-1">Related solution ONE step away</span>

**Structure**

<table id="bkmrk-solutions-as-display"><tbody><tr><td>Solutions as displayed</td><td>"Some child" : "Parent" -&gt; "Father or mother"</td></tr><tr><td>Solution system names</td><td>child : PARENT -&gt; parent</td></tr><tr><td>Database table names</td><td>data\_child : PARENT -&gt; data\_parent</td></tr></tbody></table>

**Data model**

- data\_child 
    - PARENT: Key to the "parent" solution
- data\_parent 
    - GRANDPARENT: Key to the "grandparent" solution
    - PARENTNAME: Descriptive field

**Cube schema**

```xml
...
<Dimension name="Example" foreignKey="PARENT">
   <Hierarchy hasAll="true" primaryKey="DataID" primaryKeyTable="data_parent">
      <Level name="Parent" column="PARENTNAME" uniqueMembers="true" />
   </Hierarchy>
</Dimension>
...
```

### <span class="mw-headline" id="bkmrk-related-solution-two-1">Related solution TWO steps away</span>

Note that the schemas for multi join tables are written from "inside out", that might seem counterintuitive i relation to what you want to display in the cube later.

**Structure**

<table id="bkmrk-solutions-as-display-1"><tbody><tr><td>Solutions as displayed</td><td>"Some child" : "Parent" -&gt; "Father or mother" : "Grand parent" -&gt; "Grandma and Grandpa's"</td></tr><tr><td>Solution system names</td><td>child : PARENT -&gt; parent : GRANDPARENT -&gt; grandparent</td></tr><tr><td>Database table names</td><td>data\_child : PARENT -&gt; data\_parent : GRANDPARENT -&gt; data\_grandparent</td></tr></tbody></table>

**Data model**

- data\_child 
    - PARENT: Key field pointing to the "parent" solution

- data\_parent 
    - GRANDPARENT: Key field pointing to the "grandparent" solution
    - PARENTNAME: Descriptive field

- data\_grandparent 
    - GRANDPARENTNAME: Descriptive field

**Cube schema**

```xml
...
<Dimension name="Example" foreignKey="PARENT">
   <Hierarchy hasAll="true" primaryKey="DataID" primaryKeyTable="data_parent">
      <Join leftKey="GRANDPARENT" rightKey="DataID" />
      <Level name="Grandparent" table="data_grandparent" column="GRANDPARENTNAME" uniqueMembers="true" />
      <Level name="Parent" table="data_parent" column="PARENTNAME" uniqueMembers="true" />
   </Hierarchy>
</Dimension>
...
```

## <span class="mw-headline" id="bkmrk-defining-dimensions--5">Defining dimensions for inline values</span>

### <span class="mw-headline" id="bkmrk-text-values-1">Text values</span>

**Cube schema**

```xml
 ...
 <Dimension name="Category">
     <Hierarchy hasAll="true">
         <Level name="Name" column="NAME" uniqueMembers="true"/>
     </Hierarchy>
 </Dimension>
 ...
```

### <span id="bkmrk-"></span><span class="mw-headline" id="bkmrk-year%2Finteger-values-1">Year/integer values</span>

**Cube schema**

```xml
 ...
 <Dimension name="Period" type="TimeDimension">
     <Hierarchy hasAll="true">
         <Level name="Aar" column="YEAR" type="Numeric" uniqueMembers="false" levelType="TimeYears"/>
     </Hierarchy>
 </Dimension>
 ...
```

### <span id="bkmrk--1"></span><span class="mw-headline" id="bkmrk-date-%2F-datetime-valu-1">Date / datetime values</span>

**Cube schema**

```xml
...
<Dimension name="Period" type="TimeDimension">
   <Hierarchy name="Periode" hasAll="true" allMemberName="All period">
      <Level name="Aar" levelType="TimeYears" uniqueMembers="true">
         <KeyExpression>
            <SQL dialect="mysql">Year(CreatedAt)</SQL>
            <SQL dialect="generic">YEAR</SQL>
         </KeyExpression>
      </Level>
      <Level name="Maaned" uniqueMembers="false" levelType="TimeMonths">
         <KeyExpression>
            <SQL dialect="mysql">Month(CreatedAt)</SQL>
            <SQL dialect="generic">Month</SQL>
         </KeyExpression>
      </Level>
   </Hierarchy>
</Dimension>
...
```

### <span class="mw-headline" id="bkmrk-enumeration-values-1">Enumeration values</span>

**Cube schema**

```xml
 ...
 <Dimension name="Severity">
     <Hierarchy hasAll="true" primaryKey="SEVERITY">
         <InlineTable alias="enumeration">
         <ColumnDefs>
             <ColumnDef name="id" type="Numeric"/>
             <ColumnDef name="desc" type="String"/>
         </ColumnDefs>
         <Rows>
             <Row>
                 <Value column="id">1</Value>
                 <Value column="desc">High</Value>
             </Row>
             <Row>
                 <Value column="id">2</Value>
                 <Value column="desc">Medium</Value>
             </Row>
             ... more values ...
        </Rows>
     </InlineTable>
     <Level name="Severity" column="id" nameColumn="desc" uniqueMembers="true"/>
 </Hierarchy>
 </Dimension>
 ...
```

## <span class="mw-headline" id="bkmrk-defining-measures-1">Defining measures</span>

### <span class="mw-headline" id="bkmrk-normal-values-1">Normal values</span>

Measures are allways numeric values, that can be agggated to higher levels (the levels in the dimensions)

<table id="bkmrk-type-aggregator-exam"><tbody><tr><th>Type</th><th>Aggregator</th><th>Examples</th></tr><tr><td>Sums</td><td>SUM</td><td>Time spent, costs</td></tr><tr><td>Average</td><td>AVG</td><td>Process time</td></tr></tbody></table>

  
**Cube schema**

### <span class="mw-headline" id="bkmrk-calculated-values-1">Calculated values</span>

# Analyse Activity

User activity will display an OLAP cube for **changes and updates** to certain solutions.

Changes are defined by updates to the underlying record.

## <span class="mw-headline" id="bkmrk-how-to-activate-this-1">How to activate this feature</span>

- User must have the **Business analyst** property set
- The solution must have **Activity monitoring** property set
- The service **OLAP Activity** must be active

# Analyse Flowsteps

User activity will display an OLAP cube for **flow in each process step** for certain solutions.

The total flow time is defined as the time span between entry and exit point in time for each flow step (status).

Furthermore each status can have targets for good performance defined:

- Goal: Expected process time in this step
- Tolerance: Acceptable process time in this step

## <span class="mw-headline" id="bkmrk-how-to-activate-this-1">How to activate this feature</span>

- User must have the **Business analyst** property set
- The solution must have **Activity monitoring** property set
- The service **OLAP Flowsteps** must be active

# Analyse Flowtotal

User activity will display an OLAP cube for **total flow** time to certain solutions.

The total flow time is defined as the time span between creation and point in time where a final status i reached.

Furthermore each solution can have targets for good performance defined:

- Goal: Expected total process time
- Tolerance: Acceptable total process time

## <span class="mw-headline" id="bkmrk-how-to-activate-this-1">How to activate this feature</span>

- User must have the **Business analyst** property set
- The solution must have **Activity monitoring** property set
- The service **OLAP Flow total** must be active

# WebDAV

## <span class="mw-headline" id="bkmrk-about-our-webdav-imp-1">About our WebDAV implementation</span>

Supported document types: Word, Excel, Powerpoint.

The implementation does not require SSL to be enabled on the server, but it is recommended.

Sometimes the action of saving a file, that was opened using WebDAV will fail, but only the fist time, if it happens at all.

To fix this, you just have to save the document again, and the issue will be gone for you, until you re-sign-in.

## <span class="mw-headline" id="bkmrk-how-to-enable-1">How to enable</span>

Toggle the policy `webdavSupport` and `webdavSessionTokens` to `true`.

[![image.png](https://docs.tsnocode.com/uploads/images/gallery/2025-04/scaled-1680-/FqkVOinmGFKiHHKV-image.png)](https://docs.tsnocode.com/uploads/images/gallery/2025-04/FqkVOinmGFKiHHKV-image.png)

<div class="thumb tright" id="bkmrk--2"></div>When WebDAV is enabled, the files-field, will add tiny pencils next to the supported file-types.

Clicking on the file icon will prompt the file to be opened in the associated application. Clicking on the filename, will prompt a download of the file.

  
You can control how long the auth-tokens are valid for, using policy `webdavSessionLifetime`.  
You can control whether a token should automatically be re-validated if it times out using policy `webdavSessionTokensRevalidate`.

### Testing WebDAV

Upload a word or excel file to a file field and save the record.  
Re-open the record and click the type icon with the pencil next to the file name.  
If the appropriate program opens, edit the file and save it.  
Close the program.  
Open the file again (either download it or re-open it via WebDAV), if your changes are still there, WebDAV is working.

### Common config issues

The following policies have to be set up correctly, otherwise the WebDAV feature will not work.

applicationServer  
applicationIsBehindAReverseProxy  
applicationName  
applicationBasePath  
securitySslPages

## Map WebDAV as a drive

Add a network location with the address `https://[domain]/[app]/webdav/drive/`.

On linux, replace the protocol with `davs`.

## <span class="mw-headline" id="bkmrk-enabling-basic-auth--1">Enabling basic auth to your server</span>

Office has stopped officially supporting Basic-auth, which is the backup authentication for our implementation.

To allow Office to connect to your server, and authenticate using Basic-auth, execute the following command, as admin, from the commandline.

```powershell
REG ADD HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\16.0\Common\Identity /t REG_EXPAND_SZ /v basichostallowlist /d "HOSTNAME"
```

<div class="mw-highlight mw-highlight-lang-console mw-content-ltr" dir="ltr" id="bkmrk--3"></div>Where HOSTNAME is the domain of your server, eg. `wiki.tsnocode.com`.

## How to enable support in LibreOffice on Debian

Run the following commands

```bash
xdg-mime default libreoffice-writer.desktop  x-scheme-handler/ms-word
xdg-mime default libreoffice-calc.desktop  x-scheme-handler/ms-excel
xdg-mime default libreoffice-impress.desktop  x-scheme-handler/ms-powerpoint
```

# LDAP

Basic configuration is the name of the LDAP server and domain that is binded to the application

- ldapServer
- ldapDomainDefault

All configuration options are found in: [Policy#Active directory](https://wiki.tsnocode.com/index.php?title=Policy#Active_directory "Policy")

## <span class="mw-headline" id="bkmrk-synchronization-opti-1">Synchronization options</span>

Different options for LDAP integration exists

1. Validate credentials: Check username/password against LDAP
2. Maintain groups: Add missing groups as defined in the LDAP
3. Create missing users: Create users with correct LDAP credentials

Credential validation (1) is mandatory, while group synchronization (2) and automatic user creation (3) is optional.

1. ldapAuthentication
2. ldapMaintainGroupsOnLogon
3. ldapCreateUsers

Groups and usersd in Tempus Serva will be marked with the LDAP path of the object. This path is also editable, so that groups or users can be mapped to other names in Tempus Serva than in the LDAP.

## <span class="mw-headline" id="bkmrk-failover-mechanism-1">Failover mechanism</span>

In case the LDAP is not responding the server can be allowed to use local application credentials:

- ldapAuthenticationFallback

The server will initially try to validate against the LDAP, after which the validation is done against the local user table. In order to allow this operation, an encrypted copy of the user password is stored on every successfull LDAP authentication. Note this behaviour is only active if the fallback authentication is enabled.

## <span class="mw-headline" id="bkmrk-ldap-service-account-1">LDAP service account</span>

In order to communicate with the LDAP server, the Tempus Serva application will need its own acount to carry out many of the synchronization operations:

- ldapUsername
- ldapPassword

No permissions except lookup rights are required for this role.

# Jobnet

# Jobnet v1

It's possible to upload jobs to JobNet using the tsJobnet servlet.  
This site documents the interface.

### <span class="mw-headline" id="bkmrk-usage-1">Usage</span>

Send a POST or GET-request to the servlet-endpoint "/addJob" with the required parameters as url-parameters.  
If a parameter is missing, the servlet will return HTTP-CODE 403 and a message in the body, telling you what was wrong.

### <span class="mw-headline" id="bkmrk-all-fields-1">All fields</span>

<table class="wikitable" id="bkmrk-parameter-type-defau"><tbody><tr><td>**Parameter**</td><td>**Type**</td><td>**Default**</td><td>**Required**</td><td>**Description**</td></tr><tr><td>ApplicationDetails.ApplicationMethods.ApplyByEmail</td><td>String (email)</td><td> </td><td>No</td><td> </td></tr><tr><td>ApplicationDetails.ApplicationMethods.ApplyByLetter</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>ApplicationDetails.ApplicationMethods.ApplyByTelephone</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>ApplicationDetails.ApplicationMethods.ApplyOnline</td><td>String (url)</td><td> </td><td>No</td><td> </td></tr><tr><td>ApplicationDetails.Description</td><td>String</td><td> </td><td>No</td><td>A short description of the job, not the full description, that is JobDetails.Description</td></tr><tr><td>ApplicationDetails.JobContactList.JobContactType.Email</td><td>String (email)</td><td> </td><td>Yes</td><td> </td></tr><tr><td>ApplicationDetails.JobContactList.JobContactType.PersonNameStructure.PersonGivenName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>ApplicationDetails.JobContactList.JobContactType.PersonNameStructure.PersonSurnameName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>ApplicationDetails.JobContactList.JobContactType.Title</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>ApplicationDetails.JobContactList.JobContactType.HidePhoneNumbers</td><td>Boolean</td><td>true</td><td>Yes</td><td> </td></tr><tr><td>ApplicationDetails.JobContactList.JobContactType.TelephoneNumberStructure.MobileNumber</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>ApplicationDetails.JobContactList.JobContactType.TelephoneNumberStructure.PrimaryNumber</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>ApplicationDetails.JobContactList.JobContactType.TelephoneNumberStructure.SecondaryNumber</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>ApplicationDetails.JobContactList.JobContactType.TelephoneNumberStructure.Telefax</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>ApplicationDetails.Referral</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.AddressStructure.CountryCode</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.AddressStructure.FloorIdentifier</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.AddressStructure.MunicipalityId</td><td>Integer</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.AddressStructure.MunicipalityName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.AddressStructure.PostalCity</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.AddressStructure.PostalCode</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.AddressStructure.StreetBuildingIdentifier</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.AddressStructure.StreetName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.AddressStructure.SuiteIdentifier</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.BranchCode</td><td>Integer</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.BranchDescription</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.Contact.Email</td><td>String (email)</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.Contact.PersonNameStructure.PersonGivenName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.Contact.PersonNameStructure.PersonSurnameName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.Contact.Title</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.Contact.PrimaryNumber</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.Contact.SecondaryNumber</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.Contact.Telefax</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.CvrNumber</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.Name</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>HiringOrganizationDetails.PNumber</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>HiringOrganizationDetails.Url</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>JobAdDetails.JobPublishDates.LastModifiedDate</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>JobAdDetails.JobPublishDates.PublishEndDate</td><td>String</td><td> </td><td>Yes</td><td>Has to be after PublishStartDate, but at the longest 8 weeks after</td></tr><tr><td>JobAdDetails.JobPublishDates.PublishStartDate</td><td>String</td><td> </td><td>Yes</td><td>Has to be today or after</td></tr><tr><td>JobAdDetails.Logo</td><td>String (base64)</td><td> </td><td>No</td><td>max. 200x135px / 100KB</td></tr><tr><td>JobAdDetails.PostedBy.Email</td><td>String (email)</td><td> </td><td>No</td><td> </td></tr><tr><td>JobAdDetails.PostedBy.PersonNameStructure.PersonGivenName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobAdDetails.PostedBy.PersonNameStructure.PersonSurnameName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobAdDetails.PostedBy.Title</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>JobAdDetails.PostedBy.ContactGuid</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>JobAdDetails.PostedBy.PrimaryNumber</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>JobAdDetails.PostedBy.Telefax</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>JobAdDetails.PostedBy.UserRid</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>JobAdDetails.Type</td><td>String</td><td>Normal</td><td>No</td><td>Has to be one of "WageSubsidy", "HotJob", "Normal", "SpringStone", "Flexjob", "EarlyRetirement", "JobRotation", "EarlyRetiree", "CompanyInternship", "NormalAndEarlyRetiree", or"NormalAndEarlyRetireeWithHotJob"</td></tr><tr><td>JobDetails.AuthorityId</td><td>Integer</td><td>0</td><td>No</td><td> </td></tr><tr><td>JobDetails.AuthorityPhoneNumber</td><td>String (phone-number)</td><td> </td><td>No</td><td> </td></tr><tr><td>JobDetails.Classification.EmploymentType</td><td>String</td><td>PermanentJob</td><td>No</td><td>Has to be "PermanentJob" or "LimitedPeriod"</td></tr><tr><td>JobDetails.Classification.IsEuresJob</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.Classification.IsPublicSectorJob</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.Classification.IsWorkInDenmarkJob</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.Description</td><td>String</td><td> </td><td>Yes</td><td>The description of the job. If it contains html ([subset](https://starwiki.atlassian.net/wiki/spaces/FYS/pages/56590365/JobAdService)) it has to be wrapped in &lt;!\[CDATA\[\]\]&gt;</td></tr><tr><td>JobDetails.DriversLicences</td><td>Comma-List</td><td> </td><td>No</td><td>Required drivers licenses to apply for the job. Can be any combination of A1, A, B, BE, C, CE, D, DE, A10, AMa, AMb, A2, C1, D1, C1E, D1E, Taxi, Traktor, Truck, BusErhverv, DigitalFartskriver, EUGodsErhverv and EUBusErhverv, seperated by a comma</td></tr><tr><td>JobDetails.HidePhonenumbers</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.IsAnonymousEmployer</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.IsDisabilityFriendly</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.JobDates.ApplicationDeadlineDate</td><td>String (date)</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobDetails.JobDates.EmploymentDate</td><td>String (date)</td><td> </td><td>No</td><td> </td></tr><tr><td>JobDetails.JobDates.StartAsSoonAsPossible</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.JobLocation.Address.CountryCode</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobDetails.JobLocation.Address.FloorIdentifier</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>JobDetails.JobLocation.Address.MunicipalityId</td><td>Integer</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobDetails.JobLocation.Address.MunicipalityName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobDetails.JobLocation.Address.PostalCity</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobDetails.JobLocation.Address.PostalCode</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobDetails.JobLocation.Address.StreetBuildingIdentifier</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobDetails.JobLocation.Address.StreetName</td><td>String</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobDetails.JobLocation.Address.SuiteIdentifier</td><td>String</td><td> </td><td>No</td><td> </td></tr><tr><td>JobDetails.JobLocation.NoLocalBusinessAddress</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.NumberOfPositions</td><td>Integer</td><td>1</td><td>No</td><td> </td></tr><tr><td>JobDetails.OccupationConceptUri</td><td>String ([JobnetConceptUri](https://docs.tsnocode.com/books/standard-integrations/page/jobnetconcepturi "JobnetConceptUri"))</td><td> </td><td>Yes</td><td> </td></tr><tr><td>JobDetails.Schedule.DailyWorkTime.Day</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.Schedule.DailyWorkTime.Evening</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.Schedule.DailyWorkTime.Night</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.Schedule.DailyWorkTime.Weekend</td><td>Boolean</td><td>false</td><td>No</td><td> </td></tr><tr><td>JobDetails.Schedule.WeeklyWorkTimeTypes</td><td>String</td><td>FullTime</td><td>No</td><td>Has to be PartTime or FullTime</td></tr><tr><td>JobDetails.Schedule.WorkHour.Max</td><td>Integer</td><td> </td><td>No</td><td>Expected maximum of hours of work, a week, if PartTime</td></tr><tr><td>JobDetails.Schedule.WorkHour.Min</td><td>Integer</td><td> </td><td>No</td><td>Expected minimum of hours of work, a week, if PartTime</td></tr><tr><td>JobDetails.Title</td><td>String</td><td> </td><td>Yes</td><td>The title being hired for</td></tr></tbody></table>

### <span class="mw-headline" id="bkmrk-notes-1">Notes</span>

At least one application method (ApplicationDetails.ApplicationMethods) has to be set.  
At least one phonenumber has to be set for job-contact (ApplicationDetails.JobContactList.JobContactType.TelephoneNumberStructure).  
If schedule (JobDetails.Schedule.WeeklyWorkTimeTypes) equals "FullTime", workhours (JobDetails.Schedule.WorkHour.Min and JobDetails.Schedule.WorkHour.Max) can't be set.  
JobDetails.Schedule.WorkHour.Min has to be lower than JobDetails.Schedule.WorkHour.Max and between 0 and 36.  
JobDetails.Schedule.WorkHour.Max has to be greater than JobDetails.Schedule.WorkHour.Min and between 1 and 36.  
If schedule (JobDetails.Schedule.WeeklyWorkTimeTypes) Equals "PartTime", workhours (JobDetails.Schedule.WorkHour.Min and JobDetails.Schedule.WorkHour.Max) have to be set.

# JobnetConceptUri

Jobs uploadet to JobNet require the parameter JobDetails.JobLocation.OccupationConceptUri to be set to a valid OccupationConceptUri.  
A wrapper has been made for this, it is part of the JobNet servlet.

## <span class="mw-headline" id="bkmrk-usage-1">Usage</span>

Send a POST or GET-request to the servlet-endpoint "/getOccupations".  
If an error occurs, the servlet will return HTTP-CODE 403 or 500 and a message in the body, telling you what was wrong.  
If the request is successful a JSONArray wil be returned.  
Invalid OccupationConceptUri's will be returned, because of backwards-compatibility. Filter these via the "valid" attribute.

### <span class="mw-headline" id="bkmrk-sample-1">Sample</span>

```json
[
    {
        "valid": false,
        "description": "Borearbejdere (sten) betjener boremaskinen, der borer huller i stenblokke. De behandler granit, sandsten, marmor og skifer i overensstemmelse med specifikationerne.",
        "label": "borearbejder - sten",
        "uri": "http://data.star.dk/esco/occupation/2fbdc3fa-aeab-4e45-bccf-9421b84c687f"
    },
    ...
]
```

# Link to QR codes

You can use our online QR coder to convert links to mobile phone QR codes.

Add the link in question as the request string (after the ?)

Example:

```
https://omega.tempusserva.dk/qr/encode?https://docs.tsnocode.com/books/standard-integrations/page/link-to-qr-codes
```

As of version 11524 this servlet is now built into the platform and can be used like this:

```
qr?https://docs.tsnocode.com/books/standard-integrations/page/link-to-qr-codes
```

### <span class="mw-headline" id="bkmrk-transforming-access--1">Transforming access tokens to QR codes</span>

Assuming you allready have an interface for a solution, you can add a button to build QR codes containing the tokens for selected elements.

First create a field of the type **Button: Script execution**.

Then set up the JS to handle open a window with a code:

```javascript
let url = "main?command=dk.p2e.blanket.codeunit.common.PagePublicTokenBuilderQR&interface=foobar&SagID=257&DataID=" + DataID;
window.open(url, "QR code", "width=300,height=300,menubar=no,resizable=no,toolbar=no,location=no");
```

In the above you will need to change

- SagID
- interface

# Oauth2 authentication

# Understanding Oauth 2

Oauth authentication will put icons on the login page for fast and easy SSO wth multiple vendors.

The user will be authenticated if the email matches between the provider and the Tempus Serva user.

The following providers are supported.

- Google
- LinkedIn
- Facebook
- Azure
- ADFS
- WordPress (Via [plugin](https://wordpress.org/plugins/oauth2-provider))

From version 6191 it is possible to setup TS to create a new account, if a user signed in with oauth/sso and wasn't found among the existing users.

To do this enable the configuration `oauthCreateNewUsersAllow` and set their initial GroupID in the configuration `oauthNewUserGroup`.

# Setting up SingleSignon

Before going into the detailed configuration please make sure https/SSL is enabled.

Set the following configurations to true

- securitySslLogin
- securitySslPages

Next activate service icons on the login page

- oauthLoginDisplay

### <span class="mw-headline" id="bkmrk-google-oauth-1">Google Oauth</span>

Using an existing Google account , go to the \[[credentials section](https://console.developers.google.com/apis/credentials?project=oauth2-234210&folder&organizationId)\].

Navigate to "Credentials" in the left menu.

First setup Oauth messages in the **Oauth conscent** section

- Logo, privacy policies etc. are not required but make things look better
- Note that domain authentication is not required

Next setup setup credentials

1. Navigate back to credentials
2. Click **Create credentials**
3. Fill out the information 
    - Authorized JavaScript origins: [https://alpha.tempusserva.dk](https://alpha.tempusserva.dk/)
    - Authorized redirect URIs: [https://alpha.tempusserva.dk/TempusServa/SignInGoogle](https://alpha.tempusserva.dk/TempusServa/SignInGoogle)
4. Credentials are generated
5. Copy credentials to your Tempus Serva configuration 
    - oauthGoogleClient = \[Client ID\]
    - oauthGoogleSecret = \[Client secret\]
6. Finally 
    - oauthGoogleAllow = true

### <span class="mw-headline" id="bkmrk-linkedin-oauth-1">LinkedIn Oauth</span>

[Follow the guide](https://medium.com/@ellesmuse/how-to-get-a-linkedin-access-token-a53f9b62f0ce)

Copy credentials to

- oauthLinkedinClient
- oauthLinkedinSecret

Enable

- oauthLinkedinAllow

Callback URL

- [https://sample.tsnocode.com/app/SignInLinkedin](https://sample.tsnocode.com/app/SignInLinkedin)

### <span class="mw-headline" id="bkmrk-facebook-oauth-1">Facebook Oauth</span>

[Follow the guide](https://developers.facebook.com/docs/facebook-login/access-tokens/)

Copy credentials to

- oauthFacebookClient
- oauthFacebookSecret

Enable

- oauthFacebookAllow

Callback URL

- [https://sample.tsnocode.com/app/SignInFB](https://sample.tsnocode.com/app/SignInFB)

### <span class="mw-headline" id="bkmrk-azure-oauth-1">Azure Oauth</span>

[Follow the guide](https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-oauth2)

Copy credentials to

- oauthAzureTenant
- oauthAzureClient
- oauthAzureSecret

Enable

- oauthAzureAllow

Callback URL

- [https://sample.tsnocode.com/app/SignInAzure](https://sample.tsnocode.com/app/SignInAzure)

### <span class="mw-headline" id="bkmrk-adfs-oauth-1">ADFS Oauth</span>

[Follow the guide](https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-protocols-oauth-code)

Copy credentials to

- oauthAdfsServer
- oauthAdfsClient

Enable

- oauthAdfsAllow

Callback URL

- [https://sample.tsnocode.com/app/SignInADFS](https://sample.tsnocode.com/app/SignInADFS)

### <span class="mw-headline" id="bkmrk-wordpress-1">WordPress</span>

1. Install and activate the [plugin](https://wordpress.org/plugins/oauth2-provider)
2. Enable the Oauth-server (Oath Server -&gt; Settings -&gt; Enable Oauth Server)
3. Create a new client (Oauth Server -&gt; Clients -&gt; Add New Client) 
    1. Give it a descriptive name
    2. Add the Redirect URI (Should be something like: `https://[ts-hostname]/[ts-instance]/SignInWP`)
    3. Assign it admin rights
    4. Save it
4. Copy credentials to Configurations 
    - oauthWPClient
    - oauthWPSecret
5. Input wordpress domain/link to Configuration (no trailing /) 
    - oauthWPHost
6. Enable Configuration 
    - oauthWPAllow

# Outlook calendar

## <span class="mw-headline" id="bkmrk-exporting-data-items-1">Exporting data items as calendar entrys</span>

How to display an export to calendar on each data item

1. Create a button: Parametrized URL
2. Build an URL using the following parameters 
    - title \[field\]
    - SagID \[value\]
    - DataID \[value\]
    - *OPTION A*
        - from \[field\]
        - minutes \[field or value\]
    - *OPTION B*
        - from \[field\]
        - to \[field\]
    - *OPTIONAL*
        - content \[field\]
        - hidelink \[value\]

Note that

- a link will be displayed in the body of the message unless **hidelink** is present
- use normal references for fields (without brackets and no constants)
- the SagID value must match that of the solution in question

### <span class="mw-headline" id="bkmrk-example-configuratio-1">Example configuration</span>

```
 exportcard?SagID=1&DataID=[DataID]&title=COMPANY&content=AGENDA&from=MEETINGDATE&minutes=MINUTES
```

```
 exportcard?SagID=1&DataID=[DataID]&title=COMPANY&from=MEETINGDATE&minutes=60
```

# Pentaho

# Installing Pentaho CE

[https://www.hitachivantara.com/en-us/pdfd/white-paper/pentaho-community-edition-installation-guide-for-windows-whitepaper.pdf](https://www.hitachivantara.com/en-us/pdfd/white-paper/pentaho-community-edition-installation-guide-for-windows-whitepaper.pdf)

[https://sourceforge.net/projects/pentaho/](https://sourceforge.net/projects/pentaho/)

# Setting up Mondrian

[https://rpbouman.blogspot.com/2016/03/need-mondrian-war-checkout-xmondrian.html](https://rpbouman.blogspot.com/2016/03/need-mondrian-war-checkout-xmondrian.html)

[https://github.com/rpbouman/xmondrian](https://github.com/rpbouman/xmondrian)

NOT [https://sourceforge.net/projects/mondrian/](https://sourceforge.net/projects/mondrian/)

# Connecting Mondrian to TS



# Connecting Pentaho to Mondrian