@haiiro/docuflow 0.0.12 → 0.0.15

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 (40) hide show
  1. package/README.md +41 -9
  2. package/bin/install.mjs +69 -3
  3. package/manifest.json +114 -18
  4. package/package.json +6 -3
  5. package/public/app/docflow-app.css +2 -0
  6. package/public/app/docflow-app.js +95 -0
  7. package/src/front-components/docflow-documents.front-component.mjs +41 -41
  8. package/src/front-components/docflow-documents.front-component.tsx +49 -32
  9. package/src/front-components/docflow-processes.front-component.mjs +34 -34
  10. package/src/front-components/docflow-processes.front-component.tsx +24 -11
  11. package/src/front-components/docflow-tasks.front-component.mjs +40 -39
  12. package/src/front-components/docflow-tasks.front-component.tsx +19 -14
  13. package/src/front-components/launch-process.front-component.mjs +75 -0
  14. package/src/front-components/launch-process.front-component.tsx +270 -0
  15. package/src/front-components/process-steps.front-component.mjs +26 -26
  16. package/src/front-components/process-steps.front-component.tsx +56 -10
  17. package/src/front-components/task-form.front-component.mjs +45 -44
  18. package/src/front-components/task-form.front-component.tsx +12 -1
  19. package/src/logic-functions/bitrix-app.logic-function.mjs +186 -88
  20. package/src/logic-functions/bitrix-app.logic-function.ts +24 -61
  21. package/src/logic-functions/bitrix-event.logic-function.mjs +22 -1
  22. package/src/logic-functions/bitrix-event.logic-function.ts +2 -2
  23. package/src/logic-functions/bitrix-install.logic-function.mjs +26 -12
  24. package/src/logic-functions/bitrix-install.logic-function.ts +2 -5
  25. package/src/logic-functions/complete-task.logic-function.mjs +22 -1
  26. package/src/logic-functions/complete-task.logic-function.ts +2 -2
  27. package/src/logic-functions/docflow-app-session.logic-function.mjs +60064 -0
  28. package/src/logic-functions/docflow-app-session.logic-function.ts +54 -0
  29. package/src/logic-functions/docflow-app.logic-function.mjs +60149 -0
  30. package/src/logic-functions/docflow-app.logic-function.ts +38 -0
  31. package/src/logic-functions/launch-process.logic-function.mjs +134 -40
  32. package/src/logic-functions/launch-process.logic-function.ts +85 -24
  33. package/src/logic-functions/publish-process.logic-function.mjs +32 -11
  34. package/src/logic-functions/publish-process.logic-function.ts +8 -4
  35. package/src/logic-functions/save-launch-fields.logic-function.mjs +194 -17
  36. package/src/logic-functions/save-launch-fields.logic-function.ts +26 -8
  37. package/src/logic-functions/save-step-settings.logic-function.mjs +24 -2
  38. package/src/logic-functions/save-step-settings.logic-function.ts +2 -2
  39. package/src/logic-functions/save-task-form.logic-function.mjs +22 -1
  40. package/src/logic-functions/save-task-form.logic-function.ts +2 -2
@@ -1734,6 +1734,66 @@ var CREATE_DRAFT_FROM_WORKFLOW_VERSION_MUTATION = `mutation CreateDraftFromWorkf
1734
1734
  createDraftFromWorkflowVersion(input: $input) { id }
1735
1735
  }`;
1736
1736
 
1737
+ // src/shared/api/object-summaries.ts
1738
+ import { MetadataApiClient } from "twenty-client-sdk/metadata";
1739
+ var OBJECTS_PAGE_SIZE = 100;
1740
+ var listLaunchObjects = async () => {
1741
+ const client = new MetadataApiClient();
1742
+ const launchObjects = [];
1743
+ let after;
1744
+ do {
1745
+ const { objects } = await client.query({
1746
+ objects: {
1747
+ __args: {
1748
+ paging: { first: OBJECTS_PAGE_SIZE, ...after !== void 0 ? { after } : {} },
1749
+ filter: { isActive: { is: true }, isSystem: { is: false } }
1750
+ },
1751
+ pageInfo: { hasNextPage: true, endCursor: true },
1752
+ edges: {
1753
+ node: {
1754
+ id: true,
1755
+ nameSingular: true,
1756
+ namePlural: true,
1757
+ labelSingular: true,
1758
+ icon: true,
1759
+ fieldsList: {
1760
+ id: true,
1761
+ name: true,
1762
+ label: true,
1763
+ type: true,
1764
+ icon: true,
1765
+ isActive: true,
1766
+ isSystem: true,
1767
+ relation: { type: true }
1768
+ }
1769
+ }
1770
+ }
1771
+ }
1772
+ });
1773
+ for (const { node } of objects.edges) {
1774
+ launchObjects.push({
1775
+ id: node.id,
1776
+ nameSingular: node.nameSingular,
1777
+ namePlural: node.namePlural,
1778
+ labelSingular: node.labelSingular,
1779
+ icon: node.icon,
1780
+ fields: (node.fieldsList ?? []).map((field) => ({
1781
+ id: field.id,
1782
+ name: field.name,
1783
+ label: field.label,
1784
+ type: String(field.type),
1785
+ icon: field.icon,
1786
+ isActive: field.isActive,
1787
+ isSystem: field.isSystem,
1788
+ relationType: field.relation?.type ?? null
1789
+ }))
1790
+ });
1791
+ }
1792
+ after = objects.pageInfo.hasNextPage === true ? objects.pageInfo.endCursor ?? void 0 : void 0;
1793
+ } while (after !== void 0);
1794
+ return launchObjects;
1795
+ };
1796
+
1737
1797
  // src/shared/api/rest-response.util.ts
1738
1798
  var readDataValue = (response) => {
1739
1799
  const data = response?.data;
@@ -1759,6 +1819,7 @@ var readRestRecord = (response) => {
1759
1819
  var DOCFLOW_LAUNCH_PARAMETERS_KEY = "smbDocflowParameters";
1760
1820
  var LAUNCH_PARAMETERS_STEP_INPUT = `{{trigger.${DOCFLOW_LAUNCH_PARAMETERS_KEY}}}`;
1761
1821
  var LAUNCH_TRIGGER_NAME = "\u0417\u0430\u043F\u0443\u0441\u043A \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438";
1822
+ var LAUNCH_RECORD_KEY = "record";
1762
1823
  var FORM_FIELD_ROLE_SETTING = "smbRole";
1763
1824
  var TRIGGER_STEP_ID = "trigger";
1764
1825
 
@@ -1801,6 +1862,8 @@ var isLaunchParametersStep = (step) => {
1801
1862
  var isLaunchTrigger = (trigger) => trigger?.type === "WEBHOOK" && trigger.name === LAUNCH_TRIGGER_NAME;
1802
1863
  var findLaunchParametersStep = (steps) => (steps ?? []).find(isLaunchParametersStep);
1803
1864
  var parseLaunchFields = (value) => getFillableFormFields(parseFormFields(value));
1865
+ var parseLaunchObject = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : void 0;
1866
+ var getLaunchFieldsError = (fields) => fields.some(({ name }) => name === LAUNCH_RECORD_KEY) ? `\u0418\u043C\u044F \u043F\u043E\u043B\u044F \xAB${LAUNCH_RECORD_KEY}\xBB \u0437\u0430\u043D\u044F\u0442\u043E: \u0442\u0430\u043A \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u043F\u043E\u043B\u0443\u0447\u0430\u0435\u0442 \u0437\u0430\u043F\u0438\u0441\u044C, \u043D\u0430 \u043A\u043E\u0442\u043E\u0440\u043E\u0439 \u0435\u0433\u043E \u0437\u0430\u043F\u0443\u0441\u0442\u0438\u043B\u0438` : void 0;
1804
1867
 
1805
1868
  // src/shared/form/form-field-builder.util.ts
1806
1869
  var OUTPUT_LEAF_TYPES = {
@@ -1831,6 +1894,96 @@ var buildFieldsExpectedBody = (fields) => Object.fromEntries(
1831
1894
  ])
1832
1895
  );
1833
1896
 
1897
+ // src/shared/workflow/launch-record.util.ts
1898
+ var COMPOSITE_PARTS = {
1899
+ ACTOR: [["source", "SELECT"], ["workspaceMemberId", "UUID"], ["name", "TEXT"], ["context", "RAW_JSON"]],
1900
+ ADDRESS: [
1901
+ ["addressStreet1", "TEXT"],
1902
+ ["addressStreet2", "TEXT"],
1903
+ ["addressCity", "TEXT"],
1904
+ ["addressPostcode", "TEXT"],
1905
+ ["addressState", "TEXT"],
1906
+ ["addressCountry", "TEXT"],
1907
+ ["addressLat", "NUMERIC"],
1908
+ ["addressLng", "NUMERIC"]
1909
+ ],
1910
+ CURRENCY: [["amountMicros", "NUMERIC"], ["currencyCode", "TEXT"]],
1911
+ EMAILS: [["primaryEmail", "TEXT"], ["additionalEmails", "RAW_JSON"]],
1912
+ FULL_NAME: [["firstName", "TEXT"], ["lastName", "TEXT"]],
1913
+ LINKS: [["primaryLinkLabel", "TEXT"], ["primaryLinkUrl", "TEXT"], ["secondaryLinks", "RAW_JSON"]],
1914
+ PHONES: [
1915
+ ["primaryPhoneNumber", "TEXT"],
1916
+ ["primaryPhoneCountryCode", "TEXT"],
1917
+ ["primaryPhoneCallingCode", "TEXT"],
1918
+ ["additionalPhones", "RAW_JSON"]
1919
+ ],
1920
+ RICH_TEXT: [["blocknote", "TEXT"], ["markdown", "TEXT"]]
1921
+ };
1922
+ var EXCLUDED_SYSTEM_FIELDS = ["searchVector", "position"];
1923
+ var toTitleCase = (camelCase) => camelCase.replace(/([A-Z])/g, " $1").replace(/^./, (first) => first.toUpperCase());
1924
+ var buildFieldSchema = (field) => {
1925
+ const icon = field.icon ?? void 0;
1926
+ if (field.type === "RELATION") {
1927
+ return [
1928
+ [
1929
+ `${field.name}Id`,
1930
+ {
1931
+ isLeaf: true,
1932
+ icon,
1933
+ type: "UUID",
1934
+ label: toTitleCase(`${field.name}Id`),
1935
+ value: null,
1936
+ fieldMetadataId: field.id,
1937
+ isCompositeSubField: false
1938
+ }
1939
+ ]
1940
+ ];
1941
+ }
1942
+ const parts = COMPOSITE_PARTS[field.type];
1943
+ if (parts !== void 0) {
1944
+ return [
1945
+ [
1946
+ field.name,
1947
+ {
1948
+ isLeaf: false,
1949
+ icon,
1950
+ type: field.type,
1951
+ label: field.label,
1952
+ fieldMetadataId: field.id,
1953
+ value: Object.fromEntries(
1954
+ parts.map(([name, type]) => [
1955
+ name,
1956
+ { isLeaf: true, type, label: toTitleCase(name), value: null, fieldMetadataId: field.id, isCompositeSubField: true }
1957
+ ])
1958
+ )
1959
+ }
1960
+ ]
1961
+ ];
1962
+ }
1963
+ return [
1964
+ [
1965
+ field.name,
1966
+ { isLeaf: true, icon, type: field.type, label: field.label, value: null, fieldMetadataId: field.id, isCompositeSubField: false }
1967
+ ]
1968
+ ];
1969
+ };
1970
+ var isRecordVariable = (field) => field.isActive !== false && !(field.isSystem === true && EXCLUDED_SYSTEM_FIELDS.includes(field.name)) && field.type !== "MORPH_RELATION" && (field.type !== "RELATION" || field.relationType === "MANY_TO_ONE");
1971
+ var buildLaunchRecordOutputSchema = (object) => ({
1972
+ isLeaf: false,
1973
+ icon: object.icon ?? void 0,
1974
+ label: object.labelSingular,
1975
+ value: {
1976
+ object: {
1977
+ icon: object.icon ?? void 0,
1978
+ label: object.labelSingular,
1979
+ objectMetadataId: object.id,
1980
+ fieldIdName: "id"
1981
+ },
1982
+ fields: Object.fromEntries(object.fields.filter(isRecordVariable).flatMap(buildFieldSchema)),
1983
+ _outputSchemaType: "RECORD"
1984
+ }
1985
+ });
1986
+
1834
1987
  // node_modules/twenty-client-sdk/dist/chunk-Dqa2HsxW.mjs
1835
1988
  var e = Object.create;
1836
1989
  var t = Object.defineProperty;
@@ -61230,7 +61383,7 @@ var MANUAL_TRIGGER = {
61230
61383
  },
61231
61384
  nextStepIds: []
61232
61385
  };
61233
- var buildLaunchTrigger = (base, fields) => ({
61386
+ var buildLaunchTrigger = (base, fields, launchObject) => ({
61234
61387
  name: LAUNCH_TRIGGER_NAME,
61235
61388
  type: "WEBHOOK",
61236
61389
  position: base.position ?? { x: 0, y: 0 },
@@ -61238,12 +61391,19 @@ var buildLaunchTrigger = (base, fields) => ({
61238
61391
  settings: {
61239
61392
  httpMethod: "POST",
61240
61393
  authentication: "API_KEY",
61241
- expectedBody: buildFieldsExpectedBody(fields),
61242
- outputSchema: buildFieldsOutputSchema(fields)
61394
+ expectedBody: {
61395
+ ...buildFieldsExpectedBody(fields),
61396
+ ...launchObject !== void 0 ? { [LAUNCH_RECORD_KEY]: { id: "" } } : {}
61397
+ },
61398
+ outputSchema: {
61399
+ ...buildFieldsOutputSchema(fields),
61400
+ ...launchObject !== void 0 ? { [LAUNCH_RECORD_KEY]: buildLaunchRecordOutputSchema(launchObject) } : {}
61401
+ }
61243
61402
  }
61244
61403
  });
61245
61404
  var buildManualTrigger = (base) => ({
61246
61405
  ...MANUAL_TRIGGER,
61406
+ ...base.type === "MANUAL" ? { name: base.name ?? MANUAL_TRIGGER.name, settings: base.settings } : {},
61247
61407
  position: base.position ?? MANUAL_TRIGGER.position,
61248
61408
  nextStepIds: base.nextStepIds ?? []
61249
61409
  });
@@ -61298,7 +61458,8 @@ var removeLegacyLaunchStep = async (draft) => {
61298
61458
  };
61299
61459
  var syncLaunchTrigger = async ({
61300
61460
  workflowId,
61301
- fields
61461
+ fields,
61462
+ launchObject
61302
61463
  }) => {
61303
61464
  const draft = await findOrCreateDraft(workflowId);
61304
61465
  if (draft === void 0) {
@@ -61315,7 +61476,7 @@ var syncLaunchTrigger = async ({
61315
61476
  variables: {
61316
61477
  input: {
61317
61478
  workflowVersionId: draft.id,
61318
- trigger: fields.length > 0 ? buildLaunchTrigger(trigger, fields) : buildManualTrigger(trigger)
61479
+ trigger: fields.length > 0 || launchObject !== void 0 ? buildLaunchTrigger(trigger, fields, launchObject) : buildManualTrigger(trigger)
61319
61480
  }
61320
61481
  }
61321
61482
  });
@@ -119595,13 +119756,6 @@ var AL = class {
119595
119756
  }
119596
119757
  };
119597
119758
 
119598
- // src/server/http-response.ts
119599
- var jsonResponse = (status, body) => new AL(JSON.stringify(body), {
119600
- status,
119601
- headers: { "content-type": "application/json; charset=utf-8" }
119602
- });
119603
- var readJsonBody = (body) => typeof body === "object" && body !== null && !Array.isArray(body) ? body : {};
119604
-
119605
119759
  // src/shared/log.util.ts
119606
119760
  var PREFIX = "[docflow]";
119607
119761
  var describeError = (error) => {
@@ -119614,6 +119768,23 @@ var describeError = (error) => {
119614
119768
  var logDocflow = (event, details) => console.info(PREFIX, event, details ?? "");
119615
119769
  var logDocflowError = (event, error, details) => console.error(PREFIX, event, describeError(error), details ?? "");
119616
119770
 
119771
+ // src/server/http-response.ts
119772
+ var jsonResponse = (status, body) => new AL(JSON.stringify(body), {
119773
+ status,
119774
+ headers: { "content-type": "application/json; charset=utf-8" }
119775
+ });
119776
+ var readJsonBody = (body) => typeof body === "object" && body !== null && !Array.isArray(body) ? body : {};
119777
+ var withRouteErrors = (name, handler2) => async (...args) => {
119778
+ try {
119779
+ return await handler2(...args);
119780
+ } catch (error) {
119781
+ logDocflowError(`${name}: unexpected error`, error);
119782
+ return jsonResponse(500, {
119783
+ message: `\u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u043E\u0431\u043E\u0440\u043E\u0442\u0430: ${error instanceof Error ? error.message : String(error)}`
119784
+ });
119785
+ }
119786
+ };
119787
+
119617
119788
  // src/universal-identifiers.ts
119618
119789
  var SAVE_LAUNCH_FIELDS_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER = "d2cce9f2-6ba4-415c-aa3d-5d9aba6ae89c";
119619
119790
 
@@ -119622,23 +119793,29 @@ var handler = async (event) => {
119622
119793
  const body = readJsonBody(event.body);
119623
119794
  const workflowId = body.workflowId;
119624
119795
  const fields = parseLaunchFields(body.fields);
119796
+ const launchObjectName = parseLaunchObject(body.launchObject);
119625
119797
  if (typeof workflowId !== "string" || workflowId.length === 0 || !Array.isArray(body.fields)) {
119626
119798
  return jsonResponse(400, { message: "\u041D\u0435 \u0443\u043A\u0430\u0437\u0430\u043D \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0438\u043B\u0438 \u043F\u043E\u043B\u044F" });
119627
119799
  }
119628
- const fieldsError = getFormFieldsError(fields);
119800
+ const fieldsError = getFormFieldsError(fields) ?? (launchObjectName !== void 0 ? getLaunchFieldsError(fields) : void 0);
119629
119801
  if (fieldsError !== void 0) {
119630
119802
  return jsonResponse(422, { message: fieldsError });
119631
119803
  }
119804
+ const launchObject = launchObjectName !== void 0 ? (await listLaunchObjects()).find(({ nameSingular }) => nameSingular === launchObjectName) : void 0;
119805
+ if (launchObjectName !== void 0 && launchObject === void 0) {
119806
+ return jsonResponse(422, { message: `\u041E\u0431\u044A\u0435\u043A\u0442 \xAB${launchObjectName}\xBB \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D` });
119807
+ }
119632
119808
  const workflow = readRestRecord(
119633
119809
  await restClient("person").patch(`/rest/workflows/${workflowId}`, {
119634
- smbLaunchFields: fields.length > 0 ? fields : null
119810
+ smbLaunchFields: fields.length > 0 ? fields : null,
119811
+ smbLaunchObject: launchObject?.nameSingular ?? null
119635
119812
  })
119636
119813
  );
119637
119814
  if (workflow === void 0) {
119638
119815
  return jsonResponse(404, { message: "\u041F\u0440\u043E\u0446\u0435\u0441\u0441 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D" });
119639
119816
  }
119640
119817
  try {
119641
- const result = await syncLaunchTrigger({ workflowId, fields });
119818
+ const result = await syncLaunchTrigger({ workflowId, fields, launchObject });
119642
119819
  logDocflow("launch fields: draft synced", { workflowId, result });
119643
119820
  if (result?.status === "NOT_MANUAL") {
119644
119821
  return jsonResponse(409, {
@@ -119656,9 +119833,9 @@ var handler = async (event) => {
119656
119833
  var save_launch_fields_logic_function_default = defineLogicFunction({
119657
119834
  universalIdentifier: SAVE_LAUNCH_FIELDS_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER,
119658
119835
  name: "docflow-save-launch-fields",
119659
- description: "\u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u0438 \u0434\u0435\u043B\u0430\u0435\u0442 \u043F\u0435\u0440\u0432\u044B\u043C \u0431\u043B\u043E\u043A\u043E\u043C \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \xAB\u0417\u0430\u043F\u0443\u0441\u043A \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438\xBB",
119836
+ description: "\u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u0438 \u043E\u0431\u044A\u0435\u043A\u0442 \u0437\u0430\u043F\u0443\u0441\u043A\u0430 \u0438 \u0434\u0435\u043B\u0430\u0435\u0442 \u043F\u0435\u0440\u0432\u044B\u043C \u0431\u043B\u043E\u043A\u043E\u043C \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430 \xAB\u0417\u0430\u043F\u0443\u0441\u043A \u0441 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u0430\u043C\u0438\xBB",
119660
119837
  timeoutSeconds: 30,
119661
- handler,
119838
+ handler: withRouteErrors("save-launch-fields", handler),
119662
119839
  httpRouteTriggerSettings: {
119663
119840
  path: "/docflow/processes/launch-fields",
119664
119841
  httpMethod: "POST",
@@ -2,37 +2,55 @@ import { defineLogicFunction } from 'twenty-sdk/define';
2
2
  import { type RoutePayload } from 'twenty-sdk/logic-function';
3
3
 
4
4
  import { CoreGraphqlError } from 'src/shared/api/core-graphql.util';
5
+ import { listLaunchObjects } from 'src/shared/api/object-summaries';
5
6
  import { readRestRecord } from 'src/shared/api/rest-response.util';
6
7
  import { getFormFieldsError } from 'src/shared/form/form-fields.util';
7
- import { parseLaunchFields } from 'src/shared/workflow/launch-step.util';
8
+ import {
9
+ getLaunchFieldsError,
10
+ parseLaunchFields,
11
+ parseLaunchObject,
12
+ } from 'src/shared/workflow/launch-step.util';
8
13
  import { syncLaunchTrigger } from 'src/server/launch-step';
9
- import { jsonResponse, readJsonBody } from 'src/server/http-response';
14
+ import { jsonResponse, readJsonBody, withRouteErrors } from 'src/server/http-response';
10
15
  import { restClient } from 'src/server/records';
11
16
  import { logDocflow, logDocflowError } from 'src/shared/log.util';
12
17
  import { SAVE_LAUNCH_FIELDS_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/universal-identifiers';
13
18
 
14
- // Saves the parameters of a process and puts them into its draft as the
15
- // "Запуск с параметрами" trigger, whose fields later steps read as variables.
19
+ // Saves the parameters of a process and the object it is launched on, and
20
+ // puts them into its draft as the "Запуск с параметрами" trigger, whose
21
+ // fields and record later steps read as variables.
16
22
  const handler = async (event: RoutePayload) => {
17
23
  const body = readJsonBody(event.body);
18
24
  const workflowId = body.workflowId;
19
25
  const fields = parseLaunchFields(body.fields);
26
+ const launchObjectName = parseLaunchObject(body.launchObject);
20
27
 
21
28
  if (typeof workflowId !== 'string' || workflowId.length === 0 || !Array.isArray(body.fields)) {
22
29
  return jsonResponse(400, { message: 'Не указан процесс или поля' });
23
30
  }
24
31
 
25
- const fieldsError = getFormFieldsError(fields);
32
+ const fieldsError =
33
+ getFormFieldsError(fields) ?? (launchObjectName !== undefined ? getLaunchFieldsError(fields) : undefined);
26
34
 
27
35
  if (fieldsError !== undefined) {
28
36
  return jsonResponse(422, { message: fieldsError });
29
37
  }
30
38
 
39
+ const launchObject =
40
+ launchObjectName !== undefined
41
+ ? (await listLaunchObjects()).find(({ nameSingular }) => nameSingular === launchObjectName)
42
+ : undefined;
43
+
44
+ if (launchObjectName !== undefined && launchObject === undefined) {
45
+ return jsonResponse(422, { message: `Объект «${launchObjectName}» не найден` });
46
+ }
47
+
31
48
  // Written as the person: who may not change the process gets the refusal
32
49
  // of the API, not a half-saved draft.
33
50
  const workflow = readRestRecord<{ id: string }>(
34
51
  await restClient('person').patch(`/rest/workflows/${workflowId}`, {
35
52
  smbLaunchFields: fields.length > 0 ? fields : null,
53
+ smbLaunchObject: launchObject?.nameSingular ?? null,
36
54
  }),
37
55
  );
38
56
 
@@ -41,7 +59,7 @@ const handler = async (event: RoutePayload) => {
41
59
  }
42
60
 
43
61
  try {
44
- const result = await syncLaunchTrigger({ workflowId, fields });
62
+ const result = await syncLaunchTrigger({ workflowId, fields, launchObject });
45
63
 
46
64
  logDocflow('launch fields: draft synced', { workflowId, result });
47
65
 
@@ -67,9 +85,9 @@ const handler = async (event: RoutePayload) => {
67
85
  export default defineLogicFunction({
68
86
  universalIdentifier: SAVE_LAUNCH_FIELDS_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER,
69
87
  name: 'docflow-save-launch-fields',
70
- description: 'Сохраняет параметры запуска и делает первым блоком процесса «Запуск с параметрами»',
88
+ description: 'Сохраняет параметры и объект запуска и делает первым блоком процесса «Запуск с параметрами»',
71
89
  timeoutSeconds: 30,
72
- handler,
90
+ handler: withRouteErrors('save-launch-fields', handler),
73
91
  httpRouteTriggerSettings: {
74
92
  path: '/docflow/processes/launch-fields',
75
93
  httpMethod: 'POST',
@@ -119312,12 +119312,33 @@ var AL = class {
119312
119312
  }
119313
119313
  };
119314
119314
 
119315
+ // src/shared/log.util.ts
119316
+ var PREFIX = "[docflow]";
119317
+ var describeError = (error) => {
119318
+ if (error instanceof Error) {
119319
+ const { status, body } = error;
119320
+ return { name: error.name, message: error.message, status, body };
119321
+ }
119322
+ return error;
119323
+ };
119324
+ var logDocflowError = (event, error, details) => console.error(PREFIX, event, describeError(error), details ?? "");
119325
+
119315
119326
  // src/server/http-response.ts
119316
119327
  var jsonResponse = (status, body) => new AL(JSON.stringify(body), {
119317
119328
  status,
119318
119329
  headers: { "content-type": "application/json; charset=utf-8" }
119319
119330
  });
119320
119331
  var readJsonBody = (body) => typeof body === "object" && body !== null && !Array.isArray(body) ? body : {};
119332
+ var withRouteErrors = (name, handler2) => async (...args) => {
119333
+ try {
119334
+ return await handler2(...args);
119335
+ } catch (error) {
119336
+ logDocflowError(`${name}: unexpected error`, error);
119337
+ return jsonResponse(500, {
119338
+ message: `\u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u043E\u0431\u043E\u0440\u043E\u0442\u0430: ${error instanceof Error ? error.message : String(error)}`
119339
+ });
119340
+ }
119341
+ };
119321
119342
 
119322
119343
  // src/shared/api/rest-response.util.ts
119323
119344
  var readDataValue = (response) => {
@@ -119427,7 +119448,8 @@ var applyStepSettings = ({
119427
119448
  createId
119428
119449
  }) => {
119429
119450
  const ordinaryFields = fields.filter((field) => getFormFieldRole(field) === void 0).map((field) => {
119430
- const { [FORM_FIELD_REQUIRED_SETTING]: _removed, ...rest } = field.settings ?? {};
119451
+ const rest = { ...field.settings };
119452
+ delete rest[FORM_FIELD_REQUIRED_SETTING];
119431
119453
  const isRequired = settings.requiredFieldIds.includes(field.id);
119432
119454
  return {
119433
119455
  ...field,
@@ -119517,7 +119539,7 @@ var save_step_settings_logic_function_default = defineLogicFunction({
119517
119539
  name: "docflow-save-step-settings",
119518
119540
  description: "\u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u043D\u0438\u0442\u0435\u043B\u044F, \u0441\u0440\u043E\u043A \u0438 \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u043F\u043E\u043B\u044F \u0448\u0430\u0433\u0430 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u0430",
119519
119541
  timeoutSeconds: 15,
119520
- handler,
119542
+ handler: withRouteErrors("save-step-settings", handler),
119521
119543
  httpRouteTriggerSettings: {
119522
119544
  path: "/docflow/processes/step-settings",
119523
119545
  httpMethod: "POST",
@@ -1,7 +1,7 @@
1
1
  import { defineLogicFunction } from 'twenty-sdk/define';
2
2
  import { type RoutePayload } from 'twenty-sdk/logic-function';
3
3
 
4
- import { jsonResponse, readJsonBody } from 'src/server/http-response';
4
+ import { jsonResponse, readJsonBody, withRouteErrors } from 'src/server/http-response';
5
5
  import { restClient, type WorkflowVersionRecord } from 'src/server/records';
6
6
  import { callCoreGraphql, UPDATE_WORKFLOW_VERSION_STEP_MUTATION } from 'src/shared/api/core-graphql.util';
7
7
  import { readRestRecord } from 'src/shared/api/rest-response.util';
@@ -81,7 +81,7 @@ export default defineLogicFunction({
81
81
  name: 'docflow-save-step-settings',
82
82
  description: 'Сохраняет исполнителя, срок и обязательные поля шага процесса',
83
83
  timeoutSeconds: 15,
84
- handler,
84
+ handler: withRouteErrors('save-step-settings', handler),
85
85
  httpRouteTriggerSettings: {
86
86
  path: '/docflow/processes/step-settings',
87
87
  httpMethod: 'POST',
@@ -119339,12 +119339,33 @@ var AL = class {
119339
119339
  }
119340
119340
  };
119341
119341
 
119342
+ // src/shared/log.util.ts
119343
+ var PREFIX = "[docflow]";
119344
+ var describeError = (error) => {
119345
+ if (error instanceof Error) {
119346
+ const { status, body } = error;
119347
+ return { name: error.name, message: error.message, status, body };
119348
+ }
119349
+ return error;
119350
+ };
119351
+ var logDocflowError = (event, error, details) => console.error(PREFIX, event, describeError(error), details ?? "");
119352
+
119342
119353
  // src/server/http-response.ts
119343
119354
  var jsonResponse = (status, body) => new AL(JSON.stringify(body), {
119344
119355
  status,
119345
119356
  headers: { "content-type": "application/json; charset=utf-8" }
119346
119357
  });
119347
119358
  var readJsonBody = (body) => typeof body === "object" && body !== null && !Array.isArray(body) ? body : {};
119359
+ var withRouteErrors = (name, handler2) => async (...args) => {
119360
+ try {
119361
+ return await handler2(...args);
119362
+ } catch (error) {
119363
+ logDocflowError(`${name}: unexpected error`, error);
119364
+ return jsonResponse(500, {
119365
+ message: `\u041E\u0448\u0438\u0431\u043A\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u043E\u043E\u0431\u043E\u0440\u043E\u0442\u0430: ${error instanceof Error ? error.message : String(error)}`
119366
+ });
119367
+ }
119368
+ };
119348
119369
 
119349
119370
  // src/shared/api/rest-response.util.ts
119350
119371
  var readDataValue = (response) => {
@@ -119414,7 +119435,7 @@ var save_task_form_logic_function_default = defineLogicFunction({
119414
119435
  name: "docflow-save-task-form",
119415
119436
  description: "\u0421\u043E\u0445\u0440\u0430\u043D\u044F\u0435\u0442 \u043F\u043E\u043B\u044F \u0437\u0430\u0434\u0430\u043D\u0438\u044F \u0438 \u0438\u0445 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u044F",
119416
119437
  timeoutSeconds: 15,
119417
- handler,
119438
+ handler: withRouteErrors("save-task-form", handler),
119418
119439
  httpRouteTriggerSettings: {
119419
119440
  path: "/docflow/tasks/form",
119420
119441
  httpMethod: "POST",
@@ -2,7 +2,7 @@ import { defineLogicFunction } from 'twenty-sdk/define';
2
2
  import { type RoutePayload } from 'twenty-sdk/logic-function';
3
3
 
4
4
  import { getFormFieldsError, parseFormFields, parseFormValues } from 'src/shared/form/form-fields.util';
5
- import { jsonResponse, readJsonBody } from 'src/server/http-response';
5
+ import { jsonResponse, readJsonBody, withRouteErrors } from 'src/server/http-response';
6
6
  import { getTask, updateTask } from 'src/server/records';
7
7
  import { isProcessTask } from 'src/server/task-completion';
8
8
  import { SAVE_TASK_FORM_LOGIC_FUNCTION_UNIVERSAL_IDENTIFIER } from 'src/universal-identifiers';
@@ -53,7 +53,7 @@ export default defineLogicFunction({
53
53
  name: 'docflow-save-task-form',
54
54
  description: 'Сохраняет поля задания и их значения',
55
55
  timeoutSeconds: 15,
56
- handler,
56
+ handler: withRouteErrors('save-task-form', handler),
57
57
  httpRouteTriggerSettings: {
58
58
  path: '/docflow/tasks/form',
59
59
  httpMethod: 'POST',