@tstdl/base 0.93.95 → 0.93.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/document-management/api/document-management.api.d.ts +19 -1
- package/document-management/api/document-management.api.js +8 -4
- package/document-management/models/document-category.model.d.ts +1 -0
- package/document-management/models/document-category.model.js +7 -1
- package/document-management/models/document-property.model.d.ts +1 -0
- package/document-management/models/document-property.model.js +7 -1
- package/document-management/models/document-type.model.d.ts +1 -0
- package/document-management/models/document-type.model.js +7 -1
- package/document-management/models/document-workflow.model.d.ts +1 -0
- package/document-management/models/document-workflow.model.js +6 -1
- package/document-management/server/api/document-management.api.d.ts +1 -0
- package/document-management/server/api/document-management.api.js +8 -7
- package/document-management/server/drizzle/{0000_glamorous_lorna_dane.sql → 0000_needy_steel_serpent.sql} +7 -0
- package/document-management/server/drizzle/meta/0000_snapshot.json +49 -1
- package/document-management/server/drizzle/meta/_journal.json +2 -2
- package/document-management/server/module.d.ts +1 -0
- package/document-management/server/module.js +1 -0
- package/document-management/server/services/document-category-type.service.d.ts +8 -3
- package/document-management/server/services/document-category-type.service.js +49 -6
- package/document-management/server/services/document-management.service.js +17 -15
- package/document-management/server/services/document-property.service.d.ts +3 -1
- package/document-management/server/services/document-property.service.js +23 -2
- package/document-management/server/services/document-validation.service.js +2 -1
- package/document-management/server/services/document-workflow.service.d.ts +3 -3
- package/document-management/server/services/document-workflow.service.js +34 -15
- package/document-management/server/services/document.service.d.ts +1 -1
- package/document-management/server/services/document.service.js +7 -2
- package/document-management/service-models/categories-and-types.view-model.d.ts +6 -0
- package/document-management/service-models/categories-and-types.view-model.js +18 -0
- package/document-management/service-models/document-management.view-model.d.ts +1 -0
- package/document-management/service-models/document-management.view-model.js +5 -0
- package/document-management/service-models/document.service-model.d.ts +7 -0
- package/document-management/service-models/document.service-model.js +7 -1
- package/document-management/service-models/enriched/enriched-document-category.view.d.ts +1 -0
- package/document-management/service-models/enriched/enriched-document-category.view.js +2 -0
- package/document-management/service-models/enriched/enriched-document-type.view.d.ts +1 -0
- package/document-management/service-models/enriched/enriched-document-type.view.js +2 -0
- package/document-management/tests/document-management-core.test.d.ts +1 -0
- package/document-management/tests/document-management-core.test.js +162 -0
- package/document-management/tests/document.service.test.d.ts +1 -0
- package/document-management/tests/document.service.test.js +139 -0
- package/document-management/tests/enum-helpers.test.d.ts +1 -0
- package/document-management/tests/enum-helpers.test.js +452 -0
- package/document-management/tests/helper.d.ts +24 -0
- package/document-management/tests/helper.js +39 -0
- package/package.json +5 -5
|
@@ -18,7 +18,7 @@ import { distinct } from '../../../utils/array/index.js';
|
|
|
18
18
|
import { compareByValueSelection } from '../../../utils/comparison.js';
|
|
19
19
|
import { groupToMap, groupToSingleMap } from '../../../utils/iterable-helpers/index.js';
|
|
20
20
|
import { fromEntries, objectEntries } from '../../../utils/object/index.js';
|
|
21
|
-
import { assertDefinedPass, isDefined, isNotNull,
|
|
21
|
+
import { assertDefinedPass, isDefined, isNotNull, isNull, isUndefined } from '../../../utils/type-guards.js';
|
|
22
22
|
import { filter, merge } from 'rxjs';
|
|
23
23
|
import { DocumentAssignmentScope, DocumentAssignmentTask, DocumentCategory, DocumentCollectionAssignment, DocumentRequest, DocumentRequestCollectionAssignment, DocumentRequestTemplate, DocumentRequestsTemplate, DocumentType, DocumentTypeProperty, DocumentValidationExecution, DocumentWorkflowStep } from '../../models/index.js';
|
|
24
24
|
import { documentAssignmentScope, documentAssignmentTask, documentCollectionAssignment, documentRequest, documentRequestCollectionAssignment } from '../schemas.js';
|
|
@@ -29,7 +29,6 @@ import { DocumentPropertyService } from './document-property.service.js';
|
|
|
29
29
|
import { DocumentTagService } from './document-tag.service.js';
|
|
30
30
|
import { DocumentWorkflowService } from './document-workflow.service.js';
|
|
31
31
|
import { DocumentService } from './document.service.js';
|
|
32
|
-
import { enumTypeKey } from './enum-type-key.js';
|
|
33
32
|
import { DocumentManagementSingleton } from './singleton.js';
|
|
34
33
|
let DocumentManagementService = DocumentManagementService_1 = class DocumentManagementService extends Transactional {
|
|
35
34
|
#documentCollectionService = injectTransactional(DocumentCollectionService);
|
|
@@ -92,7 +91,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
92
91
|
throw new BadRequestError('At least one collection ID must be provided to load document management data.');
|
|
93
92
|
}
|
|
94
93
|
return await this.transaction(async (tx) => {
|
|
95
|
-
const [collections, documentCollectionAssignments, requestAssignments, assignmentScopes, { categories, types }] = await Promise.all([
|
|
94
|
+
const [collections, documentCollectionAssignments, requestAssignments, assignmentScopes, { categories, types, properties }] = await Promise.all([
|
|
96
95
|
this.#documentCollectionService.repository.withTransaction(tx).loadManyByQuery({ tenantId, id: { $in: collectionIds } }),
|
|
97
96
|
this.#documentCollectionAssignmentRepository.withTransaction(tx).loadManyByQuery({ tenantId, collectionId: { $in: collectionIds } }),
|
|
98
97
|
this.#documentRequestCollectionAssignmentRepository.withTransaction(tx).loadManyByQuery({ tenantId, collectionId: { $in: collectionIds } }),
|
|
@@ -110,9 +109,8 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
110
109
|
const requestDocumentIds = requests.map((request) => request.documentId).filter(isNotNull);
|
|
111
110
|
const assignmentTaskDocumentIds = assignmentTasks.map((task) => task.documentId);
|
|
112
111
|
const documentIds = distinct([...assignmentDocumentIds, ...requestDocumentIds, ...assignmentTaskDocumentIds]);
|
|
113
|
-
const [documents,
|
|
112
|
+
const [documents, propertyValues, workflows] = await Promise.all([
|
|
114
113
|
this.#documentService.repository.withTransaction(tx).loadManyByQuery({ tenantId, id: { $in: documentIds } }, { order: { 'metadata.createTimestamp': 'desc' } }),
|
|
115
|
-
this.#documentPropertyService.withTransaction(tx).loadViews(tenantId),
|
|
116
114
|
this.#documentPropertyService.withTransaction(tx).loadDocumentPropertyValues(tenantId, documentIds),
|
|
117
115
|
this.#documentWorkflowService.withTransaction(tx).loadWorkflows(tenantId, documentIds),
|
|
118
116
|
]);
|
|
@@ -164,7 +162,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
164
162
|
categories,
|
|
165
163
|
types,
|
|
166
164
|
tags,
|
|
167
|
-
properties
|
|
165
|
+
properties,
|
|
168
166
|
};
|
|
169
167
|
});
|
|
170
168
|
}
|
|
@@ -182,25 +180,27 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
182
180
|
});
|
|
183
181
|
}
|
|
184
182
|
async initializeCategoriesAndTypes(tenantId, data) {
|
|
183
|
+
this.#documentCategoryTypeService.invalidateCache(tenantId);
|
|
184
|
+
this.#documentPropertyService.invalidateCache(tenantId);
|
|
185
185
|
const categoryEntries = objectEntries(data.categoryLabels);
|
|
186
186
|
const typeEntries = objectEntries(data.typeLabels);
|
|
187
187
|
const propertyEntries = objectEntries(data.propertyConfigurations);
|
|
188
188
|
const { categoryMap, typeMap, propertyMap } = await this.transaction(async (tx) => {
|
|
189
189
|
const { categories: dbCategories, types: dbTypes } = await this.#documentCategoryTypeService.withTransaction(tx).loadCategoriesAndTypes(tenantId);
|
|
190
190
|
const dbProperties = await this.#documentPropertyService.withTransaction(tx).repository.loadManyByQuery({ tenantId });
|
|
191
|
-
const categories = dbCategories.filter((category) =>
|
|
192
|
-
const types = dbTypes.filter((type) =>
|
|
193
|
-
const properties = dbProperties.filter((property) =>
|
|
194
|
-
const enumKeyCategoryMap = groupToSingleMap(categories, (category) => category.
|
|
195
|
-
const enumKeyTypeMap = groupToSingleMap(types, (type) => type.
|
|
196
|
-
const enumKeyPropertyMap = groupToSingleMap(properties, (property) => property.
|
|
191
|
+
const categories = dbCategories.filter((category) => isNotNull(category.key));
|
|
192
|
+
const types = dbTypes.filter((type) => isNotNull(type.key));
|
|
193
|
+
const properties = dbProperties.filter((property) => isNotNull(property.key));
|
|
194
|
+
const enumKeyCategoryMap = groupToSingleMap(categories, (category) => category.key);
|
|
195
|
+
const enumKeyTypeMap = groupToSingleMap(types, (type) => type.key);
|
|
196
|
+
const enumKeyPropertyMap = groupToSingleMap(properties, (property) => property.key);
|
|
197
197
|
for (const [enumKey, label] of categoryEntries) {
|
|
198
198
|
const category = enumKeyCategoryMap.get(enumKey);
|
|
199
199
|
const parentKey = assertDefinedPass(data.categoryParents[enumKey], `Parent category not defined for ${enumKey}`);
|
|
200
200
|
const parentCategory = isNull(parentKey) ? null : assertDefinedPass(enumKeyCategoryMap.get(parentKey));
|
|
201
201
|
const parentCategoryId = parentCategory?.id ?? null;
|
|
202
202
|
if (isUndefined(category)) {
|
|
203
|
-
const category = await this.#documentCategoryTypeService.withTransaction(tx).createCategory({ tenantId, label, parentId: parentCategoryId, enumKey });
|
|
203
|
+
const category = await this.#documentCategoryTypeService.withTransaction(tx).createCategory({ tenantId, label, parentId: parentCategoryId, key: enumKey });
|
|
204
204
|
enumKeyCategoryMap.set(enumKey, category);
|
|
205
205
|
this.#logger.info(`Created category "${category.label}"`);
|
|
206
206
|
}
|
|
@@ -215,7 +215,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
215
215
|
const enumCategory = data.typeCategories[enumKey];
|
|
216
216
|
const category = assertDefinedPass(enumKeyCategoryMap.get(enumCategory));
|
|
217
217
|
if (isUndefined(type)) {
|
|
218
|
-
const type = await this.#documentCategoryTypeService.withTransaction(tx).createType({ tenantId, label, categoryId: category.id, enumKey });
|
|
218
|
+
const type = await this.#documentCategoryTypeService.withTransaction(tx).createType({ tenantId, label, categoryId: category.id, key: enumKey });
|
|
219
219
|
enumKeyTypeMap.set(enumKey, type);
|
|
220
220
|
this.#logger.info(`Created type "${type.label}" in category "${category.label}"`);
|
|
221
221
|
}
|
|
@@ -228,7 +228,7 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
228
228
|
for (const [enumKey, [dataType, label]] of propertyEntries) {
|
|
229
229
|
const property = enumKeyPropertyMap.get(enumKey);
|
|
230
230
|
if (isUndefined(property)) {
|
|
231
|
-
const newProperty = await this.#documentPropertyService.withTransaction(tx).createProperty({ tenantId, label, dataType, enumKey });
|
|
231
|
+
const newProperty = await this.#documentPropertyService.withTransaction(tx).createProperty({ tenantId, label, dataType, key: enumKey });
|
|
232
232
|
enumKeyPropertyMap.set(enumKey, newProperty);
|
|
233
233
|
this.#logger.info(`Created property "${newProperty.label}" of type "${dataType}"`);
|
|
234
234
|
}
|
|
@@ -249,6 +249,8 @@ let DocumentManagementService = DocumentManagementService_1 = class DocumentMana
|
|
|
249
249
|
}
|
|
250
250
|
return { categoryMap: enumKeyCategoryMap, typeMap: enumKeyTypeMap, propertyMap: enumKeyPropertyMap };
|
|
251
251
|
});
|
|
252
|
+
this.#documentCategoryTypeService.invalidateCache(tenantId);
|
|
253
|
+
this.#documentPropertyService.invalidateCache(tenantId);
|
|
252
254
|
const mappedCategories = categoryEntries.map(([key]) => [key, assertDefinedPass(categoryMap.get(key), 'Could not map document category.')]);
|
|
253
255
|
const mappedTypes = typeEntries.map(([key]) => [key, assertDefinedPass(typeMap.get(key), 'Could not map document type.')]);
|
|
254
256
|
const mappedProperties = propertyEntries.map(([key]) => [key, assertDefinedPass(propertyMap.get(key), 'Could not map document property.')]);
|
|
@@ -5,6 +5,8 @@ import type { DocumentPropertyValueView, DocumentPropertyView, SetDocumentProper
|
|
|
5
5
|
export declare class DocumentPropertyService extends Transactional {
|
|
6
6
|
#private;
|
|
7
7
|
readonly repository: import("../../../orm/server/index.js").EntityRepository<DocumentProperty>;
|
|
8
|
+
invalidateCache(tenantId: string): void;
|
|
9
|
+
getPropertyId(tenantId: string, key: string): Promise<string>;
|
|
8
10
|
readonly documentProperties: import("drizzle-orm/pg-core").WithSubqueryWithSelection<{
|
|
9
11
|
documentId: import("drizzle-orm").SQL.Aliased<string>;
|
|
10
12
|
propertyId: import("drizzle-orm").SQL.Aliased<string>;
|
|
@@ -51,7 +53,7 @@ export declare class DocumentPropertyService extends Transactional {
|
|
|
51
53
|
tenantId: string;
|
|
52
54
|
label: string;
|
|
53
55
|
dataType: DocumentPropertyDataType;
|
|
54
|
-
|
|
56
|
+
key?: string;
|
|
55
57
|
}): Promise<DocumentProperty>;
|
|
56
58
|
updateProperty(tenantId: string, id: string, update: {
|
|
57
59
|
label?: string;
|
|
@@ -15,7 +15,6 @@ import { assertBooleanPass, assertNumberPass, assertStringPass, isBoolean, isNot
|
|
|
15
15
|
import { DocumentProperty, DocumentPropertyDataType, DocumentPropertyValue, DocumentTypeProperty } from '../../models/index.js';
|
|
16
16
|
import { document, documentProperty, documentPropertyValue, documentType, documentTypeProperty } from '../schemas.js';
|
|
17
17
|
import { DocumentManagementObservationService } from './document-management-observation.service.js';
|
|
18
|
-
import { enumTypeKey } from './enum-type-key.js';
|
|
19
18
|
import { DocumentManagementSingleton } from './singleton.js';
|
|
20
19
|
const documentPropertyValueValidators = {
|
|
21
20
|
[DocumentPropertyDataType.Text]: (value) => isString(value) || isNull(value),
|
|
@@ -30,6 +29,18 @@ let DocumentPropertyService = class DocumentPropertyService extends Transactiona
|
|
|
30
29
|
#documentTypePropertyRepository = injectRepository(DocumentTypeProperty);
|
|
31
30
|
#observationService = inject(DocumentManagementObservationService);
|
|
32
31
|
repository = injectRepository(DocumentProperty);
|
|
32
|
+
#propertyCache = new Map();
|
|
33
|
+
invalidateCache(tenantId) {
|
|
34
|
+
this.#propertyCache.delete(tenantId);
|
|
35
|
+
}
|
|
36
|
+
async getPropertyId(tenantId, key) {
|
|
37
|
+
const cache = await this.#getPropertyCache(tenantId);
|
|
38
|
+
const id = cache.get(key);
|
|
39
|
+
if (isUndefined(id)) {
|
|
40
|
+
throw new NotFoundError(`Document property with key "${key}" not found for tenant "${tenantId}".`);
|
|
41
|
+
}
|
|
42
|
+
return id;
|
|
43
|
+
}
|
|
33
44
|
documentProperties = this.session.$with('documentProperties').as((qb) => qb
|
|
34
45
|
.select({
|
|
35
46
|
documentId: autoAlias(document.id),
|
|
@@ -55,6 +66,7 @@ let DocumentPropertyService = class DocumentPropertyService extends Transactiona
|
|
|
55
66
|
return {
|
|
56
67
|
id: property.id,
|
|
57
68
|
tenantId: property.tenantId,
|
|
69
|
+
key: property.key,
|
|
58
70
|
label: property.label,
|
|
59
71
|
dataType: property.dataType,
|
|
60
72
|
typeIds,
|
|
@@ -66,7 +78,7 @@ let DocumentPropertyService = class DocumentPropertyService extends Transactiona
|
|
|
66
78
|
tenantId: data.tenantId,
|
|
67
79
|
label: data.label,
|
|
68
80
|
dataType: data.dataType,
|
|
69
|
-
|
|
81
|
+
key: data.key ?? null,
|
|
70
82
|
});
|
|
71
83
|
}
|
|
72
84
|
async updateProperty(tenantId, id, update) {
|
|
@@ -120,6 +132,15 @@ let DocumentPropertyService = class DocumentPropertyService extends Transactiona
|
|
|
120
132
|
this.#observationService.documentChange(document.id, tx);
|
|
121
133
|
});
|
|
122
134
|
}
|
|
135
|
+
async #getPropertyCache(tenantId) {
|
|
136
|
+
let cache = this.#propertyCache.get(tenantId);
|
|
137
|
+
if (isUndefined(cache)) {
|
|
138
|
+
const properties = await this.#documentPropertyRepository.loadManyByQuery({ tenantId, key: { $neq: null } });
|
|
139
|
+
cache = new Map(properties.map((property) => [property.key, property.id]));
|
|
140
|
+
this.#propertyCache.set(tenantId, cache);
|
|
141
|
+
}
|
|
142
|
+
return cache;
|
|
143
|
+
}
|
|
123
144
|
};
|
|
124
145
|
DocumentPropertyService = __decorate([
|
|
125
146
|
DocumentManagementSingleton()
|
|
@@ -56,7 +56,8 @@ let DocumentValidationService = DocumentValidationService_1 = class DocumentVali
|
|
|
56
56
|
if (isNull(document.typeId)) {
|
|
57
57
|
throw new BadRequestError('Document has no type');
|
|
58
58
|
}
|
|
59
|
-
const
|
|
59
|
+
const latestWorkflow = await this.#documentWorkflowService.loadLatestWorkflow(tenantId, documentId);
|
|
60
|
+
const workflow = await this.#documentWorkflowService.initiateWorkflow(document.tenantId, documentId, DocumentWorkflowStep.Validation, latestWorkflow?.skipAi ?? false);
|
|
60
61
|
const typeValidations = await this.#documentTypeValidationService.loadManyByQuery({ tenantId, typeId: document.typeId });
|
|
61
62
|
for (const typeValidation of typeValidations) {
|
|
62
63
|
const validationDefinition = await this.#validationDefinitionService.loadByQuery({ tenantId, id: typeValidation.validationId });
|
|
@@ -2,7 +2,7 @@ import type { AfterResolveContext } from '../../../injector/index.js';
|
|
|
2
2
|
import { afterResolve } from '../../../injector/interfaces.js';
|
|
3
3
|
import { Transactional } from '../../../orm/server/index.js';
|
|
4
4
|
import type { OneOrMany } from '../../../types/index.js';
|
|
5
|
-
import { DocumentWorkflow, DocumentWorkflowStep } from '../../models/document-workflow.model.js';
|
|
5
|
+
import { DocumentWorkflow, DocumentWorkflowState, DocumentWorkflowStep } from '../../models/document-workflow.model.js';
|
|
6
6
|
export declare class DocumentWorkflowService extends Transactional {
|
|
7
7
|
#private;
|
|
8
8
|
private readonly documentService;
|
|
@@ -11,8 +11,8 @@ export declare class DocumentWorkflowService extends Transactional {
|
|
|
11
11
|
loadLatestWorkflow(tenantId: string, documentId: string): Promise<DocumentWorkflow>;
|
|
12
12
|
loadLatestWorkflows(tenantId: string, documentIds: string[]): Promise<DocumentWorkflow[]>;
|
|
13
13
|
loadWorkflows(tenantId: string, documentId: OneOrMany<string>): Promise<DocumentWorkflow[]>;
|
|
14
|
-
proceedWorkflow(tenantId: string, documentId: string, userId: string): Promise<void>;
|
|
15
|
-
initiateWorkflow(tenantId: string, documentId: string, step: DocumentWorkflowStep): Promise<DocumentWorkflow>;
|
|
14
|
+
proceedWorkflow(tenantId: string, documentId: string, userId: string, targetState: DocumentWorkflowState): Promise<void>;
|
|
15
|
+
initiateWorkflow(tenantId: string, documentId: string, step: DocumentWorkflowStep, skipAi: boolean): Promise<DocumentWorkflow>;
|
|
16
16
|
private setWorkflowState;
|
|
17
17
|
private processWorkflowJob;
|
|
18
18
|
private processClassificationWorkflow;
|
|
@@ -16,7 +16,6 @@ import { injectRepository, injectTransactional, Transactional } from '../../../o
|
|
|
16
16
|
import { TaskProcessResult, TaskQueue } from '../../../task-queue/task-queue.js';
|
|
17
17
|
import { toArray } from '../../../utils/array/array.js';
|
|
18
18
|
import { currentTimestamp } from '../../../utils/date-time.js';
|
|
19
|
-
import { _throw } from '../../../utils/throw.js';
|
|
20
19
|
import { isNotNull, isNull } from '../../../utils/type-guards.js';
|
|
21
20
|
import { and, desc, eq, inArray } from 'drizzle-orm';
|
|
22
21
|
import { DocumentAssignmentScope } from '../../models/document-assignment-scope.model.js';
|
|
@@ -61,28 +60,48 @@ let DocumentWorkflowService = DocumentWorkflowService_1 = class DocumentWorkflow
|
|
|
61
60
|
async loadWorkflows(tenantId, documentId) {
|
|
62
61
|
return await this.repository.loadManyByQuery({ tenantId, documentId: { $in: toArray(documentId) } }, { order: { 'documentId': 'asc', 'metadata.createTimestamp': 'desc' } });
|
|
63
62
|
}
|
|
64
|
-
async proceedWorkflow(tenantId, documentId, userId) {
|
|
63
|
+
async proceedWorkflow(tenantId, documentId, userId, targetState) {
|
|
65
64
|
await this.transaction(async (tx) => {
|
|
66
65
|
const workflow = await this.withTransaction(tx).loadLatestWorkflow(tenantId, documentId);
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
const canProceed = (workflow.state == DocumentWorkflowState.Review) || (workflow.skipAi && (workflow.state == DocumentWorkflowState.Pending));
|
|
67
|
+
if (!canProceed) {
|
|
68
|
+
throw new BadRequestError('Current workflow is not in a state that can be proceeded.');
|
|
69
|
+
}
|
|
70
|
+
if ((workflow.state == DocumentWorkflowState.Review) && (targetState != DocumentWorkflowState.Completed)) {
|
|
71
|
+
throw new BadRequestError('Can only proceed to completed state from review.');
|
|
72
|
+
}
|
|
73
|
+
if ((targetState != DocumentWorkflowState.Review) && (targetState != DocumentWorkflowState.Completed)) {
|
|
74
|
+
throw new BadRequestError('Invalid target state.');
|
|
69
75
|
}
|
|
70
76
|
if (isNotNull(workflow.completeUserId)) {
|
|
71
77
|
throw new BadRequestError('Latest workflow is already completed.');
|
|
72
78
|
}
|
|
73
|
-
await this.repository.withTransaction(tx).update(workflow.id, {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.
|
|
79
|
+
await this.repository.withTransaction(tx).update(workflow.id, {
|
|
80
|
+
state: targetState,
|
|
81
|
+
completeUserId: (targetState == DocumentWorkflowState.Completed) ? userId : undefined,
|
|
82
|
+
completeTimestamp: (targetState == DocumentWorkflowState.Completed) ? currentTimestamp() : undefined,
|
|
83
|
+
});
|
|
84
|
+
if (targetState == DocumentWorkflowState.Completed) {
|
|
85
|
+
await match(workflow.step)
|
|
86
|
+
.with(DocumentWorkflowStep.Classification, async () => {
|
|
87
|
+
if (!workflow.skipAi) {
|
|
88
|
+
throw new BadRequestError('Proceeding from classification occurs automatically.');
|
|
89
|
+
}
|
|
90
|
+
await this.withTransaction(tx).initiateWorkflow(tenantId, documentId, DocumentWorkflowStep.Extraction, workflow.skipAi);
|
|
91
|
+
})
|
|
92
|
+
.with(DocumentWorkflowStep.Extraction, async () => await this.withTransaction(tx).initiateWorkflow(tenantId, documentId, DocumentWorkflowStep.Assignment, workflow.skipAi))
|
|
93
|
+
.with(DocumentWorkflowStep.Assignment, async () => await this.withTransaction(tx).initiateWorkflow(tenantId, documentId, DocumentWorkflowStep.Validation, workflow.skipAi))
|
|
94
|
+
.with(DocumentWorkflowStep.Validation, () => { })
|
|
95
|
+
.exhaustive();
|
|
96
|
+
}
|
|
80
97
|
this.#observationService.documentChange(workflow.id, tx);
|
|
81
98
|
});
|
|
82
99
|
}
|
|
83
|
-
async initiateWorkflow(tenantId, documentId, step) {
|
|
84
|
-
const workflow = await this.repository.insert({ tenantId, documentId, step, state: 'pending', failReason: null, completeTimestamp: null, completeUserId: null });
|
|
85
|
-
|
|
100
|
+
async initiateWorkflow(tenantId, documentId, step, skipAi) {
|
|
101
|
+
const workflow = await this.repository.insert({ tenantId, documentId, step, state: 'pending', failReason: null, completeTimestamp: null, completeUserId: null, skipAi });
|
|
102
|
+
if (!skipAi) {
|
|
103
|
+
await this.#taskQueue.enqueue('workflow', { workflowId: workflow.id });
|
|
104
|
+
}
|
|
86
105
|
this.#observationService.documentChange(workflow.id, this.session);
|
|
87
106
|
return workflow;
|
|
88
107
|
}
|
|
@@ -104,7 +123,7 @@ let DocumentWorkflowService = DocumentWorkflowService_1 = class DocumentWorkflow
|
|
|
104
123
|
if (workflow.step == DocumentWorkflowStep.Classification) {
|
|
105
124
|
// no need for after classification review. Automatically start extraction.
|
|
106
125
|
await this.setWorkflowState(workflow.id, DocumentWorkflowState.Completed);
|
|
107
|
-
await this.initiateWorkflow(workflow.tenantId, workflow.documentId, DocumentWorkflowStep.Extraction);
|
|
126
|
+
await this.initiateWorkflow(workflow.tenantId, workflow.documentId, DocumentWorkflowStep.Extraction, workflow.skipAi);
|
|
108
127
|
}
|
|
109
128
|
else {
|
|
110
129
|
await this.setWorkflowState(workflow.id, DocumentWorkflowState.Review);
|
|
@@ -5,7 +5,7 @@ import type { CreateDocumentParameters, SetDocumentPropertyParameters, UpdateDoc
|
|
|
5
5
|
export declare class DocumentService extends Transactional {
|
|
6
6
|
#private;
|
|
7
7
|
readonly repository: import("../../../orm/server/index.js").EntityRepository<Document>;
|
|
8
|
-
create(tenantId: string, { typeId, title, subtitle, date, summary, tags, approval, comment, originalFileName, assignment, properties, metadata }: TypedOmit<CreateDocumentParameters, 'uploadId'>, contentSource: Uint8Array<ArrayBuffer> | ReadableStream<Uint8Array<ArrayBuffer>> | {
|
|
8
|
+
create(tenantId: string, { typeId, title, subtitle, date, summary, tags, approval, comment, originalFileName, assignment, properties, skipAi, skipWorkflow, metadata }: TypedOmit<CreateDocumentParameters, 'uploadId'>, contentSource: Uint8Array<ArrayBuffer> | ReadableStream<Uint8Array<ArrayBuffer>> | {
|
|
9
9
|
uploadId: string;
|
|
10
10
|
uploadKey: string;
|
|
11
11
|
}): Promise<Document>;
|
|
@@ -18,6 +18,7 @@ import { readableStreamFromPromise } from '../../../utils/stream/from-promise.js
|
|
|
18
18
|
import { tryIgnoreLogAsync } from '../../../utils/try-ignore.js';
|
|
19
19
|
import { isDefined, isNotReadableStream, isNotUint8Array, isUndefined } from '../../../utils/type-guards.js';
|
|
20
20
|
import { Document, DocumentApproval, DocumentAssignmentScope, DocumentAssignmentTask, DocumentWorkflowStep } from '../../models/index.js';
|
|
21
|
+
import { DocumentManagementConfiguration } from '../module.js';
|
|
21
22
|
import { DocumentCollectionService } from './document-collection.service.js';
|
|
22
23
|
import { DocumentFileService } from './document-file.service.js';
|
|
23
24
|
import { DocumentManagementObservationService } from './document-management-observation.service.js';
|
|
@@ -27,6 +28,7 @@ import { DocumentTagService } from './document-tag.service.js';
|
|
|
27
28
|
import { DocumentWorkflowService } from './document-workflow.service.js';
|
|
28
29
|
import { DocumentManagementSingleton } from './singleton.js';
|
|
29
30
|
let DocumentService = DocumentService_1 = class DocumentService extends Transactional {
|
|
31
|
+
#configuration = inject(DocumentManagementConfiguration);
|
|
30
32
|
#documentFileService = injectTransactional(DocumentFileService);
|
|
31
33
|
#documentTagService = injectTransactional(DocumentTagService);
|
|
32
34
|
#requestService = injectTransactional(DocumentRequestService);
|
|
@@ -38,7 +40,7 @@ let DocumentService = DocumentService_1 = class DocumentService extends Transact
|
|
|
38
40
|
#observationService = inject(DocumentManagementObservationService);
|
|
39
41
|
#logger = inject(Logger, DocumentService_1.name);
|
|
40
42
|
repository = injectRepository(Document);
|
|
41
|
-
async create(tenantId, { typeId, title, subtitle, date, summary, tags, approval, comment, originalFileName, assignment, properties, metadata }, contentSource) {
|
|
43
|
+
async create(tenantId, { typeId, title, subtitle, date, summary, tags, approval, comment, originalFileName, assignment, properties, skipAi, skipWorkflow, metadata }, contentSource) {
|
|
42
44
|
const document = await this.transaction(async (tx) => {
|
|
43
45
|
const isUpload = isNotUint8Array(contentSource) && isNotReadableStream(contentSource);
|
|
44
46
|
const document = await this.repository.withTransaction(tx).insert({
|
|
@@ -74,7 +76,10 @@ let DocumentService = DocumentService_1 = class DocumentService extends Transact
|
|
|
74
76
|
await this.#documentPropertyService.withTransaction(tx).setPropertyValues(document, properties);
|
|
75
77
|
}
|
|
76
78
|
await this.withTransaction(tx).createAssignment(document, assignment, tx);
|
|
77
|
-
|
|
79
|
+
if (skipWorkflow != true) {
|
|
80
|
+
const effectiveSkipAi = this.#configuration.skipAi ?? skipAi ?? false;
|
|
81
|
+
await this.#workflowService.withTransaction(tx).initiateWorkflow(tenantId, document.id, DocumentWorkflowStep.Classification, effectiveSkipAi);
|
|
82
|
+
}
|
|
78
83
|
return document;
|
|
79
84
|
});
|
|
80
85
|
this.#observationService.documentChange(document.id);
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { DocumentCategory, DocumentType } from '../models/index.js';
|
|
2
|
+
import { DocumentPropertyView } from './document-management.view-model.js';
|
|
2
3
|
export declare class DocumentCategoryView extends DocumentCategory {
|
|
3
4
|
children: DocumentCategoryView[];
|
|
4
5
|
types: DocumentType[];
|
|
5
6
|
}
|
|
7
|
+
export declare class CategoriesAndTypesData {
|
|
8
|
+
categories: DocumentCategory[];
|
|
9
|
+
types: DocumentType[];
|
|
10
|
+
properties: DocumentPropertyView[];
|
|
11
|
+
}
|
|
@@ -9,6 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { Array, deferred } from '../../schema/index.js';
|
|
11
11
|
import { DocumentCategory, DocumentType } from '../models/index.js';
|
|
12
|
+
import { DocumentPropertyView } from './document-management.view-model.js';
|
|
12
13
|
export class DocumentCategoryView extends DocumentCategory {
|
|
13
14
|
children;
|
|
14
15
|
types;
|
|
@@ -21,3 +22,20 @@ __decorate([
|
|
|
21
22
|
Array(DocumentType),
|
|
22
23
|
__metadata("design:type", Array)
|
|
23
24
|
], DocumentCategoryView.prototype, "types", void 0);
|
|
25
|
+
export class CategoriesAndTypesData {
|
|
26
|
+
categories;
|
|
27
|
+
types;
|
|
28
|
+
properties;
|
|
29
|
+
}
|
|
30
|
+
__decorate([
|
|
31
|
+
Array(DocumentCategory),
|
|
32
|
+
__metadata("design:type", Array)
|
|
33
|
+
], CategoriesAndTypesData.prototype, "categories", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
Array(DocumentType),
|
|
36
|
+
__metadata("design:type", Array)
|
|
37
|
+
], CategoriesAndTypesData.prototype, "types", void 0);
|
|
38
|
+
__decorate([
|
|
39
|
+
Array(DocumentPropertyView),
|
|
40
|
+
__metadata("design:type", Array)
|
|
41
|
+
], CategoriesAndTypesData.prototype, "properties", void 0);
|
|
@@ -41,6 +41,7 @@ export declare class DocumentRequestView extends DocumentRequest {
|
|
|
41
41
|
export declare class DocumentPropertyView implements TypedOmit<DocumentProperty, 'metadata'> {
|
|
42
42
|
id: string;
|
|
43
43
|
tenantId: string;
|
|
44
|
+
key: string | null;
|
|
44
45
|
label: string;
|
|
45
46
|
dataType: DocumentPropertyDataType;
|
|
46
47
|
typeIds: string[];
|
|
@@ -134,6 +134,7 @@ __decorate([
|
|
|
134
134
|
export class DocumentPropertyView {
|
|
135
135
|
id;
|
|
136
136
|
tenantId;
|
|
137
|
+
key;
|
|
137
138
|
label;
|
|
138
139
|
dataType;
|
|
139
140
|
typeIds;
|
|
@@ -146,6 +147,10 @@ __decorate([
|
|
|
146
147
|
StringProperty({ nullable: true }),
|
|
147
148
|
__metadata("design:type", String)
|
|
148
149
|
], DocumentPropertyView.prototype, "tenantId", void 0);
|
|
150
|
+
__decorate([
|
|
151
|
+
StringProperty({ nullable: true }),
|
|
152
|
+
__metadata("design:type", Object)
|
|
153
|
+
], DocumentPropertyView.prototype, "key", void 0);
|
|
149
154
|
__decorate([
|
|
150
155
|
StringProperty(),
|
|
151
156
|
__metadata("design:type", String)
|
|
@@ -46,6 +46,8 @@ export declare const createDocumentParametersSchema: import("../../schema/index.
|
|
|
46
46
|
}> | undefined;
|
|
47
47
|
}[] | undefined;
|
|
48
48
|
tags?: string[] | undefined;
|
|
49
|
+
skipAi?: boolean | undefined;
|
|
50
|
+
skipWorkflow?: boolean | undefined;
|
|
49
51
|
metadata?: Partial<{
|
|
50
52
|
attributes: import("../../orm/index.js").HasDefault<import("../../orm/index.js").Json<import("../../orm/index.js").EntityMetadataAttributes>>;
|
|
51
53
|
}> | undefined;
|
|
@@ -232,6 +234,10 @@ export declare const addOrArchiveDocumentToOrFromCollectionParametersSchema: imp
|
|
|
232
234
|
attributes: import("../../orm/index.js").HasDefault<import("../../orm/index.js").Json<import("../../orm/index.js").EntityMetadataAttributes>>;
|
|
233
235
|
}> | undefined;
|
|
234
236
|
}>;
|
|
237
|
+
export declare const proceedDocumentWorkflowParametersSchema: import("../../schema/index.js").ObjectSchema<{
|
|
238
|
+
id: string;
|
|
239
|
+
state: "error" | "pending" | "running" | "completed" | "review" | "failed";
|
|
240
|
+
}>;
|
|
235
241
|
export type MetadataParameter = SchemaOutput<typeof metadataParameterObjectSchema>;
|
|
236
242
|
export type LoadDataParameters = SchemaOutput<typeof loadDataParametersSchema>;
|
|
237
243
|
export type CreateCollectionParameters = SchemaOutput<typeof createCollectionParametersSchema>;
|
|
@@ -257,3 +263,4 @@ export type AddOrArchiveDocumentToOrFromCollectionParameters = SchemaOutput<type
|
|
|
257
263
|
export type UpdateDocumentParameters = SchemaOutput<typeof updateDocumentParametersSchema>;
|
|
258
264
|
export type UpdateDocumentRequestParameters = SchemaOutput<typeof updateDocumentRequestParametersSchema>;
|
|
259
265
|
export type DeleteDocumentRequestParameters = SchemaOutput<typeof deleteDocumentRequestParametersSchema>;
|
|
266
|
+
export type ProceedDocumentWorkflowParameters = SchemaOutput<typeof proceedDocumentWorkflowParametersSchema>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EntityMetadata } from '../../orm/index.js';
|
|
2
2
|
import { array, assign, boolean, enumeration, never, nullable, number, object, omit, oneOrMany, optional, partial, pick, string, union } from '../../schema/index.js';
|
|
3
|
-
import { Document, DocumentAssignmentTarget, DocumentCategory, DocumentCollection, DocumentCollectionAssignment, DocumentProperty, DocumentPropertyValue, DocumentRequest, DocumentRequestsTemplate, DocumentRequestTemplate, DocumentType, DocumentTypeProperty } from '../models/index.js';
|
|
3
|
+
import { Document, DocumentAssignmentTarget, DocumentCategory, DocumentCollection, DocumentCollectionAssignment, DocumentProperty, DocumentPropertyValue, DocumentRequest, DocumentRequestsTemplate, DocumentRequestTemplate, DocumentType, DocumentTypeProperty, DocumentWorkflowState } from '../models/index.js';
|
|
4
4
|
export const metadataParameterSchema = optional(partial(pick(EntityMetadata, 'attributes')));
|
|
5
5
|
export const metadataParameterObjectSchema = object({ metadata: metadataParameterSchema });
|
|
6
6
|
export const setDocumentPropertyParametersSchema = assign(pick(DocumentPropertyValue, ['propertyId']), object({ value: union(string(), number(), boolean(), nullable(never())) }), metadataParameterObjectSchema);
|
|
@@ -19,6 +19,8 @@ export const createDocumentParametersSchema = assign(partial(pick(Document, ['ty
|
|
|
19
19
|
})),
|
|
20
20
|
tags: optional(array(string())),
|
|
21
21
|
properties: optional(array(setDocumentPropertyParametersSchema)),
|
|
22
|
+
skipAi: optional(boolean()),
|
|
23
|
+
skipWorkflow: optional(boolean()),
|
|
22
24
|
}), metadataParameterObjectSchema);
|
|
23
25
|
export const updateDocumentParametersSchema = assign(pick(Document, ['id']), partial(pick(Document, ['title', 'subtitle', 'date', 'comment', 'typeId'])), object({
|
|
24
26
|
tags: optional(array(string())),
|
|
@@ -53,3 +55,7 @@ export const rejectDocumentRequestParametersSchema = assign(pick(DocumentRequest
|
|
|
53
55
|
export const createDocumentPropertyParametersSchema = assign(pick(DocumentProperty, ['label', 'dataType']), metadataParameterObjectSchema);
|
|
54
56
|
export const assignPropertyToTypeParametersSchema = assign(pick(DocumentTypeProperty, ['typeId', 'propertyId']), metadataParameterObjectSchema);
|
|
55
57
|
export const addOrArchiveDocumentToOrFromCollectionParametersSchema = assign(pick(DocumentCollectionAssignment, ['collectionId', 'documentId']), metadataParameterObjectSchema);
|
|
58
|
+
export const proceedDocumentWorkflowParametersSchema = object({
|
|
59
|
+
id: string(),
|
|
60
|
+
state: enumeration(DocumentWorkflowState),
|
|
61
|
+
});
|
|
@@ -7,6 +7,7 @@ export declare class EnrichedDocumentCategory implements TypedOmit<DocumentCateg
|
|
|
7
7
|
#private;
|
|
8
8
|
readonly id: string;
|
|
9
9
|
readonly tenantId: string;
|
|
10
|
+
readonly key: string | null;
|
|
10
11
|
readonly label: string;
|
|
11
12
|
readonly parent: EnrichedDocumentCategory | null;
|
|
12
13
|
readonly helper: {
|
|
@@ -16,6 +16,7 @@ export class EnrichedDocumentCategory {
|
|
|
16
16
|
#data;
|
|
17
17
|
id;
|
|
18
18
|
tenantId;
|
|
19
|
+
key;
|
|
19
20
|
label;
|
|
20
21
|
parent;
|
|
21
22
|
helper = lazyObject({
|
|
@@ -52,6 +53,7 @@ export class EnrichedDocumentCategory {
|
|
|
52
53
|
this.#data = data;
|
|
53
54
|
this.id = category.id;
|
|
54
55
|
this.tenantId = category.tenantId;
|
|
56
|
+
this.key = category.key;
|
|
55
57
|
this.label = category.label;
|
|
56
58
|
this.parent = parent;
|
|
57
59
|
}
|
|
@@ -7,6 +7,7 @@ export declare class EnrichedDocumentType implements TypedOmit<DocumentType, 'ca
|
|
|
7
7
|
#private;
|
|
8
8
|
readonly id: string;
|
|
9
9
|
readonly tenantId: string;
|
|
10
|
+
readonly key: string | null;
|
|
10
11
|
readonly label: string;
|
|
11
12
|
readonly category: EnrichedDocumentCategory;
|
|
12
13
|
readonly helper: {
|
|
@@ -14,6 +14,7 @@ export class EnrichedDocumentType {
|
|
|
14
14
|
#data;
|
|
15
15
|
id;
|
|
16
16
|
tenantId;
|
|
17
|
+
key;
|
|
17
18
|
label;
|
|
18
19
|
category;
|
|
19
20
|
helper = lazyObject({
|
|
@@ -26,6 +27,7 @@ export class EnrichedDocumentType {
|
|
|
26
27
|
this.#data = data;
|
|
27
28
|
this.id = type.id;
|
|
28
29
|
this.tenantId = type.tenantId;
|
|
30
|
+
this.key = type.key;
|
|
29
31
|
this.label = type.label;
|
|
30
32
|
this.category = category;
|
|
31
33
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|