kcode-pi 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +358 -0
- package/dist/cli/kcode.d.ts +15 -0
- package/dist/cli/kcode.js +153 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +7 -0
- package/docs/KCODE_DISTRIBUTION.md +91 -0
- package/extensions/kingdee-harness.ts +180 -0
- package/extensions/kingdee-header.ts +122 -0
- package/extensions/kingdee-tools.ts +379 -0
- package/knowledge/.backup/v1.0.0/version.json +10 -0
- package/knowledge/cangqiong/product-notes.md +15 -0
- package/knowledge/common/business-flows.md +115 -0
- package/knowledge/common/config-guides.md +110 -0
- package/knowledge/common/error-patterns.md +170 -0
- package/knowledge/common/implementation.md +144 -0
- package/knowledge/cosmic/hard-constraints.md +38 -0
- package/knowledge/cosmic/ksql-datafix.md +34 -0
- package/knowledge/cosmic/platform-baseline.md +32 -0
- package/knowledge/cosmic/plugin-decision-matrix.md +40 -0
- package/knowledge/cosmic/review-checklist.md +40 -0
- package/knowledge/cosmic/unittest.md +35 -0
- package/knowledge/enterprise/api-reference.md +186 -0
- package/knowledge/enterprise/code-patterns.md +217 -0
- package/knowledge/enterprise/plugin-lifecycle.md +188 -0
- package/knowledge/enterprise/tables.json +159 -0
- package/knowledge/flagship/api-reference.md +237 -0
- package/knowledge/flagship/code-patterns.md +246 -0
- package/knowledge/flagship/cosmic-platform-note.md +15 -0
- package/knowledge/flagship/plugin-lifecycle.md +248 -0
- package/knowledge/flagship/tables.json +159 -0
- package/knowledge/version.json +10 -0
- package/knowledge/xinghan/product-notes.md +15 -0
- package/package.json +71 -0
- package/prompts/kd-discuss.md +11 -0
- package/prompts/kd-execute.md +12 -0
- package/prompts/kd-plan.md +12 -0
- package/prompts/kd-ship.md +12 -0
- package/prompts/kd-spec.md +12 -0
- package/prompts/kd-verify.md +12 -0
- package/skills/kd-check/SKILL.md +26 -0
- package/skills/kd-cosmic-dev/SKILL.md +82 -0
- package/skills/kd-cosmic-review/SKILL.md +90 -0
- package/skills/kd-cosmic-unittest/SKILL.md +92 -0
- package/skills/kd-debug/SKILL.md +30 -0
- package/skills/kd-discuss/SKILL.md +24 -0
- package/skills/kd-execute/SKILL.md +22 -0
- package/skills/kd-gen/SKILL.md +34 -0
- package/skills/kd-ksql/SKILL.md +86 -0
- package/skills/kd-plan/SKILL.md +24 -0
- package/skills/kd-ship/SKILL.md +22 -0
- package/skills/kd-spec/SKILL.md +24 -0
- package/skills/kd-verify/SKILL.md +22 -0
- package/themes/kcode-dark.json +81 -0
- package/vendor/kingdee-skills/cosmic-unittest/SKILL.md +788 -0
- package/vendor/kingdee-skills/cosmic-unittest/author-cache.json +5 -0
- package/vendor/kingdee-skills/cosmic-unittest/cosmic-unittest-skill-overview.html +746 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/business-test.md +205 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/common-test.md +257 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/formplugin-test.md +560 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/op-plugin-test.md +231 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/validator-test.md +232 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/business-helper.md +184 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/common-module.md +355 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/convert-plugin.md +130 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/formplugin.md +235 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/op-plugin.md +226 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/validator.md +206 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/SKILL.md +674 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/advanced-scenario-checklist.md +307 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/algox-performance-checklist.md +129 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/coding-standard-checklist.md +491 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/cosmic-api-checklist.md +285 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-access-checklist.md +261 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-transaction-checklist.md +390 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/domain-logic-checklist.md +295 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/form-plugin-checklist.md +508 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/infra-checklist.md +254 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ksql-checklist.md +305 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/lifecycle-checklist.md +298 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/operation-plugin-checklist.md +442 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/test-mock-checklist.md +120 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ui-performance-checklist.md +320 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/pattern-matcher.py +336 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/review-score-calculator.py +121 -0
- package/vendor/kingdee-skills/ok-cosmic/CHANGELOG.md +295 -0
- package/vendor/kingdee-skills/ok-cosmic/README.md +460 -0
- package/vendor/kingdee-skills/ok-cosmic/SKILL.md +287 -0
- package/vendor/kingdee-skills/ok-cosmic/agents/openai.yaml +17 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/BatchImportPluginTemplate.java +93 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/BillPlugInTemplate.java +156 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ConvertPlugInTemplate.java +255 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/FormPluginTemplate.java +597 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/IWorkflowPluginTemplate.java +91 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ListPluginTemplate.java +194 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/OpPluginTemplate.java +201 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/OpenApiControllerTemplate.java +103 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/PrintPluginTemplate.java +95 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ReportFormPluginTemplate.java +257 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ReportListDataPluginTemplate.java +70 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/StandardTreeListPluginTemplate.java +130 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/TaskTemplate.java +80 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/TreeListPluginTemplate.java +152 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/WriteBackPlugInTemplate.java +286 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/attachment/AttachmentUploadBindSample.java +93 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/BotpTracePushSample.java +168 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/SampleConvertPlugin.java +223 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/cache/SampleCacheUsage.java +218 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/concurrent/SampleThreadPoolBatch.java +156 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectCrudSample.java +205 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectOpsSample.java +100 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/BeforeOperationConfirmSample.java +217 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ConfirmDialogSample.java +131 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/EntryRowCalculateSample.java +116 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/F7FilterSample.java +134 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/GetAndSetValueSample.java +176 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/HyperlinkJumpSample.java +124 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/OpenBillModalSample.java +253 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ReturnParentDataSample.java +295 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/TreeControlSample.java +140 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ViewControlOpsSample.java +132 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPluginBasicSample.java +170 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPreOpenFilterSample.java +68 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/message/MessageNotifySample.java +95 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/SampleMQConsumer.java +198 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/sample_mq.xml +15 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OpAddValidatorsSample.java +137 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OperationOptionBridgeSample.java +228 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/package-info.java +19 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BaseDataQuerySample.java +194 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BatchQuerySample.java +368 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/DataSetQueryStatSample.java +131 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportFormPlugin.java +179 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportListDataPlugin.java +616 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/snippets-guide.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/task/ScheduleTaskSample.java +160 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/workflow/SampleWorkflowPlugin.java +302 -0
- package/vendor/kingdee-skills/ok-cosmic/manifest.json +78 -0
- package/vendor/kingdee-skills/ok-cosmic/ok-cosmic-intro.html +903 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/attachment-api.md +114 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/botp-convert.md +98 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/dynamic-object.md +113 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/entity-metadata.md +123 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/event-lifecycle.md +184 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/flex-prop.md +114 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/form-utils.md +133 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/operate-chain.md +159 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/plugin-base.md +218 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/query-dataset.md +149 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/request-context.md +88 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/view-handler.md +157 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-bill.md +76 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-botp.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-form.md +165 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-import.md +69 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-list.md +227 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-openapi.md +112 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-operation.md +135 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-print.md +65 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-data.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-form.md +90 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-task.md +62 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-tree-list.md +71 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-workflow.md +82 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-writeback.md +71 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-algo.md +67 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-cache.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-model-svc.md +82 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-object.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-entity-model.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-exception.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-file.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-id.md +47 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-lock.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-log.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-network-control.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-orm-access.md +78 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-request-context.md +62 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-threadpool.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-tx.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-utils.md +67 -0
- package/vendor/kingdee-skills/ok-cosmic/requirements.txt +2 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/a-layer-rules.json +24 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/anti-patterns.md +48 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/cheat-sheet.md +256 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/coding-preferences.md +140 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/constraints.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/decision-matrix.md +222 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/intent-routing.md +94 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/platform-baseline.md +69 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/post-check.md +109 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/config_loader.py +204 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-api-knowledge.py +910 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-basedata-query.py +359 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-config-check.py +181 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-extpoints-query.py +389 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-form-metadata.py +856 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-check.py +262 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-lint.py +293 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/__init__.py +2 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/base.py +393 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/resource_check.py +176 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/scene_check.py +375 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/style_check.py +434 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/verify_check.py +36 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/route_client.py +186 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/script_utils.py +40 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/sqlite_cache.py +142 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-commons.jar +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-features.jar +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic-docs.db +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic.json +13 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup-mac.sh +18 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup-windows.bat +53 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup.jar +0 -0
- package/vendor/kingdee-skills/ok-ksql/SKILL.md +81 -0
- package/vendor/kingdee-skills/ok-ksql/agents/openai.yaml +7 -0
- package/vendor/kingdee-skills/ok-ksql/manifest.json +14 -0
- package/vendor/kingdee-skills/ok-ksql/references/ksql-datafix.md +452 -0
- package/vendor/kingdee-skills/ok-ksql/scripts/ksql_lint.py +363 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 批量查询样本。
|
|
3
|
+
* <p>
|
|
4
|
+
* 适用插件:表单插件、操作插件、服务层
|
|
5
|
+
* 优先封装:DynamicObjectUtils、AlgoUtils
|
|
6
|
+
* 原生兜底:BusinessDataServiceHelper.load(...)、QueryServiceHelper.query(...)、QFilter、DataSet
|
|
7
|
+
* 相关 lint 规则:STYLE-015、STYLE-017、STYLE-010
|
|
8
|
+
* <p>
|
|
9
|
+
* 使用场景:
|
|
10
|
+
* 1. 先按关联键分组,再一次性批量查询;
|
|
11
|
+
* 2. 查询结果先转本地 Map,再做校验、反写或恢复;
|
|
12
|
+
* 3. 表单插件、操作插件、服务层都可以直接复用这里的查询分组思路。
|
|
13
|
+
* 4. 注意:QueryServiceHelper.query(...) / QueryServiceHelper.queryDataSet(...) 查出来的数据默认用于读取、分组、聚合,不要直接当成可更新实体回写保存。
|
|
14
|
+
*/
|
|
15
|
+
package kd.cd.common.snippets.query;
|
|
16
|
+
|
|
17
|
+
import kd.bos.algo.DataSet;
|
|
18
|
+
import kd.bos.dataentity.entity.DynamicObject;
|
|
19
|
+
import kd.bos.dataentity.entity.DynamicObjectCollection;
|
|
20
|
+
import kd.bos.orm.query.QCP;
|
|
21
|
+
import kd.bos.orm.query.QFilter;
|
|
22
|
+
import kd.bos.servicehelper.BusinessDataServiceHelper;
|
|
23
|
+
import kd.bos.servicehelper.QueryServiceHelper;
|
|
24
|
+
import kd.cd.common.util.AlgoUtils;
|
|
25
|
+
import kd.cd.common.util.DynamicObjectUtils;
|
|
26
|
+
import kd.cd.common.entity.EntityUtils;
|
|
27
|
+
import kd.cd.common.util.SelectFieldBuilder;
|
|
28
|
+
import kd.cd.core.util.BigDecimalUtils;
|
|
29
|
+
import kd.cd.core.util.CharSequenceUtils;
|
|
30
|
+
import kd.cd.core.util.CollectionUtils;
|
|
31
|
+
|
|
32
|
+
import java.math.BigDecimal;
|
|
33
|
+
import java.util.ArrayList;
|
|
34
|
+
import java.util.Arrays;
|
|
35
|
+
import java.util.Collections;
|
|
36
|
+
import java.util.HashMap;
|
|
37
|
+
import java.util.List;
|
|
38
|
+
import java.util.Map;
|
|
39
|
+
import java.util.Set;
|
|
40
|
+
import java.util.stream.Collectors;
|
|
41
|
+
|
|
42
|
+
public final class BatchQuerySample {
|
|
43
|
+
private static final String FIELD_ROW_SEQ = "seq";
|
|
44
|
+
private static final String FIELD_REL_BILL_NO = "relbillno";
|
|
45
|
+
private static final String FIELD_REL_ENTRY_ID = "relentryid";
|
|
46
|
+
private static final String FIELD_AMT = "amount";
|
|
47
|
+
private static final String REL_FORM_ID = "related_form";
|
|
48
|
+
private static final String REL_ENTRY_KEY = "relentryentity";
|
|
49
|
+
private static final String REL_PLAN_AMT = "planamount";
|
|
50
|
+
private static final String REL_LOCKED_AMT = "lockedamount";
|
|
51
|
+
|
|
52
|
+
private static final String FIELD_TARGET_BILL_NO = "targetbillno";
|
|
53
|
+
private static final String SOURCE_SUB_ENTRY_KEY = "subentryentity";
|
|
54
|
+
private static final String FIELD_ITEM = "item";
|
|
55
|
+
private static final String FIELD_ACCOUNT = "account";
|
|
56
|
+
private static final String TARGET_FORM_ID = "target_form";
|
|
57
|
+
private static final String TARGET_ENTRY_KEY = "targetentryentity";
|
|
58
|
+
|
|
59
|
+
private static final String SOURCE_FORM_ID = "source_form";
|
|
60
|
+
private static final String FIELD_BILL_NO = "billno";
|
|
61
|
+
private static final String FIELD_DIMENSION = "dimension";
|
|
62
|
+
private static final String FIELD_VALUE = "value";
|
|
63
|
+
private static final String MID_FORM_ID = "latest_value_mid";
|
|
64
|
+
private static final String FIELD_MID_DIMENSION = "dimensionref";
|
|
65
|
+
private static final String FIELD_MID_VALUE = "latestvalue";
|
|
66
|
+
|
|
67
|
+
private BatchQuerySample() {
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 范式一:按关联单号分组,一次性加载关联单,本地映射后做校验。
|
|
72
|
+
*/
|
|
73
|
+
public static List<String> validateRelationRows(List<DynamicObject> entryRows) {
|
|
74
|
+
Map<String, List<DynamicObject>> rowsByBillNo = entryRows.stream()
|
|
75
|
+
.filter(row -> CharSequenceUtils.isNotBlank(row.getString(FIELD_REL_BILL_NO)))
|
|
76
|
+
.collect(Collectors.groupingBy(row -> row.getString(FIELD_REL_BILL_NO)));
|
|
77
|
+
if (CollectionUtils.isEmpty(rowsByBillNo)) {
|
|
78
|
+
return Collections.emptyList();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Map<String, DynamicObject> relatedBillMap = loadRelatedBillMap(rowsByBillNo.keySet());
|
|
82
|
+
List<String> errors = new ArrayList<>(32);
|
|
83
|
+
for (Map.Entry<String, List<DynamicObject>> entry : rowsByBillNo.entrySet()) {
|
|
84
|
+
DynamicObject relatedBill = relatedBillMap.get(entry.getKey());
|
|
85
|
+
if (relatedBill == null) {
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
Map<String, DynamicObject> relatedEntryMap = relatedBill.getDynamicObjectCollection(REL_ENTRY_KEY).stream()
|
|
89
|
+
.collect(Collectors.toMap(
|
|
90
|
+
row -> row.getString("id"),
|
|
91
|
+
row -> row,
|
|
92
|
+
(left, right) -> left
|
|
93
|
+
));
|
|
94
|
+
for (DynamicObject row : entry.getValue()) {
|
|
95
|
+
DynamicObject relatedEntry = relatedEntryMap.get(row.getString(FIELD_REL_ENTRY_ID));
|
|
96
|
+
if (relatedEntry == null) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
BigDecimal availableAmt = BigDecimalUtils.subtract(
|
|
100
|
+
BigDecimalUtils.nullToZero(relatedEntry.getBigDecimal(REL_PLAN_AMT)),
|
|
101
|
+
BigDecimalUtils.nullToZero(relatedEntry.getBigDecimal(REL_LOCKED_AMT))
|
|
102
|
+
);
|
|
103
|
+
BigDecimal currentAmt = BigDecimalUtils.nullToZero(row.getBigDecimal(FIELD_AMT));
|
|
104
|
+
if (BigDecimalUtils.largeThan(currentAmt, availableAmt)) {
|
|
105
|
+
errors.add(String.format(
|
|
106
|
+
"row %s amount %s > available %s",
|
|
107
|
+
row.get(FIELD_ROW_SEQ),
|
|
108
|
+
BigDecimalUtils.toPlainString(currentAmt),
|
|
109
|
+
BigDecimalUtils.toPlainString(availableAmt)
|
|
110
|
+
));
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return errors;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 范式二:按目标单号聚合,一次性加载目标单,本地重建子分录后返回待保存单据。
|
|
119
|
+
*/
|
|
120
|
+
public static DynamicObject[] buildTargetBillsForSave(List<DynamicObject> sourceRows) {
|
|
121
|
+
if (CollectionUtils.isEmpty(sourceRows)) {
|
|
122
|
+
return new DynamicObject[0];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
List<DynamicObject> targetRows = sourceRows.stream()
|
|
126
|
+
.filter(row -> CharSequenceUtils.isNotBlank(row.getString(FIELD_TARGET_BILL_NO)))
|
|
127
|
+
.collect(Collectors.toList());
|
|
128
|
+
if (CollectionUtils.isEmpty(targetRows)) {
|
|
129
|
+
return new DynamicObject[0];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
Set<String> targetBillNos = DynamicObjectUtils.setOf(targetRows, FIELD_TARGET_BILL_NO);
|
|
133
|
+
Map<String, List<DynamicObject>> rowsByTargetBillNo = targetRows.stream()
|
|
134
|
+
.collect(Collectors.groupingBy(row -> row.getString(FIELD_TARGET_BILL_NO)));
|
|
135
|
+
Map<String, DynamicObject> targetBillMap = loadTargetBillMap(targetBillNos);
|
|
136
|
+
|
|
137
|
+
List<DynamicObject> needSaveBills = new ArrayList<>(32);
|
|
138
|
+
for (Map.Entry<String, List<DynamicObject>> entry : rowsByTargetBillNo.entrySet()) {
|
|
139
|
+
DynamicObject targetBill = targetBillMap.get(entry.getKey());
|
|
140
|
+
if (targetBill == null) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
rewriteTargetEntries(targetBill.getDynamicObjectCollection(TARGET_ENTRY_KEY), entry.getValue());
|
|
144
|
+
needSaveBills.add(targetBill);
|
|
145
|
+
}
|
|
146
|
+
return needSaveBills.toArray(new DynamicObject[0]);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 范式三-A:按当前数据直接同步中间表,返回待 update/save 的数据。
|
|
151
|
+
*/
|
|
152
|
+
public static DynamicObject[] buildMidBillsForSync(List<DynamicObject> currentRows) {
|
|
153
|
+
if (CollectionUtils.isEmpty(currentRows)) {
|
|
154
|
+
return new DynamicObject[0];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
Map<String, DynamicObject> currentRowMap = new HashMap<>(currentRows.size());
|
|
158
|
+
for (DynamicObject row : currentRows) {
|
|
159
|
+
Object dimensionId = row.get(FIELD_DIMENSION + ".id");
|
|
160
|
+
if (EntityUtils.isNotEmptyPk(dimensionId)) {
|
|
161
|
+
currentRowMap.put(keyOf(dimensionId), row);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (CollectionUtils.isEmpty(currentRowMap)) {
|
|
165
|
+
return new DynamicObject[0];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
List<DynamicObject> currentRowList = new ArrayList<>(currentRowMap.size());
|
|
169
|
+
currentRowList.addAll(currentRowMap.values());
|
|
170
|
+
Set<Object> dimensionIds = DynamicObjectUtils.setOf(currentRowList, FIELD_DIMENSION + ".id");
|
|
171
|
+
Map<String, DynamicObject> midBillMap = loadMidBillMap(dimensionIds);
|
|
172
|
+
List<DynamicObject> result = new ArrayList<>(currentRowMap.size());
|
|
173
|
+
|
|
174
|
+
for (Map.Entry<String, DynamicObject> entry : currentRowMap.entrySet()) {
|
|
175
|
+
DynamicObject row = entry.getValue();
|
|
176
|
+
DynamicObject midBill = midBillMap.get(entry.getKey());
|
|
177
|
+
if (midBill == null) {
|
|
178
|
+
midBill = DynamicObjectUtils.newDynamicObject(MID_FORM_ID);
|
|
179
|
+
midBill.set("number", row.getString(FIELD_DIMENSION + ".masterid.number"));
|
|
180
|
+
midBill.set(FIELD_MID_DIMENSION + "_id", row.get(FIELD_DIMENSION + ".id"));
|
|
181
|
+
midBill.set("enable", "1");
|
|
182
|
+
midBill.set("status", "C");
|
|
183
|
+
}
|
|
184
|
+
applyLatestValue(midBill, row.getString(FIELD_BILL_NO), row.getBigDecimal(FIELD_VALUE));
|
|
185
|
+
result.add(midBill);
|
|
186
|
+
}
|
|
187
|
+
return result.toArray(new DynamicObject[0]);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 范式三-B:按维度集合一次性查询候选记录,在内存里取每组最近一条,再回填中间表。
|
|
192
|
+
*/
|
|
193
|
+
public static DynamicObject[] buildMidBillsForRecover(List<DynamicObject> affectedRows) {
|
|
194
|
+
if (CollectionUtils.isEmpty(affectedRows)) {
|
|
195
|
+
return new DynamicObject[0];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
Set<Object> dimensionIds = DynamicObjectUtils.setOf(affectedRows, FIELD_DIMENSION + ".id");
|
|
199
|
+
Map<String, DynamicObject> midBillMap = Arrays.stream(loadMidBillsForUpdateByQuery(dimensionIds))
|
|
200
|
+
.collect(Collectors.toMap(
|
|
201
|
+
bill -> keyOf(bill.get(FIELD_MID_DIMENSION + ".id")),
|
|
202
|
+
bill -> bill,
|
|
203
|
+
(left, right) -> left
|
|
204
|
+
));
|
|
205
|
+
Map<String, LatestValue> latestValueMap = queryLatestValueMap(dimensionIds);
|
|
206
|
+
List<DynamicObject> updateList = new ArrayList<>(midBillMap.size());
|
|
207
|
+
|
|
208
|
+
for (DynamicObject midBill : midBillMap.values()) {
|
|
209
|
+
LatestValue latestValue = latestValueMap.get(keyOf(midBill.get(FIELD_MID_DIMENSION + ".id")));
|
|
210
|
+
applyLatestValue(
|
|
211
|
+
midBill,
|
|
212
|
+
latestValue == null ? "" : latestValue.billNo,
|
|
213
|
+
latestValue == null ? BigDecimal.ZERO : latestValue.value
|
|
214
|
+
);
|
|
215
|
+
updateList.add(midBill);
|
|
216
|
+
}
|
|
217
|
+
return updateList.toArray(new DynamicObject[0]);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* 范式四:用 QueryServiceHelper.query(...) 一次性查出候选明细,再按维度分组或直接取每组第一条。
|
|
222
|
+
* 注意:这里拿到的是扁平查询结果,只适合读取、分组、聚合,不能直接拿去做后续 update/save。
|
|
223
|
+
* 需要更新时,应该回到 BusinessDataServiceHelper.load(...) 重新加载实体包,或 newDynamicObject(...) 后再保存。
|
|
224
|
+
*/
|
|
225
|
+
public static Map<String, LatestValue> queryLatestValueMapByQuery(Set<Object> dimensionIds) {
|
|
226
|
+
// QueryServiceHelper.query(...) 返回的是扁平结果集,适合做“批量查 + 分组映射”,不适合直接回写更新。
|
|
227
|
+
DynamicObjectCollection rows = QueryServiceHelper.query(
|
|
228
|
+
SOURCE_FORM_ID,
|
|
229
|
+
new SelectFieldBuilder()
|
|
230
|
+
.append("entryentity." + FIELD_DIMENSION + ".id", "dimensionId")
|
|
231
|
+
.append("entryentity." + FIELD_VALUE, "value")
|
|
232
|
+
.appendAll(FIELD_BILL_NO)
|
|
233
|
+
.toString(),
|
|
234
|
+
new QFilter("billstatus", QCP.equals, "C")
|
|
235
|
+
.and("entryentity." + FIELD_DIMENSION + ".id", QCP.in, dimensionIds)
|
|
236
|
+
.toArray(),
|
|
237
|
+
"auditdate desc," + FIELD_BILL_NO + " desc"
|
|
238
|
+
);
|
|
239
|
+
return rows.stream().collect(Collectors.toMap(
|
|
240
|
+
row -> keyOf(row.get("dimensionId")),
|
|
241
|
+
row -> new LatestValue(row.getString(FIELD_BILL_NO), BigDecimalUtils.nullToZero(row.getBigDecimal("value"))),
|
|
242
|
+
(left, right) -> left
|
|
243
|
+
));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* 范式四-B:如果前一步必须用 query(...) 查范围,后面又要更新,
|
|
248
|
+
* 正确桥接方式是“先 query 出 id,再 load 实体包”,不要直接修改 query 的扁平结果。
|
|
249
|
+
*/
|
|
250
|
+
public static DynamicObject[] loadMidBillsForUpdateByQuery(Set<Object> dimensionIds) {
|
|
251
|
+
if (CollectionUtils.isEmpty(dimensionIds)) {
|
|
252
|
+
return new DynamicObject[0];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
DynamicObjectCollection flatRows = QueryServiceHelper.query(
|
|
256
|
+
MID_FORM_ID,
|
|
257
|
+
"id",
|
|
258
|
+
new QFilter(FIELD_MID_DIMENSION + ".id", QCP.in, dimensionIds).toArray()
|
|
259
|
+
);
|
|
260
|
+
Set<Object> ids = DynamicObjectUtils.setOf(flatRows, "id");
|
|
261
|
+
if (CollectionUtils.isEmpty(ids)) {
|
|
262
|
+
return new DynamicObject[0];
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return BusinessDataServiceHelper.load(
|
|
266
|
+
MID_FORM_ID,
|
|
267
|
+
"id,name," + FIELD_MID_DIMENSION + "," + FIELD_MID_VALUE,
|
|
268
|
+
new QFilter("id", QCP.in, ids).toArray()
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private static Map<String, DynamicObject> loadRelatedBillMap(Set<String> relatedBillNos) {
|
|
273
|
+
DynamicObject[] relatedBills = BusinessDataServiceHelper.load(
|
|
274
|
+
REL_FORM_ID,
|
|
275
|
+
"billno,relentryentity.id,relentryentity." + REL_PLAN_AMT + ",relentryentity." + REL_LOCKED_AMT,
|
|
276
|
+
new QFilter("billno", QCP.in, relatedBillNos).toArray()
|
|
277
|
+
);
|
|
278
|
+
return Arrays.stream(relatedBills).collect(Collectors.toMap(
|
|
279
|
+
bill -> bill.getString("billno"),
|
|
280
|
+
bill -> bill,
|
|
281
|
+
(left, right) -> left
|
|
282
|
+
));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private static Map<String, DynamicObject> loadTargetBillMap(Set<String> targetBillNos) {
|
|
286
|
+
DynamicObject[] targetBills = BusinessDataServiceHelper.load(
|
|
287
|
+
TARGET_FORM_ID,
|
|
288
|
+
"billno,createtime," + TARGET_ENTRY_KEY + "." + FIELD_ITEM
|
|
289
|
+
+ "," + TARGET_ENTRY_KEY + "." + FIELD_AMT
|
|
290
|
+
+ "," + TARGET_ENTRY_KEY + "." + FIELD_ACCOUNT,
|
|
291
|
+
new QFilter("billno", QCP.in, targetBillNos).toArray(),
|
|
292
|
+
"createtime desc"
|
|
293
|
+
);
|
|
294
|
+
return Arrays.stream(targetBills).collect(Collectors.toMap(
|
|
295
|
+
bill -> bill.getString("billno"),
|
|
296
|
+
bill -> bill,
|
|
297
|
+
(left, right) -> left
|
|
298
|
+
));
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private static void rewriteTargetEntries(DynamicObjectCollection targetEntries, List<DynamicObject> sourceRows) {
|
|
302
|
+
targetEntries.clear();
|
|
303
|
+
for (DynamicObject sourceRow : sourceRows) {
|
|
304
|
+
for (DynamicObject sourceSubRow : sourceRow.getDynamicObjectCollection(SOURCE_SUB_ENTRY_KEY)) {
|
|
305
|
+
DynamicObject newRow = targetEntries.addNew();
|
|
306
|
+
newRow.set(FIELD_ITEM, sourceSubRow.get(FIELD_ITEM));
|
|
307
|
+
newRow.set(FIELD_AMT, sourceSubRow.get(FIELD_AMT));
|
|
308
|
+
newRow.set(FIELD_ACCOUNT, sourceSubRow.get(FIELD_ACCOUNT));
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* 关键点:不要在维度循环里 query(..., top 1),先一次性查出候选记录,再利用排序结果取每组第一条。
|
|
315
|
+
*/
|
|
316
|
+
private static Map<String, LatestValue> queryLatestValueMap(Set<Object> dimensionIds) {
|
|
317
|
+
try (DataSet ds = QueryServiceHelper.queryDataSet(
|
|
318
|
+
SOURCE_FORM_ID, SOURCE_FORM_ID,
|
|
319
|
+
new SelectFieldBuilder()
|
|
320
|
+
.append("entryentity." + FIELD_DIMENSION + ".id", "dimensionId")
|
|
321
|
+
.appendAll(FIELD_BILL_NO)
|
|
322
|
+
.append("entryentity." + FIELD_VALUE, "value")
|
|
323
|
+
.toString(),
|
|
324
|
+
new QFilter("billstatus", QCP.equals, "C")
|
|
325
|
+
.and("entryentity." + FIELD_DIMENSION + ".id", QCP.in, dimensionIds)
|
|
326
|
+
.toArray(),
|
|
327
|
+
"auditdate desc," + FIELD_BILL_NO + " desc"
|
|
328
|
+
)) {
|
|
329
|
+
return AlgoUtils.stream(ds).collect(Collectors.toMap(
|
|
330
|
+
row -> keyOf(row.get("dimensionId")),
|
|
331
|
+
row -> new LatestValue(row.getString(FIELD_BILL_NO), BigDecimalUtils.nullToZero(row.getBigDecimal("value"))),
|
|
332
|
+
(left, right) -> left
|
|
333
|
+
));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private static Map<String, DynamicObject> loadMidBillMap(Set<?> dimensionIds) {
|
|
338
|
+
DynamicObject[] midBills = BusinessDataServiceHelper.load(
|
|
339
|
+
MID_FORM_ID,
|
|
340
|
+
"name," + FIELD_MID_DIMENSION + "," + FIELD_MID_VALUE,
|
|
341
|
+
new QFilter(FIELD_MID_DIMENSION + ".id", QCP.in, dimensionIds).toArray()
|
|
342
|
+
);
|
|
343
|
+
return Arrays.stream(midBills).collect(Collectors.toMap(
|
|
344
|
+
bill -> keyOf(bill.get(FIELD_MID_DIMENSION + ".id")),
|
|
345
|
+
bill -> bill,
|
|
346
|
+
(left, right) -> left
|
|
347
|
+
));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private static void applyLatestValue(DynamicObject midBill, String billNo, BigDecimal value) {
|
|
351
|
+
midBill.set("name", billNo);
|
|
352
|
+
midBill.set(FIELD_MID_VALUE, BigDecimalUtils.nullToZero(value));
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
private static String keyOf(Object value) {
|
|
356
|
+
return value == null ? "" : String.valueOf(value);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
private static class LatestValue {
|
|
360
|
+
private final String billNo;
|
|
361
|
+
private final BigDecimal value;
|
|
362
|
+
|
|
363
|
+
private LatestValue(String billNo, BigDecimal value) {
|
|
364
|
+
this.billNo = billNo;
|
|
365
|
+
this.value = value;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QueryServiceHelper.queryDataSet(...) + DataSet 聚合示例。
|
|
3
|
+
* <p>
|
|
4
|
+
* 适用插件:表单插件、报表插件、服务层
|
|
5
|
+
* 优先封装:QueryServiceHelper、AlgoUtils
|
|
6
|
+
* 原生兜底:DataSet、Row、QFilter
|
|
7
|
+
* 相关 lint 规则:RESOURCE-004、STYLE-015
|
|
8
|
+
* <p>
|
|
9
|
+
* 使用场景:在表单/报表中按条件查询一批单据,
|
|
10
|
+
* 既要拿最新一条记录,也要顺手做金额求和、主键收集。
|
|
11
|
+
* 进阶场景:多 DataSet 按维度 groupBy / leftJoin 后,再做差额统计和动态排序。
|
|
12
|
+
*/
|
|
13
|
+
package kd.cd.common.snippets.query;
|
|
14
|
+
|
|
15
|
+
import kd.bos.algo.DataSet;
|
|
16
|
+
import kd.bos.algo.Row;
|
|
17
|
+
import kd.bos.dataentity.resource.ResManager;
|
|
18
|
+
import kd.bos.orm.query.QCP;
|
|
19
|
+
import kd.bos.orm.query.QFilter;
|
|
20
|
+
import kd.bos.servicehelper.QueryServiceHelper;
|
|
21
|
+
import kd.cd.common.plugin.AbstractFormPluginExt;
|
|
22
|
+
import kd.cd.common.util.AlgoUtils;
|
|
23
|
+
import kd.cd.common.util.SelectFieldBuilder;
|
|
24
|
+
import kd.cd.core.util.BigDecimalUtils;
|
|
25
|
+
|
|
26
|
+
import java.math.BigDecimal;
|
|
27
|
+
import java.util.ArrayList;
|
|
28
|
+
import java.util.List;
|
|
29
|
+
import java.util.Set;
|
|
30
|
+
|
|
31
|
+
public class DataSetQueryStatSample extends AbstractFormPluginExt {
|
|
32
|
+
private static final String PUR_ORDER_FORM_ID = "pm_purorderbill";
|
|
33
|
+
private static final String AP_INVOICE_FORM_ID = "ap_invoice";
|
|
34
|
+
|
|
35
|
+
// --- 查询已审核采购订单金额汇总,并取最新一条单据 ---
|
|
36
|
+
public BigDecimal queryAuditedOrderTotal(String supplierNumber, Object orgId) {
|
|
37
|
+
QFilter filter = new QFilter("billstatus", QCP.equals, "C")
|
|
38
|
+
.and(new QFilter("supplier.number", QCP.equals, supplierNumber))
|
|
39
|
+
.and(new QFilter("org.id", QCP.equals, orgId));
|
|
40
|
+
|
|
41
|
+
try (DataSet ds = QueryServiceHelper.queryDataSet(PUR_ORDER_FORM_ID, PUR_ORDER_FORM_ID,
|
|
42
|
+
"id,billno,amountandtax", filter.toArray(), "billno desc", 200)) {
|
|
43
|
+
BigDecimal totalAmount = BigDecimalUtils.nullToZero(AlgoUtils.sumOf(ds.copy(), "amountandtax"));
|
|
44
|
+
if (ds.hasNext()) {
|
|
45
|
+
Row latestRow = ds.next();
|
|
46
|
+
getView().showTipNotification(String.format(
|
|
47
|
+
ResManager.loadKDString("最新单据:%s", "DataSetQueryStatSample_0", "kd-cd-common-snippets"),
|
|
48
|
+
latestRow.getString("billno")
|
|
49
|
+
));
|
|
50
|
+
}
|
|
51
|
+
return totalAmount;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// --- 只收集满足条件的主键集合 ---
|
|
56
|
+
public Set<Object> queryAuditedOrderIds(Object orgId) {
|
|
57
|
+
QFilter filter = new QFilter("billstatus", QCP.equals, "C")
|
|
58
|
+
.and(new QFilter("org.id", QCP.equals, orgId));
|
|
59
|
+
try (DataSet ds = QueryServiceHelper.queryDataSet(
|
|
60
|
+
PUR_ORDER_FORM_ID, PUR_ORDER_FORM_ID,
|
|
61
|
+
"id",
|
|
62
|
+
filter.toArray(), null, -1)
|
|
63
|
+
) {
|
|
64
|
+
return AlgoUtils.setOf(ds, "id");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// --- 按供应商汇总订单金额和发票金额,再计算未开票差额 ---
|
|
69
|
+
public BigDecimal queryUninvoicedAmount(Object orgId) {
|
|
70
|
+
QFilter filter = new QFilter("billstatus", QCP.equals, "C")
|
|
71
|
+
.and(new QFilter("org.id", QCP.equals, orgId));
|
|
72
|
+
String orderFields = new SelectFieldBuilder()
|
|
73
|
+
.append("supplier.id", "supplierId")
|
|
74
|
+
.appendAll("amountandtax")
|
|
75
|
+
.toString();
|
|
76
|
+
String invoiceFields = new SelectFieldBuilder()
|
|
77
|
+
.append("receivablessupp.id", "supplierId")
|
|
78
|
+
.append("pricetaxtotal", "invoicedamount")
|
|
79
|
+
.toString();
|
|
80
|
+
try (DataSet orderDs = QueryServiceHelper.queryDataSet(
|
|
81
|
+
PUR_ORDER_FORM_ID, PUR_ORDER_FORM_ID,
|
|
82
|
+
orderFields, filter.toArray(), null, -1
|
|
83
|
+
);
|
|
84
|
+
DataSet invoiceDs = QueryServiceHelper.queryDataSet(
|
|
85
|
+
AP_INVOICE_FORM_ID, AP_INVOICE_FORM_ID,
|
|
86
|
+
invoiceFields, filter.toArray(), null, -1
|
|
87
|
+
);
|
|
88
|
+
DataSet invoiceSum = invoiceDs.groupBy(new String[]{"supplierId"}).sum("invoicedamount").finish();
|
|
89
|
+
DataSet joined = orderDs.leftJoin(invoiceSum)
|
|
90
|
+
.on("supplierId", "supplierId")
|
|
91
|
+
.select("supplierId", "amountandtax", "invoicedamount")
|
|
92
|
+
.finish()
|
|
93
|
+
) {
|
|
94
|
+
BigDecimal gap = BigDecimal.ZERO;
|
|
95
|
+
for (Row row : joined) {
|
|
96
|
+
BigDecimal orderAmount = BigDecimalUtils.nullToZero(row.getBigDecimal("amountandtax"));
|
|
97
|
+
BigDecimal invoicedAmount = BigDecimalUtils.nullToZero(row.getBigDecimal("invoicedamount"));
|
|
98
|
+
gap = BigDecimalUtils.add(gap, BigDecimalUtils.subtract(orderAmount, invoicedAmount));
|
|
99
|
+
}
|
|
100
|
+
return gap;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// --- 动态排序 + 按需移除中间字段:适合报表首条预览或调试 ---
|
|
105
|
+
public void previewOrderSummary(Object orgId, boolean orderByAmount, boolean keepSupplierField) {
|
|
106
|
+
QFilter filter = new QFilter("billstatus", QCP.equals, "C")
|
|
107
|
+
.and(new QFilter("org.id", QCP.equals, orgId));
|
|
108
|
+
List<String> orderBys = new ArrayList<>(32);
|
|
109
|
+
orderBys.add(orderByAmount ? "amountandtax desc" : "billno desc");
|
|
110
|
+
orderBys.add("billno asc");
|
|
111
|
+
// orderBy 返回派生 DataSet,统一纳入 try-with-resources 确保关闭
|
|
112
|
+
SelectFieldBuilder builder = new SelectFieldBuilder()
|
|
113
|
+
.appendAll("id", "billno", "amountandtax");
|
|
114
|
+
if (keepSupplierField) {
|
|
115
|
+
builder.append("supplier.number", "supplierNumber");
|
|
116
|
+
}
|
|
117
|
+
String fields = builder.toString();
|
|
118
|
+
try (DataSet ds = QueryServiceHelper.queryDataSet(
|
|
119
|
+
PUR_ORDER_FORM_ID, PUR_ORDER_FORM_ID, fields, filter.toArray(), null, -1);
|
|
120
|
+
DataSet result = ds.orderBy(orderBys.toArray(new String[0]))
|
|
121
|
+
) {
|
|
122
|
+
if (result.hasNext()) {
|
|
123
|
+
Row firstRow = result.next();
|
|
124
|
+
getView().showTipNotification(String.format(
|
|
125
|
+
ResManager.loadKDString("首条结果:%s", "DataSetQueryStatSample_1", "kd-cd-common-snippets"),
|
|
126
|
+
firstRow.getString("billno")
|
|
127
|
+
));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
package kd.cd.common.snippets;
|
|
2
|
+
|
|
3
|
+
import kd.bos.bill.OperationStatus;
|
|
4
|
+
import kd.bos.context.RequestContext;
|
|
5
|
+
import kd.bos.dataentity.entity.DynamicObject;
|
|
6
|
+
import kd.bos.dataentity.entity.DynamicObjectCollection;
|
|
7
|
+
import kd.bos.entity.report.FilterInfo;
|
|
8
|
+
import kd.bos.entity.report.ReportQueryParam;
|
|
9
|
+
import kd.bos.form.FormShowParameter;
|
|
10
|
+
import kd.bos.form.ShowType;
|
|
11
|
+
import kd.bos.report.ReportList;
|
|
12
|
+
import kd.bos.form.events.HyperLinkClickEvent;
|
|
13
|
+
import kd.bos.form.events.HyperLinkClickListener;
|
|
14
|
+
import kd.bos.report.events.SearchEvent;
|
|
15
|
+
import kd.bos.report.events.SortAndFilterEvent;
|
|
16
|
+
import kd.bos.report.filter.ReportFilter;
|
|
17
|
+
import kd.bos.report.filter.SearchListener;
|
|
18
|
+
import kd.bos.report.plugin.AbstractReportFormPlugin;
|
|
19
|
+
import kd.cd.common.form.FormUtils;
|
|
20
|
+
import kd.cd.common.form.ShowParameterUtils;
|
|
21
|
+
import kd.cd.core.util.BigDecimalUtils;
|
|
22
|
+
|
|
23
|
+
import java.math.BigDecimal;
|
|
24
|
+
import java.math.RoundingMode;
|
|
25
|
+
import java.util.EventObject;
|
|
26
|
+
import java.util.List;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 报表界面插件示例 —— 配套 SampleReportListDataPlugin 使用。
|
|
30
|
+
* <p>
|
|
31
|
+
* 适用插件:报表插件
|
|
32
|
+
* 优先封装:FormUtils、ShowParameterUtils
|
|
33
|
+
* 原生兜底:AbstractReportFormPlugin、ReportFilter、HyperLinkClickEvent
|
|
34
|
+
* 相关 lint 规则:STYLE-013
|
|
35
|
+
* <p>
|
|
36
|
+
* 使用场景:
|
|
37
|
+
* 1. afterCreateNewData 设置查询条件默认值(如当前组织);
|
|
38
|
+
* 2. verifyQuery 查询前校验(showErrorMessage 替代 KDBizException);
|
|
39
|
+
* 3. registerListener 控制过滤面板不折叠;
|
|
40
|
+
* 4. processRowData 对取数结果做二次加工(百分比计算、格式化);
|
|
41
|
+
* 5. setSortAndFilter 设置列排序/过滤能力;
|
|
42
|
+
* 6. hyperLinkClick 超链接跳转关联单据。
|
|
43
|
+
*/
|
|
44
|
+
public class SampleReportFormPlugin extends AbstractReportFormPlugin
|
|
45
|
+
implements HyperLinkClickListener {
|
|
46
|
+
|
|
47
|
+
// ==================== 一、初始化默认值 ====================
|
|
48
|
+
/**
|
|
49
|
+
* 设置查询条件默认值。
|
|
50
|
+
* 典型用法:将当前登录用户的组织设置为默认组织。
|
|
51
|
+
*/
|
|
52
|
+
@Override
|
|
53
|
+
public void afterCreateNewData(EventObject e) {
|
|
54
|
+
super.afterCreateNewData(e);
|
|
55
|
+
// 设置默认组织为当前登录组织
|
|
56
|
+
long currentOrgId = RequestContext.get().getOrgId();
|
|
57
|
+
this.getModel().setValue("kdcd_org", currentOrgId);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ==================== 二、注册事件监听 ====================
|
|
61
|
+
/**
|
|
62
|
+
* 注册查询面板的事件监听。
|
|
63
|
+
* 典型用法:设置过滤条件面板不折叠。
|
|
64
|
+
*/
|
|
65
|
+
@Override
|
|
66
|
+
public void registerListener(EventObject e) {
|
|
67
|
+
super.registerListener(e);
|
|
68
|
+
// 过滤条件面板不折叠
|
|
69
|
+
ReportFilter reportFilter = getControl("reportfilterap");
|
|
70
|
+
if (reportFilter != null) {
|
|
71
|
+
reportFilter.addSearchListener(searchEvent -> {
|
|
72
|
+
ReportFilter filter = (ReportFilter) searchEvent.getSource();
|
|
73
|
+
filter.setCollapse(false);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ==================== 三、查询前校验 ====================
|
|
79
|
+
/**
|
|
80
|
+
* 点击「查询」按钮后、取数之前的校验入口。
|
|
81
|
+
* 返回 false 时框架不会调用取数插件的 query(),并且可以在这里用 showErrorNotification 提示用户。
|
|
82
|
+
* <p>注意:不要用 throw KDBizException,否则用户看到的是异常弹窗而非友好提示。</p>
|
|
83
|
+
*/
|
|
84
|
+
@Override
|
|
85
|
+
public boolean verifyQuery(ReportQueryParam queryParam) {
|
|
86
|
+
FilterInfo filterInfo = queryParam.getFilter();
|
|
87
|
+
DynamicObject org = filterInfo.getDynamicObject("kdcd_org");
|
|
88
|
+
if (org == null) {
|
|
89
|
+
this.getView().showErrorNotification("请选择组织");
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (filterInfo.getDate("kdcd_date") == null) {
|
|
94
|
+
this.getView().showErrorNotification("请选择截止日期");
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ==================== 四、列排序和过滤 ====================
|
|
101
|
+
/**
|
|
102
|
+
* 设置所有列均支持排序和过滤。
|
|
103
|
+
*/
|
|
104
|
+
@Override
|
|
105
|
+
public void setSortAndFilter(List<SortAndFilterEvent> allColumns) {
|
|
106
|
+
super.setSortAndFilter(allColumns);
|
|
107
|
+
for (SortAndFilterEvent column : allColumns) {
|
|
108
|
+
column.setFilter(true);
|
|
109
|
+
column.setSort(true);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ==================== 五、行数据二次加工 ====================
|
|
114
|
+
/**
|
|
115
|
+
* 对取数插件返回的行数据进行二次处理。
|
|
116
|
+
* 典型用法:
|
|
117
|
+
* - 计算衍生字段(如百分比、差异等)
|
|
118
|
+
* - 格式化显示(如金额格式化、缩进处理)
|
|
119
|
+
* - 字段拼接(如名称 + 编码)
|
|
120
|
+
*
|
|
121
|
+
*/
|
|
122
|
+
@Override
|
|
123
|
+
public void processRowData(String gridPk, DynamicObjectCollection rowData, ReportQueryParam queryParam) {
|
|
124
|
+
super.processRowData(gridPk, rowData, queryParam);
|
|
125
|
+
|
|
126
|
+
for (DynamicObject row : rowData) {
|
|
127
|
+
BigDecimal budgetAmt = BigDecimalUtils.nullToZero(row.getBigDecimal("kdcd_budget_amt"));
|
|
128
|
+
BigDecimal actualAmt = BigDecimalUtils.nullToZero(row.getBigDecimal("kdcd_actual_amt"));
|
|
129
|
+
|
|
130
|
+
// 示例1:计算执行率 = (实际金额 / 预算金额) * 100%
|
|
131
|
+
if (BigDecimalUtils.largeThanZero(budgetAmt)) {
|
|
132
|
+
BigDecimal rate = BigDecimalUtils.divide(
|
|
133
|
+
BigDecimalUtils.multiply(actualAmt, BigDecimalUtils.valueOf(100)),
|
|
134
|
+
budgetAmt, 2, RoundingMode.HALF_UP);
|
|
135
|
+
row.set("kdcd_exec_rate", BigDecimalUtils.toPlainString(rate) + "%");
|
|
136
|
+
} else {
|
|
137
|
+
row.set("kdcd_exec_rate", "0%");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 示例2:差异金额已由取数插件计算,此处可做格式化
|
|
141
|
+
// BigDecimal diffAmt = row.getBigDecimal("kdcd_diff_amt");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ==================== 六、超链接点击处理 ====================
|
|
146
|
+
/**
|
|
147
|
+
* 处理报表中超链接列的点击事件。
|
|
148
|
+
* 典型用法:点击单据编号跳转到对应单据的详情页面。
|
|
149
|
+
*
|
|
150
|
+
*/
|
|
151
|
+
@Override
|
|
152
|
+
public void hyperLinkClick(HyperLinkClickEvent evt) {
|
|
153
|
+
String fieldKey = evt.getFieldName();
|
|
154
|
+
DynamicObject rowData = evt.getRowData();
|
|
155
|
+
|
|
156
|
+
if ("kdcd_item_name".equals(fieldKey)) {
|
|
157
|
+
// 获取行数据中的单据ID
|
|
158
|
+
long billId = rowData.getLong("kdcd_bill_id");
|
|
159
|
+
String billFormId = "kdcd_sample_bill_a"; // 目标单据标识
|
|
160
|
+
|
|
161
|
+
// 打开单据详情(查看模式,新标签页)
|
|
162
|
+
FormShowParameter showParam = ShowParameterUtils.getBill(
|
|
163
|
+
billFormId, billId, OperationStatus.VIEW, ShowType.MainNewTabPage);
|
|
164
|
+
this.getView().showForm(showParam);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ==================== 七、获取报表列表控件 ====================
|
|
169
|
+
/**
|
|
170
|
+
* 通过 FormUtils.getReportList() 获取报表列表控件。
|
|
171
|
+
* 可用于自定义列表行为(如设置选中行、刷新等)。
|
|
172
|
+
*/
|
|
173
|
+
private void reportListExample() {
|
|
174
|
+
ReportList reportList = FormUtils.getReportList(this.getView());
|
|
175
|
+
reportList.refresh();
|
|
176
|
+
// reportList.setCurrentSelectedRowIndex(0); // 设置选中行
|
|
177
|
+
// reportList.refreshData(); // 刷新数据
|
|
178
|
+
}
|
|
179
|
+
}
|