@haiiro/docuflow 0.0.1 → 0.0.3

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.
Files changed (136) hide show
  1. package/{.twenty/output/manifest.json → manifest.json} +9 -9
  2. package/package.json +15 -3
  3. package/{.twenty/output/src → src}/front-components/docflow-documents.front-component.mjs +24 -24
  4. package/src/front-components/docflow-documents.front-component.tsx +21 -6
  5. package/{.twenty/output/src → src}/front-components/docflow-processes.front-component.mjs +18 -18
  6. package/{.twenty/output/src → src}/front-components/docflow-tasks.front-component.mjs +18 -18
  7. package/{.twenty/output/src → src}/front-components/process-steps.front-component.mjs +19 -19
  8. package/src/front-components/process-steps.front-component.tsx +6 -1
  9. package/{.twenty/output/src → src}/front-components/task-form.front-component.mjs +24 -24
  10. package/{.twenty/output/src → src}/logic-functions/launch-process.logic-function.mjs +14 -0
  11. package/src/logic-functions/launch-process.logic-function.ts +5 -0
  12. package/{.twenty/output/src → src}/logic-functions/save-launch-fields.logic-function.mjs +14 -0
  13. package/src/logic-functions/save-launch-fields.logic-function.ts +5 -0
  14. package/.gitignore +0 -43
  15. package/.nvmrc +0 -1
  16. package/.oxlintrc.json +0 -37
  17. package/.twenty/output/README.md +0 -128
  18. package/.twenty/output/package.json +0 -49
  19. package/.twenty/output/public/cover.svg +0 -15
  20. package/.twenty/output/public/logo.svg +0 -9
  21. package/.twenty/output/src/front-components/docflow-documents.front-component.tsx +0 -516
  22. package/.twenty/output/src/front-components/docflow-processes.front-component.tsx +0 -266
  23. package/.twenty/output/src/front-components/docflow-tasks.front-component.tsx +0 -303
  24. package/.twenty/output/src/front-components/process-steps.front-component.tsx +0 -286
  25. package/.twenty/output/src/front-components/task-form.front-component.tsx +0 -29
  26. package/.twenty/output/src/logic-functions/bitrix-app.logic-function.ts +0 -143
  27. package/.twenty/output/src/logic-functions/bitrix-event.logic-function.ts +0 -54
  28. package/.twenty/output/src/logic-functions/bitrix-install.logic-function.ts +0 -81
  29. package/.twenty/output/src/logic-functions/capture-file.logic-function.ts +0 -32
  30. package/.twenty/output/src/logic-functions/complete-task.logic-function.ts +0 -46
  31. package/.twenty/output/src/logic-functions/hand-out-form-step.logic-function.ts +0 -30
  32. package/.twenty/output/src/logic-functions/health-check.ts +0 -31
  33. package/.twenty/output/src/logic-functions/launch-parameters-step.logic-function.ts +0 -33
  34. package/.twenty/output/src/logic-functions/launch-process.logic-function.ts +0 -96
  35. package/.twenty/output/src/logic-functions/notify-bitrix-assignee.logic-function.ts +0 -85
  36. package/.twenty/output/src/logic-functions/on-attachment-created.logic-function.ts +0 -67
  37. package/.twenty/output/src/logic-functions/on-task-updated.logic-function.ts +0 -71
  38. package/.twenty/output/src/logic-functions/on-workflow-run-updated.logic-function.ts +0 -70
  39. package/.twenty/output/src/logic-functions/save-launch-fields.logic-function.ts +0 -73
  40. package/.twenty/output/src/logic-functions/save-step-settings.logic-function.ts +0 -90
  41. package/.twenty/output/src/logic-functions/save-task-form.logic-function.ts +0 -62
  42. package/.twenty/output/yarn.lock +0 -3292
  43. package/.yarn/install-state.gz +0 -0
  44. package/.yarnrc.yml +0 -1
  45. package/dist/tsconfig.spec.tsbuildinfo +0 -1
  46. package/src/__tests__/api-and-bitrix.test.ts +0 -89
  47. package/src/__tests__/form-fields.test.ts +0 -73
  48. package/src/__tests__/launch-parameters.test.ts +0 -75
  49. package/src/__tests__/step-settings.test.ts +0 -39
  50. package/src/__tests__/workflow-run.test.ts +0 -113
  51. package/src/application-config.ts +0 -58
  52. package/src/fields/company-documents.field.ts +0 -29
  53. package/src/fields/document-author.field.ts +0 -32
  54. package/src/fields/document-counterparty.field.ts +0 -32
  55. package/src/fields/document-folder-child-folders.field.ts +0 -23
  56. package/src/fields/document-folder-documents.field.ts +0 -24
  57. package/src/fields/document-folder-parent-folder.field.ts +0 -31
  58. package/src/fields/document-folder.field.ts +0 -32
  59. package/src/fields/document-responsible.field.ts +0 -32
  60. package/src/fields/member-authored-documents.field.ts +0 -29
  61. package/src/fields/member-responsible-documents.field.ts +0 -29
  62. package/src/fields/task-form-schema.field.ts +0 -21
  63. package/src/fields/task-form-values.field.ts +0 -21
  64. package/src/fields/task-instruction.field.ts +0 -21
  65. package/src/fields/task-section.field.ts +0 -36
  66. package/src/fields/task-workflow-run-id.field.ts +0 -21
  67. package/src/fields/task-workflow-step-id.field.ts +0 -21
  68. package/src/fields/task-workflow-thread-id.field.ts +0 -21
  69. package/src/fields/workflow-launch-fields.field.ts +0 -21
  70. package/src/fields/workflow-section.field.ts +0 -36
  71. package/src/front/api.ts +0 -56
  72. package/src/front/format.ts +0 -60
  73. package/src/front/hooks/use-async.ts +0 -54
  74. package/src/front/hooks/use-workspace-members.ts +0 -22
  75. package/src/front/types.ts +0 -48
  76. package/src/front/ui/components.tsx +0 -436
  77. package/src/front/ui/form-field-inputs.tsx +0 -187
  78. package/src/front/ui/form-fields-builder.tsx +0 -183
  79. package/src/front/ui/task-work-panel.tsx +0 -218
  80. package/src/navigation-menu-items/docflow.navigation-menu-item.ts +0 -15
  81. package/src/objects/document-folder.object.ts +0 -80
  82. package/src/objects/document.object.ts +0 -312
  83. package/src/page-layouts/docflow.page-layout.ts +0 -86
  84. package/src/page-layouts/task-docflow.page-layout-tab.ts +0 -36
  85. package/src/page-layouts/workflow-steps.page-layout-tab.ts +0 -37
  86. package/src/roles/default-role.ts +0 -21
  87. package/src/roles/docflow-member.role.ts +0 -23
  88. package/src/server/bitrix-page.ts +0 -52
  89. package/src/server/bitrix-portal.ts +0 -67
  90. package/src/server/document-capture.ts +0 -102
  91. package/src/server/http-response.ts +0 -18
  92. package/src/server/launch-step.ts +0 -166
  93. package/src/server/process-tasks.ts +0 -116
  94. package/src/server/records.ts +0 -132
  95. package/src/server/task-completion.ts +0 -100
  96. package/src/server/workflow-engine.ts +0 -105
  97. package/src/shared/api/core-graphql.util.ts +0 -98
  98. package/src/shared/api/object-summaries.ts +0 -79
  99. package/src/shared/api/rest-filter.util.ts +0 -37
  100. package/src/shared/api/rest-response.util.ts +0 -43
  101. package/src/shared/bitrix/bitrix-client.ts +0 -114
  102. package/src/shared/bitrix/bitrix.type.ts +0 -25
  103. package/src/shared/bitrix/bitrix.util.ts +0 -139
  104. package/src/shared/docflow.constants.ts +0 -31
  105. package/src/shared/documents/attachment-target.util.ts +0 -64
  106. package/src/shared/engine.ts +0 -11
  107. package/src/shared/form/form-field-builder.util.ts +0 -122
  108. package/src/shared/form/form-field.type.ts +0 -23
  109. package/src/shared/form/form-fields.util.ts +0 -138
  110. package/src/shared/form/step-settings.util.ts +0 -104
  111. package/src/shared/workflow/launch-step.util.ts +0 -56
  112. package/src/shared/workflow/run-target-record.util.ts +0 -70
  113. package/src/shared/workflow/workflow-run.type.ts +0 -35
  114. package/src/shared/workflow/workflow-run.util.ts +0 -174
  115. package/src/universal-identifiers.ts +0 -199
  116. package/src/views/all-document-folders.view.ts +0 -50
  117. package/src/views/all-documents.view.ts +0 -91
  118. package/src/views/documents-by-status.view.ts +0 -76
  119. package/tsconfig.json +0 -37
  120. package/tsconfig.spec.json +0 -9
  121. package/vitest.unit.config.ts +0 -14
  122. /package/{.twenty/output/front-component-shared-dependencies.mjs → front-component-shared-dependencies.mjs} +0 -0
  123. /package/{.twenty/output/src → src}/logic-functions/bitrix-app.logic-function.mjs +0 -0
  124. /package/{.twenty/output/src → src}/logic-functions/bitrix-event.logic-function.mjs +0 -0
  125. /package/{.twenty/output/src → src}/logic-functions/bitrix-install.logic-function.mjs +0 -0
  126. /package/{.twenty/output/src → src}/logic-functions/capture-file.logic-function.mjs +0 -0
  127. /package/{.twenty/output/src → src}/logic-functions/complete-task.logic-function.mjs +0 -0
  128. /package/{.twenty/output/src → src}/logic-functions/hand-out-form-step.logic-function.mjs +0 -0
  129. /package/{.twenty/output/src → src}/logic-functions/health-check.mjs +0 -0
  130. /package/{.twenty/output/src → src}/logic-functions/launch-parameters-step.logic-function.mjs +0 -0
  131. /package/{.twenty/output/src → src}/logic-functions/notify-bitrix-assignee.logic-function.mjs +0 -0
  132. /package/{.twenty/output/src → src}/logic-functions/on-attachment-created.logic-function.mjs +0 -0
  133. /package/{.twenty/output/src → src}/logic-functions/on-task-updated.logic-function.mjs +0 -0
  134. /package/{.twenty/output/src → src}/logic-functions/on-workflow-run-updated.logic-function.mjs +0 -0
  135. /package/{.twenty/output/src → src}/logic-functions/save-step-settings.logic-function.mjs +0 -0
  136. /package/{.twenty/output/src → src}/logic-functions/save-task-form.logic-function.mjs +0 -0
@@ -1,89 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { restAnd, restEq, restIlike, restIn, sanitizeFilterText } from 'src/shared/api/rest-filter.util';
4
- import { readRestList, readRestRecord } from 'src/shared/api/rest-response.util';
5
- import { escapeHtml, parseBitrixGrant, parseBitrixRequest } from 'src/shared/bitrix/bitrix.util';
6
- import { getAttachmentTarget, getDocumentNameFromFile, readFileItems } from 'src/shared/documents/attachment-target.util';
7
- import { getDocflowEngine } from 'src/shared/engine';
8
-
9
- describe('REST helpers', () => {
10
- it('builds filters the REST parser accepts and strips what breaks it', () => {
11
- expect(restAnd(restEq('smbSection', 'DOCFLOW'), undefined, restIlike('title', "ООО (Ромашка), 'x'"))).toBe(
12
- "and(smbSection[eq]:'DOCFLOW',title[ilike]:'%ООО Ромашка x%')",
13
- );
14
- expect(restIn('origin', ['RECORD', 'PROCESS'])).toBe('origin[in]:["RECORD","PROCESS"]');
15
- expect(restAnd()).toBeUndefined();
16
- expect(sanitizeFilterText('a[b]')).toBe('a b');
17
- });
18
-
19
- it('reads records under the single key of data', () => {
20
- expect(readRestList({ data: { tasks: [{ id: '1' }] }, pageInfo: { hasNextPage: true } })).toEqual({
21
- records: [{ id: '1' }],
22
- pageInfo: { hasNextPage: true },
23
- });
24
- expect(readRestRecord({ data: { createTask: { id: '1' } } })).toEqual({ id: '1' });
25
- expect(readRestRecord({ errors: [] })).toBeUndefined();
26
- });
27
- });
28
-
29
- describe('Bitrix24', () => {
30
- it('reads an application launch and an event from form fields', () => {
31
- expect(
32
- parseBitrixRequest({ rawBody: 'AUTH_ID=a&REFRESH_ID=r&member_id=m', query: { DOMAIN: 'acme.bitrix24.ru' } }),
33
- ).toMatchObject({ authId: 'a', refreshId: 'r', memberId: 'm', domain: 'acme.bitrix24.ru' });
34
- expect(
35
- parseBitrixRequest({
36
- rawBody: 'event=ONAPPUNINSTALL&auth%5Bapplication_token%5D=t&auth%5Bmember_id%5D=m',
37
- }),
38
- ).toMatchObject({ event: 'ONAPPUNINSTALL', applicationToken: 't', memberId: 'm' });
39
- });
40
-
41
- it('accepts a grant only with an https portal REST address', () => {
42
- const grant = {
43
- member_id: 'm',
44
- access_token: 'a',
45
- refresh_token: 'r',
46
- expires_in: 3600,
47
- client_endpoint: 'https://acme.bitrix24.ru/rest/',
48
- };
49
-
50
- expect(parseBitrixGrant(grant, 0)).toMatchObject({
51
- memberId: 'm',
52
- portalDomain: 'acme.bitrix24.ru',
53
- expiresAt: 3_600_000,
54
- });
55
- expect(parseBitrixGrant({ ...grant, client_endpoint: 'http://evil/rest/' }, 0)).toBeUndefined();
56
- expect(parseBitrixGrant({ ...grant, client_endpoint: 'https://a.ru/rest/?x=1' }, 0)).toBeUndefined();
57
- });
58
-
59
- it('escapes what goes into the portal page', () => {
60
- expect(escapeHtml('<b>"Акт"</b>')).toBe('&lt;b&gt;&quot;Акт&quot;&lt;/b&gt;');
61
- });
62
- });
63
-
64
- describe('documents', () => {
65
- it('finds the record an attachment belongs to', () => {
66
- expect(getAttachmentTarget({ authorId: 'a', targetTaskId: null, targetCompanyId: 'c' })).toEqual({
67
- objectNameSingular: 'company',
68
- recordId: 'c',
69
- });
70
- expect(getAttachmentTarget({ targetMyDealId: 'd' })).toEqual({ objectNameSingular: 'myDeal', recordId: 'd' });
71
- expect(getAttachmentTarget({ authorId: 'a' })).toBeUndefined();
72
- });
73
-
74
- it('reads files and names a document after its file', () => {
75
- expect(readFileItems([{ fileId: 'f', label: 'Акт.pdf', url: 'u' }, { fileId: '' }])).toEqual([
76
- { fileId: 'f', label: 'Акт.pdf', url: 'u' },
77
- ]);
78
- expect(getDocumentNameFromFile('Договор 12.pdf')).toBe('Договор 12');
79
- expect(getDocumentNameFromFile('.env')).toBe('.env');
80
- });
81
- });
82
-
83
- describe('engine', () => {
84
- it('runs on the app unless the fork is named', () => {
85
- expect(getDocflowEngine(undefined)).toBe('APP');
86
- expect(getDocflowEngine('FORK')).toBe('FORK');
87
- expect(getDocflowEngine('fork')).toBe('APP');
88
- });
89
- });
@@ -1,73 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { type FormField } from 'src/shared/form/form-field.type';
4
- import {
5
- buildFormStepResponse,
6
- buildFormValues,
7
- findMissingRequiredFields,
8
- getFormFieldsError,
9
- isFormValueEmpty,
10
- parseFormFields,
11
- } from 'src/shared/form/form-fields.util';
12
-
13
- const amount: FormField = {
14
- id: 'f1',
15
- name: 'amount',
16
- label: 'Сумма',
17
- type: 'NUMBER',
18
- settings: { smbIsRequired: true },
19
- };
20
- const city: FormField = { id: 'f2', name: 'city', label: 'Город', type: 'TEXT', value: 'Пенза' };
21
- const approver: FormField = {
22
- id: 'f3',
23
- name: 'approver',
24
- label: 'Согласует',
25
- type: 'RECORD',
26
- value: '{{trigger.workspaceMemberId}}',
27
- settings: { smbRole: 'ASSIGNEE', smbIsRequired: true },
28
- };
29
-
30
- describe('form fields', () => {
31
- it('treats blank text, empty lists and a record without id as empty', () => {
32
- expect(isFormValueEmpty(' ')).toBe(true);
33
- expect(isFormValueEmpty([])).toBe(true);
34
- expect(isFormValueEmpty({ id: '' })).toBe(true);
35
- expect(isFormValueEmpty(0)).toBe(false);
36
- expect(isFormValueEmpty({ id: 'x' })).toBe(false);
37
- });
38
-
39
- it('names empty required fields a person fills, never service fields', () => {
40
- expect(
41
- findMissingRequiredFields({ fields: [amount, city, approver], values: {} }).map(
42
- ({ name }) => name,
43
- ),
44
- ).toEqual(['amount']);
45
- });
46
-
47
- it('builds values from typed and preset values of fillable fields', () => {
48
- expect(buildFormValues({ fields: [amount, city, approver], values: { amount: 5 } })).toEqual({
49
- amount: 5,
50
- city: 'Пенза',
51
- });
52
- });
53
-
54
- it('answers a step with every field, service ones included', () => {
55
- expect(
56
- buildFormStepResponse({
57
- fields: [amount, { ...approver, value: { id: 'member-1' } }],
58
- values: { amount: 7 },
59
- }),
60
- ).toEqual({ amount: 7, approver: { id: 'member-1' } });
61
- });
62
-
63
- it('rejects repeated and malformed field names', () => {
64
- expect(getFormFieldsError([amount, amount])).toContain('повторяется');
65
- expect(getFormFieldsError([{ ...amount, name: '1st' }])).toContain('латинской');
66
- expect(getFormFieldsError([amount, city])).toBeUndefined();
67
- });
68
-
69
- it('keeps only well-formed fields from stored JSON', () => {
70
- expect(parseFormFields([amount, { name: 'x' }, null, 'text'])).toEqual([amount]);
71
- expect(parseFormFields({ fields: [] })).toEqual([]);
72
- });
73
- });
@@ -1,75 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { type FormField } from 'src/shared/form/form-field.type';
4
- import {
5
- applyBuilderFieldKind,
6
- buildFieldNameFromLabel,
7
- buildFieldsOutputSchema,
8
- buildUniqueFieldName,
9
- getBuilderFieldKind,
10
- } from 'src/shared/form/form-field-builder.util';
11
- import {
12
- buildLaunchParameters,
13
- findLaunchParametersStep,
14
- } from 'src/shared/workflow/launch-step.util';
15
-
16
- const contract: FormField = { id: 'f1', name: 'contract', label: 'Договор', type: 'TEXT' };
17
- const manager: FormField = {
18
- id: 'f2',
19
- name: 'manager',
20
- label: 'Руководитель',
21
- type: 'RECORD',
22
- settings: { objectName: 'workspaceMember' },
23
- };
24
-
25
- describe('launch parameters', () => {
26
- it('names a field after its label in latin letters', () => {
27
- expect(buildFieldNameFromLabel('Сумма договора')).toBe('summaDogovora');
28
- expect(buildFieldNameFromLabel('2 подписи')).toBe('field2Podpisi');
29
- expect(buildUniqueFieldName('Сумма', ['summa', 'summa2'])).toBe('summa3');
30
- });
31
-
32
- it('switches a field between the builder kinds', () => {
33
- const company = applyBuilderFieldKind(manager, 'COMPANY');
34
-
35
- expect(company.settings).toEqual({ objectName: 'company' });
36
- expect(getBuilderFieldKind(company)).toBe('COMPANY');
37
- expect(getBuilderFieldKind(applyBuilderFieldKind(company, 'DATE'))).toBe('DATE');
38
- });
39
-
40
- it('sends every parameter, an empty one as null', () => {
41
- expect(
42
- buildLaunchParameters({ fields: [contract, manager], values: { contract: '', manager: { id: 'm' } } }),
43
- ).toEqual({ contract: null, manager: { id: 'm' } });
44
- });
45
-
46
- it('describes the parameters as variables of the step', () => {
47
- expect(buildFieldsOutputSchema([contract, manager])).toEqual({
48
- contract: { isLeaf: true, type: 'string', label: 'Договор', value: null },
49
- manager: {
50
- isLeaf: false,
51
- type: 'object',
52
- label: 'Руководитель',
53
- value: { id: { isLeaf: true, type: 'string', label: 'Id', value: null } },
54
- },
55
- });
56
- });
57
-
58
- it('finds the launch step by the parameters it takes', () => {
59
- const launchStep = {
60
- id: 'launch',
61
- name: 'Запуск с параметрами',
62
- type: 'LOGIC_FUNCTION',
63
- settings: {
64
- input: {
65
- logicFunctionId: 'fn',
66
- logicFunctionInput: { parameters: '{{trigger.smbDocflowParameters}}' },
67
- },
68
- },
69
- };
70
- const codeStep = { ...launchStep, id: 'code', settings: { input: { logicFunctionInput: {} } } };
71
-
72
- expect(findLaunchParametersStep([codeStep, launchStep])?.id).toBe('launch');
73
- expect(findLaunchParametersStep([codeStep])).toBeUndefined();
74
- });
75
- });
@@ -1,39 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { type FormField } from 'src/shared/form/form-field.type';
4
- import { applyStepSettings, readStepSettings } from 'src/shared/form/step-settings.util';
5
-
6
- const amount: FormField = { id: 'f1', name: 'amount', label: 'Сумма', type: 'NUMBER' };
7
- const createId = () => 'new-id';
8
-
9
- describe('step settings', () => {
10
- it('writes the assignee, the deadline and the required marks into the fields', () => {
11
- const fields = applyStepSettings({
12
- fields: [amount],
13
- settings: { assignee: 'member-1', dueInDays: 2, requiredFieldIds: ['f1'] },
14
- createId,
15
- });
16
-
17
- expect(readStepSettings(fields)).toEqual({
18
- assignee: 'member-1',
19
- dueInDays: 2,
20
- requiredFieldIds: ['f1'],
21
- });
22
- expect(fields.find(({ id }) => id === 'f1')?.settings).toEqual({ smbIsRequired: true });
23
- });
24
-
25
- it('removes the role fields and the required marks when cleared', () => {
26
- const configured = applyStepSettings({
27
- fields: [amount],
28
- settings: { assignee: 'member-1', dueInDays: 2, requiredFieldIds: ['f1'] },
29
- createId,
30
- });
31
- const cleared = applyStepSettings({
32
- fields: configured,
33
- settings: { assignee: null, dueInDays: null, requiredFieldIds: [] },
34
- createId,
35
- });
36
-
37
- expect(cleared).toEqual([{ ...amount, settings: {} }]);
38
- });
39
- });
@@ -1,113 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { getRunTargetRecord } from 'src/shared/workflow/run-target-record.util';
4
- import { type WorkflowRunState } from 'src/shared/workflow/workflow-run.type';
5
- import {
6
- getFormStepTaskRequests,
7
- resolveStepVariable,
8
- } from 'src/shared/workflow/workflow-run.util';
9
-
10
- const MANAGER_ID = '22222222-2222-4222-8222-222222222222';
11
- const STARTER_ID = '55555555-5555-4555-8555-555555555555';
12
- const DEAL_ID = '33333333-3333-4333-8333-333333333333';
13
- const NOW = new Date('2026-10-15T10:00:00.000Z');
14
-
15
- const parametersStep = {
16
- id: 'parameters',
17
- name: 'Параметры',
18
- type: 'FORM',
19
- settings: { input: [{ id: 'p1', name: 'manager', label: 'Руководитель', type: 'RECORD' }] },
20
- };
21
-
22
- const approveStep = {
23
- id: 'approve',
24
- name: 'Согласовать',
25
- type: 'FORM',
26
- settings: {
27
- input: [
28
- { id: 'f1', name: 'amount', label: 'Сумма', type: 'NUMBER', settings: { smbIsRequired: true } },
29
- {
30
- id: 'f2',
31
- name: 'assignee',
32
- label: 'Исполнитель',
33
- type: 'RECORD',
34
- value: '{{parameters.manager}}',
35
- settings: { smbRole: 'ASSIGNEE' },
36
- },
37
- { id: 'f3', name: 'due', label: 'Срок', type: 'DATE', settings: { smbRole: 'DUE_AT', smbDueInDays: 2 } },
38
- ],
39
- },
40
- };
41
-
42
- const buildState = (approveInfo: object): WorkflowRunState => ({
43
- flow: {
44
- trigger: { type: 'MANUAL', nextStepIds: ['parameters'] },
45
- steps: [parametersStep, approveStep],
46
- },
47
- stepInfos: {
48
- trigger: { status: 'SUCCESS', result: { smbDocflowParameters: { manager: { id: MANAGER_ID } } } },
49
- parameters: { status: 'SUCCESS', result: { manager: { id: MANAGER_ID } } },
50
- approve: approveInfo,
51
- },
52
- });
53
-
54
- describe('workflow run', () => {
55
- it('reads a variable from what an earlier step returned', () => {
56
- const { stepInfos } = buildState({});
57
-
58
- expect(resolveStepVariable('{{parameters.manager}}', stepInfos)).toEqual({ id: MANAGER_ID });
59
- expect(resolveStepVariable('{{parameters.missing.id}}', stepInfos)).toBeUndefined();
60
- expect(resolveStepVariable('Привет {{x}}', stepInfos)).toBe('Привет {{x}}');
61
- });
62
-
63
- it('hands a waiting step out to the assignee picked at launch', () => {
64
- const [request] = getFormStepTaskRequests({
65
- state: buildState({ status: 'PENDING', threadId: 'thread-1' }),
66
- runStarterWorkspaceMemberId: STARTER_ID,
67
- now: NOW,
68
- });
69
-
70
- expect(request).toMatchObject({
71
- stepId: 'approve',
72
- threadId: 'thread-1',
73
- assigneeWorkspaceMemberId: MANAGER_ID,
74
- dueAt: '2026-10-17T10:00:00.000Z',
75
- });
76
- expect(request.fields.map(({ name }) => name)).toEqual(['amount']);
77
- });
78
-
79
- it('gives an unassigned step of a manual run to the starter, even the first one', () => {
80
- const state: WorkflowRunState = {
81
- flow: { trigger: { type: 'MANUAL', nextStepIds: ['parameters'] }, steps: [parametersStep] },
82
- stepInfos: { parameters: { status: 'PENDING', threadId: 't' } },
83
- };
84
-
85
- expect(
86
- getFormStepTaskRequests({ state, runStarterWorkspaceMemberId: STARTER_ID, now: NOW }),
87
- ).toMatchObject([{ stepId: 'parameters', assigneeWorkspaceMemberId: STARTER_ID }]);
88
- expect(
89
- getFormStepTaskRequests({
90
- state: { ...state, flow: { ...state.flow, trigger: { type: 'CRON' } } },
91
- runStarterWorkspaceMemberId: STARTER_ID,
92
- now: NOW,
93
- }),
94
- ).toEqual([]);
95
- });
96
-
97
- it('skips a step that is answered or not opened yet', () => {
98
- for (const info of [{ status: 'SUCCESS', threadId: 't' }, { status: 'PENDING' }]) {
99
- expect(
100
- getFormStepTaskRequests({ state: buildState(info), runStarterWorkspaceMemberId: null, now: NOW }),
101
- ).toEqual([]);
102
- }
103
- });
104
-
105
- it('links a task of a deal event to that deal', () => {
106
- expect(
107
- getRunTargetRecord({
108
- flow: { trigger: { type: 'DATABASE_EVENT', settings: { eventName: 'opportunity.created' } } },
109
- stepInfos: { trigger: { result: { recordId: DEAL_ID } } },
110
- }),
111
- ).toEqual({ targetFieldName: 'targetOpportunityId', recordId: DEAL_ID });
112
- });
113
- });
@@ -1,58 +0,0 @@
1
- import { defineApplication, FieldType } from 'twenty-sdk/define';
2
-
3
- import {
4
- BITRIX_CLIENT_ID_VARIABLE_UNIVERSAL_IDENTIFIER,
5
- BITRIX_CLIENT_SECRET_VARIABLE_UNIVERSAL_IDENTIFIER,
6
- CRM_PUBLIC_URL_VARIABLE_UNIVERSAL_IDENTIFIER,
7
- DOCFLOW_ENGINE_VARIABLE_UNIVERSAL_IDENTIFIER,
8
- } from 'src/universal-identifiers';
9
-
10
- // The same identifier stands in the fork's module manifest
11
- // (twenty-front/src/modules/smb-docflow/manifest.ts): one application, whether
12
- // it runs on the NorDiv server or on its own.
13
- export const APPLICATION_UNIVERSAL_IDENTIFIER =
14
- '0b7d5d2e-8c4f-4a59-9a43-2f6f1c3e9d21';
15
-
16
- export default defineApplication({
17
- universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
18
- displayName: 'Документооборот',
19
- description:
20
- 'Задания с полями для заполнения, процессы с запуском по параметрам, документы со всех записей, приложение Битрикс24',
21
- author: 'NorDiv',
22
- logo: 'public/logo.svg',
23
- galleryImages: ['public/cover.svg'],
24
- applicationVariables: {
25
- DOCFLOW_ENGINE: {
26
- universalIdentifier: DOCFLOW_ENGINE_VARIABLE_UNIVERSAL_IDENTIFIER,
27
- label: 'Кто выполняет серверную работу',
28
- description:
29
- 'APP — серверную работу делают функции приложения. FORK — её делает сервер NorDiv; функции приложения тогда ничего не делают, чтобы не было двойной работы.',
30
- type: FieldType.SELECT,
31
- options: [
32
- { label: 'Функции приложения', value: 'APP' },
33
- { label: 'Сервер NorDiv', value: 'FORK' },
34
- ],
35
- value: 'APP',
36
- isSecret: false,
37
- },
38
- CRM_PUBLIC_URL: {
39
- universalIdentifier: CRM_PUBLIC_URL_VARIABLE_UNIVERSAL_IDENTIFIER,
40
- label: 'Адрес CRM компании',
41
- description:
42
- 'Например https://acme.example.ru — ссылки из Битрикс24 ведут сюда.',
43
- isSecret: false,
44
- },
45
- BITRIX_CLIENT_ID: {
46
- universalIdentifier: BITRIX_CLIENT_ID_VARIABLE_UNIVERSAL_IDENTIFIER,
47
- label: 'Битрикс24: код приложения (client_id)',
48
- description: 'Из карточки локального приложения на портале.',
49
- isSecret: false,
50
- },
51
- BITRIX_CLIENT_SECRET: {
52
- universalIdentifier: BITRIX_CLIENT_SECRET_VARIABLE_UNIVERSAL_IDENTIFIER,
53
- label: 'Битрикс24: ключ приложения (client_secret)',
54
- description: 'Из карточки локального приложения на портале.',
55
- isSecret: true,
56
- },
57
- },
58
- });
@@ -1,29 +0,0 @@
1
- import {
2
- FieldType,
3
- RelationType,
4
- STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
5
- defineField,
6
- } from 'twenty-sdk/define';
7
-
8
- import {
9
- COMPANY_DOCUMENTS_UNIVERSAL_IDENTIFIER,
10
- DOCUMENT_COUNTERPARTY_UNIVERSAL_IDENTIFIER,
11
- DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
12
- } from 'src/universal-identifiers';
13
-
14
- export default defineField({
15
- universalIdentifier: COMPANY_DOCUMENTS_UNIVERSAL_IDENTIFIER,
16
- objectUniversalIdentifier:
17
- STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
18
- type: FieldType.RELATION,
19
- name: 'smbDocuments',
20
- label: 'Документы',
21
- icon: 'IconFiles',
22
- relationTargetObjectMetadataUniversalIdentifier:
23
- DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
24
- relationTargetFieldMetadataUniversalIdentifier:
25
- DOCUMENT_COUNTERPARTY_UNIVERSAL_IDENTIFIER,
26
- universalSettings: {
27
- relationType: RelationType.ONE_TO_MANY,
28
- },
29
- });
@@ -1,32 +0,0 @@
1
- import {
2
- FieldType,
3
- OnDeleteAction,
4
- RelationType,
5
- STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
6
- defineField,
7
- } from 'twenty-sdk/define';
8
-
9
- import {
10
- DOCUMENT_AUTHOR_UNIVERSAL_IDENTIFIER,
11
- DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
12
- MEMBER_AUTHORED_DOCUMENTS_UNIVERSAL_IDENTIFIER,
13
- } from 'src/universal-identifiers';
14
-
15
- export default defineField({
16
- universalIdentifier: DOCUMENT_AUTHOR_UNIVERSAL_IDENTIFIER,
17
- objectUniversalIdentifier: DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
18
- type: FieldType.RELATION,
19
- name: 'author',
20
- label: 'Автор',
21
- icon: 'IconUser',
22
- isNullable: true,
23
- relationTargetObjectMetadataUniversalIdentifier:
24
- STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.workspaceMember.universalIdentifier,
25
- relationTargetFieldMetadataUniversalIdentifier:
26
- MEMBER_AUTHORED_DOCUMENTS_UNIVERSAL_IDENTIFIER,
27
- universalSettings: {
28
- relationType: RelationType.MANY_TO_ONE,
29
- onDelete: OnDeleteAction.SET_NULL,
30
- joinColumnName: 'authorId',
31
- },
32
- });
@@ -1,32 +0,0 @@
1
- import {
2
- FieldType,
3
- OnDeleteAction,
4
- RelationType,
5
- STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
6
- defineField,
7
- } from 'twenty-sdk/define';
8
-
9
- import {
10
- COMPANY_DOCUMENTS_UNIVERSAL_IDENTIFIER,
11
- DOCUMENT_COUNTERPARTY_UNIVERSAL_IDENTIFIER,
12
- DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
13
- } from 'src/universal-identifiers';
14
-
15
- export default defineField({
16
- universalIdentifier: DOCUMENT_COUNTERPARTY_UNIVERSAL_IDENTIFIER,
17
- objectUniversalIdentifier: DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
18
- type: FieldType.RELATION,
19
- name: 'counterparty',
20
- label: 'Контрагент',
21
- icon: 'IconBuildingSkyscraper',
22
- isNullable: true,
23
- relationTargetObjectMetadataUniversalIdentifier:
24
- STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
25
- relationTargetFieldMetadataUniversalIdentifier:
26
- COMPANY_DOCUMENTS_UNIVERSAL_IDENTIFIER,
27
- universalSettings: {
28
- relationType: RelationType.MANY_TO_ONE,
29
- onDelete: OnDeleteAction.SET_NULL,
30
- joinColumnName: 'counterpartyId',
31
- },
32
- });
@@ -1,23 +0,0 @@
1
- import { FieldType, RelationType, defineField } from 'twenty-sdk/define';
2
-
3
- import {
4
- DOCUMENT_FOLDER_CHILD_FOLDERS_UNIVERSAL_IDENTIFIER,
5
- DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
6
- DOCUMENT_FOLDER_PARENT_FOLDER_UNIVERSAL_IDENTIFIER,
7
- } from 'src/universal-identifiers';
8
-
9
- export default defineField({
10
- universalIdentifier: DOCUMENT_FOLDER_CHILD_FOLDERS_UNIVERSAL_IDENTIFIER,
11
- objectUniversalIdentifier: DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
12
- type: FieldType.RELATION,
13
- name: 'childFolders',
14
- label: 'Вложенные папки',
15
- icon: 'IconFolders',
16
- relationTargetObjectMetadataUniversalIdentifier:
17
- DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
18
- relationTargetFieldMetadataUniversalIdentifier:
19
- DOCUMENT_FOLDER_PARENT_FOLDER_UNIVERSAL_IDENTIFIER,
20
- universalSettings: {
21
- relationType: RelationType.ONE_TO_MANY,
22
- },
23
- });
@@ -1,24 +0,0 @@
1
- import { FieldType, RelationType, defineField } from 'twenty-sdk/define';
2
-
3
- import {
4
- DOCUMENT_FOLDER_DOCUMENTS_UNIVERSAL_IDENTIFIER,
5
- DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
6
- DOCUMENT_FOLDER_UNIVERSAL_IDENTIFIER,
7
- DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
8
- } from 'src/universal-identifiers';
9
-
10
- export default defineField({
11
- universalIdentifier: DOCUMENT_FOLDER_DOCUMENTS_UNIVERSAL_IDENTIFIER,
12
- objectUniversalIdentifier: DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
13
- type: FieldType.RELATION,
14
- name: 'documents',
15
- label: 'Документы',
16
- icon: 'IconFiles',
17
- relationTargetObjectMetadataUniversalIdentifier:
18
- DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
19
- relationTargetFieldMetadataUniversalIdentifier:
20
- DOCUMENT_FOLDER_UNIVERSAL_IDENTIFIER,
21
- universalSettings: {
22
- relationType: RelationType.ONE_TO_MANY,
23
- },
24
- });
@@ -1,31 +0,0 @@
1
- import {
2
- FieldType,
3
- OnDeleteAction,
4
- RelationType,
5
- defineField,
6
- } from 'twenty-sdk/define';
7
-
8
- import {
9
- DOCUMENT_FOLDER_CHILD_FOLDERS_UNIVERSAL_IDENTIFIER,
10
- DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
11
- DOCUMENT_FOLDER_PARENT_FOLDER_UNIVERSAL_IDENTIFIER,
12
- } from 'src/universal-identifiers';
13
-
14
- export default defineField({
15
- universalIdentifier: DOCUMENT_FOLDER_PARENT_FOLDER_UNIVERSAL_IDENTIFIER,
16
- objectUniversalIdentifier: DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
17
- type: FieldType.RELATION,
18
- name: 'parentFolder',
19
- label: 'Родительская папка',
20
- icon: 'IconFolderUp',
21
- isNullable: true,
22
- relationTargetObjectMetadataUniversalIdentifier:
23
- DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
24
- relationTargetFieldMetadataUniversalIdentifier:
25
- DOCUMENT_FOLDER_CHILD_FOLDERS_UNIVERSAL_IDENTIFIER,
26
- universalSettings: {
27
- relationType: RelationType.MANY_TO_ONE,
28
- onDelete: OnDeleteAction.SET_NULL,
29
- joinColumnName: 'parentFolderId',
30
- },
31
- });
@@ -1,32 +0,0 @@
1
- import {
2
- FieldType,
3
- OnDeleteAction,
4
- RelationType,
5
- defineField,
6
- } from 'twenty-sdk/define';
7
-
8
- import {
9
- DOCUMENT_FOLDER_DOCUMENTS_UNIVERSAL_IDENTIFIER,
10
- DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
11
- DOCUMENT_FOLDER_UNIVERSAL_IDENTIFIER,
12
- DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
13
- } from 'src/universal-identifiers';
14
-
15
- export default defineField({
16
- universalIdentifier: DOCUMENT_FOLDER_UNIVERSAL_IDENTIFIER,
17
- objectUniversalIdentifier: DOCUMENT_OBJECT_UNIVERSAL_IDENTIFIER,
18
- type: FieldType.RELATION,
19
- name: 'folder',
20
- label: 'Папка',
21
- icon: 'IconFolder',
22
- isNullable: true,
23
- relationTargetObjectMetadataUniversalIdentifier:
24
- DOCUMENT_FOLDER_OBJECT_UNIVERSAL_IDENTIFIER,
25
- relationTargetFieldMetadataUniversalIdentifier:
26
- DOCUMENT_FOLDER_DOCUMENTS_UNIVERSAL_IDENTIFIER,
27
- universalSettings: {
28
- relationType: RelationType.MANY_TO_ONE,
29
- onDelete: OnDeleteAction.SET_NULL,
30
- joinColumnName: 'folderId',
31
- },
32
- });