@steedos-labs/plugin-workflow 3.0.0-beta.28 → 3.0.0-beta.29

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.
@@ -1,7 +1,8 @@
1
- // Steedos.authRequest("/api/workflow/v2/get_object_workflows", {
2
- // type: 'get',
3
- // success: (data)=>{
4
- // window.Creator.object_workflows = data;
5
- // Creator.dataInit.set(true);
6
- // }
7
- // });
1
+ waitForThing(Builder, 'settings.context.user.authToken').then(function(){
2
+ Steedos.authRequest("/api/workflow/v2/get_object_workflows", {
3
+ type: 'get',
4
+ success: (data)=>{
5
+ window.Steedos.object_workflows = data;
6
+ }
7
+ });
8
+ })
@@ -0,0 +1,794 @@
1
+ const _eval = require('eval');
2
+ const objectql = require('@steedos/objectql');
3
+ const { getCollection, _makeNewID } = require('../utils/collection');
4
+ const PermissionManager = require('./permission_manager');
5
+
6
+ const getObjectConfig = (objectApiName) => {
7
+ return objectql.getObject(objectApiName).toConfig();
8
+ };
9
+
10
+ const getObjectNameFieldKey = (objectApiName) => {
11
+ return objectql.getObject(objectApiName).NAME_FIELD_KEY;
12
+ };
13
+
14
+ const getRelateds = async (objectApiName) => {
15
+ return await objectql.getObject(objectApiName).getRelateds();
16
+ };
17
+
18
+ const objectFindOne = async (objectApiName, query) => {
19
+ query.top = 1;
20
+ const resolve = await objectql.getObject(objectApiName).find(query);
21
+ if (resolve && resolve.length > 0) {
22
+ return resolve[0];
23
+ } else {
24
+ return null;
25
+ }
26
+ };
27
+
28
+ const objectFind = async (objectApiName, query) => {
29
+ return await objectql.getObject(objectApiName).find(query);
30
+ };
31
+
32
+ const objectUpdate = async (objectApiName, id, data) => {
33
+ return await objectql.getObject(objectApiName).update(id, data);
34
+ };
35
+
36
+ const getRelatedObjectFieldCode = (relatedObjectsKeys, key) => {
37
+ return _.find(relatedObjectsKeys, (relatedObjectsKey) => {
38
+ return key.startsWith(relatedObjectsKey + '.');
39
+ });
40
+ };
41
+
42
+ const getFormTableFieldCode = (formTableFieldsCode, key) => {
43
+ return _.find(formTableFieldsCode, (formTableFieldCode) => {
44
+ return key.startsWith(formTableFieldCode + '.');
45
+ });
46
+ };
47
+
48
+ const getFormTableField = (formTableFields, key) => {
49
+ return _.find(formTableFields, (f) => {
50
+ return f.code == key;
51
+ });
52
+ };
53
+
54
+ const getFormField = (formFields, key) => {
55
+ let ff = null;
56
+ _.forEach(formFields, (f) => {
57
+ if (ff) {
58
+ return;
59
+ }
60
+ if (f.type == 'section') {
61
+ ff = _.find(f.fields, (sf) => {
62
+ return sf.code == key;
63
+ });
64
+ } else if (f.code == key) {
65
+ ff = f;
66
+ }
67
+ });
68
+ return ff;
69
+ };
70
+
71
+ const getFormTableSubField = (tableField, subFieldCode) => {
72
+ return _.find(tableField.fields, (f) => {
73
+ return f.code == subFieldCode;
74
+ });
75
+ };
76
+
77
+ const getFieldOdataValue = async (objName, id, referenceToFieldName) => {
78
+ const obj = objectql.getObject(objName);
79
+ const nameKey = getObjectNameFieldKey(objName);
80
+ if (!obj) {
81
+ return;
82
+ }
83
+ if (_.isString(id)) {
84
+ const _record = await objectFindOne(objName, { filters: [[referenceToFieldName, '=', id]] });
85
+ if (_record) {
86
+ _record['@label'] = _record[nameKey];
87
+ return _record;
88
+ }
89
+ } else if (_.isArray(id)) {
90
+ const _records = [];
91
+ const records = await objectFind(objName, { filters: [[referenceToFieldName, 'in', id]] });
92
+ for (const _record of records) {
93
+ _record['@label'] = _record[nameKey];
94
+ _records.push(_record);
95
+ }
96
+ if (!_.isEmpty(_records)) {
97
+ return _records;
98
+ }
99
+ }
100
+ return;
101
+ };
102
+
103
+ const getSelectUserValue = (userId, spaceId) => {
104
+ const su = Creator.getCollection('space_users').findOne({ space: spaceId, user: userId });
105
+ su.id = userId;
106
+ return su;
107
+ };
108
+
109
+ const getSelectUserValues = (userIds, spaceId) => {
110
+ const sus = [];
111
+ if (_.isArray(userIds)) {
112
+ _.each(userIds, (userId) => {
113
+ const su = getSelectUserValue(userId, spaceId);
114
+ if (su) {
115
+ sus.push(su);
116
+ }
117
+ });
118
+ }
119
+ return sus;
120
+ };
121
+
122
+ const getSelectOrgValue = (orgId, spaceId) => {
123
+ const org = Creator.getCollection('organizations').findOne(orgId, { fields: { _id: 1, name: 1, fullname: 1 } });
124
+ org.id = orgId;
125
+ return org;
126
+ };
127
+
128
+ const getSelectOrgValues = (orgIds, spaceId) => {
129
+ const orgs = [];
130
+ if (_.isArray(orgIds)) {
131
+ _.each(orgIds, (orgId) => {
132
+ const org = getSelectOrgValue(orgId, spaceId);
133
+ if (org) {
134
+ orgs.push(org);
135
+ }
136
+ });
137
+ }
138
+ return orgs;
139
+ };
140
+
141
+ const getFileFieldValue = (recordFieldId, fType) => {
142
+ if (_.isEmpty(recordFieldId)) {
143
+ return;
144
+ }
145
+ let collection;
146
+ if (fType == 'image') {
147
+ collection = 'images';
148
+ } else if (fType == 'file') {
149
+ collection = 'files';
150
+ }
151
+ let query;
152
+ if (_.isString(recordFieldId)) {
153
+ query = { _id: { $in: [recordFieldId] } };
154
+ } else {
155
+ query = { _id: { $in: recordFieldId } };
156
+ }
157
+ const files = Creator.Collections[`cfs.${collection}.filerecord`].find(query);
158
+ const value = [];
159
+ files.forEach((f) => {
160
+ const newFile = new FS.File();
161
+ newFile.attachData(f.createReadStream('files'), {
162
+ type: f.original.type
163
+ }, (err) => {
164
+ if (err) {
165
+ throw new Error(err.error, err.reason);
166
+ }
167
+ newFile.name(f.name());
168
+ newFile.size(f.size());
169
+ const metadata = {
170
+ owner: f.metadata.owner
171
+ };
172
+ newFile.metadata = metadata;
173
+ newFile._id = _makeNewID();
174
+ cfs[collection].insert(newFile);
175
+ value.push(newFile._id);
176
+ });
177
+ });
178
+ if (value.length > 0) {
179
+ if (_.isString(recordFieldId)) {
180
+ return value[0];
181
+ } else {
182
+ return value;
183
+ }
184
+ }
185
+ };
186
+
187
+ const getInstanceFieldValue = (objField, formField, record, object_field, spaceId, recordFieldValue, enableAmisform) => {
188
+ if (enableAmisform && formField.steedos_field) {
189
+ return recordFieldValue;
190
+ }
191
+ recordFieldValue = record[objField.name];
192
+ let value;
193
+ if (formField && objField && formField.type == 'odata' && ['lookup', 'master_detail'].includes(objField.type) && _.isString(objField.reference_to)) {
194
+ const referenceToFieldName = objField.reference_to_field || '_id';
195
+ const referenceToObjectName = objField.reference_to;
196
+ let odataFieldValue;
197
+ if (objField.multiple && formField.is_multiselect) {
198
+ odataFieldValue = getFieldOdataValue(referenceToObjectName, recordFieldValue, referenceToFieldName);
199
+ } else if (!objField.multiple && !formField.is_multiselect) {
200
+ odataFieldValue = getFieldOdataValue(referenceToObjectName, recordFieldValue, referenceToFieldName);
201
+ }
202
+ value = odataFieldValue;
203
+ } else if (formField && objField && ['user', 'group'].includes(formField.type) && ['lookup', 'master_detail'].includes(objField.type) && (['users', 'organizations'].includes(objField.reference_to) || ('space_users' == objField.reference_to && 'user' == objField.reference_to_field))) {
204
+ if (!_.isEmpty(recordFieldValue)) {
205
+ let selectFieldValue;
206
+ if (formField.type == 'user') {
207
+ if (objField.multiple && formField.is_multiselect) {
208
+ selectFieldValue = getSelectUserValues(recordFieldValue, spaceId);
209
+ } else if (!objField.multiple && !formField.is_multiselect) {
210
+ selectFieldValue = getSelectUserValue(recordFieldValue, spaceId);
211
+ }
212
+ } else if (formField.type == 'group') {
213
+ if (objField.multiple && formField.is_multiselect) {
214
+ selectFieldValue = getSelectOrgValues(recordFieldValue, spaceId);
215
+ } else if (!objField.multiple && !formField.is_multiselect) {
216
+ selectFieldValue = getSelectOrgValue(recordFieldValue, spaceId);
217
+ }
218
+ }
219
+ if (selectFieldValue) {
220
+ value = selectFieldValue;
221
+ }
222
+ }
223
+ } else if (formField && objField && formField.type == 'date' && recordFieldValue) {
224
+ value = uuflowManagerForInitApproval.formatDate(recordFieldValue);
225
+ } else if (formField && objField && formField.type == 'time' && recordFieldValue) {
226
+ value = uuflowManagerForInitApproval.formatTime(recordFieldValue);
227
+ } else if (formField && objField && recordFieldValue && (formField.type == 'image' || formField.type == 'file')) {
228
+ value = getFileFieldValue(recordFieldValue, formField.type);
229
+ } else if (formField && objField && recordFieldValue && formField.type == 'lookup' && ['lookup', 'master_detail'].includes(objField.type) && _.isString(objField.reference_to)) {
230
+ value = recordFieldValue;
231
+ } else if (formField && objField && recordFieldValue && (formField.type == 'multiSelect')) {
232
+ value = recordFieldValue.join(',');
233
+ } else if (record.hasOwnProperty(object_field)) {
234
+ value = recordFieldValue;
235
+ }
236
+ return value;
237
+ };
238
+
239
+ const checkRequiredDetails = (requiredDetails, masterRecord) => {
240
+ if (!_.isEmpty(requiredDetails)) {
241
+ requiredDetails.forEach((rd) => {
242
+ const subTableName = rd.split('.')[0];
243
+ const subTableMasterField = rd.split('.')[1];
244
+ const subObjConfig = getObjectConfig(subTableName);
245
+ const masterField = subObjConfig.fields[subTableMasterField];
246
+ const refId = masterField.reference_to_field || '_id';
247
+ const masterFieldValue = masterRecord[refId];
248
+ const subTableRecord = objectFindOne(subTableName, { filters: [[subTableMasterField, '=', masterFieldValue]], fields: [subTableMasterField] });
249
+ if (!subTableRecord) {
250
+ throw new Error(`请先新增${subObjConfig.label}`);
251
+ }
252
+ });
253
+ }
254
+ return;
255
+ };
256
+
257
+ const uuflowManagerForInitApproval = {};
258
+
259
+ uuflowManagerForInitApproval.check_authorization = (req) => {
260
+ const query = req.query;
261
+ const userId = query["X-User-Id"];
262
+ const authToken = query["X-Auth-Token"];
263
+
264
+ if (!userId || !authToken) {
265
+ throw new Error(401, 'Unauthorized');
266
+ }
267
+
268
+ const hashedToken = Accounts._hashLoginToken(authToken);
269
+ const user = Meteor.users.findOne({
270
+ _id: userId,
271
+ "services.resume.loginTokens.hashedToken": hashedToken
272
+ });
273
+
274
+ if (!user) {
275
+ throw new Error(401, 'Unauthorized');
276
+ }
277
+
278
+ return user;
279
+ };
280
+
281
+ uuflowManagerForInitApproval.getSpace = async (space_id) => {
282
+ const coll = await getCollection('spaces');
283
+ const space = await coll.findOne({_id: space_id});
284
+ if (!space) {
285
+ throw new Error('error!', "space_id有误或此space已经被删除");
286
+ }
287
+ return space;
288
+ };
289
+
290
+ uuflowManagerForInitApproval.getFlow = async (flow_id) => {
291
+ const coll = await getCollection('flows');
292
+ const flow = coll.findOne({_id: flow_id});
293
+ if (!flow) {
294
+ throw new Error('error!', "id有误或此流程已经被删除");
295
+ }
296
+ return flow;
297
+ };
298
+
299
+ uuflowManagerForInitApproval.getSpaceUser = async (space_id, user_id) => {
300
+ const coll = await getCollection('space_users');
301
+ const space_user = coll.findOne({ space: space_id, user: user_id });
302
+ if (!space_user) {
303
+ throw new Error('error!', "user_id对应的用户不属于当前space");
304
+ }
305
+ return space_user;
306
+ };
307
+
308
+ uuflowManagerForInitApproval.getSpaceUserOrgInfo = async (space_user) => {
309
+ const coll = await getCollection('organizations');
310
+ const info = new Object();
311
+ info.organization = space_user.organization;
312
+ const org = coll.findOne({_id: space_user.organization}, { fields: { name: 1, fullname: 1 } });
313
+ info.organization_name = org.name;
314
+ info.organization_fullname = org.fullname;
315
+ return info;
316
+ };
317
+
318
+ uuflowManagerForInitApproval.isFlowEnabled = (flow) => {
319
+ if (flow.state !== "enabled") {
320
+ throw new Error('error!', "流程未启用,操作失败");
321
+ }
322
+ };
323
+
324
+ uuflowManagerForInitApproval.isFlowSpaceMatched = (flow, space_id) => {
325
+ if (flow.space !== space_id) {
326
+ throw new Error('error!', "流程和工作区ID不匹配");
327
+ }
328
+ };
329
+
330
+ uuflowManagerForInitApproval.getForm = async (form_id) => {
331
+ const coll = await getCollection('forms');
332
+ const form = coll.findOne({_id: form_id});
333
+ if (!form) {
334
+ throw new Error('error!', '表单ID有误或此表单已经被删除');
335
+ }
336
+ return form;
337
+ };
338
+
339
+ uuflowManagerForInitApproval.getCategory = async (category_id) => {
340
+ const coll = await getCollection('categories');
341
+ return coll.findOne({_id: category_id});
342
+ };
343
+
344
+ uuflowManagerForInitApproval.checkSyncDirection = async (object_name, flow_id) => {
345
+ const coll = await getCollection('object_workflows')
346
+ const ow = await coll.findOne({
347
+ object_name: object_name,
348
+ flow_id: flow_id
349
+ });
350
+ if (!ow) {
351
+ throw new Error('error!', '未找到对象流程映射记录。');
352
+ }
353
+ const syncDirection = ow.sync_direction || 'both';
354
+ if (!['both', 'obj_to_ins'].includes(syncDirection)) {
355
+ throw new Error('error!', '不支持的同步方向。');
356
+ }
357
+ };
358
+
359
+ uuflowManagerForInitApproval.create_instance = async (instance_from_client, user_info) => {
360
+
361
+ const insColl = await getCollection('instances');
362
+
363
+ await uuflowManagerForInitApproval.checkSyncDirection(instance_from_client["record_ids"][0].o, instance_from_client["flow"]);
364
+
365
+ await uuflowManagerForInitApproval.checkIsInApproval(instance_from_client["record_ids"][0], instance_from_client["space"]);
366
+
367
+ const space_id = instance_from_client["space"];
368
+ const flow_id = instance_from_client["flow"];
369
+ const user_id = user_info._id;
370
+ let trace_from_client = null;
371
+ let approve_from_client = null;
372
+ if (instance_from_client["traces"] && instance_from_client["traces"][0]) {
373
+ trace_from_client = instance_from_client["traces"][0];
374
+ if (trace_from_client["approves"] && trace_from_client["approves"][0]) {
375
+ approve_from_client = instance_from_client["traces"][0]["approves"][0];
376
+ }
377
+ }
378
+
379
+ const space = await uuflowManagerForInitApproval.getSpace(space_id);
380
+ const flow = await uuflowManagerForInitApproval.getFlow(flow_id);
381
+ const space_user = await uuflowManagerForInitApproval.getSpaceUser(space_id, user_id);
382
+ const space_user_org_info = await uuflowManagerForInitApproval.getSpaceUserOrgInfo(space_user);
383
+ uuflowManagerForInitApproval.isFlowEnabled(flow);
384
+ uuflowManagerForInitApproval.isFlowSpaceMatched(flow, space_id);
385
+
386
+ const form = await uuflowManagerForInitApproval.getForm(flow.form);
387
+
388
+ const permissions = await PermissionManager.getFlowPermissions(flow_id, user_id);
389
+
390
+ if (!permissions.includes("add")) {
391
+ throw new Error('error!', "当前用户没有此流程的新建权限");
392
+ }
393
+
394
+ const now = new Date();
395
+ const ins_obj = {};
396
+ ins_obj._id = await _makeNewID();
397
+ ins_obj.space = space_id;
398
+ ins_obj.flow = flow_id;
399
+ ins_obj.flow_version = flow.current._id;
400
+ ins_obj.form = flow.form;
401
+ ins_obj.form_version = flow.current.form_version;
402
+ ins_obj.name = flow.name;
403
+ ins_obj.submitter = user_id;
404
+ ins_obj.submitter_name = user_info.name;
405
+ ins_obj.applicant = instance_from_client["applicant"] ? instance_from_client["applicant"] : user_id;
406
+ ins_obj.applicant_name = instance_from_client["applicant_name"] ? instance_from_client["applicant_name"] : user_info.name;
407
+ ins_obj.applicant_organization = instance_from_client["applicant_organization"] ? instance_from_client["applicant_organization"] : space_user.organization;
408
+ ins_obj.applicant_organization_name = instance_from_client["applicant_organization_name"] ? instance_from_client["applicant_organization_name"] : space_user_org_info.organization_name;
409
+ ins_obj.applicant_organization_fullname = instance_from_client["applicant_organization_fullname"] ? instance_from_client["applicant_organization_fullname"] : space_user_org_info.organization_fullname;
410
+ ins_obj.applicant_company = instance_from_client["applicant_company"] ? instance_from_client["applicant_company"] : space_user.company_id;
411
+ ins_obj.state = 'draft';
412
+ ins_obj.code = '';
413
+ ins_obj.is_archived = false;
414
+ ins_obj.is_deleted = false;
415
+ ins_obj.created = now;
416
+ ins_obj.created_by = user_id;
417
+ ins_obj.modified = now;
418
+ ins_obj.modified_by = user_id;
419
+
420
+ ins_obj.record_ids = instance_from_client["record_ids"];
421
+
422
+ if (space_user.company_id) {
423
+ ins_obj.company_id = space_user.company_id;
424
+ }
425
+
426
+ const trace_obj = {};
427
+ trace_obj._id = _makeNewID();
428
+ trace_obj.instance = ins_obj._id;
429
+ trace_obj.is_finished = false;
430
+ const start_step = _.find(flow.current.steps, (step) => {
431
+ return step.step_type === 'start';
432
+ });
433
+ trace_obj.step = start_step._id;
434
+ trace_obj.name = start_step.name;
435
+
436
+ trace_obj.start_date = now;
437
+ const appr_obj = {};
438
+ appr_obj._id = _makeNewID();
439
+ appr_obj.instance = ins_obj._id;
440
+ appr_obj.trace = trace_obj._id;
441
+ appr_obj.is_finished = false;
442
+ appr_obj.user = instance_from_client["applicant"] ? instance_from_client["applicant"] : user_id;
443
+ appr_obj.user_name = instance_from_client["applicant_name"] ? instance_from_client["applicant_name"] : user_info.name;
444
+ appr_obj.handler = user_id;
445
+ appr_obj.handler_name = user_info.name;
446
+ appr_obj.handler_organization = space_user.organization;
447
+ appr_obj.handler_organization_name = space_user_org_info.name;
448
+ appr_obj.handler_organization_fullname = space_user_org_info.fullname;
449
+ appr_obj.type = 'draft';
450
+ appr_obj.start_date = now;
451
+ appr_obj.read_date = now;
452
+ appr_obj.is_read = true;
453
+ appr_obj.is_error = false;
454
+ appr_obj.description = '';
455
+ const relatedTablesInfo = {};
456
+ appr_obj.values = await uuflowManagerForInitApproval.initiateValues(ins_obj.record_ids[0], flow_id, space_id, form.current.fields, relatedTablesInfo);
457
+
458
+ trace_obj.approves = [appr_obj];
459
+ ins_obj.traces = [trace_obj];
460
+
461
+ ins_obj.values = appr_obj.values;
462
+
463
+ ins_obj.inbox_users = instance_from_client.inbox_users || [];
464
+
465
+ ins_obj.current_step_name = start_step.name;
466
+
467
+ if (flow.auto_remind === true) {
468
+ ins_obj.auto_remind = true;
469
+ }
470
+
471
+ if (form.category) {
472
+ const category = await uuflowManagerForInitApproval.getCategory(form.category);
473
+ if (category) {
474
+ ins_obj.category_name = category.name;
475
+ ins_obj.category = category._id;
476
+ }
477
+ }
478
+
479
+ const new_ins_id = ins_obj._id;
480
+
481
+ await insColl.insert(ins_obj);
482
+
483
+ await uuflowManagerForInitApproval.initiateRecordInstanceInfo(ins_obj.record_ids[0], new_ins_id, space_id);
484
+
485
+ //TODO 处理附件
486
+ // await uuflowManagerForInitApproval.initiateAttach(ins_obj.record_ids[0], space_id, ins_obj._id, appr_obj._id);
487
+
488
+ return new_ins_id;
489
+ };
490
+
491
+ uuflowManagerForInitApproval.initiateValues = async (recordIds, flowId, spaceId, fields, relatedTablesInfo) => {
492
+ const owColl = await getCollection('object_workflows');
493
+ const flowColl = await getCollection('flows');
494
+ const formColl = await getCollection('forms')
495
+ const fieldCodes = [];
496
+ _.each(fields, (f) => {
497
+ if (f.type == 'section') {
498
+ _.each(f.fields, (ff) => {
499
+ fieldCodes.push(ff.code);
500
+ });
501
+ } else {
502
+ fieldCodes.push(f.code);
503
+ }
504
+ });
505
+
506
+ const values = {};
507
+ const objectName = recordIds.o;
508
+ const object = getObjectConfig(objectName);
509
+ const recordId = recordIds.ids[0];
510
+ const ow = await owColl.findOne({
511
+ object_name: objectName,
512
+ flow_id: flowId
513
+ });
514
+ const record = await objectFindOne(objectName, { filters: [['_id', '=', recordId]] });
515
+ const flow = await flowColl.findOne({_id: flowId}, { fields: { form: 1, enableAmisform: 1 } });
516
+ const enableAmisform = flow.enable_amisform;
517
+ if (ow && record) {
518
+ const requiredDetails = ow.required_details || [];
519
+ checkRequiredDetails(requiredDetails, record);
520
+
521
+ const form = await formColl.findOne({_id: flow.form});
522
+ const formFields = form.current.fields || [];
523
+ const relatedObjects = await getRelateds(objectName);
524
+ const relatedObjectsKeys = _.pluck(relatedObjects, 'object_name');
525
+ const formTableFields = _.filter(formFields, (formField) => {
526
+ return formField.type == 'table';
527
+ });
528
+ const formTableFieldsCode = _.pluck(formTableFields, 'code');
529
+
530
+ const tableFieldCodes = [];
531
+ const tableFieldMap = [];
532
+ const tableToRelatedMap = {};
533
+
534
+ ow.field_map?.forEach((fm) => {
535
+ const object_field = fm.object_field;
536
+ const workflow_field = fm.workflow_field;
537
+ if (!object_field || !workflow_field) {
538
+ throw new Error(400, '未找到字段,请检查对象流程映射字段配置');
539
+ }
540
+ const relatedObjectFieldCode = getRelatedObjectFieldCode(relatedObjectsKeys, object_field);
541
+ const formTableFieldCode = getFormTableFieldCode(formTableFieldsCode, workflow_field);
542
+ const objField = object.fields[object_field];
543
+ const formField = getFormField(formFields, workflow_field);
544
+ const recordFieldValue = record[object_field];
545
+ if (relatedObjectFieldCode) {
546
+ const oTableCode = object_field.split('.')[0];
547
+ const oTableFieldCode = object_field.split('.')[1];
548
+ const tableToRelatedMapKey = oTableCode;
549
+ if (!tableToRelatedMap[tableToRelatedMapKey]) {
550
+ tableToRelatedMap[tableToRelatedMapKey] = {};
551
+ }
552
+
553
+ if (formTableFieldCode) {
554
+ const wTableCode = workflow_field.split('.')[0];
555
+ tableToRelatedMap[tableToRelatedMapKey]['_FROM_TABLE_CODE'] = wTableCode;
556
+ }
557
+
558
+ tableToRelatedMap[tableToRelatedMapKey][oTableFieldCode] = workflow_field;
559
+ } else if (workflow_field.indexOf('.') > 0 && object_field.indexOf('.$.') > 0) {
560
+ const wTableCode = workflow_field.split('.')[0];
561
+ const oTableCode = object_field.split('.$.')[0];
562
+ if (record.hasOwnProperty(oTableCode) && _.isArray(record[oTableCode])) {
563
+ tableFieldCodes.push(JSON.stringify({
564
+ workflow_table_field_code: wTableCode,
565
+ object_table_field_code: oTableCode
566
+ }));
567
+ tableFieldMap.push(fm);
568
+ } else if (oTableCode.indexOf('.') > 0) {
569
+ const oTableCodeReferenceFieldCode = oTableCode.split('.')[0];
570
+ const gridCode = oTableCode.split('.')[1];
571
+ const oTableCodeReferenceField = object.fields[oTableCodeReferenceFieldCode];
572
+ if (oTableCodeReferenceField && ['lookup', 'master_detail'].includes(oTableCodeReferenceField.type) && _.isString(oTableCodeReferenceField.reference_to)) {
573
+ if (record[oTableCode]) {
574
+ return;
575
+ }
576
+ const referenceToFieldName = oTableCodeReferenceField.reference_to_field || '_id';
577
+ const referenceToObjectName = oTableCodeReferenceField.reference_to;
578
+ const referenceToFieldValue = record[oTableCodeReferenceField.name];
579
+ const referenceToDoc = getFieldOdataValue(referenceToObjectName, referenceToFieldValue, referenceToFieldName);
580
+ if (referenceToDoc[gridCode]) {
581
+ record[oTableCode] = referenceToDoc[gridCode];
582
+ tableFieldCodes.push(JSON.stringify({
583
+ workflow_table_field_code: wTableCode,
584
+ object_table_field_code: oTableCode
585
+ }));
586
+ tableFieldMap.push(fm);
587
+ }
588
+ }
589
+ }
590
+ } else if (object_field.indexOf('.') > 0 && object_field.indexOf('.$.') == -1) {
591
+ const objectFieldName = object_field.split('.')[0];
592
+ const lookupFieldName = object_field.split('.')[1];
593
+ if (object) {
594
+ const objectField = object.fields[objectFieldName];
595
+ if (objectField && formField && ['lookup', 'master_detail'].includes(objectField.type) && _.isString(objectField.reference_to)) {
596
+ const lookupObjectRecord = objectFindOne(objectField.reference_to, { filters: [['_id', '=', record[objectFieldName]]], fields: [lookupFieldName] });
597
+ if (!lookupObjectRecord) {
598
+ return;
599
+ }
600
+ const objectFieldObjectName = objectField.reference_to;
601
+ const lookupFieldObj = getObjectConfig(objectFieldObjectName);
602
+ const objectLookupField = lookupFieldObj.fields[lookupFieldName];
603
+
604
+ values[workflow_field] = getInstanceFieldValue(objectLookupField, formField, lookupObjectRecord, lookupFieldName, spaceId, record[lookupFieldName], enableAmisform);
605
+ }
606
+ }
607
+ } else {
608
+ values[workflow_field] = getInstanceFieldValue(objField, formField, record, object_field, spaceId, record[object_field], enableAmisform);
609
+ }
610
+ });
611
+
612
+ _.uniq(tableFieldCodes).forEach((tfc) => {
613
+ const c = JSON.parse(tfc);
614
+ values[c.workflow_table_field_code] = [];
615
+ record[c.object_table_field_code].forEach((tr) => {
616
+ const newTr = {};
617
+ _.each(tr, (tdValue, k) => {
618
+ tableFieldMap.forEach((tfm) => {
619
+ if (tfm.object_field === (c.object_table_field_code + '.$.' + k)) {
620
+ const wTdCode = tfm.workflow_field.split('.')[1];
621
+ newTr[wTdCode] = tdValue;
622
+ }
623
+ });
624
+ });
625
+ if (!_.isEmpty(newTr)) {
626
+ values[c.workflow_table_field_code].push(newTr);
627
+ }
628
+ });
629
+ });
630
+
631
+ for (const [key, map] of Object.entries(tableToRelatedMap)) {
632
+ const tableCode = map._FROM_TABLE_CODE;
633
+ const formTableField = getFormTableField(formTableFields, tableCode);
634
+ if (!tableCode) {
635
+ console.warn('tableToRelated: [' + key + '] missing corresponding table.');
636
+ } else {
637
+ const relatedObjectName = key;
638
+ const tableValues = [];
639
+ const relatedTableItems = [];
640
+ const relatedObject = getObjectConfig(relatedObjectName);
641
+ const relatedField = _.find(relatedObject.fields, (f) => {
642
+ return ['lookup', 'master_detail'].includes(f.type) && f.reference_to == objectName;
643
+ });
644
+
645
+ const relatedFieldName = relatedField.name;
646
+
647
+ const relatedRecords = await objectFind(relatedObjectName, {
648
+ filters: [
649
+ [relatedFieldName, '=', recordId]
650
+ ]
651
+ });
652
+
653
+ for (const relatedRecord of relatedRecords) {
654
+ const tableValueItem = {};
655
+ for (const [fieldKey, valueKey] of Object.entries(map)) {
656
+ if (fieldKey !== '_FROM_TABLE_CODE') {
657
+ let tableFieldValue;
658
+ let formFieldKey;
659
+ if (valueKey.startsWith(tableCode + '.')) {
660
+ formFieldKey = (valueKey.split(".")[1]);
661
+ } else {
662
+ formFieldKey = valueKey;
663
+ }
664
+
665
+ const formField = getFormTableSubField(formTableField, formFieldKey);
666
+ const relatedObjectField = relatedObject.fields[fieldKey];
667
+ if (!formField || !relatedObjectField) {
668
+ continue;
669
+ }
670
+ tableFieldValue = getInstanceFieldValue(relatedObjectField, formField, relatedRecord, fieldKey, spaceId, relatedRecord[fieldKey], enableAmisform);
671
+ tableValueItem[formFieldKey] = tableFieldValue;
672
+ }
673
+ }
674
+ if (!_.isEmpty(tableValueItem)) {
675
+ tableValueItem._id = relatedRecord._id;
676
+ tableValues.push(tableValueItem);
677
+ relatedTableItems.push({ _table: { _id: relatedRecord._id, _code: tableCode } });
678
+ }
679
+ }
680
+ values[tableCode] = tableValues;
681
+ relatedTablesInfo[relatedObjectName] = relatedTableItems;
682
+ }
683
+ }
684
+
685
+ if (ow.field_map_script) {
686
+ _.extend(values, uuflowManagerForInitApproval.evalFieldMapScript(ow.field_map_script, objectName, spaceId, recordId));
687
+ }
688
+ }
689
+
690
+ const filterValues = {};
691
+ _.each(_.keys(values), (k) => {
692
+ if (fieldCodes.includes(k)) {
693
+ filterValues[k] = values[k];
694
+ }
695
+ });
696
+
697
+ return filterValues;
698
+ };
699
+
700
+ uuflowManagerForInitApproval.evalFieldMapScript = (field_map_script, objectName, spaceId, objectId) => {
701
+ const record = objectFindOne(objectName, { filters: [['_id', '=', objectId]] });
702
+ const script = "module.exports = function (record) { " + field_map_script + " }";
703
+ const func = _eval(script, "field_map_script");
704
+ const values = func(record);
705
+ if (_.isObject(values)) {
706
+ return values;
707
+ } else {
708
+ console.error("evalFieldMapScript: 脚本返回值类型不是对象");
709
+ }
710
+ return {};
711
+ };
712
+
713
+ uuflowManagerForInitApproval.initiateAttach = (recordIds, spaceId, insId, approveId) => {
714
+ Creator.Collections['cms_files'].find({
715
+ space: spaceId,
716
+ parent: recordIds
717
+ }).forEach((cf) => {
718
+ _.each(cf.versions, (versionId, idx) => {
719
+ const f = Creator.Collections['cfs.files.filerecord'].findOne(versionId);
720
+ const newFile = new FS.File();
721
+ newFile.attachData(f.createReadStream('files'), {
722
+ type: f.original.type
723
+ }, (err) => {
724
+ if (err) {
725
+ throw new Error(err.error, err.reason);
726
+ }
727
+ newFile.name(f.name());
728
+ newFile.size(f.size());
729
+ const metadata = {
730
+ owner: f.metadata.owner,
731
+ owner_name: f.metadata.owner_name,
732
+ space: spaceId,
733
+ instance: insId,
734
+ approve: approveId,
735
+ parent: cf._id
736
+ };
737
+ if (idx === 0) {
738
+ metadata.current = true;
739
+ }
740
+ newFile.metadata = metadata;
741
+ cfs.instances.insert(newFile);
742
+ });
743
+ });
744
+ });
745
+ return;
746
+ };
747
+
748
+ uuflowManagerForInitApproval.initiateRecordInstanceInfo = async (recordIds, insId, spaceId) => {
749
+ await objectUpdate(recordIds.o, recordIds.ids[0], {
750
+ instances: [{
751
+ _id: insId,
752
+ state: 'draft'
753
+ }],
754
+ locked: true,
755
+ instance_state: 'draft'
756
+ });
757
+ return;
758
+ };
759
+
760
+ uuflowManagerForInitApproval.initiateRelatedRecordInstanceInfo = (relatedTablesInfo, insId, spaceId) => {
761
+ _.each(relatedTablesInfo, (tableItems, relatedObjectName) => {
762
+ const relatedCollection = Creator.getCollection(relatedObjectName, spaceId);
763
+ _.each(tableItems, (item) => {
764
+ relatedCollection.direct.update(item._table._id, {
765
+ $set: {
766
+ instances: [{
767
+ _id: insId,
768
+ state: 'draft'
769
+ }],
770
+ _table: item._table
771
+ }
772
+ });
773
+ });
774
+ });
775
+ return;
776
+ };
777
+
778
+ uuflowManagerForInitApproval.checkIsInApproval = async (recordIds, spaceId) => {
779
+ const record = await objectFindOne(recordIds.o, { filters: [['_id', '=', recordIds.ids[0]]], fields: ['instances'] });
780
+ if (record && record.instances && record.instances[0].state !== 'completed' && Creator.Collections.instances.find(record.instances[0]._id).count() > 0) {
781
+ throw new Error('error!', "此记录已发起流程正在审批中,待审批结束方可发起下一次审批!");
782
+ }
783
+ return;
784
+ };
785
+
786
+ uuflowManagerForInitApproval.formatDate = (date) => {
787
+ return moment(date).format("YYYY-MM-DD");
788
+ };
789
+
790
+ uuflowManagerForInitApproval.formatTime = (date) => {
791
+ return moment(date).utcOffset(0).format("1970-01-01THH:mm:00.000[Z]");
792
+ };
793
+
794
+ module.exports = uuflowManagerForInitApproval;
@@ -8,34 +8,91 @@
8
8
  const express = require("express");
9
9
  const router = express.Router();
10
10
  const { requireAuthentication } = require("@steedos/auth");
11
- const objectql = require('@steedos/objectql')
11
+ const { getCollection } = require('../utils/collection');
12
12
  const _ = require('lodash');
13
- const Fiber = function(fun){console.log('TODO Fiber...')}
14
13
 
15
- router.get('/api/workflow/v2/get_object_workflows', requireAuthentication, async function (req, res) {
16
- try {
17
- let userSession = req.user;
18
- const { spaceId, userId } = userSession;
19
- Fiber(async function () {
20
- try {
21
- Meteor.call('object_workflows.get', spaceId, userId, (error, result)=>{
22
- if(error){
23
- res.status(200).send({
24
- error: error.message
14
+ async function getObjectWorkflows(spaceId, userId) {
15
+ // 参数检查
16
+ if (typeof spaceId !== 'string') {
17
+ throw new Error('spaceId must be a string');
18
+ }
19
+ if (typeof userId !== 'string') {
20
+ throw new Error('userId must be a string');
21
+ }
22
+
23
+ // 获取space_users集合
24
+ const spaceUsersCollection = await getCollection('space_users');
25
+ const curSpaceUser = await spaceUsersCollection.findOne(
26
+ { space: spaceId, user: userId },
27
+ { projection: { organizations: 1 } }
28
+ );
29
+
30
+ if (!curSpaceUser) {
31
+ throw new Error('not-authorized');
32
+ }
33
+
34
+ // 获取organizations集合
35
+ const organizationsCollection = await getCollection('organizations');
36
+ const organizations = await organizationsCollection.find({
37
+ _id: {
38
+ $in: curSpaceUser.organizations
39
+ }
40
+ }, { projection: { parents: 1 } }).toArray();
41
+
42
+ // 获取object_workflows集合
43
+ const objectWorkflowsCollection = await getCollection('object_workflows');
44
+ let ows = await objectWorkflowsCollection.find(
45
+ { space: spaceId },
46
+ { projection: { object_name: 1, flow_id: 1, space: 1, sync_direction: 1 } }
47
+ ).toArray();
48
+
49
+ // 获取flows集合
50
+ const flowsCollection = await getCollection('flows');
51
+
52
+ // 使用Promise.all并行处理每个工作流
53
+ ows = await Promise.all(ows.map(async (o) => {
54
+ const fl = await flowsCollection.findOne(
55
+ { _id: o.flow_id, state: 'enabled' },
56
+ { projection: { name: 1, perms: 1, forbid_initiate_instance: 1 } }
57
+ );
58
+
59
+ if (fl) {
60
+ o.flow_name = fl.name;
61
+ o.can_add = false;
62
+ o.forbid_initiate_instance = fl.forbid_initiate_instance;
63
+
64
+ const perms = fl.perms;
65
+ if (perms) {
66
+ if (perms.users_can_add && perms.users_can_add.includes(userId)) {
67
+ o.can_add = true;
68
+ } else if (perms.orgs_can_add && perms.orgs_can_add.length > 0) {
69
+ if (curSpaceUser && curSpaceUser.organizations &&
70
+ _.intersection(curSpaceUser.organizations, perms.orgs_can_add).length > 0) {
71
+ o.can_add = true;
72
+ } else if (organizations) {
73
+ o.can_add = _.some(organizations, (org) => {
74
+ return org.parents && _.intersection(org.parents, perms.orgs_can_add).length > 0;
25
75
  });
26
- }else{
27
- res.status(200).send(result);
28
76
  }
29
- })
30
- } catch (error) {
31
- console.error(error);
32
- res.status(200).send({
33
- error: error.message
34
- });
77
+ }
35
78
  }
79
+ }
80
+ return o;
81
+ }));
36
82
 
37
- }).run()
38
-
83
+ // 过滤掉没有flow_name的工作流
84
+ ows = ows.filter(n => n.flow_name);
85
+
86
+ return ows;
87
+ }
88
+
89
+
90
+ router.get('/api/workflow/v2/get_object_workflows', requireAuthentication, async function (req, res) {
91
+ try {
92
+ let userSession = req.user;
93
+ const { spaceId, userId } = userSession;
94
+ const result = await getObjectWorkflows(spaceId, userId);
95
+ return res.status(200).send(result);
39
96
  } catch (error) {
40
97
  console.error(error);
41
98
  res.status(200).send({
@@ -9,34 +9,33 @@ const express = require("express");
9
9
  const router = express.Router();
10
10
  const { requireAuthentication } = require("@steedos/auth");
11
11
  const _ = require('lodash');
12
- const Fiber = function(fun){console.log('TODO Fiber...')}
13
-
12
+ const uuflowManagerForInitApproval = require('../manager/uuflowManagerForInitApproval');
13
+ const { getCollection } = require('../utils/collection');
14
14
  router.post('/api/object/workflow/drafts', requireAuthentication, async function (req, res) {
15
15
  try {
16
16
  let userSession = req.user;
17
17
  const { Instances } = req.body;
18
18
  const userId = userSession.userId;
19
19
  const inserted_instances = [];
20
- Fiber(function () {
21
- try {
22
- _.each(Instances, (instance_from_client)=>{
23
- const new_ins_id = uuflowManagerForInitApproval.create_instance(instance_from_client, Object.assign({}, userSession, {_id: userSession.userId}))
20
+ const coll = await getCollection('instances');
21
+ try {
22
+ for (const instance_from_client of Instances) {
23
+ const new_ins_id = await uuflowManagerForInitApproval.create_instance(instance_from_client, Object.assign({}, userSession, {_id: userSession.userId}))
24
24
 
25
- new_ins = Creator.Collections.instances.findOne({ _id: new_ins_id }, { fields: { space: 1, flow: 1, flow_version: 1, form: 1, form_version: 1 } })
25
+ new_ins = await coll.findOne({ _id: new_ins_id }, { fields: { space: 1, flow: 1, flow_version: 1, form: 1, form_version: 1 } })
26
26
 
27
- inserted_instances.push(new_ins)
28
- })
29
- res.status(200).send({
30
- inserts: inserted_instances
31
- });
32
- } catch (error) {
33
- console.error(error);
34
- res.status(200).send({
35
- error: error.message
36
- });
27
+ inserted_instances.push(new_ins)
37
28
  }
38
-
39
- }).run()
29
+
30
+ res.status(200).send({
31
+ inserts: inserted_instances
32
+ });
33
+ } catch (error) {
34
+ console.error(error);
35
+ res.status(200).send({
36
+ error: error.message
37
+ });
38
+ }
40
39
 
41
40
  } catch (error) {
42
41
  console.error(error);
package/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@steedos-labs/plugin-workflow",
3
- "version": "3.0.0-beta.28",
3
+ "version": "3.0.0-beta.29",
4
4
  "main": "package.service.js",
5
5
  "license": "MIT",
6
6
  "scripts": {
7
- "build": "tsc && webpack --config webpack.config.js",
8
7
  "build:watch": "tsc --watch",
9
- "release": "yarn build && npm publish --registry https://registry.npmjs.org && open https://npmmirror.com/sync/@steedos-labs/plugin-workflow && cnpm sync @steedos-labs/plugin-workflow"
8
+ "release": "npm publish --registry https://registry.npmjs.org && open https://npmmirror.com/sync/@steedos-labs/plugin-workflow && cnpm sync @steedos-labs/plugin-workflow"
10
9
  },
11
10
  "dependencies": {
12
11
  "graphql-parse-resolve-info": "^4.12.3",