diff --git a/@rbv b/@rbv
index e83ed9efb..0ace77d3b 160000
--- a/@rbv
+++ b/@rbv
@@ -1 +1 @@
-Subproject commit e83ed9efb79ef2bffacbbe8c2288aecc7bcd2042
+Subproject commit 0ace77d3b95da6f61691bd2610230c4bd979011d
diff --git a/pom.xml b/pom.xml
index b4a139fd8..7019e6f30 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
com.rebuild
rebuild
- 3.4.3
+ 3.4.4
rebuild
Building your business-systems freely!
@@ -38,19 +38,17 @@
-
+
+
- -->
com.github.eirslett
frontend-maven-plugin
diff --git a/src/main/java/com/rebuild/core/Application.java b/src/main/java/com/rebuild/core/Application.java
index 5dcf8432d..620265639 100644
--- a/src/main/java/com/rebuild/core/Application.java
+++ b/src/main/java/com/rebuild/core/Application.java
@@ -73,11 +73,11 @@ public class Application implements ApplicationListener
/**
* Rebuild Version
*/
- public static final String VER = "3.4.3";
+ public static final String VER = "3.4.4";
/**
* Rebuild Build [MAJOR]{1}[MINOR]{2}[PATCH]{2}[BUILD]{2}
*/
- public static final int BUILD = 3040308;
+ public static final int BUILD = 3040409;
static {
// Driver for DB
diff --git a/src/main/java/com/rebuild/core/service/BaseService.java b/src/main/java/com/rebuild/core/service/BaseService.java
index a2149dd4a..e6568f883 100644
--- a/src/main/java/com/rebuild/core/service/BaseService.java
+++ b/src/main/java/com/rebuild/core/service/BaseService.java
@@ -149,7 +149,8 @@ public class BaseService extends InternalPersistService {
for (Field n2nField : n2nFields) {
ID[] newRefs;
if (isNew) {
- newRefs = record.getIDArray(n2nField.getName());
+ Object maybeNull = record.getObjectValue(n2nField.getName());
+ newRefs = NullValue.is(maybeNull) ? ID.EMPTY_ID_ARRAY : (ID[]) maybeNull;
if (newRefs == null || newRefs.length == 0) continue;
} else {
if (record.hasValue(n2nField.getName())) {
diff --git a/src/main/java/com/rebuild/core/service/approval/FlowParser.java b/src/main/java/com/rebuild/core/service/approval/FlowParser.java
index e97f392c0..593af78c2 100644
--- a/src/main/java/com/rebuild/core/service/approval/FlowParser.java
+++ b/src/main/java/com/rebuild/core/service/approval/FlowParser.java
@@ -14,7 +14,14 @@ import com.rebuild.core.support.i18n.Language;
import com.rebuild.utils.JSONUtils;
import org.apache.commons.lang.StringUtils;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* 流程解析
@@ -102,11 +109,14 @@ public class FlowParser {
return Collections.emptyList();
}
- // 非条件分支,只会有一个节点
+ // 非条件分支只会有一个节点
if (!FlowNode.TYPE_BRANCH.equals(next.get(0).getType())) {
return next;
}
+ // fix:Gitee#I8326Z 移除非条件节点
+ next.removeIf(flowNode -> !flowNode.getType().equals(FlowNode.TYPE_BRANCH));
+
// 条件节点优先级排序
next.sort((o1, o2) -> {
int p1 = ((FlowBranch) o1).getPriority();
diff --git a/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java b/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java
index 340fdd4d2..b2871ab79 100644
--- a/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java
+++ b/src/main/java/com/rebuild/core/service/query/AdvFilterParser.java
@@ -335,26 +335,32 @@ public class AdvFilterParser extends SetUser {
int x = NumberUtils.toInt(value);
value = formatDate(addDay(x), 0);
} else if (ParseHelper.EVW.equalsIgnoreCase(op) || ParseHelper.EVM.equalsIgnoreCase(op)) {
- Calendar c = CalendarUtils.getInstance();
+ final Calendar today = CalendarUtils.getInstance();
+
int x = NumberUtils.toInt(value);
if (ParseHelper.EVW.equalsIgnoreCase(op)) {
- boolean isSunday = c.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY;
- if (isSunday) c.add(Calendar.DAY_OF_WEEK, -1);
+ boolean isSunday = today.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY;
+ if (isSunday) today.add(Calendar.DAY_OF_WEEK, -1);
if (x < 1) x = 1;
if (x > 7) x = 7;
x += 1;
if (x <= 7) {
- c.set(Calendar.DAY_OF_WEEK, x);
+ today.set(Calendar.DAY_OF_WEEK, x);
} else {
- c.set(Calendar.DAY_OF_WEEK, 7);
- c.add(Calendar.DAY_OF_WEEK, 1);
+ today.set(Calendar.DAY_OF_WEEK, 7);
+ today.add(Calendar.DAY_OF_WEEK, 1);
}
} else {
if (x < 1) x = 1;
if (x > 31) x = 31;
- c.set(Calendar.DAY_OF_MONTH, x);
+
+ // v3.4.4 每月最后一天
+ int maxDayOfMonth = today.getActualMaximum(Calendar.DAY_OF_MONTH);
+ if (x > maxDayOfMonth) x = maxDayOfMonth;
+
+ today.set(Calendar.DAY_OF_MONTH, x);
}
- value = formatDate(c.getTime(), 0);
+ value = formatDate(today.getTime(), 0);
}
else if (ParseHelper.YTA.equalsIgnoreCase(op)) {
value = formatDate(addDay(-1), 0);
diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/AutoHoldTriggerAction.java b/src/main/java/com/rebuild/core/service/trigger/impl/AutoHoldTriggerAction.java
new file mode 100644
index 000000000..52f1cd454
--- /dev/null
+++ b/src/main/java/com/rebuild/core/service/trigger/impl/AutoHoldTriggerAction.java
@@ -0,0 +1,160 @@
+/*!
+Copyright (c) Ruifang Tech and/or its owners. All rights reserved.
+*/
+
+package com.rebuild.core.service.trigger.impl;
+
+import cn.devezhao.persist4j.Entity;
+import cn.devezhao.persist4j.Field;
+import cn.devezhao.persist4j.dialect.FieldType;
+import cn.devezhao.persist4j.engine.ID;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.rebuild.core.Application;
+import com.rebuild.core.metadata.EntityHelper;
+import com.rebuild.core.metadata.MetadataHelper;
+import com.rebuild.core.privileges.bizz.InternalPermission;
+import com.rebuild.core.service.approval.ApprovalHelper;
+import com.rebuild.core.service.approval.ApprovalState;
+import com.rebuild.core.service.general.OperatingContext;
+import com.rebuild.core.service.trigger.ActionContext;
+import com.rebuild.core.service.trigger.TriggerAction;
+import com.rebuild.core.service.trigger.TriggerException;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author devezhao
+ * @since 2023/9/27
+ */
+@Slf4j
+public abstract class AutoHoldTriggerAction extends TriggerAction {
+
+ private Set willRecords;
+
+ protected AutoHoldTriggerAction(ActionContext actionContext) {
+ super(actionContext);
+ }
+
+ // 删除前保持
+ @Override
+ protected void prepare(OperatingContext operatingContext) throws TriggerException {
+ if (operatingContext.getAction() == InternalPermission.DELETE_BEFORE) {
+ willRecords = getRelatedRecords(
+ actionContext, operatingContext.getAnyRecord().getPrimary());
+ }
+ }
+
+ /**
+ * 获取待处理记录
+ *
+ * @param operatingContext
+ * @return
+ */
+ protected Set getWillRecords(OperatingContext operatingContext) {
+ if (willRecords == null) {
+ willRecords = getRelatedRecords(
+ actionContext, operatingContext.getAnyRecord().getPrimary());
+ }
+ return willRecords;
+ }
+
+ /**
+ * 获取相关记录
+ *
+ * @param actionContext
+ * @param sourceRecordId
+ * @return
+ */
+ protected static Set getRelatedRecords(ActionContext actionContext, ID sourceRecordId) {
+ // 共享的需要使用记录 ID
+ if (sourceRecordId.getEntityCode() == EntityHelper.ShareAccess) {
+ Object[] o = Application.getQueryFactory().uniqueNoFilter(sourceRecordId, "recordId");
+ if (o == null) return Collections.emptySet();
+ sourceRecordId = (ID) o[0];
+ }
+
+ final JSONObject actionContent = (JSONObject) actionContext.getActionContent();
+
+ Entity sourceEntity = actionContext.getSourceEntity();
+ JSONArray fs = actionContent.getJSONArray("fields");
+
+ List fieldsSelf = new ArrayList<>();
+ List fieldsRefto = new ArrayList<>();
+ boolean hasSelf = false;
+
+ for (Object o : fs) {
+ String field = (String) o;
+ if (field.contains(".")) {
+ String[] fieldAndEntity = field.split("\\.");
+ if (MetadataHelper.containsField(fieldAndEntity[1], fieldAndEntity[0])) {
+ fieldsRefto.add(MetadataHelper.getField(fieldAndEntity[1], fieldAndEntity[0]));
+ }
+
+ } else {
+ if (SOURCE_SELF.equals(field)) {
+ fieldsSelf.add(sourceEntity.getPrimaryField().getName());
+ hasSelf = true;
+ } if (sourceEntity.containsField(field)) {
+ fieldsSelf.add(field);
+ }
+ }
+ }
+
+ final Set relateds = new HashSet<>();
+
+ if (!fieldsSelf.isEmpty()) {
+ fieldsSelf.add(sourceEntity.getPrimaryField().getName());
+ Object[] o = Application.getQueryFactory().uniqueNoFilter(sourceRecordId, fieldsSelf.toArray(new String[0]));
+ if (o != null) {
+ for (Object id : o) {
+ if (id != null) {
+ if (id instanceof ID[]) Collections.addAll(relateds, (ID[]) id);
+ else relateds.add((ID) id);
+ }
+ }
+ }
+ }
+
+ for (Field refto : fieldsRefto) {
+ Entity ownEntity = refto.getOwnEntity();
+ String sql = String.format("select %s from %s where ",
+ ownEntity.getPrimaryField().getName(), ownEntity.getName());
+
+ // N2N
+ if (refto.getType() == FieldType.REFERENCE_LIST) {
+ sql += String.format(
+ "exists (select recordId from NreferenceItem where ^%s = recordId and belongField = '%s' and referenceId = '%s')",
+ ownEntity.getPrimaryField().getName(), refto.getName(), sourceRecordId);
+ } else {
+ sql += String.format("%s = '%s'", refto.getName(), sourceRecordId);
+ }
+
+ Object[][] array = Application.createQueryNoFilter(sql).array();
+ for (Object[] o : array) relateds.add((ID) o[0]);
+ }
+
+ // 不含自己
+ if (!hasSelf) relateds.remove(sourceRecordId);
+
+ return relateds;
+ }
+
+ /**
+ * 获取审核状态
+ *
+ * @param recordId
+ * @return
+ * @see ApprovalHelper#getApprovalState(ID)
+ */
+ protected static ApprovalState getApprovalState(ID recordId) {
+ Entity entity = MetadataHelper.getEntity(recordId.getEntityCode());
+ if (MetadataHelper.hasApprovalField(entity)) return ApprovalHelper.getApprovalState(recordId);
+ else return null;
+ }
+}
diff --git a/src/main/java/com/rebuild/core/service/trigger/impl/AutoShare.java b/src/main/java/com/rebuild/core/service/trigger/impl/AutoShare.java
index 96cc5dadf..f0fb97955 100644
--- a/src/main/java/com/rebuild/core/service/trigger/impl/AutoShare.java
+++ b/src/main/java/com/rebuild/core/service/trigger/impl/AutoShare.java
@@ -8,10 +8,12 @@ See LICENSE and COMMERCIAL in the project root for license information.
package com.rebuild.core.service.trigger.impl;
import cn.devezhao.bizz.privileges.impl.BizzPermission;
+import cn.devezhao.persist4j.Entity;
import cn.devezhao.persist4j.engine.ID;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.rebuild.core.Application;
+import com.rebuild.core.metadata.MetadataHelper;
import com.rebuild.core.privileges.PrivilegesGuardContextHolder;
import com.rebuild.core.privileges.UserHelper;
import com.rebuild.core.service.general.EntityService;
@@ -19,6 +21,7 @@ import com.rebuild.core.service.general.GeneralEntityServiceContextHolder;
import com.rebuild.core.service.general.OperatingContext;
import com.rebuild.core.service.trigger.ActionContext;
import com.rebuild.core.service.trigger.ActionType;
+import com.rebuild.core.service.trigger.TriggerAction;
import com.rebuild.core.service.trigger.TriggerException;
import com.rebuild.core.service.trigger.TriggerResult;
import lombok.extern.slf4j.Slf4j;
@@ -33,7 +36,7 @@ import java.util.Set;
* @since 2019/8/23
*/
@Slf4j
-public class AutoShare extends AutoAssign {
+public class AutoShare extends TriggerAction {
public AutoShare(ActionContext context) {
super(context);
@@ -44,6 +47,12 @@ public class AutoShare extends AutoAssign {
return ActionType.AUTOSHARE;
}
+ @Override
+ public boolean isUsableSourceEntity(int entityCode) {
+ Entity e = MetadataHelper.getEntity(entityCode);
+ return MetadataHelper.hasPrivilegesField(e) || e.getMainEntity() != null;
+ }
+
@Override
public Object execute(OperatingContext operatingContext) throws TriggerException {
final JSONObject content = (JSONObject) actionContext.getActionContent();
diff --git a/src/main/resources/web/assets/js/rb-forms.js b/src/main/resources/web/assets/js/rb-forms.js
index fa54e7d0a..eea6dac12 100644
--- a/src/main/resources/web/assets/js/rb-forms.js
+++ b/src/main/resources/web/assets/js/rb-forms.js
@@ -2683,7 +2683,24 @@ class RbFormTag extends RbFormElement {
}
}
- // isValueUnchanged() {}
+ setValue(val) {
+ if (typeof val === 'object') val = val.join('$$$$')
+ super.setValue(val)
+
+ // fix: v3.4.4
+ if ($empty(val)) {
+ this.__select2.val(null).trigger('change')
+ } else {
+ const names = []
+ val.split('$$$$').forEach((name) => {
+ if (!names.includes(name)) {
+ const o = new Option(name, name, true, true)
+ this.__select2.append(o)
+ names.push(name)
+ }
+ })
+ }
+ }
}
// 不支持/未开放的字段
diff --git a/src/test/resources/frontjs-sdk-demo.js b/src/test/resources/frontjs-sdk-demo.js
new file mode 100644
index 000000000..2f49b63ae
--- /dev/null
+++ b/src/test/resources/frontjs-sdk-demo.js
@@ -0,0 +1,97 @@
+// FrontJS 演示
+// 建议将上方匹配路径设置为 `/` 以便观察效果
+
+const demoEntityName = 'Account' // TODO 修改为你要测试的实体
+const demoFieldName = 'AccountName' // TODO 修改为你要测试的实体字段
+
+let _List, _Form, _View
+
+FrontJS.ready(() => {
+ _List = FrontJS.DataList
+ _Form = FrontJS.Form
+ _View = FrontJS.View
+
+ demoAddButton()
+
+ demoForList()
+
+ demoForForm()
+})
+
+// 添加按钮
+// 注意在 onOpen 中调用
+function demoAddButton() {
+ _List.onOpen(() => {
+ _List.addButtonGroup({
+ text: 'FrontJS!',
+ items: [
+ {
+ text: '获取第一选中',
+ onClick: () => {
+ alert(_List.getSelectedId())
+ },
+ },
+ {
+ text: '获取全部选中',
+ onClick: () => {
+ alert(_List.getSelectedIds())
+ },
+ },
+ ],
+ })
+ })
+
+ _Form.onOpen(() => {
+ _Form.addButton({
+ text: 'FrontJS!',
+ onClick: () => {
+ alert(_Form.getCurrentId())
+ },
+ })
+ })
+
+ _View.onOpen(() => {
+ _Form.addButton({
+ text: 'FrontJS!',
+ onClick: () => {
+ alert(_Form.getCurrentId())
+ },
+ })
+ })
+}
+
+// 列表操作
+function demoForList() {
+ // 指定字段加粗加红显示
+ const fieldKey = `${demoEntityName}.${demoFieldName}`
+ _List.regCellRender(fieldKey, function (v) {
+ return {JSON.stringify(v)}
+ })
+
+ // 轻量级表单
+ _List.onOpen(() => {
+ _List.addButton({
+ text: 'LiteForm',
+ onClick: () => {
+ const id = _List.getSelectedId()
+ if (!id) {
+ alert('请选择一条记录')
+ return
+ }
+
+ FrontJS.openLiteForm(id, [{ field: demoFieldName, tip: '提示', readonly: true, nullable: true }])
+ },
+ })
+ })
+}
+
+// 表单操作
+function demoForForm() {
+ // 监听指定字段值变化
+ const lookFieldKey = `${demoEntityName}.${demoFieldName}`
+ _Form.onFieldValueChange(function (fieldKey, fieldValue, recordId) {
+ if (lookFieldKey === fieldKey) {
+ RbHighbar.create(`记录: ${recordId || ''} 的新值 : ${fieldValue}`)
+ }
+ })
+}