@tstdl/base 0.92.85 → 0.92.87

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 (152) hide show
  1. package/ai/ai.service.d.ts +4 -4
  2. package/ai/ai.service.js +27 -14
  3. package/ai/types.d.ts +5 -4
  4. package/api/server/gateway.js +1 -1
  5. package/authentication/authentication.api.d.ts +9 -9
  6. package/authentication/models/schemas.d.ts +2 -2
  7. package/authentication/server/authentication-ancillary.service.d.ts +6 -4
  8. package/authentication/server/authentication-ancillary.service.js +5 -5
  9. package/cancellation/token.d.ts +1 -1
  10. package/context/context.d.ts +1 -9
  11. package/context/context.js +8 -5
  12. package/document-management/api/document-management.api.d.ts +142 -110
  13. package/document-management/models/document-category.model.d.ts +1 -0
  14. package/document-management/models/document-category.model.js +2 -0
  15. package/document-management/models/document-collection-document.model.js +7 -3
  16. package/document-management/models/document-property-value.model.d.ts +13 -14
  17. package/document-management/models/document-property-value.model.js +60 -27
  18. package/document-management/models/document-property.model.d.ts +2 -0
  19. package/document-management/models/document-property.model.js +4 -1
  20. package/document-management/models/document-request-assignment-task-collection.model.d.ts +7 -0
  21. package/document-management/models/document-request-assignment-task-collection.model.js +32 -0
  22. package/document-management/models/document-request-assignment-task.model.d.ts +14 -0
  23. package/document-management/models/document-request-assignment-task.model.js +72 -0
  24. package/document-management/models/document-request-collection.model.d.ts +1 -0
  25. package/document-management/models/document-request-collection.model.js +7 -3
  26. package/document-management/models/document-request-file.model.d.ts +7 -2
  27. package/document-management/models/document-request-file.model.js +29 -4
  28. package/document-management/models/document-request.model.d.ts +1 -0
  29. package/document-management/models/document-requests-template.js +2 -0
  30. package/document-management/models/document-type-property.model.js +7 -3
  31. package/document-management/models/document-type.model.d.ts +1 -0
  32. package/document-management/models/document-type.model.js +7 -3
  33. package/document-management/models/document.model.d.ts +5 -2
  34. package/document-management/models/document.model.js +21 -6
  35. package/document-management/models/index.d.ts +2 -0
  36. package/document-management/models/index.js +2 -0
  37. package/document-management/models/service-models/document.service-model.d.ts +84 -65
  38. package/document-management/models/service-models/document.service-model.js +11 -6
  39. package/document-management/models/service-models/document.view-model.d.ts +1 -1
  40. package/document-management/models/service-models/document.view-model.js +2 -2
  41. package/document-management/server/drizzle/{0000_sloppy_fenris.sql → 0000_cool_victor_mancha.sql} +99 -43
  42. package/document-management/server/drizzle/meta/0000_snapshot.json +518 -130
  43. package/document-management/server/drizzle/meta/_journal.json +2 -2
  44. package/document-management/server/drizzle.config.js +1 -1
  45. package/document-management/server/module.d.ts +3 -2
  46. package/document-management/server/module.js +4 -2
  47. package/document-management/server/schemas.d.ts +36 -0
  48. package/document-management/server/schemas.js +37 -0
  49. package/document-management/server/services/document-management-ancillary.service.d.ts +4 -0
  50. package/document-management/server/services/document-management-ancillary.service.js +13 -0
  51. package/document-management/server/services/document-management.service.d.ts +71 -22
  52. package/document-management/server/services/document-management.service.js +528 -81
  53. package/document-management/server/services/index.d.ts +1 -0
  54. package/document-management/server/services/index.js +1 -0
  55. package/eslint.config.js +1 -0
  56. package/examples/document-management/main.d.ts +5 -0
  57. package/examples/document-management/main.js +20 -2
  58. package/examples/orm/schemas.d.ts +1 -1
  59. package/file/index.d.ts +1 -0
  60. package/file/index.js +1 -0
  61. package/file/temporary-file.d.ts +17 -0
  62. package/file/temporary-file.js +49 -0
  63. package/http/server/http-server-response.d.ts +2 -0
  64. package/http/server/http-server-response.js +13 -0
  65. package/injector/index.d.ts +1 -0
  66. package/injector/index.js +1 -0
  67. package/injector/injector.js +19 -7
  68. package/injector/interfaces.d.ts +1 -1
  69. package/injector/interfaces.js +1 -1
  70. package/injector/resolution.d.ts +15 -0
  71. package/injector/resolution.js +6 -0
  72. package/logger/console/logger.d.ts +1 -1
  73. package/logger/logger.d.ts +1 -1
  74. package/mail/drizzle.config.js +1 -1
  75. package/mail/models/schemas.d.ts +1 -1
  76. package/object-storage/object-storage.d.ts +5 -7
  77. package/object-storage/s3/s3.object-storage.d.ts +0 -1
  78. package/object-storage/s3/s3.object-storage.js +0 -3
  79. package/orm/{server/data-types → data-types}/numeric-date.js +2 -3
  80. package/orm/decorators.d.ts +17 -8
  81. package/orm/decorators.js +13 -7
  82. package/orm/entity.d.ts +5 -7
  83. package/orm/entity.js +11 -7
  84. package/orm/index.d.ts +2 -0
  85. package/orm/index.js +2 -0
  86. package/orm/query.d.ts +1 -3
  87. package/orm/query.js +0 -1
  88. package/orm/repository.types.d.ts +32 -0
  89. package/orm/repository.types.js +1 -0
  90. package/orm/server/database-schema.d.ts +4 -4
  91. package/orm/server/drizzle/schema-converter.d.ts +1 -1
  92. package/orm/server/drizzle/schema-converter.js +48 -19
  93. package/orm/server/index.d.ts +1 -0
  94. package/orm/server/index.js +1 -0
  95. package/orm/server/query-converter.d.ts +1 -2
  96. package/orm/server/query-converter.js +66 -61
  97. package/orm/server/repository.d.ts +80 -43
  98. package/orm/server/repository.js +219 -112
  99. package/orm/server/sqls.d.ts +15 -0
  100. package/orm/server/sqls.js +19 -0
  101. package/orm/server/types.d.ts +3 -3
  102. package/orm/types.d.ts +4 -4
  103. package/orm/utils.d.ts +3 -0
  104. package/orm/utils.js +6 -0
  105. package/package.json +23 -19
  106. package/pdf/pdf.service.d.ts +0 -1
  107. package/pdf/pdf.service.js +1 -95
  108. package/pdf/utils.d.ts +3 -1
  109. package/pdf/utils.js +129 -4
  110. package/promise/lazy-promise.d.ts +3 -3
  111. package/queue/enqueue-batch.d.ts +1 -0
  112. package/queue/enqueue-batch.js +1 -1
  113. package/queue/mongo/queue.d.ts +9 -4
  114. package/queue/mongo/queue.js +5 -6
  115. package/queue/postgres/drizzle/0000_zippy_moondragon.sql +11 -0
  116. package/queue/postgres/drizzle/meta/0000_snapshot.json +90 -0
  117. package/queue/postgres/drizzle/meta/_journal.json +13 -0
  118. package/queue/postgres/drizzle.config.d.ts +2 -0
  119. package/queue/postgres/drizzle.config.js +11 -0
  120. package/queue/postgres/index.d.ts +4 -0
  121. package/queue/postgres/index.js +4 -0
  122. package/queue/postgres/job.model.d.ts +13 -0
  123. package/queue/postgres/job.model.js +55 -0
  124. package/queue/postgres/module.d.ts +9 -0
  125. package/queue/postgres/module.js +29 -0
  126. package/queue/postgres/queue.d.ts +28 -0
  127. package/queue/postgres/queue.js +147 -0
  128. package/queue/postgres/queue.provider.d.ts +7 -0
  129. package/queue/postgres/queue.provider.js +21 -0
  130. package/queue/postgres/schemas.d.ts +3 -0
  131. package/queue/postgres/schemas.js +4 -0
  132. package/queue/provider.d.ts +2 -1
  133. package/queue/queue.d.ts +32 -6
  134. package/queue/queue.js +43 -0
  135. package/schema/schemas/object.d.ts +1 -1
  136. package/utils/date-time.d.ts +4 -2
  137. package/utils/date-time.js +10 -3
  138. package/utils/format-error.js +0 -1
  139. package/utils/object/lazy-property.js +0 -1
  140. package/utils/timing.d.ts +4 -3
  141. package/utils/timing.js +3 -1
  142. package/utils/try-ignore.d.ts +9 -2
  143. package/utils/try-ignore.js +30 -6
  144. package/document-management/models/schemas.d.ts +0 -33
  145. package/document-management/models/schemas.js +0 -34
  146. /package/orm/{server/data-types → data-types}/bytea.d.ts +0 -0
  147. /package/orm/{server/data-types → data-types}/bytea.js +0 -0
  148. /package/orm/{server/data-types → data-types}/index.d.ts +0 -0
  149. /package/orm/{server/data-types → data-types}/index.js +0 -0
  150. /package/orm/{server/data-types → data-types}/numeric-date.d.ts +0 -0
  151. /package/orm/{server/data-types → data-types}/timestamp.d.ts +0 -0
  152. /package/orm/{server/data-types → data-types}/timestamp.js +0 -0
@@ -4,63 +4,179 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
4
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
- import { getEntityMap } from '../../../database/index.js';
7
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
8
+ if (value !== null && value !== void 0) {
9
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
10
+ var dispose, inner;
11
+ if (async) {
12
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
13
+ dispose = value[Symbol.asyncDispose];
14
+ }
15
+ if (dispose === void 0) {
16
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
17
+ dispose = value[Symbol.dispose];
18
+ if (async) inner = dispose;
19
+ }
20
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
21
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
22
+ env.stack.push({ value: value, dispose: dispose, async: async });
23
+ }
24
+ else if (async) {
25
+ env.stack.push({ async: true });
26
+ }
27
+ return value;
28
+ };
29
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
30
+ return function (env) {
31
+ function fail(e) {
32
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
33
+ env.hasError = true;
34
+ }
35
+ var r, s = 0;
36
+ function next() {
37
+ while (r = env.stack.pop()) {
38
+ try {
39
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
40
+ if (r.dispose) {
41
+ var result = r.dispose.call(r.value);
42
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
43
+ }
44
+ else s |= 1;
45
+ }
46
+ catch (e) {
47
+ fail(e);
48
+ }
49
+ }
50
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
51
+ if (env.hasError) throw env.error;
52
+ }
53
+ return next();
54
+ };
55
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
56
+ var e = new Error(message);
57
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
58
+ });
59
+ var DocumentManagementService_1;
60
+ import { and, eq, inArray, ne, notExists, sql } from 'drizzle-orm';
61
+ import { P, match } from 'ts-pattern';
62
+ import { AiService } from '../../../ai/index.js';
8
63
  import { Enumerable } from '../../../enumerable/index.js';
9
64
  import { BadRequestError } from '../../../errors/index.js';
10
- import { getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
11
- import { Singleton, inject, injectArgument, provide, resolveArgumentType } from '../../../injector/index.js';
65
+ import { TemporaryFile, getMimeType, getMimeTypeExtensions } from '../../../file/index.js';
66
+ import { Singleton, afterResolve, inject, injectArgument, provide, resolveArgumentType } from '../../../injector/index.js';
67
+ import { Logger } from '../../../logger/logger.js';
12
68
  import { ObjectStorage } from '../../../object-storage/index.js';
13
- import { DatabaseConfig, EntityRepositoryConfig, injectRepository } from '../../../orm/server/index.js';
14
- import { toArray } from '../../../utils/array/index.js';
15
- import { assertDefinedPass, compareByValueSelectionToOrder, currentTimestamp, digest, isBoolean, isDefined, isNotNull, isNull, isNumber, isString, isUint8Array, millisecondsPerMinute } from '../../../utils/index.js';
69
+ import { getEntityMap } from '../../../orm/index.js';
70
+ import { DatabaseConfig, EntityRepositoryConfig, TRANSACTION_TIMESTAMP, arrayAgg, coalesce, injectRepository, jsonAgg, toJsonb } from '../../../orm/server/index.js';
71
+ import { getPdfPageCount } from '../../../pdf/index.js';
72
+ import { Queue } from '../../../queue/queue.js';
73
+ import { array, boolean, enumeration, integer, nullable, number, object, string } from '../../../schema/index.js';
74
+ import { distinct, toArray } from '../../../utils/array/index.js';
75
+ import { compareByValueSelectionToOrder } from '../../../utils/comparison.js';
76
+ import { digest } from '../../../utils/cryptography.js';
77
+ import { currentTimestamp, dateObjectToNumericDate } from '../../../utils/date-time.js';
16
78
  import { groupToMap } from '../../../utils/iterable-helpers/index.js';
79
+ import { fromEntries, objectEntries } from '../../../utils/object/object.js';
17
80
  import { readBinaryStream } from '../../../utils/stream/index.js';
18
- import { Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, DocumentProperty, DocumentPropertyBooleanValue, DocumentPropertyDataType, DocumentPropertyDecimalValue, DocumentPropertyIntegerValue, DocumentPropertyTextValue, DocumentRequest, DocumentRequestCollection, DocumentRequestFile, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentTypeProperty } from '../../models/index.js';
81
+ import { tryIgnoreLogAsync } from '../../../utils/try-ignore.js';
82
+ import { assertBooleanPass, assertDefined, assertDefinedPass, assertNotNullPass, assertNumberPass, assertStringPass, isBoolean, isDefined, isNotNull, isNull, isNumber, isString, isUint8Array, isUndefined } from '../../../utils/type-guards.js';
83
+ import { millisecondsPerMinute } from '../../../utils/units.js';
84
+ import { Document, DocumentCategory, DocumentCollection, DocumentCollectionDocument, DocumentFile, DocumentProperty, DocumentPropertyDataType, DocumentPropertyValue, DocumentRequest, DocumentRequestAssignmentTask, DocumentRequestAssignmentTaskCollection, DocumentRequestAssignmentTaskPropertyValue, DocumentRequestCollection, DocumentRequestFile, DocumentRequestFilePropertyValue, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentTypeProperty } from '../../models/index.js';
19
85
  import { DocumentManagementConfig } from '../module.js';
20
- let DocumentManagementService = class DocumentManagementService {
21
- documentService = injectRepository(Document);
22
- documentFileService = injectRepository(DocumentFile);
23
- documentCollectionService = injectRepository(DocumentCollection);
86
+ import { documentCategory, documentProperty, documentRequest, documentRequestAssignmentTask, documentRequestAssignmentTaskCollection, documentRequestAssignmentTaskPropertyValue, documentRequestCollection, documentRequestFile, documentType } from '../schemas.js';
87
+ import { DocumentManagementAncillaryService } from './document-management-ancillary.service.js';
88
+ const defaultGenerationOptions = {
89
+ model: 'gemini-2.0-flash',
90
+ generationOptions: {
91
+ maxOutputTokens: 2048,
92
+ temperature: 0.2,
93
+ topP: 0.2
94
+ }
95
+ };
96
+ let DocumentManagementService = DocumentManagementService_1 = class DocumentManagementService {
97
+ #aiService = inject(AiService);
98
+ ancillaryService = inject(DocumentManagementAncillaryService);
24
99
  documentCategoryService = injectRepository(DocumentCategory);
25
- documentTypeService = injectRepository(DocumentType);
100
+ documentCollectionDocumentService = injectRepository(DocumentCollectionDocument);
101
+ documentCollectionService = injectRepository(DocumentCollection);
102
+ documentFileService = injectRepository(DocumentFile);
103
+ documentPropertyService = injectRepository(DocumentProperty);
104
+ documentPropertyValueService = injectRepository(DocumentPropertyValue);
105
+ documentRequestAssignmentTaskCollectionService = injectRepository(DocumentRequestAssignmentTaskCollection);
106
+ documentRequestAssignmentTaskService = injectRepository(DocumentRequestAssignmentTask);
107
+ documentRequestCollectionService = injectRepository(DocumentRequestCollection);
108
+ documentRequestFilePropertyValueService = injectRepository(DocumentRequestFilePropertyValue);
109
+ documentRequestAssignmentTaskPropertyValueService = injectRepository(DocumentRequestAssignmentTaskPropertyValue);
110
+ documentRequestFileService = injectRepository(DocumentRequestFile);
26
111
  documentRequestService = injectRepository(DocumentRequest);
27
112
  documentRequestsTemplateService = injectRepository(DocumentRequestsTemplate);
28
113
  documentRequestTemplateService = injectRepository(DocumentRequestTemplate);
29
- documentRequestCollectionService = injectRepository(DocumentRequestCollection);
30
- documentRequestFileService = injectRepository(DocumentRequestFile);
31
- documentPropertyService = injectRepository(DocumentProperty);
114
+ documentService = injectRepository(Document);
32
115
  documentTypePropertyService = injectRepository(DocumentTypeProperty);
33
- documentPropertyTextValueService = injectRepository(DocumentPropertyTextValue);
34
- documentPropertyIntegerValueService = injectRepository(DocumentPropertyIntegerValue);
35
- documentPropertyDecimalValueService = injectRepository(DocumentPropertyDecimalValue);
36
- documentPropertyBooleanValueService = injectRepository(DocumentPropertyBooleanValue);
37
- documentCollectionDocumentService = injectRepository(DocumentCollectionDocument);
116
+ documentTypeService = injectRepository(DocumentType);
38
117
  fileObjectStorage = inject(ObjectStorage, (injectArgument(this, { optional: true }) ?? inject(DocumentManagementConfig)).fileObjectStorageModule);
118
+ extractionQueue = inject((Queue), { name: 'DocumentManagement:extraction', processTimeout: 15 * millisecondsPerMinute });
119
+ assignmentQueue = inject((Queue), { name: 'DocumentManagement:assignment', processTimeout: 15 * millisecondsPerMinute });
120
+ logger = inject(Logger, DocumentManagementService_1.name);
121
+ [afterResolve](_, { cancellationSignal }) {
122
+ this.processQueues(cancellationSignal);
123
+ }
124
+ processQueues(cancellationSignal) {
125
+ this.extractionQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
126
+ const [entry] = objectEntries(job.data);
127
+ this.logger.info(`Processing extraction job for ${entry?.[0]} "${entry?.[1]}"`);
128
+ await match(job.data)
129
+ .with({ documentId: P.string.select() }, async (documentId) => this.enrichDocument(documentId))
130
+ .with({ requestFileId: P.string.select() }, async (requestFileId) => this.enrichDocumentRequestFile(requestFileId))
131
+ .with({ requestAssignmentTaskId: P.string.select() }, async (requestAssignmentTaskId) => this.enrichDocumentRequestAssignmentTask(requestAssignmentTaskId))
132
+ .exhaustive();
133
+ }, this.logger);
134
+ this.assignmentQueue.process({ concurrency: 1, cancellationSignal }, async (job) => {
135
+ this.logger.info(`Processing assignment job "${job.data.requestAssignmentTaskId}"`);
136
+ await this.assignDocumentRequest(job.data.requestAssignmentTaskId);
137
+ }, this.logger);
138
+ }
139
+ async resolveNames(...collectionsOrIds) {
140
+ if (collectionsOrIds.length == 0) {
141
+ return [];
142
+ }
143
+ const loadIds = collectionsOrIds.filter((collection) => isString(collection));
144
+ if (loadIds.length == 0) {
145
+ return this.ancillaryService.resolveNames(collectionsOrIds);
146
+ }
147
+ const loadedCollections = await this.documentCollectionService.loadManyByQuery({ id: { $in: loadIds } });
148
+ const collections = collectionsOrIds.map((collectionOrId) => isString(collectionOrId)
149
+ ? assertDefinedPass(loadedCollections.find((collection) => collection.id == collectionOrId), `Could not load collection "${collectionOrId}".`)
150
+ : collectionOrId);
151
+ return this.ancillaryService.resolveNames(collections);
152
+ }
153
+ async resolveNamesMap(...collectionsOrIds) {
154
+ const names = await this.resolveNames(...collectionsOrIds);
155
+ const entries = collectionsOrIds.map((collectionOrId, index) => [isString(collectionOrId) ? collectionOrId : collectionOrId.id, names[index]]);
156
+ return fromEntries(entries);
157
+ }
39
158
  async loadData(collectionIds, collectionsMetadata) {
40
159
  return this.documentCollectionService.transaction(async (_, transaction) => {
41
160
  const [collections, collectionDocuments, requestCollections, categories, types] = await Promise.all([
42
161
  this.documentCollectionService.withTransaction(transaction).loadMany(collectionIds),
43
162
  this.documentCollectionDocumentService.withTransaction(transaction).loadManyByQuery({ collectionId: { $in: collectionIds } }),
44
163
  this.documentRequestCollectionService.withTransaction(transaction).loadManyByQuery({ collectionId: { $in: collectionIds } }),
45
- this.documentCategoryService.withTransaction(transaction).loadManyByQuery({}, { order: { label: 1 } }),
46
- this.documentTypeService.withTransaction(transaction).loadManyByQuery({}, { order: { label: 1 } })
164
+ this.documentCategoryService.withTransaction(transaction).loadManyByQuery({}, { order: 'label' }),
165
+ this.documentTypeService.withTransaction(transaction).loadManyByQuery({}, { order: 'label' })
47
166
  ]);
48
167
  const documentIds = collectionDocuments.map((document) => document.documentId);
49
168
  const requestIds = requestCollections.map((requestCollection) => requestCollection.requestId);
50
- const [documents, requests, requestFiles, textValues, integerValues, decimalValues, booleanValues] = await Promise.all([
169
+ const [documents, requests, requestFiles, propertyValues] = await Promise.all([
51
170
  this.documentService.withTransaction(transaction).loadManyByQuery({ id: { $in: documentIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
52
171
  this.documentRequestService.withTransaction(transaction).loadManyByQuery({ id: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
53
172
  this.documentRequestFileService.withTransaction(transaction).loadManyByQuery({ requestId: { $in: requestIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
54
- this.documentPropertyTextValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
55
- this.documentPropertyIntegerValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
56
- this.documentPropertyDecimalValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } }),
57
- this.documentPropertyBooleanValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } })
173
+ this.documentPropertyValueService.withTransaction(transaction).loadManyByQuery({ documentId: { $in: documentIds } })
58
174
  ]);
59
175
  const documentFileIds = documents.map((document) => document.fileId);
60
176
  const requestFileIds = requestFiles.map((requestFile) => requestFile.fileId);
61
177
  const files = await this.documentFileService.withTransaction(transaction).loadManyByQuery({ id: { $in: [...documentFileIds, ...requestFileIds] } }, { order: { 'metadata.createTimestamp': 'desc' } });
62
178
  const requestsFilesMap = groupToMap(requestFiles, (requestFile) => requestFile.requestId);
63
- const valuesMap = Enumerable.from(textValues).concat(integerValues).concat(decimalValues).concat(booleanValues).groupToMap((value) => value.documentId);
179
+ const valuesMap = Enumerable.from(propertyValues).groupToMap((value) => value.documentId);
64
180
  const collectionViews = collections.toSorted(compareByValueSelectionToOrder(collectionIds, (collection) => collection.id)).map((collection) => ({
65
181
  ...collection,
66
182
  name: collectionsMetadata?.[collection.id]?.name ?? null,
@@ -88,7 +204,7 @@ let DocumentManagementService = class DocumentManagementService {
88
204
  }
89
205
  async loadDocumentRequestsTemplateData() {
90
206
  const [requestsTemplates, requestTemplates] = await Promise.all([
91
- this.documentRequestsTemplateService.loadManyByQuery({}, { order: { label: 'asc' } }),
207
+ this.documentRequestsTemplateService.loadManyByQuery({}, { order: 'label' }),
92
208
  this.documentRequestTemplateService.loadManyByQuery({})
93
209
  ]);
94
210
  const templates = requestsTemplates.map((requestsTemplate) => ({
@@ -99,8 +215,8 @@ let DocumentManagementService = class DocumentManagementService {
99
215
  }
100
216
  async loadCategoriesAndTypes() {
101
217
  const [categories, types] = await Promise.all([
102
- this.documentCategoryService.loadManyByQuery({}, { order: { label: 1 } }),
103
- this.documentTypeService.loadManyByQuery({}, { order: { label: 1 } })
218
+ this.documentCategoryService.loadManyByQuery({}, { order: 'label' }),
219
+ this.documentTypeService.loadManyByQuery({}, { order: 'label' })
104
220
  ]);
105
221
  return { categories, types };
106
222
  }
@@ -169,21 +285,20 @@ let DocumentManagementService = class DocumentManagementService {
169
285
  async deleteDocumentRequestTemplate(parameters) {
170
286
  return this.documentRequestTemplateService.delete(parameters.id);
171
287
  }
172
- async createDocument({ typeId, addition, date, expiration, originalFileName, collectionIds, properties, metadata }, content) {
173
- return this.documentService.transaction(async (_, transaction) => {
288
+ async createDocument({ typeId, title, subtitle, pages, date, summary, tags, originalFileName, collectionIds, properties, metadata }, content) {
289
+ const document = await this.documentService.transaction(async (_, transaction) => {
174
290
  const documentFile = await this.createDocumentFile(content, originalFileName, transaction);
175
- const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, addition, date, expiration, metadata });
176
- if (isDefined(collectionIds)) {
177
- for (const collectionId of toArray(collectionIds)) {
178
- await this.documentCollectionDocumentService.withTransaction(transaction).insert({ collectionId, documentId: document.id, archiveTimestamp: null });
179
- }
291
+ const document = await this.documentService.withTransaction(transaction).insert({ fileId: documentFile.id, typeId, title, subtitle, pages, date, summary, tags, metadata });
292
+ for (const collectionId of toArray(collectionIds)) {
293
+ await this.documentCollectionDocumentService.withTransaction(transaction).insert({ collectionId, documentId: document.id, archiveTimestamp: null });
180
294
  }
181
295
  if (isDefined(properties)) {
182
- const mappedProperties = properties.map((property) => ({ ...property, documentId: document.id }));
183
- await this.setDocumentProperties(mappedProperties, transaction);
296
+ await this.setPropertyValues({ documentId: document.id, properties }, transaction);
184
297
  }
185
298
  return document;
186
299
  });
300
+ void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ documentId: document.id }), this.logger);
301
+ return document;
187
302
  }
188
303
  async approveDocumentRequestFile({ id, approvalComment, documentMetadata, requestFileMetadata }) {
189
304
  return this.documentRequestFileService.transaction(async (_, transaction) => {
@@ -196,15 +311,18 @@ let DocumentManagementService = class DocumentManagementService {
196
311
  const document = await this.documentService.withTransaction(transaction).insert({
197
312
  fileId: requestFile.fileId,
198
313
  typeId: request.typeId,
199
- addition: requestFile.addition,
200
- date: null,
201
- expiration: null,
314
+ title: requestFile.title,
315
+ subtitle: requestFile.subtitle,
316
+ pages: requestFile.pages,
317
+ date: requestFile.date,
318
+ summary: requestFile.summary,
319
+ tags: requestFile.tags,
202
320
  metadata: documentMetadata
203
321
  });
204
322
  for (const { collectionId } of requestCollections) {
205
323
  await this.addDocumentToCollection({ collectionId, documentId: document.id }, transaction);
206
324
  }
207
- await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: true, approvalComment, approvalTimestamp: currentTimestamp(), createdDocumentId: document.id, metadata: requestFileMetadata });
325
+ await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: true, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, createdDocumentId: document.id, metadata: requestFileMetadata });
208
326
  return document;
209
327
  });
210
328
  }
@@ -214,11 +332,11 @@ let DocumentManagementService = class DocumentManagementService {
214
332
  if (requestFile.approval == true) {
215
333
  throw new BadRequestError('Document request file already accepted.');
216
334
  }
217
- await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp: currentTimestamp(), metadata });
335
+ await this.documentRequestFileService.withTransaction(transaction).update(id, { approval: false, approvalComment, approvalTimestamp: TRANSACTION_TIMESTAMP, metadata });
218
336
  });
219
337
  }
220
- async updateDocumentRequestFile({ id, addition, approvalComment, metadata }) {
221
- return this.documentRequestFileService.update(id, { addition, approvalComment, metadata });
338
+ async updateDocumentRequestFile({ id, title, approvalComment, metadata }) {
339
+ return this.documentRequestFileService.update(id, { title, approvalComment, metadata });
222
340
  }
223
341
  async deleteDocumentRequestFile({ id, metadata }) {
224
342
  const requestFile = await this.documentRequestFileService.load(id);
@@ -227,12 +345,14 @@ let DocumentManagementService = class DocumentManagementService {
227
345
  }
228
346
  await this.documentRequestFileService.delete(id, metadata);
229
347
  }
230
- async createDocumentFile(content, originalFileName, dbTransaction) {
231
- return this.documentFileService.useTransaction(dbTransaction, async (_, transaction) => {
232
- const contentAsUint8Array = isUint8Array(content) ? content : await readBinaryStream(content);
233
- const hash = await digest('SHA-256', contentAsUint8Array).toHex();
234
- const mimeType = await getMimeType(contentAsUint8Array);
235
- const documentFile = await this.documentFileService.withTransaction(transaction).insert({
348
+ async createDocumentFile(content, originalFileName, transaction) {
349
+ const contentAsUint8Array = isUint8Array(content) ? content : await readBinaryStream(content);
350
+ const hash = await digest('SHA-256', contentAsUint8Array).toHex();
351
+ const mimeType = await getMimeType(contentAsUint8Array);
352
+ return this.documentFileService.useTransaction(transaction, async (_, transaction) => {
353
+ const documentFile = await this.documentFileService
354
+ .withTransaction(transaction)
355
+ .insert({
236
356
  originalFileName,
237
357
  mimeType,
238
358
  hash,
@@ -243,8 +363,8 @@ let DocumentManagementService = class DocumentManagementService {
243
363
  return documentFile;
244
364
  });
245
365
  }
246
- async createDocumentRequestFile({ requestId, addition, originalFileName, metadata }, content) {
247
- return this.documentRequestFileService.transaction(async (_, transaction) => {
366
+ async createDocumentRequestFile({ requestId, title, subtitle, date, summary, tags, originalFileName, metadata }, content) {
367
+ const documentRequestFile = await this.documentRequestFileService.transaction(async (_, transaction) => {
248
368
  const [request, existingRequestFiles] = await Promise.all([
249
369
  this.documentRequestService.withTransaction(transaction).load(requestId),
250
370
  this.documentRequestFileService.withTransaction(transaction).loadManyByQuery({ requestId })
@@ -254,8 +374,26 @@ let DocumentManagementService = class DocumentManagementService {
254
374
  throw new BadRequestError('Maximum amount of allowed files reached.');
255
375
  }
256
376
  const file = await this.createDocumentFile(content, originalFileName, transaction);
257
- return this.documentRequestFileService.withTransaction(transaction).insert({ requestId, fileId: file.id, addition, createdDocumentId: null, approval: null, approvalComment: null, approvalTimestamp: null, metadata });
377
+ return await this.documentRequestFileService
378
+ .withTransaction(transaction)
379
+ .insert({
380
+ requestId,
381
+ fileId: file.id,
382
+ title,
383
+ subtitle,
384
+ pages: null,
385
+ date,
386
+ summary,
387
+ tags,
388
+ createdDocumentId: null,
389
+ approval: null,
390
+ approvalComment: null,
391
+ approvalTimestamp: null,
392
+ metadata
393
+ });
258
394
  });
395
+ void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestFileId: documentRequestFile.id }), this.logger);
396
+ return documentRequestFile;
259
397
  }
260
398
  async createDocumentRequest(parameters, transaction) {
261
399
  if (parameters.collectionIds.length == 0) {
@@ -268,13 +406,36 @@ let DocumentManagementService = class DocumentManagementService {
268
406
  return request;
269
407
  });
270
408
  }
409
+ async createDocumentRequestAssignmentTask({ originalFileName, collectionIds }, content) {
410
+ if (collectionIds.length == 0) {
411
+ throw new BadRequestError('No target collectionId specified.');
412
+ }
413
+ const file = await this.createDocumentFile(content, originalFileName);
414
+ return this.documentRequestAssignmentTaskService.transaction(async (_, transaction) => {
415
+ const task = await this.documentRequestAssignmentTaskService.withTransaction(transaction).insert({
416
+ fileId: file.id,
417
+ assignedRequestFileId: null,
418
+ typeId: null,
419
+ title: null,
420
+ subtitle: null,
421
+ pages: null,
422
+ date: null,
423
+ summary: null,
424
+ tags: null,
425
+ assignmentTries: 0
426
+ });
427
+ const newTaksCollections = collectionIds.map((collectionId) => ({ requestAssignmentTaskId: task.id, collectionId }));
428
+ await this.documentRequestAssignmentTaskCollectionService.withTransaction(transaction).insertMany(newTaksCollections);
429
+ void tryIgnoreLogAsync(async () => this.extractionQueue.enqueue({ requestAssignmentTaskId: task.id }), this.logger);
430
+ return task;
431
+ });
432
+ }
271
433
  async updateDocument(parameters, transaction) {
272
434
  const { id: documentId, properties: propertyUpdates, ...update } = parameters;
273
435
  await this.documentService.useTransaction(transaction, async (repository, tx) => {
274
436
  await repository.update(documentId, update);
275
437
  if (isDefined(propertyUpdates)) {
276
- const mappedPropertyUpdates = propertyUpdates.map((property) => ({ documentId, ...property }));
277
- await this.setDocumentProperties(mappedPropertyUpdates, tx);
438
+ await this.setPropertyValues({ documentId, properties: propertyUpdates }, tx);
278
439
  }
279
440
  });
280
441
  }
@@ -297,19 +458,41 @@ let DocumentManagementService = class DocumentManagementService {
297
458
  }
298
459
  await this.documentRequestService.withOptionalTransaction(transaction).delete(id, metadata);
299
460
  }
300
- async setDocumentProperties(setDocumentPropertyItems, transaction) {
301
- if ((setDocumentPropertyItems.length == 0)) {
461
+ async setPropertyValues({ documentId, requestFileId, requestAssignmentTaskId, properties: items }, transaction) {
462
+ if ((items.length == 0)) {
302
463
  return;
303
464
  }
465
+ if ((Number(isDefined(documentId)) + Number(isDefined(requestFileId)) + Number(isDefined(requestAssignmentTaskId))) != 1) {
466
+ throw new BadRequestError('Exactly one of documentId, requestFileId or requestAssignmentTaskId must be specified.');
467
+ }
304
468
  await this.documentPropertyService.useTransaction(transaction, async (_, tx) => {
305
- const propertyIds = setDocumentPropertyItems.map((property) => property.propertyId);
469
+ const propertyIds = items.map((property) => property.propertyId);
306
470
  const properties = await this.documentPropertyService.withTransaction(tx).loadManyByQuery({ id: { $in: propertyIds } });
307
471
  const propertiesMap = getEntityMap(properties);
308
- for (const { documentId, propertyId, value, metadata } of setDocumentPropertyItems) {
472
+ const propertyItems = items.map(({ propertyId, value, metadata }) => {
309
473
  const property = assertDefinedPass(propertiesMap.get(propertyId));
310
474
  validatePropertyValue(propertyId, property.dataType, value);
311
- const propertyValueService = this.getDocumentPropertyValueService(property.dataType);
312
- await propertyValueService.withTransaction(tx).upsert(['documentId', 'propertyId'], { documentId, propertyId, value: value, metadata }, undefined);
475
+ return {
476
+ propertyId,
477
+ text: (property.dataType == 'text') ? assertStringPass(value) : null,
478
+ integer: (property.dataType == 'integer') ? assertNumberPass(value) : null,
479
+ decimal: (property.dataType == 'decimal') ? assertNumberPass(value) : null,
480
+ boolean: (property.dataType == 'boolean') ? assertBooleanPass(value) : null,
481
+ date: (property.dataType == 'date') ? assertNumberPass(value) : null,
482
+ metadata
483
+ };
484
+ });
485
+ if (isDefined(documentId)) {
486
+ await this.documentPropertyValueService.withTransaction(tx)
487
+ .upsertMany(['documentId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, documentId })));
488
+ }
489
+ else if (isDefined(requestFileId)) {
490
+ await this.documentRequestFilePropertyValueService.withTransaction(tx)
491
+ .upsertMany(['requestFileId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, requestFileId })));
492
+ }
493
+ else if (isDefined(requestAssignmentTaskId)) {
494
+ await this.documentRequestAssignmentTaskPropertyValueService.withTransaction(tx)
495
+ .upsertMany(['requestAssignmentTaskId', 'propertyId'], propertyItems.map((propertyItem) => ({ ...propertyItem, requestAssignmentTaskId })));
313
496
  }
314
497
  });
315
498
  }
@@ -317,7 +500,7 @@ let DocumentManagementService = class DocumentManagementService {
317
500
  await this.documentCollectionDocumentService.withOptionalTransaction(transaction).upsert(['collectionId', 'documentId'], { ...parameters, archiveTimestamp: null });
318
501
  }
319
502
  async archiveDocument({ collectionId, documentId, metadata }) {
320
- await this.documentCollectionDocumentService.updateByQuery({ collectionId, documentId }, { archiveTimestamp: currentTimestamp(), metadata });
503
+ await this.documentCollectionDocumentService.updateByQuery({ collectionId, documentId }, { archiveTimestamp: TRANSACTION_TIMESTAMP, metadata });
321
504
  }
322
505
  async createProperty(parameters) {
323
506
  return this.documentPropertyService.insert(parameters);
@@ -325,19 +508,282 @@ let DocumentManagementService = class DocumentManagementService {
325
508
  async assignPropertyToType(parameters) {
326
509
  await this.documentTypePropertyService.insert(parameters);
327
510
  }
328
- getDocumentPropertyValueService(dataType) {
329
- switch (dataType) {
330
- case DocumentPropertyDataType.Text:
331
- return this.documentPropertyTextValueService;
332
- case DocumentPropertyDataType.Integer:
333
- return this.documentPropertyIntegerValueService;
334
- case DocumentPropertyDataType.Decimal:
335
- return this.documentPropertyDecimalValueService;
336
- case DocumentPropertyDataType.Boolean:
337
- return this.documentPropertyBooleanValueService;
338
- default:
339
- throw new BadRequestError('Unknown property data type.');
511
+ async enrichDocument(documentId) {
512
+ const document = await this.documentService.load(documentId);
513
+ const { properties, ...extractionResult } = await this.extractFileInformation(document.fileId, document.typeId);
514
+ await this.documentService.transaction(async (documentService, transaction) => {
515
+ await documentService.update(document.id, extractionResult);
516
+ await this.setPropertyValues({ documentId, properties: properties }, transaction);
517
+ });
518
+ }
519
+ async enrichDocumentRequestFile(requestFileId) {
520
+ const requestFile = await this.documentRequestFileService.load(requestFileId);
521
+ const request = await this.documentRequestService.load(requestFile.requestId);
522
+ const { properties, typeId: _, ...extractionResult } = await this.extractFileInformation(requestFile.fileId, request.typeId);
523
+ await this.documentRequestFileService.transaction(async (documentRequestFileService, transaction) => {
524
+ await documentRequestFileService.update(requestFile.id, extractionResult);
525
+ await this.setPropertyValues({ requestFileId, properties: properties }, transaction);
526
+ });
527
+ }
528
+ async enrichDocumentRequestAssignmentTask(requestAssignmentTaskId) {
529
+ const task = await this.documentRequestAssignmentTaskService.load(requestAssignmentTaskId);
530
+ const { properties, ...extractionResult } = await this.extractFileInformation(task.fileId);
531
+ await this.documentRequestAssignmentTaskService.transaction(async (documentRequestAssignmentTaskService, transaction) => {
532
+ await documentRequestAssignmentTaskService.update(task.id, extractionResult);
533
+ await this.setPropertyValues({ requestAssignmentTaskId, properties: properties }, transaction);
534
+ });
535
+ void tryIgnoreLogAsync(async () => this.assignmentQueue.enqueue({ requestAssignmentTaskId }), this.logger);
536
+ }
537
+ async assignDocumentRequest(requestAssignmentTaskId) {
538
+ const session = this.documentRequestAssignmentTaskCollectionService.session;
539
+ const requestAssignmentTaskProperties = session.$with('requestAssignmentTaskProperty').as(session
540
+ .select({
541
+ requestAssignmentTaskId: documentRequestAssignmentTaskPropertyValue.requestAssignmentTaskId,
542
+ propertyId: documentRequestAssignmentTaskPropertyValue.propertyId,
543
+ label: documentProperty.label,
544
+ value: coalesce(toJsonb(documentRequestAssignmentTaskPropertyValue.text), toJsonb(documentRequestAssignmentTaskPropertyValue.integer), toJsonb(documentRequestAssignmentTaskPropertyValue.decimal), toJsonb(documentRequestAssignmentTaskPropertyValue.boolean), toJsonb(documentRequestAssignmentTaskPropertyValue.date)).as('value')
545
+ })
546
+ .from(documentRequestAssignmentTaskPropertyValue)
547
+ .innerJoin(documentProperty, eq(documentProperty.id, documentRequestAssignmentTaskPropertyValue.propertyId)));
548
+ const [task] = await session
549
+ .with(requestAssignmentTaskProperties)
550
+ .select({
551
+ fileId: documentRequestAssignmentTask.fileId,
552
+ collectionIds: arrayAgg(documentRequestAssignmentTaskCollection.collectionId),
553
+ typeCategory: documentCategory.label,
554
+ typeGroup: documentType.group,
555
+ typeLabel: documentType.label,
556
+ title: documentRequestAssignmentTask.title,
557
+ subtitle: documentRequestAssignmentTask.subtitle,
558
+ pages: documentRequestAssignmentTask.pages,
559
+ date: documentRequestAssignmentTask.date,
560
+ summary: documentRequestAssignmentTask.summary,
561
+ tags: documentRequestAssignmentTask.tags,
562
+ assignmentTries: documentRequestAssignmentTask.assignmentTries,
563
+ properties: sql `coalesce(${jsonAgg(requestAssignmentTaskProperties)} FILTER (WHERE ${requestAssignmentTaskProperties.propertyId} IS NOT NULL), '[]'::json)`
564
+ })
565
+ .from(documentRequestAssignmentTask)
566
+ .innerJoin(documentRequestAssignmentTaskCollection, eq(documentRequestAssignmentTaskCollection.requestAssignmentTaskId, documentRequestAssignmentTask.id))
567
+ .innerJoin(documentType, eq(documentType.id, documentRequestAssignmentTask.typeId))
568
+ .innerJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
569
+ .leftJoin(requestAssignmentTaskProperties, eq(requestAssignmentTaskProperties.requestAssignmentTaskId, documentRequestAssignmentTask.id))
570
+ .where(eq(documentRequestAssignmentTask.id, requestAssignmentTaskId))
571
+ .groupBy(documentRequestAssignmentTask.fileId, requestAssignmentTaskProperties.label, documentCategory.label, documentType.group, documentType.label, documentRequestAssignmentTask.title, documentRequestAssignmentTask.subtitle, documentRequestAssignmentTask.pages, documentRequestAssignmentTask.date, documentRequestAssignmentTask.summary, documentRequestAssignmentTask.tags, documentRequestAssignmentTask.assignmentTries);
572
+ assertDefined(task);
573
+ if (task.assignmentTries >= 3) {
574
+ this.logger.error(`Ignoring assignment task "${requestAssignmentTaskId}" because max tries are reached.`);
575
+ return;
340
576
  }
577
+ const requestsWithoutFiles = await session
578
+ .select({
579
+ id: documentRequest.id,
580
+ collectionIds: arrayAgg(documentRequestCollection.collectionId),
581
+ documentCategory: documentCategory.label,
582
+ documentGroup: documentType.group,
583
+ documentType: documentType.label,
584
+ comment: documentRequest.comment
585
+ })
586
+ .from(documentRequest)
587
+ .innerJoin(documentRequestCollection, eq(documentRequestCollection.requestId, documentRequest.id))
588
+ .innerJoin(documentType, eq(documentType.id, documentRequest.typeId))
589
+ .innerJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
590
+ .where(and(inArray(documentRequestCollection.collectionId, task.collectionIds), notExists(session
591
+ .select()
592
+ .from(documentRequestFile)
593
+ .where(and(eq(documentRequestFile.requestId, documentRequest.id), ne(documentRequestFile.approval, false))))))
594
+ .groupBy(documentRequest.id, documentCategory.label, documentType.group, documentType.label, documentRequest.comment);
595
+ const requestsCollectionIds = distinct(requestsWithoutFiles.flatMap((request) => request.collectionIds));
596
+ const collectionNamesMap = await this.resolveNamesMap(...requestsCollectionIds);
597
+ const requests = requestsWithoutFiles.map((request) => ({
598
+ id: request.id,
599
+ collections: request.collectionIds.map((collectionId) => assertDefinedPass(collectionNamesMap[collectionId])),
600
+ documentCategory: request.documentCategory,
601
+ documentGroup: request.documentGroup ?? undefined,
602
+ documentType: request.documentType,
603
+ comment: request.comment ?? undefined
604
+ }));
605
+ const propertyEntries = task.properties.map((property) => [property.label, assertNotNullPass(property.value)]);
606
+ const context = {
607
+ file: {
608
+ documentCategory: task.typeCategory,
609
+ documentGroup: task.typeGroup ?? undefined,
610
+ documentType: task.typeLabel,
611
+ properties: fromEntries(propertyEntries)
612
+ },
613
+ requests
614
+ };
615
+ const result = await this.#aiService.generate({
616
+ ...defaultGenerationOptions,
617
+ generationOptions: {
618
+ maxOutputTokens: 100,
619
+ temperature: 0,
620
+ topP: 0.2
621
+ },
622
+ generationSchema: object({ requestId: nullable(string()) }),
623
+ contents: [{
624
+ role: 'user',
625
+ parts: [
626
+ {
627
+ text: `<context>
628
+ ${JSON.stringify(context, null, 2)}
629
+ </context>
630
+
631
+ Ordne die Datei unter "file" der passenden Anforderungen unter "requests" zu. Gib es als JSON im angegebenen Schema aus. Wenn keine Anforderung passt, setze requestId auf null.`
632
+ }
633
+ ]
634
+ }]
635
+ });
636
+ await this.documentRequestAssignmentTaskService.update(requestAssignmentTaskId, { assignmentTries: sql `${documentRequestAssignmentTask.assignmentTries} + 1` });
637
+ if (isNull(result.json.requestId)) {
638
+ return;
639
+ }
640
+ await this.documentRequestFileService.transaction(async (_, transaction) => {
641
+ const requestFile = await this.documentRequestFileService.withTransaction(transaction).insert({
642
+ requestId: result.json.requestId,
643
+ fileId: task.fileId,
644
+ title: task.title,
645
+ subtitle: task.subtitle,
646
+ pages: task.pages,
647
+ date: task.date,
648
+ summary: task.summary,
649
+ tags: task.tags,
650
+ createdDocumentId: null,
651
+ approval: null,
652
+ approvalComment: null,
653
+ approvalTimestamp: null
654
+ });
655
+ await this.setPropertyValues({ requestFileId: requestFile.id, properties: task.properties }, transaction);
656
+ });
657
+ }
658
+ async extractFileInformation(fileId, assumeTypeId) {
659
+ const env_1 = { stack: [], error: void 0, hasError: false };
660
+ try {
661
+ const file = await this.documentFileService.load(fileId);
662
+ const fileContentStream = this.getDocumentFileContentStream(fileId);
663
+ const tmpFile = __addDisposableResource(env_1, await TemporaryFile.from(fileContentStream), true);
664
+ const filePart = await this.#aiService.processFile({ path: tmpFile.path, mimeType: file.mimeType });
665
+ const pages = file.mimeType.includes('pdf')
666
+ ? await tryIgnoreLogAsync(async () => getPdfPageCount(tmpFile.path), this.logger, null)
667
+ : null;
668
+ const types = await this.documentTypeService.session
669
+ .select({
670
+ typeId: documentType.id,
671
+ categoryLabel: documentCategory.label,
672
+ typeGroup: documentType.group,
673
+ typeLabel: documentType.label
674
+ })
675
+ .from(documentType)
676
+ .leftJoin(documentCategory, eq(documentCategory.id, documentType.categoryId))
677
+ .where(isString(assumeTypeId) ? eq(documentType.id, assumeTypeId) : undefined);
678
+ const typeLabelEntries = types.map((type) => ({ id: type.typeId, label: `${type.categoryLabel} | ${type.typeGroup} | ${type.typeLabel}` }));
679
+ const typeLabels = typeLabelEntries.map(({ label }) => label);
680
+ const documentTypeGeneration = await this.#aiService.generate({
681
+ ...defaultGenerationOptions,
682
+ generationSchema: object({
683
+ documentType: enumeration(typeLabels)
684
+ }),
685
+ contents: [
686
+ {
687
+ role: 'user',
688
+ parts: [
689
+ { file: filePart.file },
690
+ { text: `Klassifiziere den Inhalt des Dokuments in das angegebenen JSON Schema.` }
691
+ ]
692
+ }
693
+ ]
694
+ });
695
+ const typeId = typeLabelEntries.find((entry) => entry.label == documentTypeGeneration.json.documentType)?.id;
696
+ const typeProperties = isDefined(typeId) ? await this.documentTypePropertyService.loadManyByQuery({ typeId }) : undefined;
697
+ const propertyIds = typeProperties?.map((property) => property.propertyId);
698
+ const properties = isDefined(propertyIds) ? await this.documentPropertyService.loadManyByQuery({ id: { $in: propertyIds } }) : undefined;
699
+ const propertiesSchemaEntries = properties?.map((property) => {
700
+ const schema = match(property.dataType)
701
+ .with('text', () => nullable(string()))
702
+ .with('integer', () => nullable(integer()))
703
+ .with('decimal', () => nullable(number()))
704
+ .with('boolean', () => nullable(boolean()))
705
+ .with('date', () => nullable(object({ year: integer(), month: integer(), day: integer() })))
706
+ .exhaustive();
707
+ return [property.label, schema];
708
+ });
709
+ const generationSchema = object({
710
+ documentTitle: string(),
711
+ documentSubtitle: nullable(string()),
712
+ documentSummary: string(),
713
+ documentTags: array(string()),
714
+ documentDate: nullable(object({ year: integer(), month: integer(), day: integer() })),
715
+ ...((isUndefined(propertiesSchemaEntries) || (propertiesSchemaEntries.length == 0))
716
+ ? {}
717
+ : { documentProperties: object(fromEntries(propertiesSchemaEntries)) })
718
+ });
719
+ const { json: extraction } = await this.#aiService.generate({
720
+ model: 'gemini-2.0-flash',
721
+ generationOptions: {
722
+ maxOutputTokens: 2048,
723
+ temperature: 0.2,
724
+ topP: 0.2
725
+ },
726
+ generationSchema,
727
+ contents: [
728
+ {
729
+ role: 'user',
730
+ parts: [
731
+ { file: filePart.file },
732
+ {
733
+ text: `Extrahiere den Inhalt des Dokuments in das angegebenen JSON Schema.
734
+
735
+ Gib in der summary ausführlich an, welche Informationen in dem Dokument vorkommen(ohne konkrete Werte).
736
+ Erstelle bis zu 7 möglichst spezifische Tags.
737
+ Antworte auf deutsch.`
738
+ }
739
+ ]
740
+ }
741
+ ]
742
+ });
743
+ const filteredDocumentTags = extraction.documentTags.filter((tag) => (tag != extraction.documentTitle) && (tag != extraction.documentSubtitle));
744
+ const date = isNotNull(extraction.documentDate) ? dateObjectToNumericDate(extraction.documentDate) : null;
745
+ const parsedProperties = isUndefined(extraction.documentProperties)
746
+ ? []
747
+ : objectEntries(extraction.documentProperties)
748
+ .map(([propertyLabel, rawValue]) => {
749
+ if (isNull(rawValue)) {
750
+ return null;
751
+ }
752
+ const property = assertDefinedPass(properties?.find((property) => property.label == propertyLabel));
753
+ const value = match(rawValue)
754
+ .with({ year: P.number }, (val) => dateObjectToNumericDate(val))
755
+ .otherwise((val) => val);
756
+ return { propertyId: property.id, dataType: property.dataType, value };
757
+ })
758
+ .filter(isNotNull);
759
+ return {
760
+ typeId: typeId ?? null,
761
+ title: extraction.documentTitle,
762
+ subtitle: extraction.documentSubtitle,
763
+ pages,
764
+ date,
765
+ summary: extraction.documentSummary,
766
+ tags: filteredDocumentTags,
767
+ properties: parsedProperties
768
+ };
769
+ }
770
+ catch (e_1) {
771
+ env_1.error = e_1;
772
+ env_1.hasError = true;
773
+ }
774
+ finally {
775
+ const result_1 = __disposeResources(env_1);
776
+ if (result_1)
777
+ await result_1;
778
+ }
779
+ }
780
+ async getDocumentFileContent(fileId) {
781
+ const key = getDocumentFileKey(fileId);
782
+ return this.fileObjectStorage.getContent(key);
783
+ }
784
+ getDocumentFileContentStream(fileId) {
785
+ const key = getDocumentFileKey(fileId);
786
+ return this.fileObjectStorage.getContentStream(key);
341
787
  }
342
788
  async getDocumentFileContentObjectUrl(title, file, download) {
343
789
  const key = getDocumentFileKey(file.id);
@@ -346,11 +792,11 @@ let DocumentManagementService = class DocumentManagementService {
346
792
  const filename = `${title}.${fileExtension}`;
347
793
  return this.fileObjectStorage.getDownloadUrl(key, currentTimestamp() + (5 * millisecondsPerMinute), {
348
794
  'Response-Content-Type': file.mimeType,
349
- 'Response-Content-Disposition': `${disposition}; filename="${encodeURIComponent(filename)}"`
795
+ 'Response-Content-Disposition': `${disposition}; filename = "${encodeURIComponent(filename)}"`
350
796
  });
351
797
  }
352
798
  };
353
- DocumentManagementService = __decorate([
799
+ DocumentManagementService = DocumentManagementService_1 = __decorate([
354
800
  Singleton({
355
801
  providers: [
356
802
  provide(EntityRepositoryConfig, { useValue: { schema: 'document_management' } }),
@@ -360,13 +806,14 @@ DocumentManagementService = __decorate([
360
806
  ], DocumentManagementService);
361
807
  export { DocumentManagementService };
362
808
  function getDocumentFileKey(id) {
363
- return `${id.slice(0, 2)}/${id.slice(0, 4)}/${id}`;
809
+ return `${id.slice(0, 2)} /${id.slice(0, 4)}/${id} `;
364
810
  }
365
811
  const validators = {
366
812
  [DocumentPropertyDataType.Text]: (value) => isString(value) || isNull(value),
367
813
  [DocumentPropertyDataType.Integer]: (value) => isNumber(value) || isNull(value),
368
814
  [DocumentPropertyDataType.Decimal]: (value) => isNumber(value) || isNull(value),
369
- [DocumentPropertyDataType.Boolean]: (value) => isBoolean(value) || isNull(value)
815
+ [DocumentPropertyDataType.Boolean]: (value) => isBoolean(value) || isNull(value),
816
+ [DocumentPropertyDataType.Date]: (value) => isNumber(value) || isNull(value)
370
817
  };
371
818
  function validatePropertyValue(propertyId, dataType, value) {
372
819
  const valid = validators[dataType](value);