Nama ERP DocsNama ERP Docs
Home
Guides
Examples
GitHub
Home
Guides
Examples
GitHub
  • Home

    • Guides

      • بعض المعلومات عن استعمال هذا الموقع
      • Utility Links
      • System Minimum Requirements
      • Reports Guide (Jasper Reports)
      • Keyboard Shortcuts
      • Layout Names:
      • Field Values Calculator
      • Importing Data from Excel or Queries
      • Tempo Language Manual
      • Sending Invoices and Documents to Customers
      • SMS and WhatsApp Configuration in Nama ERP
      • Field Filter with Criteria
      • تعديل الترجمات في نظام نما
      • Criteria Based Validation
      • Attendance Machine Formula Guide
      • Email By Parameterized Reports In Task Scheduler
      • Nama Properties
      • Customer Reward Points (Loyalty Points)
      • دليل استعمال النقاط الفنية في نقاط البيع
      • ORACLE JDBC Integration Connection in context.xml for integration purposes
    • Examples

      • أمثلة لمسارات كيان
    • Reprocessing Transactions

      • Reprocessing Quantity, Cost, and Stock Ages
      • Accounting Utilities - Ledger and Debt Ages Reprocessing
      • Queries to Check for (and Fix) Cost And Qty Problems
      • Inventory Related Utility Queries
      • Manufacturing Utilities
      • Fixed Assets Module Utilities
      • Real Estate Utilities
      • Database Related Operations
      • General Purpose Utility Queries
      • Replication Utilities
    • Frequently Asked Questions

      • أسئلة عامة
      • أسئلة شائعة عن مسارات الكيان
      • أسئلة في موديول التوزيع والمخازن والمبيعات والمشتريات
      • أسئلة شائعة في موديول الرواتب والموارد البشرية
      • أسئلة شائعة عن تصميم التقارير
      • أسئلة شائعة حول أداة إنشاء التقارير
      • أسئلة شائعة عن الموافقات
      • أسئلة شائعة حول فلترة الحقول

Tempo Language Manual

This guide introduces the Tempo language, developed by the NAMA team, to help implementers create dynamic messages for customers, employees, and suppliers. Tempo is used in various outputs such as notifications, emails, SMS messages, and validation error messages.

What is Tempo?

Tempo lets you embed dynamic values in text templates. For example, to display an error message stating that an employee cannot take more than five days of vacation, you can include the employee’s name dynamically:

Employee {employee.name1} cannot take more than five days of vacation.

If you want to include a hyperlink to the employee record:

{link(employee)}

How to Discover Field Names

To find field names in any screen:

  1. Press CTRL + ALT + I
  2. Right-click on any field to see its internal ID, table name, and column name

Using the Tempo Web Editor

NAMA provides a web-based editor for writing and testing Tempo syntax:

  • It supports auto-completion with Ctrl + Space
  • It checks syntax as you write

👉 Try it here: Tempo EditorTempo Editor


When to Use Tempo

Tempo can be used in two major contexts:

Two Usage Modes

  1. Query Results Used in dashboards, notifications by query, or validation messages where a query is involved.

  2. Record Rendering Used in entity-based messages (e.g., approvals, flows) to directly access record fields.

Key Difference: Only in record mode can you access nested fields (e.g., customer.group.code). In query result mode, such navigation won't work as expected.


Tempo Syntax Overview

1. Accessing Record Fields

  • Use {fieldName} to show a field from the current record:
This Employee's Arabic name is {name1}
  • For related records (e.g., employee in a vacation request):
This Employee's Arabic name is {employee.name1}
  • For indirect references (e.g., employee in a subsidiary field):
This Employee's Arabic name is {subsidiary.$toReal.name1}

2. Writing Comments

To add comments in your Tempo code:

{comment} This was written by Khaled {endcomment}

3. Disabling Tempo Parsing

If you want to prevent the whole template or part of it from being parsed:

<notempo/>

4. Parsing a Field as Tempo Template

To parse the content of a field (e.g., remarks) as a Tempo template:

{tempo}{customer.remarks}{endtempo}

5. Escaping Curly Brackets

If you need to show {code} literally without rendering:

\{code\}

6. CSS-Friendly Brackets

To avoid issues when working with HTML/CSS, enable CSS-friendly brackets:

<useCSSFriendlyBrackets/>

Now you can write:

%{code}%

Instead of:

{code}

7. Handling Editor Errors

Sometimes the Tempo editor incorrectly flags correct syntax. You can prefix such expressions with # to ignore the error:

Incorrect (editor shows error):

{time.$hours}

Corrected:

{#time.$hours}

8. Creating Line Breaks

Use {enter} to insert a line break in HTML messages:

Line 1{enter}
Line 2

Creating Hyperlinks in Tempo

1. Link to a Field or Record

You can generate clickable links for fields or records using two approaches:

Method 1: Basic Link (Displays Field as Link)

Use the link() function to make the field itself a hyperlink:

{link(targetField)}

Example: To create a link for the customer record:

{link(customer)}

Method 2: Titled Link (Custom Text as Link)

Use titledlink() with custom link content:

{titledlink(targetField)} Your custom link text {endlink}

Example: To show a customer link with the title “Current Customer code is ABC”:

{titledlink(customer)} Current Customer code is {code} {endlink}

2. Relative Links for Web Notifications

When creating links for notifications (not emails), use relative paths for optimal behavior:

Use {shortlinks} or {directlinks}

  • {shortlinks}: Generates relative links based on the current web page
  • {directlinks}: Also generates relative links but allows for more direct access

⚠️ These are not suitable for email messages.

Example 1 – Using {shortlinks}:

{shortlinks}
The user {#firstAuthor.name2} created the document {#code}

Example 2 – Using {directlinks}:

{directlinks}
The user {#firstAuthor.name2} created the document {link($this)}

3. Linking from Query Results

If you're sending a notification based on a query:

{titledlink(entityType, id)} {code} {endlink}

This links to the record identified by entityType and id, using the record's code as the visible title.


4. Open Record in Specific Menu or View

You can customize how a link opens by specifying additional parameters:

{link(record, menu="MenuCode", newindow="true or false", view="ViewName")}

Or with a custom title:

{titledlink(record, menu="MenuCode", newindow="true or false", view="ViewName")}
Link Content Here
{endlink}

Example: Open an employee record in a new window via a specific menu and view:

{link(employee, menu="NewEmp", newindow="true", view="NewEmpsView")}

With a title:

{titledlink(employee, menu="NewEmp", newindow="true", view="NewEmpsView")}
Employee code {code}, Name {name1}
{endlink}

5. Using a Specific Base URL for All Links

To force all links to use a certain server address, use the {appurl()} tag at the start of the template:

{appurl("http://crm7.namasoft.com:8080/erp/")}

This ensures that all subsequent links are based on the provided URL.

Using Loops in Tempo

Looping Through Repeated Data (e.g. Document Details)

To display a list of repeated rows (like items in a document), use the loop block:

{loop(details)}
  Loop content here
{endloop}

Example: Display each item's code, Arabic name, quantity, and net value in a sales invoice:

{loop(details)}
{@rownumber} - {#details.item.item.code} - {#details.item.item.name2} - {#details.quantity.quantity.primeQty.value} - {#details.price.netValue}
{endloop}

Loop Variants

1. Last Line Only

Loop through just the last row:

{loop(details, last)}
  Last line content
{endloop}

2. Range of Lines

Loop through a specific range of line numbers:

{loop(details, 2, 3)}
  From line 2 to 3
{endloop}

3. From Specific Line to End

Loop from a starting line to the last line:

{loop(details, 5)}

This is equivalent to:

{loop(details, 5, last)}

Manual Counters

You can define and control your own counters for custom row numbering and referencing:

Counter Syntax

{incrementcounter(counterName)}
{decrementcounter(counterName)}
{countervalue(counterName)}

Use in Quick Creators

To use the counter in a row expression:

{r(@@counterName)}

Full Example: Creating Stock Transfer from MnOrder

This example demonstrates:

  • A loop over spareParts
  • Skipping rows where spareParts.n1 is true
  • Using a manual counter c1
  • Populating a stock transfer creation form
{creator(entity="StockTransferReq", menu="StockTransDocumentsStockTransferReq", title="Create StockTransferReq", newwindow="true")}

{f("book")}{v("STR01")}
{f("term")}{v("STR02")}
{f("branch")}{v("MS")}
{f("warehouse")}{v("W001")}
{f("toWarehouse")}{v(spareParts.warehouse.code)}
{f("toLocator")}{v(spareParts.location.code)}

{loop(spareParts)}
  {ifnot(spareParts.n1)}
    {incrementcounter(c1)}
    {f("details.item.item")}{v(spareParts.sparePart.code)}{r(@@c1)}
    {f("details.quantity.quantity.primeQty.value")}{v(spareParts.quantity)}{r(@@c1)}
    {f("details.quantity.quantity.primeQty.uom")}{v(spareParts.uom.code)}{r(@@c1)}
    {f("details.specificDimensions.warehouse")}{v("W001")}{r(@@c1)}
    {f("details.toWarehouse")}{v(spareParts.warehouse.code)}{r(@@c1)}
    {f("details.toLocator")}{v(spareParts.location.code)}{r(@@c1)}
  {endif}
{endloop}

{endcreator}

Using Tempo in Approval Notification Templates

1. Displaying Approval-Related Lines

To inform users about specific lines affected by an approval rule (e.g., prices below a threshold), use a loop over the approval rule lines:

The lines that are below the default sales price:
{loop($map.approvalRuleLines)}
  {link($map.approvalRuleLines.item.item)} - {$map.approvalRuleLines.price.unitPrice}
{endloop}

This will list each line with a link to the item and display the unit price.


2. Adding Approval Action Links

To include action buttons in your email or SMS templates for approval workflows, use the following placeholders:

{approvelink}
{rejectlink}
{returnlink}
{escalatelink}

Tips

  • These action links are mainly used in email or SMS templates defined within an approval rule.
  • It's common to include multiple action links together in a message (e.g., Approve, Reject, Return).

Creating Tables in Tempo

Tempo allows you to format tabular data using special syntax blocks. This is useful for presenting structured data like document lines, grouped totals, or summaries in a clean format.


1. Basic Table Structure

To create a table, wrap your content between {opentable} and {closetable} tags.

Example – Table of Sales Invoice Details

{opentable}
  {row}{cell}#{cell}Item Code{cell}Item Name{cell}Quantity{cell}Net Value{endrow}
  {loop(details)}
    {row}
      {cell}{@rownumber}
      {cell}{#details.item.item.code}
      {cell}{#details.item.item.name2}
      {cell}{#details.quantity.quantity.primeQty.value}
      {cell}{#details.price.netValue}
    {endrow}
  {endloop}
{closetable}

Tips

{@rownumber} represents the line number and corresponds to the # symbol in the header.


2. Rows and Cells

Drawing Rows

Use {row} and {endrow} to define a table row.

Example – Table Header Row:

{row}{cell}#{cell}Item Code{cell}Item Name{cell}Quantity{cell}Net Value{endrow}

Drawing Cells

Use {cell} to create table cells. You may optionally close each cell with {endcell}.

Example:

{cell}Item Name{endcell}

Tips

The {endcell} tag is optional and can be omitted for simplicity.


3. Grouping Data in Tables

Tempo supports grouping rows with headers and footers using the following syntax:

Group Header

{header(groupingField)}
  Header content here
{endheader}

Group Footer

{footer(groupingField)}
  Footer content here
{endfooter}

Example – Grouped Table by Item Code

{loop(details)}
  {header(details.item.item.code)}
    Item: {#details.item.item.code}
    {opentable}
      {row}{cell}Quantity{cell}Price{endrow}
  {endheader}

  {row}
    {cell}{#details.quantity.quantity.primeQty.value}
    {cell}{#details.price.unitPrice}
  {endrow}

  {footer(details.item.item.code)}
    {closetable}
  {endfooter}
{endloop}

In this example:

  • A new table is created for each unique item.code
  • The table opens in the header and closes in the footer
  • Individual item rows are inserted within the loop

Functions Available in Tempo

Accessing Current User Data

Use the following syntax to get properties of the currently logged-in user:

{$user.PROPERTY_NAME}

Example:

{$user.code}

Date and Time Functions

General Date Formatting

{formatDate(dateExpression, formatExpression)}

Example:

{formatDate(valueDate, "yyyy-MM-dd")}

Record Metadata

  • Creation date and time: {$creationDate}
  • Creation date only: {$creationDate.$toDate}
  • Current date: {$today}
  • Current date and time: {$now}

Navigating Dates

  • Next month: {date.$nextMonth}
  • Previous month: {date.$previousMonth}
  • Next day: {valueDate.$nextDay}
  • Previous day: {valueDate.$previousDay}
  • Next year: {valueDate.$nextYear}
  • Previous year: {valueDate.$previousYear}
  • Start of month: {valueDate.$monthStart}
  • End of month: {valueDate.$monthEnd}

Extracting Parts of a Date

  • Day: {valueDate.day}
  • Month: {valueDate.month}
  • Year: {valueDate.year}

Day Name

  • Arabic: {valueDate.$arDayName}
  • English: {valueDate.$enDayName}
  • Based on current language: {valueDate.$dayName}

Hijri and String Formats

  • Hijri date: {valueDate.$asHijriString}
  • DD_MM_YYYY: {valueDate.$toStringDD_MM_YYYY}
  • YYYYMMDD: {valueDate.$toStringYYYYMMDD}

Record Creation Time

  • Time only: {$creationDate.$toTime.$toStringNormal}

Tips

$toStringNormal converts time to a readable format like 12:50:10


Time Field Functions (Record Mode)

Tips

Assume the field is called time

  • Hour: {time.$hours}
  • Minute: {time.$minutesOfHour}
  • Second: {time.$secondsOfMinute}
  • Millisecond: {time.$millisOfSecond}

Time Field Functions (Query Mode)

To format a time field from a query:

{time(timeField)}

Example:

{time(fromTime)}

Or use:

{fromTime.$toStringNormal}

For total hours stored as a decimal:

{decimalToTime(decimalField)}

Array and Text Utilities

Accessing Array Elements

{details.$get(index)}

Tips

Index is zero-based. To get the first row in details, use {details.$get(0)}


Text Utilities

  • Remove all spaces:
{description1.$removeAllSpaces}
  • Normalize Arabic text (unify similar characters):
{description1.$normalizeAr}

Example:

منى ذهبت إلى المدرسة مع فؤاد

Becomes:

مني ذهبت الي المدرسه مع فواد

Translations in Tempo

Translating Enumeration Fields

  • Arabic translation:
{#orderStatus.$arabic}
  • English translation:
{#orderStatus.$english}
  • Auto-translate based on language settings:
{translate(orderStatus)}
  • Force Arabic translation:
{translateAr(orderStatus)}
  • Force English translation:
{translateEn(orderStatus)}

Example:

{translate(orderStatus)}

This translates the orderStatus value to the other language (Arabic ↔ English).

Tips

You can also use {orderStatus.$english} or {orderStatus.$arabic} directly.


Number-Related Functions in Tempo

Convert Text to Number (if possible)

  • Convert to integer:
{#description1.$tryToInt}
  • Convert to decimal:
{#description1.$tryToDecimal}

Formatting Dates and Numbers

Use the $format function on dates or numbers with your desired pattern:

{creationDate.$format."yyyy-MM-dd HH:mm:ss"}
{money.total.$format."###,###.00"}

If Statements (Conditionals) in Tempo

Tempo provides flexible conditional logic using {if}, {ifnot}, and related syntax to control when content is rendered.


Basic Conditions

  • If a field is not empty:
{if(code)}Content shown if `code` is not empty{endif}
  • If a number is not zero:
{if(money.remaining)}Remaining is {#money.remaining}{endif}
  • If a boolean is true:
{if(commitedBefore)}Record is committed before{endif}
  • Negated if condition (if the field is empty or false):
{ifnot(code)}Code is missing{endif}
{if!(code)}Code is missing{endif}
  • If a string represents a number that’s not zero:
{ifnumber(description1)}

Full Syntax Reference

SyntaxDescription
{if(value)}Renders if value is not empty
{if!(value)}, {ifnot(value)}Renders if value is empty
{if=(a,b)}, {ifequal(a,b)}Renders if a == b
{if!=(a,b)}, {ifnotequal(a,b)}Renders if a != b
{if<(a,b)}, {ifless(a,b)}Renders if a < b
{if<=(a,b)}, {iflessoreq(a,b)}Renders if a <= b
{if>(a,b)}, {ifgreater(a,b)}Renders if a > b
{if>=(a,b)}, {ifgreateroreq(a,b)}Renders if a >= b

Number-Specific Conditions

SyntaxDescription
{ifnumber(n)}Renders if n is not zero
{ifnumber!(n)}, {ifnumbernot(n)}Renders if n is zero
{ifnumber=(a,b)}, {ifnumberequal(a,b)}Renders if numbers are equal
{ifnumber!=(a,b)}, {ifnumbernotequal(a,b)}Renders if numbers are not equal
{ifnumber<(a,b)}, {ifnumberless(a,b)}Renders if a < b
{ifnumber<=(a,b)}, {ifnumberlessoreq(a,b)}Renders if a <= b
{ifnumber>(a,b)}, {ifnumbergreater(a,b)}Renders if a > b
{ifnumber>=(a,b)}, {ifnumbergreateroreq(a,b)}Renders if a >= b

Using else and else if

You can chain multiple conditions using else if=, elseif=, or else.

{if=(code,"a")}Case A
{else if=(code,"b")}Case B
{else if<(n1,5)}Case C
{else}No match found
{endif}

Tips

  • else must come last.
  • endelse is optional and not recommended.
  • You can prepend else to any if condition.

This structure allows full control over conditional content rendering within Tempo templates.

Tafqeet in Tempo

The tafqeet function converts numeric values into words, using the currency formatting defined in the global configuration.

Syntax

{tafqeet("Number", "CurrencyCode")}

Examples

Example 1: Hardcoded Values

{tafqeet("500", "EGP")}
  • On English interface → five hundred Egyptian Pounds
  • On Arabic interface → خمسمائة جنيه مصري

Example 2: Using Field Values

{tafqeet(money.netValue, money.currency.code)}

This converts the value of money.netValue using the currency code defined in the money object.


Notes

Tips

If the global configuration defines:

  • code = جم
  • altCode = EGP

And you want the conversion in English regardless of the interface language, use:

{tafqeet(money.netValue, money.currency.altCode)}

Force Language with tafqeetAr or tafqeetEn

  • Always render in Arabic:
{tafqeetAr(money.netValue, money.currency.code)}
  • Always render in English:
{tafqeetEn(money.netValue, money.currency.code)}

Executing Entity Flows via Tempo Links

To trigger an entity flow from a Tempo template (e.g. in an email), use the following syntax:

{flow(record, flowCode="EntityFlowCode")}

Example:

{flow(employee, flowCode="CreateJobOffer")}

This executes the CreateJobOffer flow for the current employee record.


Email-Related Functions

1. Setting the Email Subject

Method 1: Using subject: at the Start of the First Line

subject:The employee {name2} was updated by {$user.name2}

Must be placed at the very beginning of the email body.

Method 2: Using {subject} Block

{subject}The employee {name2} was updated by {$user.name2}{endsubject}

2. Adding Attachments

Use one or more emailattachment tags for fields or server paths:

{emailattachment(attachmentField)}
{emailattachment("C:\Path\To\File.pdf")}

Example:

subject:Attachments of employee {code} - {name1}
Dear Sir,  
Please note that the employee {name1} was changed. The email contains all files attached to the employee.  
{emailattachment(attachment)}{emailattachment(attachment1)}{emailattachment(attachment2)}{emailattachment(attachment3)}{emailattachment(attachment4)}{emailattachment(attachment5)}{emailattachment(mainFile)}

Another Example:

Attached our catalog {emailattachment("E:\Media\Prochures\catalog.pdf")}

Tips

* يُفضّل وضع جمل المرفقات في نهاية الرسالة. * تجنب ترك سطور فارغة أو مسافات بينها لتفادي ظهورها غير المرغوب فيه في البريد. * المرفقات الفارغة يتم تجاهلها تلقائيًا.

3. Prevent Auto-Attaching Images

For HTML emails that shouldn't attach images automatically, include:

<donothandleimages/>

4. Creating and Sending Messages

Message Body Block

{openmsg}
Message content here
{closemsg}

Define Recipient Address

{sendto}email-or-phone{endsendto}

Examples:

  • Send to a customer's email:
{sendto}{#email}{endsendto}
  • Send an SMS to a phone number:
{sendto}{#phoneNumber}{endsendto}

Tips

Typically used inside {loop} blocks to send individualized messages.


Example: Send Email Notifications to Customers with Overdue Invoices

Step 1: Email Template Query

select s.code invoiceCode, s.valueDate, c.code customerCode, c.name2 customerName, s.remaining, c.email
from SalesInvoice s
left join customer c on c.id = s.customer_Id
where remaining > 0 and valueDate between dateadd(month,-1,getdate()) and getdate()
order by customerCode

Step 2: Email Template Content

{loop()}
  {header(customerCode)}

  {openmsg}
  {sendto}{#email}{endsendto}
  {subject}Late Invoices of customer {#customerName}{endsubject}

  Dear {#customerName}{enter}
  Please note that the following invoices are due:

  {opentable}
  {row}{cell}Invoice Code{cell}Invoice Date{cell}Remaining{endrow}
  {endheader}

  {row}{cell}{#invoiceCode}{cell}{#valueDate}{cell}{#remaining}{endrow}

  {footer(customerCode)}
  {closetable}
  {closemsg}
  {endfooter}
{endloop}

String Manipulation Functions in Tempo

Trimming and Replacements

  • Trim spaces at the beginning and end:
{description1.$trim}
  • Convert Arabic numerals to English:
{mobile.$replaceArNumerals}

Parsing and Conversions

  • Parse JSON string to a map:
{text1.$parseJSONToMap}
  • Convert comma-separated text into a list:
{remarks.$parseCSVToList}

Substring Functions

  • Extract characters from the left:
{left(string, length)}

Example:

{left(code, 3)} → "Nam" if code is "NamaSoft"
  • Extract characters from the right:
{right(string, length)}

Example:

{right(code, 3)} → "oft" if code is "NamaSoft"
  • Extract substring from a specific range:
{substring(string, startIndex, endIndex)}

Example:

{substring("NamaSoft", 3, 5)} → "maS"

Padding (Truncating or Adding Spaces)

  • Left pad or truncate:
{leftpad(length)}YourTextHere{endpad}
  • Right pad or truncate:
{rightpad(length)}YourTextHere{endpad}

Examples:

{leftpad(10)}123{endpad}     → "       123"
{rightpad(10)}123{endpad}    → "123       "
{leftpad(5)}123456789{endpad}→ "12345"
{rightpad(5)}123456789{endpad}→ "56789"

Numeric Field Functions

Fixed-Decimal Rounding

  • Round to 0–5 decimal places:
{n1.$round0}
{n1.$round1}
{n1.$round2}
{n1.$round3}
{n1.$round4}
{n1.$round5}

Examples:

{n1.$round0} → 20 if n1 = 19.9  
{n1.$round2} → 10.33 remains 10.33

Dynamic Rounding

{round(numberExpression, decimalPlacesExpression)}

Examples:

{round(n1, "2")}
{round(money.value, money.currency.fractionalDecimalPlaces)}

Number Formatting

{formatNumber(numberExpression, formatExpression)}

Example:

{formatNumber(n1, "###,###.00")} → 1,234.50

URL Shortening in Tempo

URL shortening is particularly useful for SMS messages, where long links are not practical.

  • To use this feature, you need a YOURLS server or subscribe to Namasoft's shortening service.
  • You also need an API signature from the YOURLS server.

Syntax

{shortenurl(server="https://your-shortener.com/", signature="SIGNATURE_HERE")}
  {link($this, plainLink=true)}
{endshortenurl}

Example using Namasoft shortening service:

{shortenurl(server="https://namasoft.com/s/", signature="SIGNATURE_HERE")}
  {link($this, plainLink=true)}
{endshortenurl}

Dynamic Report Links in Notifications and Dashboards

You can add dynamic links to reports from notifications or dashboards. This is useful for generating contextual reports based on specific parameters.

Notification Example

{reportlink(reportCode="1000", runType="launch", newwindow="true")}
  {paramname("entityType")}{paramvalue(ref1.entityType)}
  {paramname("document")}{paramvalue(ref1)}
{endreportlink}

Dashboard Example

{reportlink(reportCode="1000", runType="launch", plainLink=true)}
  {paramname("entityType")}{paramvalue(entityType)}
  {paramname("document")}{paramvalue(id, entityType)}
{endreportlink}

Supported Nodes

  • Parameter by Name:
{paramname("paramName")}{paramvalue("paramValue")}
  • Reference Parameters (with optional display fields):
{paramrefvalue(entityType=..., id=..., code=..., name1=..., name2=...)}
  • Multi-value Parameters:
{parammultivalue}{code} {name1}{endmutlivalue}

CRM Questionnaire Sending

1. Embed Survey in Email

Dear Sir,{enter}
We would love you to answer the following survey.{enter}
{$renderQuestionsForMailEmbedded}{enter}
Thanks and Best Regards

2. Send Survey as Link

Dear Sir,{enter}
We would love you to answer a quick survey on the following <a href='{$questionsURL}'>URL</a>.{enter}
Thanks and Best Regards

Sending HTTP Requests from Tempo

You can send HTTP requests from within Tempo using the EASendHttpRequestByTempo entity flow. This is useful for integrating external APIs (e.g., WhatsApp, SMS, ERPs) directly from records or looped data like document lines.


Example 1: Structured Body with Named Parameters

This example sends a POST request for each line in details, with body parts defined individually:

{loop(details)}
  {httprequest}
    {requesturl}https://namasoft.com/api/v3.0/item{endurl}
    {requestmethod}POST{endmethod}
    {contenttype}application/json{endcontenttype}
    {charset}utf8{endcharset}

    {headername}api-key{endheadername}
    {headervalue}xxHjjk889523{endheadervalue}

    {paramname}company_name{endparamname}
    {paramvalue}{legalEntity.name2}{endparamvalue}

    {bodypartname}user_whatsapp_number{endbodypartname}
    {bodypartvalue}{details.ref1.$toReal.contactInfo.mobile}{endbodypartvalue}

    {bodypartname}ordernumber{endbodypartname}
    {bodypartvalue}{details.ref2.$toReal.code}{endbodypartvalue}

    {bodypart("complexObject")}
      {bodypartname}property1{endbodypartname}
      {bodypartvalue}abc{endbodypartvalue}
      {bodypartname}property2{endbodypartname}
      {bodypartvalue}abcd{endbodypartvalue}
    {endbodypart}

    {requestdescription1}optional description that can be viewed in the list view{enddescription1}
    {requestdescription2}Add row number {@rownumber} to use as extra info{enddescription2}

    {requestrelatedtoid1}{id}{endrelatedto1}
    {requestrelatedtoid2}{customer.id}{endrelatedto2}
  {endrequest}
{endloop}

Example 2: Custom JSON Body String

This version uses a manually written JSON string in the request body:

{loop(details)}
  {httprequest}
    {requesturl}https://namasoft.com/api/v3.0/item{endurl}
    {requestmethod}POST{endmethod}
    {contenttype}application/json{endcontenttype}
    {charset}utf8{endcharset}

    {headername}api-key{endheadername}
    {headervalue}xxHjjk889523{endheadervalue}

    {paramname}company_name{endparamname}
    {paramvalue}{legalEntity.name2}{endparamvalue}

    {requestbody}
      \{
        "user_whatsapp_number":"{details.ref1.$toReal.contactInfo.mobile}",
        "ordernumber":"{details.ref2.$toReal.code}"
      \}
    {endbody}
  {endrequest}
{endloop}

Both examples demonstrate sending a request per row in details, with the flexibility to include headers, parameters, individual body fields, or full custom JSON.

Creators in Tempo

In Tempo, a creator is used to generate and populate a new entity record (like a sales invoice, customer, etc.) directly from templates.


Basic Creator Syntax

{creator(entity="EntityName", menu="MenuName", title="Link Title", view="ViewName", newwindow="true/false")}
  ...field assignments...
{endcreator}
  • entity: Name of the entity to create.
  • menu (optional): Target menu name (if customized).
  • title (optional): Title shown in the creator link.
  • view (optional): Custom screen view name.
  • newwindow (optional): Whether to open in a new tab.

Example:

{creator(entity="SalesInvoice")}
{endcreator}

Setting Field Values

Use {f("FieldName")} and {v("Value")} to assign a value.

Example:

{f("n1")}{v("10")}

Dynamic Field Content

Use {creatorvalue}...{endvalue} to embed dynamic or computed text.

Example:

{creator(entity="SalesInvoice")}
  {f("code")}{v("SA000001")}
  {f("remarks")}
    {creatorvalue}
      This document was created from {#entityType} - {#code} on date {#valueDate}
    {endvalue}
  {endcreator}

Assigning Detail Line Values

You can insert data into specific rows in detail tables:

Method 1: By row number

{f("details.item.itemCode")}{v("ITEM005")}{r("2")}

Method 2: Append to new row if needed (@@end)

{f("details.item.itemCode")}{v("ITEM005")}{r("@@end")}

Method 3: Always use last row (@@last)

{f("details.item.itemCode")}{v("ITEM005")}{r("@@last")}

Method 4: Use current loop row number

{f("details.item.itemCode")}{v(details.item.itemCode)}{r(@rownumber)}

Looping Example:

{creator(entity="SalesInvoice")}
  {f("code")}{v("SA000001")}
  {loop(details)}
    {f("details.item.itemCode")}{v(details.item.itemCode)}{r(@rownumber)}
  {endloop}
{endcreator}

Use Case Example: Copying Only Non-Service Items

{creator(entity="SalesInvoice")}
  {f("book")}{v("SIV1")}
  {f("term")}{v("CASH")}

  {loop(details)}
    {if!=(details.item.item.itemType, "Service")}
      {f("details.item.itemCode")}{v(details.item.itemCode)}{r("@@end")}
      {f("details.n1")}{v(details.n1)}{r("@@last")}
      {f("details.n2")}{v(details.n2)}{r("@@last")}
    {endif}
  {endloop}

{endcreator}
  • @@end ensures a new line is added for each item.
  • @@last ensures related fields are filled in the correct last row.

Calling System GUI Actions from Creator

You can invoke UI actions (e.g., Save, Print, Delete):

{callGUIAction("actionId")}

Available actionId values:

save, saveAndContinue, duplicate, accept, approval, revise, unrevise,
print, listView, showHelpMsgs, treeView, newRecord, delete,
more, refresh, homePage, goToRecord

Sales and Purchase Prices in Tempo

Getting the Sales Price of an Item

Use the itemprice function:

{itemprice(itemIdOrCode=expression)}

This function returns the sales price for an item. Only itemIdOrCode is required. All other parameters are optional and can appear in any order.

Full Syntax

{itemprice(
  itemIdOrCode=...,
  customerIdOrCode=...,
  uomCodeOrId=...,
  qty=...,
  classificationIdOrCode=...,
  date=...,
  legalEntityIdOrCode=...,
  sectorIdOrCode=...,
  branchIdOrCode=...,
  analysisSetIdOrCode=...,
  departmentIdOrCode=...,
  revisionIdCode=...,
  colorCode=...,
  sizeCode=...,
  priceClassifier1IdOrCode=...,
  priceClassifier2IdOrCode=...,
  priceClassifier3IdOrCode=...,
  priceClassifier4IdOrCode=...,
  priceClassifier5IdOrCode=...,
  decimalPlaces=...,
  fieldToDisplay=...
)}

Tips

You can pass either the ID or the code for any parameter.

Examples

{loop(details)}
  Price of item {details.item.item.name} is {itemprice(itemIdOrCode=details.item.item.code)}
  Price for customer CST05: {itemprice(itemIdOrCode=details.item.item.code, customerIdOrCode="CST05")}
  Price for customer CST05 on 2020-01-01: {itemprice(itemIdOrCode=details.item.item.code, customerIdOrCode="CST05", date="20200101")}
{endloop}

Getting the Purchase Price of an Item

Use the itempurchaseprice function:

{itempurchaseprice(itemIdOrCode=..., supplierIdOrCode=...)}

Full Syntax

{itempurchaseprice(
  itemIdOrCode=...,
  supplierIdOrCode=...,
  uomCodeOrId=...,
  qty=...,
  classificationIdOrCode=...,
  date=...,
  legalEntityIdOrCode=...,
  sectorIdOrCode=...,
  branchIdOrCode=...,
  analysisSetIdOrCode=...,
  departmentIdOrCode=...,
  revisionIdCode=...,
  colorCode=...,
  sizeCode=...,
  priceClassifier1IdOrCode=...,
  priceClassifier2IdOrCode=...,
  priceClassifier3IdOrCode=...,
  priceClassifier4IdOrCode=...,
  priceClassifier5IdOrCode=...,
  decimalPlaces=...,
  fieldToDisplay=...
)}

Utility Fields for Templates, Notifications, and Entity Flows

Discussions

  • discussions: All related discussions
  • firstDiscussion: First discussion
  • lastDiscussion: Most recent discussion
  • preLastDiscussion: One before last

Example:

The last added discussion was {lastDiscussion.discussion} at {lastDiscussion.onTime} by {link(lastDiscussion.user)}.  
Ref1 code: {lastDiscussion.ref1.code}

Notification and System Variables

  • $notificationTarget: The employee/user receiving the notification
  • $notifier: The notification definition that triggered the message
  • $currentUsers: All currently logged-in users
  • $user, $currentUser: The current user
  • $loginLegalEntity, $loginLegalEntityId
  • $loginBranch, $loginBranchId
  • $loginSector, $loginSectorId
  • $loginDepartment, $loginDepartmentId
  • $loginAnalysisSet, $loginAnalysisSetId

Approval-Specific Fields

  • currentApprovalCase: The current approval context
  • currentApprovalCase.lastStep.comment
  • currentApprovalCase.lastStep.actualResponsible
  • currentApprovalCase.lastStep.decision
  • currentApprovalCase.lastStep.approvalDate
  • currentApprovalCase.lastStep.escalatedFrom
  • currentApprovalCase.lastStep.approvalReason
  • currentApprovalCase.lastStepDefinition.notificationRemark

Refer to Approval Case Entity for more.


Other Utilities

  • retrieverFileId: Use this to generate customer-accessible file download links

Example image URL for employee:

http://localhost:8080/erp/file.download?entityType=Employee&recordId={empId}
Edit On github
Last Updated:: 6/3/25, 7:38 PM
Prev
Importing Data from Excel or Queries
Next
Sending Invoices and Documents to Customers