设计手册
遇到问题了吗?不用担心,答案都在这。
全站搜索

Javascript 工作发动机

所有表单都可以触发 Ragic 的服务器端 JavaScript 工作流程发动机来运行复杂的业务逻辑,如计算成本和发布库存余额。基本上,任何 Ragic现有功能无法覆盖的复杂业务逻辑都可以透过服务器端程序来实现。 程序发动机基于标准的 Nashorn Java 程序发动机,它包含在 Java 平台中。Nashorn 支持 ECMAScript 5.1,所以最好避免使用 ECMAScript 6 句法。此外,由 Java 处理并传递至 JavaScript 的数据(例如数组)可能会受到一些特定限制,导致无法直接使用像 join、map 等 JavaScript 方法,然而,若在 workflow 中直接定义一个 JavaScript 数组并将数据存入其中,则可以正常使用这些 JavaScript 方法。同样地,浏览器专属的 API 例如: setTimeout、setInterval、alert、document......等等,也无法在此使用。

var query = db.getAPIQuery(pathToForm);
var entry = query.getAPIResult();
var values = entry.getFieldValues(fieldId); // 回传的是一个数组

var str = values.join(','); //不可用,会出错

var ary = [];
for(var i = 0; i < values.length; i ++) {
  ary.push(values[i]);
}
var str = ary.join(','); //可用

JavaScript 工作流程的功能

Ragic 的电子表格设计接口可以处理大部分数据管理工作,如创建、编辑和查询记录。然而,手动数据维护可能会变得有点耗时和繁琐。这时,Ragic 订户会开始考虑如何自动化这些过程。 在 Ragic 内部,有一个非常强大的程序发动机,可以编写在服务器端运行的 JavaScript,用来检索电子表格上的数据,进行修改,甚至可以一次点击创建多个纪录。典型的使用场景包括更新库存,根据另一笔纪录创建新纪录(例如从报价创建销售订单,从Sales lead创建联络人),或者用数据库数据做数据检查。 有五种主要方式来运行您的 JavaScript 工作流程:

  1. 动作单击钮
  2. Post-workflow
  3. Pre-workflow
  4. Daily Workflow
  5. Approval Workflow

还有一个 Global Workflow,您可以在其中放置多个工作流程程序共享的常用 JavaScript 函数定义。

相关名词定义

在workflow中,有几个专有名词会常出现,我们将这些名词定义如下:假如我们表单中的某笔数据,URL为https://www.ragic.com/testAP/testForm/1/3

名词 定义
APName testAP,用户的数据库名称
Path /testForm,这张表单所在的页签,前面的斜线不可省略
SheetIndex 1,这张表单在页签内的索引
pathToForm /testForm/1,即为Path跟SheetIndex的组合,通常当作一个参数使用
rootNodeId、recordId

3,这笔数据(也称做纪录)的 ID,也可以透过 getNewNodeId(keyFieldId) 或 getOldNodeId(keyFieldId) 来获取

Key field

这张表单的主键码,keyFieldId 就是主键码字段的 ID,可在数据库字段定义文档中找到,或是使用 entry.getKeyFieldId() 来获取

Field

字段即是 Field,fieldId 是字段的 ID,fieldName 是字段的名称,fieldId 可以在数据库字段定义文档中找到,或是在设计模式中选取字段后,进入“字段设置”=>“基本”,在字段名称下方的七码数字即为 fieldId

Subtable

子表格,可以想像成是一般的表单下面还有一张表单,也有自己的 keyFieldId、fieldId、rootNodeId

subtableId

子表格的 ID,可在数据库字段定义文档中找到,可以想像成是子表格的 keyFieldId

subtableRowIndex 子表格数据的索引,通常会用循环来指定
subtableFieldId

子表格字段的 ID,可在数据库字段定义文档中找到,可以想像成是子表格的 fieldId

subtableRootNodeId

子表格纪录的ID,可以透过 getSubtableRootNodeId(subtableId, subtableRowIndex) 来获取,可以想像成是子表格的rootNodeId

显示信息

您可以像这样显示一个弹出信息。请注意,信息仅在响应状态为 "WARN" 时显示。默认情况下,响应物件中的信息将不会显示,因为通常对最终订户没有帮助。要特别注意的是动作单击钮不支持setStatus('CONFIRM')。

response.setStatus('WARN');
response.setMessage(message);

另外因为 workflow 不支持 console 或 alert 等 JavaScript 指令,所以如果您想进行调试,可以透过 log.setToConsole(true) 与 log.println(message) 来显示。

var message = "hello ragic";
log.setToConsole(true); //叫出console区块
log.println(message); //打印输出hello ragic

动作单击钮

这是运行 JavaScript 工作流程最常见和最清晰的方式,通常是我们的首选推入荐。您可以在工作表的sheet scope中编写程序,并分配动作单击钮来运行程序,当订户点击将显示在右下方“动作”面板中的单击钮时。要加上 sheet scope 程序,只需右键点击一个工作表,然后选择JavaScript Workflow

并从顶部下拉菜单中选择sheet scope

然后,您可以转到表单页面设计中,加上一个类型为JS Workflow动作单击钮,并引用您编写的 JavaScript 函数。

请注意,您可以使用 {id} 作为函数调用的参数来传递当前记录的纪录 ID,例如:

setStatus({id})

当然,我们会在接下来的章节中介绍如何编写这些函数。

Post-workflow

Post-workflows 在记录保存后立即运行。通过 Post-workflow,可以方便地自动化您希望对刚保存的纪录进行的变更,而这些变更是公式无法完成的。或者,您可以对其他相关工作表的纪录进行修改,例如更新库存余额。要加上 Post-workflow,只需右键点击一个工作表,然后选择JavaScript Workflow

然后从顶部下拉菜单中选择Post-workflow。 一个典型的 Post-workflow 如下所示:

var recordId = param.getNewNodeId(keyFieldId);
var query = db.getAPIQuery(pathToForm);
var record = query.getAPIEntry(recordId);

// 对检索到的纪录进行所需操作

请参阅我们的 API 参阅 和本文档的其他部分,了解您可以做的事情。还请注意,如果您在列表页面上修改数据,Post-workflow 将不会运行。

Pre-workflow

Pre-workflows 在记录保存之前运行,因此可以用作检查的一种方式,以检查输入的数据是否符合数据库中的数据。通常,大多数检查可以通过我们的前端正则表达式检查,或自由文本字段的唯一核取方块来完成。但对于更复杂的后端检查,有时需要 Pre-workflow。 要加上 Pre-workflow,只需右键点击一个工作表,然后选择JavaScript Workflow

然后从顶部下拉菜单中选择Pre-workflow。 这里有一个简单的例子:假设我们有一个列表,

我们希望确保保存的价格不是负数。

/**

 * 主键码字段: 1000004

 * 字段名称字段 ID
 * - - - - - - - - - - - --------
 * ID : 1000001
 * 价格 : 1000002
 * 名称 : 1000003
 * 是否可用 : 1000005

 */
function showMsg(str) {
  // 设置状态为 'INVALID' 或 'ERROR' 以撤销保存。
  response.setStatus('INVALID');
  response.setMessage(str);
}

function ifThePriceIsNegative() {
  // 获取要保存的价格。
  var newPrice = param.getNewValue(1000002);
  // 将 newPrice 字符串转换为整数。
  if(parseInt(newPrice) < 0) {
    return true;
  }
}

// 如果价格为负数,则不保存。
if(ifThePriceIsNegative()) {
  showMsg('价格为负数!!');
}

现在尝试保存负数价格,我们会得到

值得注意的是,在 Ragic 中编写 Pre-workflow 时,不能使用 entry.getFieldValue(因为 Ragic 还没有保存)。请尝试使用 param 来获取旧值和新值。

Daily Workflow

Daily Workflows 每天运行一次。这对于需要每天更新结果的修改非常有用,例如根据当前日期更新公式结果。要加上 Daily Workflow,只需右键点击一个标签,然后选择Global Javascript Workflow

然后从顶部下拉菜单中选择Daily Workflow

默认情况下,Daily Workflow 在 UTC 19:00 运行。您可以在公司设置中更改运行时间和时区: 1. 前往帐户设置

2. 点击公司设置

3. 选择公司当地时区Daily Workflow 运行时间并保存。

如果您的公司设置中没有公司当地时区Daily Workflow 运行时间,请联络我们来为您更新。

Approval Workflow

Approval Workflows 在签核创建、签核、拒绝、撤销或完成后立即运行。 要加上 Post-workflow,只需右键点击一个工作表,然后选择JavaScript Workflow

然后从顶部下拉菜单中选择Approval Workflow。 您可以透过以下方法获取记录 ID:

var recordId = approvalParam.getEntryRootNodeId();

approvalParam 是签核工作流程范围内的预定义变量。您可以透过以下方法获取签核操作:

var action = approvalParam.getApprovalAction();

在获取上述数据后,您可以根据需求进行自订:

var query = db.getAPIQuery(pathToForm);
var entry = query.getAPIEntry(recordId);
if (action === 'CANCEL') {
  entry.setFieldValue(STATUS_FIELD, "签核已撤销!");
}else if (action === 'FINISH') {
  entry.setFieldValue(STATUS_FIELD, "签核已完成!");
}else if (action === 'CREATE') {
  entry.setFieldValue(STATUS_FIELD, "签核已创建!");
}else if (action === 'REJECT') {
  entry.setFieldValue(STATUS_FIELD, "签核已拒绝!");
}else {
  entry.setFieldValue(STATUS_FIELD, "签核已批准!");
}
entry.save();

Global Workflow

Global Workflow 是您可以编写的 JavaScript 工作流程模块,其他工作流程函数可以引用它。它不会自己运行,但可以在上面列出的任何类型的工作流程中引用。这是一个很好的地方来放置可能需要在多个工作表中重复的程序。要加上 Global Workflow,只需右键点击一个标签,然后选择Global Javascript Workflow

获取一笔数据

有两种不同的方式来获取订户正在查看的当前记录(适用于动作单击钮)或订户刚保存的纪录(适用于 Pre-workflow 和 Post-workflow)。

动作单击钮

表单页面动作单击钮透过分配动作单击钮来调用您在 sheet scope 中定义的函数。当您定义动作单击钮调用的函数时,您可以传递参数 {id},即点击动作单击钮所在记录的 ID,来在工作流程中使用。 您可以在动作单击钮工作流程 中看到范例。

Pre-workflow

您可以透过以下方法获取记录 ID:

var recordId = param.getNewNodeId(keyFieldId);

param 是一个预定义变量,您可以在 Pre-workflow 和 Post-workflow 中使用它。

在获取记录 ID 之后,这是获取记录(entry)的方式:

var query = db.getAPIQuery(pathToForm);
var entry = query.getAPIEntry(id);

Post-workflow

对于 Post-workflow,您可以使用与 Pre-workflow 相同的方法。但是,还有一种方便的方法来检索刚刚保存的纪录:

var entry=param.getUpdatedEntry();

如果表单页面上有字掩码字段,默认情况下您将获得掩码值。您可以透过以下方式获取未掩码的值:

var query = db.getAPIQuery(pathToForm);
query.setUpdateMode();
query.setIgnoreAllMasks(true); // 撤销所有掩码字段的掩码。
var entry = query.getAPIEntry(id);

您可以使用 query.addIgnoreMaskDomain(fieldId_of_the_masked_field) 而不是query.setIgnoreAllMasks(true) 来仅撤销特定字段的掩码。有关如何在以下部分中更新纪录的更多介绍,请参阅更新纪录

查询数据

如果您想透过过滤条件获取多笔纪录: 您可以使用addFilter(fieldId, operator, value) 来加上过滤条件,并使用getAPIResultsFull() 来获取记录列表。或是您也可以针对同一个字段做两次addFilter,这样就可以做AND过滤。以下是您可以使用的操作符列表:

操作符名称 操作符值
等于 =
正则表达式 regex
大于或等于 >=
小于或等于 <=
大于 >
小于 <
包含 like

单一条件

使用一次addFilter筛出符合条件的纪录

var colA = "1000002"; //字段A
var query = db.getAPIQuery("/workflow-demo/1");
query.addFilter(colA, '=', 'Green');
var results = query.getAPIResultsFull(); // results为所有字段A为Green的数据
var entry = results.next();
while(entry) {
  // do something
  entry = results.next();
}

复合条件(AND)

使用多次addFilter筛出符合条件的纪录

var colA = "1000002"; //字段A
var colB = "1000004"; //字段B
var query = db.getAPIQuery("/workflow-demo/1");
query.addFilter(colA, '=', 'Green');
query.addFilter(colB, '=', 'Yes');
var results = query.getAPIResultsFull(); // results为所有字段A为Green且字段B为Yes的数据
var entry = results.next();
while(entry) {
  // do something
  entry = results.next();
}

复合条件(OR)

使用多次addFilter筛出符合条件的纪录

var colA = "1000002"; //字段A
var query = db.getAPIQuery("/workflow-demo/1");
query.addFilter(colA, '=', 'Green');
query.addFilter(colA, '=', 'Red');
var results = query.getAPIResultsFull(); // results为所有字段A为Green或字段A为Red的数据
var entry = results.next();
while(entry) {
  // do something
  entry = results.next();
}

范围查询

var colA = "1000006"; //字段A
var query = db.getAPIQuery("/workflow-demo/1");
query.addFilter(colA, '>', '20');
query.addFilter(colA, '<', '40');
var results = query.getAPIResultsFull(); // results为所有字段A大于20且字段A小于40的数据
var entry = results.next();
while(entry) {
  // do something
  entry = results.next();
}

请注意,当您透过日期或日期时间进行过滤时,它们需要以下列格式表示:yyyy/MM/ddyyyy/MM/dd HH:mm:ss。 您还可以透过调用 setFullTextSearch(String queryTerm) 来使用全文搜索作为查询过滤条件,而不是 addFilter()。

更新纪录

范例链接

让我们从一个简单的范例开始,检索当前记录作为物件,使用单击钮更新其值,然后将其保存回数据库。以下是范例表单的外观:

我们希望设计一些单击钮,通过点击单击钮运行简单的服务器端 JavaScript 工作流程来更改我们的状态字段值。以下是单击钮后面的程序代码:

/**

 * AP_Name:wfdemo
 * 主键码字段: 1000013

 * 名称 ID
 * - - - - - - - - - - - --------
 * No.: 1000011
 * 状态: 1000012

 */
function setStatus(recordId, status) {

var STATUS_FIELD = 1000012; // 状态字段的字段ID
var query = db.getAPIQuery("/workflow-demo/2"); // 获取工作表的查询物件
var entry = query.getAPIEntry(recordId);// 获取当前记录的物件

// 设置当前记录的状态值
if (status) {
  entry.setFieldValue(STATUS_FIELD, status);
}
else { // 用于切换
  var newStatus = entry.getFieldValue(STATUS_FIELD) == 'On' ? 'Off' : 'On'; // 获取字段的当前值
  entry.setFieldValue(STATUS_FIELD, newStatus);
}

// 保存记录到数据库
entry.save();
}

在编写 JavaScript 工作流程时,变量

db 是预定义的,您可以在任何地方参阅它。通常,我们会调用方法getAPIQuery(pathToForm) 来获取工作表的查询物件。然后,您可以透过在工作表物件上使用 getAPIEntry(recordId) 检索记录,并使用setFieldValue(fieldId,value) 设置字段的值,或者使用getFieldValue(fieldId) 检索记录中的值。请注意,当加上日期值时,它必须是以下列格式之一:

yyyy/MM/dd

yyyy/MM/dd HH:mm:ss

HH:mm:ss

如果您正在从多选字段检索值,其中可能有多个值,请使用 getFieldValues(fieldId) 以数组形式检索所有值。您还可以调用 setFieldValue(fieldId,value,true) 并在末尾加上一个 true 参数来指定您正在“加上”一个选项到当前的值列表,而不是覆盖现有值。请注意,这些操作仅适用于多选字段。

如果您想拷贝一个多选字段值并覆盖到另一个多选字段,您不能仅仅使用 getFieldValues(fieldId)setFieldValue(fieldId,value,true)。这里有一段程序代码供您参阅:


var multipleSelectionFieldValue = entry.getFieldValues(1013251); // 1013251 是一个多选字段 
var targetMultipleSelectionField = "";

for (var i = 0; i < multipleSelectionFieldValue.length; i++) {
  if (i == 0) {
    targetMultipleSelectionField = targetMultipleSelectionField + multipleSelectionFieldValue[i];
  } 
  else {
    targetMultipleSelectionField = targetMultipleSelectionField + "|" + multipleSelectionFieldValue[i];
  }
}

entry.setFieldValue(1013252, targetMultipleSelectionField, false); // 1013251 是另一个多选字段 

请注意,您需要检索每个选项,并使用垂直条字符(|)格式化这些选项,就像您在从 Excel 或 CSV 文档导入现有数据时所做的一样。

如果您要将值设置为文档上载字段,您可以使用 setFieldFile(int fieldId, String fileName, String fileContent) 来创建一个具有您提供的 fileName 和 fileContent 的文档。Ragic 将会保存此文档到指定字段,这样文档就可以在订户界面上下载。

完成后,只需在记录物件上调用 save() 将其保存回数据库。

创建记录

创建记录与更新纪录非常相似。不同的是,您需要调用 query.insertAPIEntry() 来创建新纪录。与 getAPIEntry() 一样,它也会回传一个纪录物件,您可以使用 setFieldValue 来加上字段值。调用 entry.save() 之后,记录将被创建。

这是一个简单的范例,修改了记录更新范例来创建新纪录:

/**

 * AP_Name:wfdemo
 * 主键码字段: 1000013

 * 名称 ID
 * - - - - - - - - - - - --------
 * No.: 1000011
 * 状态: 1000012

 */
function setStatus(recordId, status) {
  var STATUS_FIELD = 1000012; // 状态字段的字段ID
  var query = db.getAPIQuery("/workflow-demo/2"); // 获取工作表的查询物件
  var entry = query.insertAPIEntry();// 创建新纪录物件

  // 创建新纪录到数据库
  entry.save();
}

子表格

范例链接

如果您在工作表中有子表格,您也可以使用我们的 API 来检索数据或进行编辑,像以下范例。表单看起来像这样:

这个工作流程将遍历子表格中的每一行,找到本年度的总金额(根据子表格中的日期字段)、金额最多的一年总额,并确定哪一年具有最高总额。这被设计为一个 Post-workflow,因此这三个只读字段将在记录保存后由工作流程填写。

/**

 * AP_Name:wfdemo
 * 主键码字段: 1000006

 * 日期子表格主键码: 1000007

 * 字段名称 字段 ID
 * - - - - - - - - - - - --------
 * No. : 1000001
 * 名称: 1000002
 * 日期: 1000003
 * 金额 : 1000004
 * 本年度总额: 1000010
 * 最高年度总额 : 1000009
 * 最高总额年份 : 1000008

 */

var KEY_FIELD = 1000006;
var AMOUNT_SUBTABLE_ID= 1000007;
var DATE_FIELD= 1000003;
var AMOUNT_FIELD= 1000004;
var MAX_YEAR_FIELD= 1000008;
var MAX_TOTAL_FIELD = 1000009;
var THIS_YEAR_TOTAL_FIELD = 1000010;

var query = db.getAPIQuery("/workflow-demo/1");

var entry = query.getAPIEntry(param.getNewNodeId(KEY_FIELD));
var subtableSize = entry.getSubtableSize(AMOUNT_SUBTABLE_ID);

var yearTotal = {}
for (var i = 0; i < subtableSize; i++) {
  var year = parseInt(entry.getSubtableFieldValue(AMOUNT_SUBTABLE_ID, i, DATE_FIELD).substr(0, 4));
  var amount = parseInt(entry.getSubtableFieldValue(AMOUNT_SUBTABLE_ID, i, AMOUNT_FIELD));
  if (year in yearTotal) {
    yearTotal[year] += amount;
  } else {
    yearTotal[year] = amount;
  }
}

var maxYear;
for (var year in yearTotal) {
  if (!maxYear || yearTotal[maxYear] < yearTotal[year]) {
    maxYear = year;
  }
}

entry.setFieldValue(MAX_YEAR_FIELD, maxYear);
entry.setFieldValue(MAX_TOTAL_FIELD, yearTotal[maxYear]);
entry.setFieldValue(THIS_YEAR_TOTAL_FIELD, yearTotal[new Date().getFullYear()]);
entry.save();

基本想法是使用 getSubtableSize(subtableId) 获取记录的子表格行数,并使用 getSubtableFieldValue(subtableId,subtableRowIndex,subtableFieldId) 检索它们的值。当您开始编辑工作流程程序时,应该能在自动生成的注释中找到子表格 ID 和字段 ID 信息。 您还可以使用 setSubtableFieldValue(subtableFieldId,subtableRootNodeId,value) 将值设置到子表格字段。subtableRootNodeId 用于指定您所指的子表格行。要查找现有子表格行的 subtableRootNodeId,您可以使用以下调用 getSubtableRootNodeId(subtableId,subtableRowIndex),它将回传包含 subtableRootNodeId 的整数。 如果需要向子表格加上一行,可以使用负数 subtableRootNodeId 例如 -100,这样所有设置到相同负数 subtableRootNodeId 的值将应用到同一新子表格行,而设置到不同负数 subtableRootNodeId 例如 -101 的值将创建具有这不同值集的另一行。

Pre-workflow 和 Post-workflow

如果您希望在 Pre-workflow 和 Post-workflow 中获取值,请参阅以下范例:

var list = param.getSubtableEntry(AMOUNT_SUBTABLE_ID);
var arr = list.toArray();
response.setStatus('WARN');
for (var i = 0; i < arr.length; i++) {
  response.setMessage('日期: '+arr[i].getNewValue(DATE_FIELD)+', 金额: '+arr[i].getNewValue(AMOUNT_FIELD)+'\r\n');
}

访问所有纪录

目前我们提供两种方式让您遍历 query 中的每笔纪录,分别是使用 getAPIResultsFull 与 getAPIResultList 两种方法。

方法名称 描述
getAPIResultsFull 利用 while 循环逐笔获取数据,对于内存负担较小,适合用来处理大量数据。
getAPIResultList 一次性地将所有数据抓进内存,较为直观,但对内存负担较大。
  //getAPIResultsFull 范例
  var query = db.getAPIQuery("/workflow-demo/1");
  var results = query.getAPIResultsFull();
  var entry = results.next();
  while(entry) {
    // do something
    entry = results.next();
  }

  //getAPIResultList 范例
  var query = db.getAPIQuery("/workflow-demo/1");
  var results = query.getAPIResultList();
  for(var i = 0; i < results.length; i ++) {
    var entry = result[i];
    // do something
  }

拷贝记录

范例链接:从这里拷贝拷贝到这里

拷贝记录是我们遇到的最常见的工作流程程序之一。我们编写了一个非常简单的函数来简化这类操作。假设我们希望查看这个工作表中的一笔纪录:

透过点击单击钮,在这个工作表中生成一笔纪录:

以下是这个动作单击钮的程序代码:

/**

 * AP_Name:wfdemo
 * 主键码字段: 1000022

 * S1 子表格主键码: 1000023
 * T1 子表格主键码: 1000029

 * 字段名称字段 ID
 * - - - - - - - - - - - --------
 * A: 1000014
 * C: 1000015
 * B: 1000016
 * D: 1000017
 * S1 : 1000018
 * S2 : 1000019
 * S3 : 1000020
 * S4 : 1000021
 * T1 : 1000024
 * T2 : 1000025
 * T3 : 1000026

 */

function copyEntry(nodeId) {
  db.entryCopier(JSON.stringify({
    THIS_PATH:"/workflow-demo/3",
    THIS_NODEID:nodeId,
    NEW_PATH:"/workflow-demo/4",
    COPY:{
      1000030:1000014,// A
      1000031:1000015,// C
      1000032:1000018,// S1
      1000033:1000020 // S3
    }
  }),response);
}

在这里,您可以看到我们可以透过一个简单的函数调用entryCopier 来完成拷贝操作。entryCopier 接受一个 JSON字符串作为其参数。只需输入源工作表、目标工作表、我们正在拷贝的纪录,以及最重要的是,哪些字段应映射到哪些字段。完成映射后,您可以非常轻松地创建动作单击钮来从一个工作表拷贝记录到另一个工作表。让我们看另一个范例。 这里,我们希望将 Lin 从 "CopyFrom" 拷贝到 "CopyTo",并将状态设置为 new。此外,我们还希望记录创建日期(请忽略本范例中的 $DATE 字段的使用)。

/**
 * 字段名称字段 ID
 * - - - - - - - - - - - --------
 * From-ID: 1000001
 * From-Name: 1000002
 * To-ID: 1000004
 * To-Name: 1000005
 * 状态: 1000007
 * 登记日期: 1000008
 */

function copyEntry(nodeId) {
  db.entryCopier(JSON.stringify({
    THIS_PATH:"/entrycopier/1",
    THIS_NODEID:nodeId,
    NEW_PATH:"/entrycopier/2",
    COPY:{
      // 目标字段:源字段
      1000004:1000001, 
      1000005:1000002, 
    }
  }),response);
  // 获取刚才拷贝的纪录的 rootNodeId
  var newEntryRootNodeId = response.getRootNodeId();
  // 我们需要创建一个 ApiQuery 来查询 '/entrycopier/2'
  var toApiQuery = db.getAPIQuery('/entrycopier/2');
  // 获取刚才拷贝的纪录
  var newEntry = toApiQuery.getAPIEntry(newEntryRootNodeId);
  newEntry.setFieldValue(1000007, "New");
  var current = new Date();
  newEntry.setFieldValue(1000008, current.getFullYear()+'/' + (current.getMonth()+1) + '/' + current.getDate());
  newEntry.save();
}

接下来,设置动作单击钮并保存。

然后,只需点击动作单击钮即可显示结果。

删除记录

您可以使用 query.deleteEntry(nodeId) 来删除纪录,若您有多笔纪录需要删除,则可透过 query.getAPIResultList() 来遍历整个 query 删除数据。

  var query = db.getAPIQuery(pathToForm);
  query.addFilter(1000001, '=', 'To Be Deleted'); //筛出 1000001 的值为 To Be Deleted 的所有纪录
  var results = query.getAPIResultList();
  for(var i = 0; i < results.length; i ++) { //将筛出的纪录全数删除
    var entry = results[i];
    query.deleteEntry(entry.getRootNodeId());
  }

若您要删除的纪录大于 1000 笔,则须搭配 setLimitSize 使用,query 默认一次仅能处理 1000 笔数据。

  //假设有 3000 笔数据需要删除

  var limitSize = 1500; //每个 query 的上限
  var query = db.getAPIQuery(pathToForm);
  query.addFilter(1000001, '=', 'To Be Deleted'); //筛出 1000001 的值为 To Be Deleted 的所有纪录
  query.setLimitSize(limitSize); //非必要,此为设置 query 最大上限,默认为 1000
  var results = query.getAPIResultList();

  while(results.length > 0) {
    for(var i = 0; i < results.length; i ++) {
      var entry = results[i];
      query.deleteEntry(entry.getRootNodeId());
    }

    //每过 1000 笔数据后重新抓取 query
    var query = db.getAPIQuery(pathToForm);
    query.addFilter(1000001, '=', 'To Be Deleted');
    query.setLimitSize(limitSize);
    results = query.getAPIResultList();
  }

检查订户权限

您可以根据将要运行程序的订户所在的群组进行一些条件处理。一个订户可以在多个用户组组中,所以通常我们可以透过调用 user.isInGroup(groupName) 来判断订户是否在该用户组组中。以下是一个典型的使用范例,这段编程为 Pre-workflow,但 user 物件和 isInGroup 调用可以在所有类型的工作流程中使用,除了 Daily Workflow,它是由系统触发的。

if(!user.isInGroup('SYSAdmin')){// 检查订户是否为 SYSAdmin
  response.setStatus('INVALID');// 如果不是,则不保存记录
  response.setMessage('您没有权限进行此操作。');// 并显示一笔信息
}

发送电邮通知

有时候您可能希望根据一组条件发送电邮通知,或者您希望真正自订您的通知信息内容。您可以为此编写服务器端 JavaScript 工作流程。这是发送电邮的程序代码,非常简单:


// 省略...先检索记录物件

var name=entry.getFieldValue(1001426);
var email=entry.getFieldValue(1001428);
var title=entry.getFieldValue(1001386);

mailer.compose(
  email,// 收件人
  null, // 副本
  'support@example.com',// 回覆地址
  'Acme, Inc.',// 显示的发件人
  title,
  '嗨 '+name+',

我们已收到您的销售订单,'+ '并将很快处理您的订单。

'+ '您可以在 https://www.ragic.com/example/1 查看您的订单详情。

'+ '谢谢。


最好的祝福,

Sophia, 销售经理

Acme, Inc.' ); // mailer.attach(myURL); // 您可以使用 .attach 来附加来自 URL 的内容 mailer.send();

请注意,如果您希望将电邮发送给多个收件人,只需用逗号分隔每个电邮地址。对于附件,您可以使用 mailer.attach(myURL); 来附加 Ragic 上的纪录,使用记录的 URL。例如,这是 Ragic 上的纪录 URL(始终忽略哈希后的 URL):

https://www.ragic.com/wfdemo/workflow-demo/2/9

这是其 HTML 友好列印版 URL:

https://www.ragic.com/wfdemo/workflow-demo/2/9.xhtml

这是其 Excel 版本 URL:

https://www.ragic.com/wfdemo/workflow-demo/2/9.xlsx

您还可以使用邮件归并 URL,例如,cid 是邮件归并的 ID,当尝试下载邮件归并文档时,您可以在 URL 中获取 cid:

https://www.ragic.com/wfdemo/workflow-demo/2/9.custom?rn=9&cid=1

我们对您可以发送的电邮数量有限制。因此请合理发送!如果您对电邮发送配额有任何疑问,请发送电邮至 support@ragic.com

创建和撤销签核

您可以透过在工作表中加上 Post-workflow 程序来启动或撤销记录的签核。首先,在设计模式中设置签核步骤。

现在,在 Post-workflow 中设置签核详细信息

function autoStartApprover() {
  var signers = [];
  signers.push({
    'stepIndex':'0',
    'approver':'kingjo@ragic.com',
    'stepName':'START'
  });
  signers.push({
    'stepIndex':'1',
    'approver':'HR0000@hr.hr',
    'stepName':'HR'
  });

  approval.create(JSON.stringify(signers));
}

autoStartApprover();

结果

{
  'stepIndex':'0',
  'approver':'kingjo@ragic.com',
  'stepName':'START'
}

是您应提供的参数格式。

stepIndex : 用于指明签核的步骤,从零开始。 approver :用于指明谁可以签核此步骤。 stepName : 用于设置签核步骤名称。

如果参数格式有误或您提供的签核人不符合设计模式中设置的规则,签核将不会创建。

在这个范例中,如果您给第二步提供参数如下:

{
  'stepIndex':'1',
  'approver':'kingjo@ragic.com',
  'stepName':'HR'
}

那么它将失败,因为 kingjo@ragic.com 不在 HR 群组中。我们还提供了三种特殊格式,用于根据您的公司树动态决定签核人员。您可以参阅此链接了解更多信息。

$DS : 签核人将设置为订户的直属主管。 $SS :签核人将设置为订户主管的主管。 $DSL : 签核人将设置为前一个签核人的主管。参数格式如下:

{
  'stepIndex':'1',
  'approver':'$DSL',
  'stepName':''
}

当使用这些特殊格式时,您不需要提供非空的 stepName。注意:如果特殊格式中的订户不符合您在设计模式中设置的规则,该签核将不会创建。

发送手机应用通知

除了电邮通知,您还可以发送手机应用通知。如果订户在其移动设备上安装了 iOS 应用或 Android 应用,则会向其发送通知。电邮引用的是您在 Ragic 帐户中登记的订户电邮地址。例如:

mailer.sendAppNotification("mike@example.com","测试手机通知");

如果您希望订户在点击此通知时被重定向到指定记录,您应提供要重定向到的表单路径(例如:/forms/1)和记录 ID。调用应如下所示:

mailer.sendAppNotification("mike@example.com","测试手机通知","/forms/1",12);

签核和其他记录信息

您可以首先对从 db.getAPIQuery 获取的查询物件发出以下命令,以包括完整的纪录信息:

query.setIfIncludeInfo(true);

然后您可以透过以下方式获取记录的签核信息:

entry.getFieldValue('_approve_status');// 获取当前签核状态
entry.getFieldValue('_approve_next');// 获取下一个应签核此记录的人
entry.getFieldValue('_create_date');// 获取记录的创建日期
entry.getFieldValue('_create_user');// 获取记录的创建订户电邮

签核状态将显示 F 表示已签核,REJ 表示拒绝,P 表示处理中。

下载和上载文件

发送 HTTP 请求

您可以向 URL 发送 HTTP GET/POST/DELETE/PUT 请求并获取回传结果:

util.getURL(String urlstring)
util.postURL(String urlstring,String postBody)
util.deleteURL(String urlstring)
util.putURL(String urlstring,String putBody)

变量 util 是预定义的。 如果您需要忽略当前 HTTP 请求的 SSL 证书检查,可以在发送请求前加上以下程序代码:

util.ignoreSSL()

此外,您可以调用 util.setHeader(String name,String value) 设置 HTTP 首部。util.removeHeader(String name) 用于移除首部。

下载和上载文件

您可以透过URL下载文件并且获取文件名称:

util.downloadFile(fileUrl);
//fileUrl:文件的来源URL
//该方法回传的值:下载下来的檔名 

当您将文件下载至数据库后,可以使用setFieldValue将檔名填回文件字段,即可在表单中预览或是将文件下载至本机。

var fileFieldId = 1000087;
var fileName = util.downloadFile(fileUrl);
entry.setFieldValue(1000087, fileName);
entry.save();

您也可以上载文件至目标网址并获取文件名称:

util.postFile(sourceFileUrl, destinationUrl)
//sourceFileUrl:文件的来源URL
//destinationUrl:文件要上载的目标URL
//该方法回传的值:上载到数据库的檔名

关于获取完整文件、图片链接的方法可以参阅这篇教学

调用其他表单的 workflow

使用 workflow 修改其他表单的数据时,默认是不会触发该表单的 workflow。如果您需要触发该表单的 post-workflow 或是 pre-workflow,可以使用以下程序代码:

var query = db.getAPIQuery(pathToForm);
var entry = query.getAPIEntry(recordId);
//运行某些动作...

entry.setIfExecuteWorkflow(true);
entry.save();

API 参阅

此处列出的预定义系统全域变量可以直接访问,无需定义。

db


getAPIResult()

遍历查询时获取第一笔纪录

getAPIResultsFull()

获取查询的纪录,透过next()方法来获取下一笔数据,搭配while循环即可逐笔获取数据

getAPIResultList()

获取查询的纪录数组

getAPIEntry(int rootNodeId)

通过节点 ID 获取记录

getKeyFieldId()

检索此查询所在工作表的主键码字段 ID。

getFieldIdByName(String fieldName)

根据指定名称获取字段 ID。如果有多个相同名称的字段,将回传第一个独立的字段 ID。

insertAPIEntry()

向查询中插入新纪录,该方法回传新纪录

addFilter(int fieldId, String operand, String value)

单击指定条件过滤数据

setIfIgnoreFixedFilter(boolean ifIgnoreFixedFilter)

设置是否忽略目标工作表的固定过滤条件

setOrder(int orderField, int orderDir)

单击指定字段 ID 和排序方向对查询数据进行排序,参数orderDir 设置为 1 表示升序排序,设置为 2 表示降序排序,设置为 3 表示次要升序排序,设置为 4 表示次要降序排序。

deleteEntry(int nodeId)

通过节点 ID 删除数据

deleteEntryToRecycleBin(int nodeId)

通过节点 ID 将数据删除到回收站

setLimitSize(int limitSize)

默认情况下,ScriptAPIQuery 每次查询回传 1000 笔纪录,您可以使用 setLimitSize 更改每次查询回传的纪录数量。然而,我们不建议每次查询回传过多的纪录,因为这可能会占用过多内存并影响性能。我们建议使用下一个方法 setLimitFrom 进行调页。

setLimitFrom(int limitFrom)

此方法用于遍历 ScriptAPIQuery 的所有记录。设置 limitFrom 将告诉 ScriptAPIQuery 从偏移量开始回传记录,以便您遍历 ScriptAPIQuery 的所有记录,因为 ScriptAPIQuery 默认每次查询回传 1000 笔纪录。您应该检查回传的纪录数量是否等于回传列表的大小,以确定是否存在下一页。

setGetUserNameAsSelectUserValue(boolean b)

设置为 false 时,查询将检索订户的电邮作为选择订户字段的值,而不是用户名称。

recalculateAll(String Path)

针对某张表单的所有数据的所有字段进行公式重算。

recalculateAll(String Path, String FieldId...)

针对某张表单的所有数据的指定字段进行公式重算,第二个参数开始皆为 FieldId,可以用逗号分隔以输入多个 FieldId。

setLogRecalcCostTime(boolean b)

设置为True就能在“Workflow 表单公式重算运行时间纪录”中显示,使用 db.recalculateAll() 公式重算一张表单所花费的时间。

entry


getFieldValue(int fieldId)

透过字段 ID 获取字段值。不包括子表格字段。

getFieldIdByName(String fieldName)

透过指定名称获取字段 ID。如果有多个相同名称的字段,将回传第一个字段值。

getKeyFieldId()

检索此工作表的主键码字段 ID。

getFieldValueByName(String fieldName)

透过字段名称获取字段值。不包括子表格字段。如果有多个相同名称的字段,将回传第一个字段值。

getFieldValues(int fieldId)

将多选字段中的所有字段值作为数组获取。不包括子表格字段。

getRootNodeId()

获取数据的根节点 ID

getRootfieldId()

获取数据的根字段 ID

getSubtableSize(int subtableRootfieldId)

透过子表格的根字段 ID 获取子表格的大小。

getSubtableRootNodeId(int subtableRootfieldId, int rowNumber)

透过子表格的根字段 ID 和子表格中的行号获取根节点 ID。

getJSON()

获取整个纪录的 JSON 表示。

setFieldValue(int fieldId, String value)

为指定字段设置值。对于子表格字段,您需要使用 setSubtableFieldValue()。

setFieldValue(int fieldId, String value, boolean appendValue)

为多选字段设置值,参数 appendValue 必须为 true。对于子表格字段,您需要使用 setSubtableFieldValue()。

setFieldFile(int fieldId, String fileName, String fileContent)

仅适用于文档上载或图形字段。将创建一个具有您提供的 fileContent 的文档上载并保存到指定字段。对于子表格字段,您需要使用 setSubtableFieldFile()。

setFieldFile(int fieldId, String fileName, String fileContent, boolean appendValue)

若要不覆盖文件字段原本的文件,参数 appendValue 必须为 true

setSubtableFieldValue(int fieldId, int subtableRootNodeId, String value)

为子表格字段设置值,您可以透过方法 getSubtableRootNodeId 获取参数 subtableRootNodeId

setSubtableFieldValue(int fieldId, int subtableRootNodeId, String value, boolean appendValue)

为多选子表格字段设置值,参数 appendValue 必须为 true

setSubtableFieldFile(int fieldId, int subtableRootNodeId, String fileName, String fileContent)

仅适用于子表格的文档上载或图形字段。将创建一个具有您提供的 fileContent 的文档上载并保存到指定字段。

setSubtableFieldFile(int fieldId, int subtableRootNodeId, String fileName, String fileContent, boolean appendValue)

若要不覆盖文件字段原本的文件,参数 appendValue 必须为 true

deleteSubtableRowByRowNumber(int subtableRootfieldId, int rowNumber)

透过根字段 ID 和子表格中的行号删除子表格行。

deleteSubtableRowAll(int subtableRootfieldId)

删除指定子表格中的所有行

deleteSubtableRow(int subtableRootfieldId, int subtableRootNodeId)

透过根字段 ID 和子表格的根节点 ID 删除子表格行

loadAllLinkAndLoad()

加载数据中链接和加载分配的所有加载字段的值。

recalculateAllFormulas()

重新计算数据中包含公式的所有字段。

recalculateFormula(int fieldId)

重新计算指定字段的公式。
注意:如果两个或多个字段共享相同的 fieldId,请改用 recalculateFormula(int fieldId, String cellName)。

recalculateFormula(int fieldId, String cellName)

使用 cellName 参数确定字段的单元格位置(如 A1,C2,H21 等),重新计算指定字段的公式。此方法适用于具有相同 fieldId 的多个字段。

loadAllDefaultValues(ScriptUser user)

加载设置了默认值的所有字段的值,参数 user 是预定义的。

loadDefaultValue(int fieldId, ScriptUser user)

加载指定字段的默认值,参数 user 是预定义的。

lock()

加锁数据

unlock()

解锁数据

save()

保存数据

setCreateHistory(boolean createHistory)

设置数据是否需要创建历史记录

isCreateHistory()

数据是否设置为创建历史记录

setIfExecuteWorkflow(boolean executeWorkflow)

设置数据是否需要运行工作流程(Pre-workflow 和 Post-workflow)

setIgnoreEmptyCheck(boolean ignoreEmptyCheck)

设置是否忽略不为空的字段检查

setRecalParentFormula(boolean recalParentFormula)

如果此工作表是由另一工作表的子表格创建的,或者被另一工作表引用,这意味着此工作表具有父工作表,则可以调用此方法设置是否需要重新计算父工作表。

setIfDoLnls(boolean)

如果该工作表链接到此工作表(源工作表),则将触发另一工作表上的同步。您应该始终在源工作表中加上此方法以触发另一工作表上的同步。

response


getStatus()

获取响应的状态。可为 SUCCESS、WARN、CONFIRM、INVALID、ERROR

setStatus(String status)

设置响应的状态。可为 SUCCESS、WARN、CONFIRM、INVALID、ERROR

setMessage(String plainMessage)

设置程序运行时显示的信息。此功能可多次调用,所有设置的信息将同时显示。

numOfMessages()

回传已设置的信息数量。

setOpenURL(String url)

仅限于已安装的工作表范围。保存编辑后将用户重定向到指定的 URL。

setOpenURLInNewTab(boolean b)

设置是否在新标签页中打开 URL。默认为 true。

user


getEmail()

获取订户的电邮地址

getUserName()

获取订户的全名

isInGroup(String groupName)

回传订户是否在具有 groupName 的用户组组中

mailer


compose(String to,String cc,String from,String fromPersonal,String subject,String content)

编写一封要发送的电邮信息。to 和 cc 参数可以包含多个电邮地址,只需用逗号分隔。

send()

发送刚编写的信息。

sendAsync()

异步发送刚编写的信息。请注意,sendAsync 每次程序运行仅能调用一次。

attach(String url)

将文档附加到信息。URL 应为完整的 https:// 开头的 URL。

setSendRaw(boolean b)

设置内容是否不转换为 HTML。如果内容已经是 HTML,请将其设置为 true。

sendAppNotification(String email,String message)

如果订户安装了 Ragic iOS 应用或 Android 应用,向该订户发送移动应用通知。

sendAppNotification(String email,String message,String pathToForm,int nodeId)

如果订户安装了 Ragic iOS 应用或 Android 应用,向该订户发送移动应用通知。订户在点击此通知时将被重定向到指定的纪录。"pathToForm" 应为 "/forms/1" 格式,不包含帐户名,包含标签文件夹和工作表索引。

util


getURL(String urlstring)

以 GET 方法调用 URL

postURL(String urlstring,String postBody)

以 POST 方法调用 URL。

deleteURL(String urlstring)

以 DELETE 方法调用 URL。

putURL(String urlstring,String putBody)

以 PUT 方法调用 URL。

downloadFile(String fileUrl)

将 fileUrl 回传的文件下载至数据库中的 upload 文件夹

downloadFile(String fileUrl, String postBody)

将 fileUrl 回传的文件下载至数据库中的 upload 文件夹

setHeader(String name,String value)

设置 HTTP 首部,将在后续的 URL 调用中使用。

ignoreSSL()

忽略当前 HTTP 请求的 SSL 证书检查

removeHeader(String name)

移除后续 URL 调用中使用的 HTTP 首部。

logWorkflowError(String text)

在工作流程日志中记录字符串字日志信息,您可以在数据库维护页面中找到它。

account


getUserName(String email)

根据电邮地址获取订户的全名。

getUserEmail(String userName)

根据用户名称获取电邮地址。

reset()

程序运行完毕后,清除所有与帐户相关的高速缓存并重新加载页面。

getTimeZoneOffset()

获取此帐户的时区时差,以毫秒为单元。

getTimeZoneOffsetInHours()

获取此帐户的时区时差,以小时为单元。

param


getUpdatedEntry()

仅适用于 Post-workflow。回传刚创建或更新的纪录。

getNewNodeId(int fieldId)

回传指定字段的新值写入后的节点 ID(整数)。

getOldNodeId(int fieldId)

回传指定字段的新值写入前的节点 ID(整数)。

getNewValue(int fieldId)

回传指定字段的新值写入后的值。

getOldValue(int fieldId)

回传指定字段的新值写入前的值。

getNewValues(int fieldId)

类似于 getNewValue,但可以同时访问多个值,这在处理多选字段时非常有用。

getOldValues(int fieldId)

类似于 getOldValue,但可以同时访问多个值,这在处理多选字段时非常有用。

getSubtableEntry(int fieldId)

回传一个参数列表,可以操作子表格中的每笔纪录。

isCreateNew()

回传数据是否新创建。

approval


create(String[] wfSigner)

仅适用于 Post-workflow。wfSigner 是一个包含特定 JSON 格式物件的数组。

  {
    'stepIndex': 签核的步骤 - 从 0 开始, 
    'approver' : 签核人的电邮, 
    'stepName' : 签核人的姓名或职称, 
  }

  范例 "单步骤签核人" : 
  wfSigner push({
    'stepIndex':'1',
    'approver':'kingjo@ragic.com',
    'stepName':'Jo'
  })

请注意,wfSigner 应满足您在设计模式中设置的签核。 例如,如果在设计模式中的第二步仅有 "HR00@gmail.com" 一个候选人,您应提供一个包含 approver: HR00@gmail.com 和 stepIndex : 1 的 JSON。

cancel()

仅适用于 Post-workflow。撤销数据中的签核。

approvalParam


getEntryRootNodeId()

获取数据的根节点 ID

getApprovalAction()

获取签核的操作。可为 CREATE、APPROVE、FINISH、CANCEL、REJECT

回最上面 目录

马上登记
免费试用 Ragic!

用 Google 帐号登记

立即科技 Ragic, Inc.
02-7728-8692
info@ragic.com
台北市中正区南昌路二段81号9楼