@legalplace/prerenderx 3.8.24 → 3.9.0
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/CHANGELOG.md +20 -0
- package/dist/libs/DocumentRender.d.ts +3 -2
- package/dist/libs/DocumentRender.js +13 -4
- package/dist/libs/FillPdfForm.d.ts +1 -1
- package/dist/libs/FillPdfForm.js +25 -15
- package/dist/libs/OptionRender.d.ts +4 -0
- package/dist/libs/OptionRender.js +125 -29
- package/dist/libs/OutputParser.d.ts +1 -1
- package/dist/libs/OutputParser.js +18 -6
- package/dist/libs/Prerender.d.ts +5 -2
- package/dist/libs/Prerender.js +100 -27
- package/dist/libs/SectionRender.d.ts +3 -0
- package/dist/libs/SectionRender.js +64 -22
- package/dist/libs/misc/FillPdfFormBase.d.ts +1 -1
- package/dist/libs/misc/FillPdfFormBase.js +11 -37
- package/dist/libs/tableOutputExpansion.d.ts +20 -0
- package/dist/libs/tableOutputExpansion.js +68 -0
- package/dist/types/types.d.ts +13 -0
- package/package.json +8 -8
- package/src/libs/DocumentRender.ts +33 -5
- package/src/libs/FillPdfForm.ts +39 -22
- package/src/libs/OptionRender.ts +218 -63
- package/src/libs/OutputParser.ts +40 -9
- package/src/libs/Prerender.ts +157 -38
- package/src/libs/SectionRender.ts +111 -28
- package/src/libs/misc/FillPdfFormBase.ts +34 -67
- package/src/libs/tableOutputExpansion.ts +145 -0
- package/src/types/types.ts +27 -0
- package/tests/Prerender.documentMultiple.test.ts +278 -0
- package/tests/Prerender.repeated.test.ts +115 -0
package/src/libs/OptionRender.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
|
+
import type { OptionV3 } from "@legalplace/models-v3-types";
|
|
1
2
|
import type { Types } from "@legalplace/referencesparser";
|
|
2
3
|
import type ConditionsRunner from "@legalplace/conditions-runner";
|
|
3
4
|
import type InputsType from "@legalplace/types/dist/inputs";
|
|
5
|
+
import {
|
|
6
|
+
isOptionDisplayed,
|
|
7
|
+
buildOutputTableCellStyleAttr,
|
|
8
|
+
buildOutputTableStyleAttr,
|
|
9
|
+
getOutputTableCellStyle,
|
|
10
|
+
replaceOutputTableMarkers,
|
|
11
|
+
stripOutputTableMarkers,
|
|
12
|
+
} from "@legalplace/referencesparser";
|
|
4
13
|
import {
|
|
5
14
|
parseOutputWithVariables,
|
|
6
15
|
getRelatedVariablesValues,
|
|
7
16
|
} from "./OutputParser";
|
|
17
|
+
import {
|
|
18
|
+
cleanupRenderedOutputHtml,
|
|
19
|
+
expandOutputTableMarkup,
|
|
20
|
+
} from "./tableOutputExpansion";
|
|
8
21
|
import type NumAuto from "./NumAuto";
|
|
9
22
|
|
|
10
23
|
class OptionRender {
|
|
@@ -40,83 +53,225 @@ class OptionRender {
|
|
|
40
53
|
this.renderOption();
|
|
41
54
|
}
|
|
42
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Returns rendered HTML outputs for this option subtree.
|
|
58
|
+
*/
|
|
43
59
|
getOutputs() {
|
|
44
60
|
return this.outputs;
|
|
45
61
|
}
|
|
46
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Returns whether an option is displayed at the current occurrence.
|
|
65
|
+
*/
|
|
66
|
+
private isDisplayed(optionId = this.id): boolean {
|
|
67
|
+
return isOptionDisplayed(
|
|
68
|
+
this.references,
|
|
69
|
+
this.ovc,
|
|
70
|
+
(type, entityId, entityIndex) => {
|
|
71
|
+
if (type === "options") {
|
|
72
|
+
const conditionObject = this.references.conditions.options[entityId];
|
|
73
|
+
return conditionObject === undefined
|
|
74
|
+
? true
|
|
75
|
+
: this.conditions.executeCondition(
|
|
76
|
+
conditionObject,
|
|
77
|
+
entityId,
|
|
78
|
+
entityIndex,
|
|
79
|
+
"options"
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (type === "sections") {
|
|
83
|
+
const conditionObject =
|
|
84
|
+
this.references.conditions.sections.main[entityId];
|
|
85
|
+
return conditionObject === undefined
|
|
86
|
+
? true
|
|
87
|
+
: this.conditions.executeCondition(
|
|
88
|
+
conditionObject,
|
|
89
|
+
entityId,
|
|
90
|
+
entityIndex,
|
|
91
|
+
"sections"
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
},
|
|
96
|
+
optionId,
|
|
97
|
+
this.index
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Renders a table cell option as a td element.
|
|
103
|
+
*/
|
|
104
|
+
private renderOutputTableCell(cellId: number): string {
|
|
105
|
+
const cellOption = this.references.options[cellId];
|
|
106
|
+
if (cellOption?.meta.tableRole !== "cell") return "";
|
|
107
|
+
|
|
108
|
+
const styleAttr = buildOutputTableCellStyleAttr(
|
|
109
|
+
getOutputTableCellStyle(cellOption)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Hidden cells are omitted entirely instead of rendering an empty td
|
|
113
|
+
if (!this.isDisplayed(cellId)) {
|
|
114
|
+
return "";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const outputReference = this.references.outputs[cellId];
|
|
118
|
+
if (outputReference === undefined) {
|
|
119
|
+
return `<td${styleAttr}></td>`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const autonums: [string, string, number, string | null][] =
|
|
123
|
+
outputReference.autonums.map((currentAn) => {
|
|
124
|
+
const fn =
|
|
125
|
+
currentAn[0] === "numauto-pre"
|
|
126
|
+
? this.NumAuto.getNumPre
|
|
127
|
+
: this.NumAuto.getNum;
|
|
128
|
+
return [
|
|
129
|
+
currentAn[0],
|
|
130
|
+
currentAn[1],
|
|
131
|
+
fn(currentAn[1], currentAn[2]),
|
|
132
|
+
currentAn[2],
|
|
133
|
+
];
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const variables = getRelatedVariablesValues(
|
|
137
|
+
cellId,
|
|
138
|
+
this.index,
|
|
139
|
+
outputReference.variables,
|
|
140
|
+
this.ovc,
|
|
141
|
+
this.references
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const content = parseOutputWithVariables(
|
|
145
|
+
outputReference.raw,
|
|
146
|
+
this.index,
|
|
147
|
+
variables,
|
|
148
|
+
autonums
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return `<td${styleAttr}>${content}</td>`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Renders a table row option as a tr element.
|
|
156
|
+
*/
|
|
157
|
+
private renderOutputTableRow(rowId: number): string {
|
|
158
|
+
const rowOption = this.references.options[rowId];
|
|
159
|
+
if (rowOption?.meta.tableRole !== "row") return "";
|
|
160
|
+
if (!this.isDisplayed(rowId)) return "";
|
|
161
|
+
|
|
162
|
+
const cells = rowOption.options
|
|
163
|
+
.map((cellId) => this.renderOutputTableCell(cellId))
|
|
164
|
+
.join("");
|
|
165
|
+
|
|
166
|
+
if (cells.length === 0) return "";
|
|
167
|
+
|
|
168
|
+
return `<tr>${cells}</tr>`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Renders a table option as a table element from its child rows.
|
|
173
|
+
*/
|
|
174
|
+
private renderOutputTable(option: OptionV3): string {
|
|
175
|
+
const rows = option.options
|
|
176
|
+
.map((rowId) => this.renderOutputTableRow(rowId))
|
|
177
|
+
.filter((row) => row.length > 0)
|
|
178
|
+
.join("");
|
|
179
|
+
|
|
180
|
+
if (rows.length === 0) return "";
|
|
181
|
+
|
|
182
|
+
const styleAttr = buildOutputTableStyleAttr(option.meta.tableStyle);
|
|
183
|
+
return `<table${styleAttr}><tbody>${rows}</tbody></table>`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Renders the current option output when displayed.
|
|
188
|
+
*/
|
|
47
189
|
private renderOption() {
|
|
48
190
|
const option = this.references.options[this.id];
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
this.index,
|
|
57
|
-
"options"
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
// Rendering documents sections
|
|
61
|
-
if (condition === true) {
|
|
62
|
-
if (
|
|
63
|
-
typeof option.meta.output === "string" &&
|
|
64
|
-
option.meta.output.trim().length > 0
|
|
65
|
-
) {
|
|
66
|
-
// output Reference
|
|
67
|
-
const outputReference = this.references.outputs[this.id];
|
|
68
|
-
|
|
69
|
-
// Autonums
|
|
70
|
-
let autonums: [string, string, number, string | null][] = [];
|
|
71
|
-
if (condition !== false) {
|
|
72
|
-
autonums = outputReference.autonums.map((currentAn) => {
|
|
73
|
-
const fn =
|
|
74
|
-
currentAn[0] === "numauto-pre"
|
|
75
|
-
? this.NumAuto.getNumPre
|
|
76
|
-
: this.NumAuto.getNum;
|
|
77
|
-
return [
|
|
78
|
-
currentAn[0],
|
|
79
|
-
currentAn[1],
|
|
80
|
-
fn(currentAn[1], currentAn[2]),
|
|
81
|
-
currentAn[2],
|
|
82
|
-
];
|
|
83
|
-
});
|
|
84
|
-
}
|
|
191
|
+
const { tableRole } = option.meta;
|
|
192
|
+
|
|
193
|
+
if (tableRole === "row" || tableRole === "cell") {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (!this.isDisplayed()) return;
|
|
85
198
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
outputReference.variables,
|
|
91
|
-
this.ovc,
|
|
92
|
-
this.references
|
|
93
|
-
);
|
|
94
|
-
let output = parseOutputWithVariables(
|
|
95
|
-
option.meta.output,
|
|
96
|
-
this.index,
|
|
97
|
-
variables,
|
|
98
|
-
autonums
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// Removing spaces between tags
|
|
102
|
-
output = output.replace(/<\/([a-z]+)>([\n\s]+)</gi, "</$1><");
|
|
103
|
-
|
|
104
|
-
// Replacing \n with line-break
|
|
105
|
-
output = output.replace(/\n/g, "<br />");
|
|
106
|
-
|
|
107
|
-
// Cleaning \xA0
|
|
108
|
-
output = output.replace(/\xA0/g, " ");
|
|
109
|
-
|
|
110
|
-
// Pushing output
|
|
111
|
-
this.outputs.push(output);
|
|
199
|
+
if (tableRole === "table") {
|
|
200
|
+
const tableHtml = this.renderOutputTable(option);
|
|
201
|
+
if (tableHtml.length > 0) {
|
|
202
|
+
this.outputs.push(cleanupRenderedOutputHtml(tableHtml));
|
|
112
203
|
}
|
|
113
|
-
|
|
204
|
+
return;
|
|
114
205
|
}
|
|
206
|
+
|
|
207
|
+
if (
|
|
208
|
+
typeof option.meta.output === "string" &&
|
|
209
|
+
option.meta.output.trim().length > 0
|
|
210
|
+
) {
|
|
211
|
+
const outputReference = this.references.outputs[this.id];
|
|
212
|
+
|
|
213
|
+
const autonums: [string, string, number, string | null][] =
|
|
214
|
+
outputReference.autonums.map((currentAn) => {
|
|
215
|
+
const fn =
|
|
216
|
+
currentAn[0] === "numauto-pre"
|
|
217
|
+
? this.NumAuto.getNumPre
|
|
218
|
+
: this.NumAuto.getNum;
|
|
219
|
+
return [
|
|
220
|
+
currentAn[0],
|
|
221
|
+
currentAn[1],
|
|
222
|
+
fn(currentAn[1], currentAn[2]),
|
|
223
|
+
currentAn[2],
|
|
224
|
+
];
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const variables = getRelatedVariablesValues(
|
|
228
|
+
this.id,
|
|
229
|
+
this.index,
|
|
230
|
+
outputReference.variables,
|
|
231
|
+
this.ovc,
|
|
232
|
+
this.references
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
let output = expandOutputTableMarkup({
|
|
236
|
+
output: option.meta.output,
|
|
237
|
+
references: this.references,
|
|
238
|
+
ovc: this.ovc,
|
|
239
|
+
conditions: this.conditions,
|
|
240
|
+
contextOptionId: this.id,
|
|
241
|
+
defaultOccurrenceIndex: this.index,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
output = parseOutputWithVariables(
|
|
245
|
+
output,
|
|
246
|
+
this.index,
|
|
247
|
+
variables,
|
|
248
|
+
autonums
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
output = replaceOutputTableMarkers(output, (tableId) => {
|
|
252
|
+
const childOption = this.references.options[tableId];
|
|
253
|
+
if (!childOption || childOption.meta.tableRole !== "table") return "";
|
|
254
|
+
if (!this.isDisplayed(tableId)) return "";
|
|
255
|
+
return this.renderOutputTable(childOption);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
output = stripOutputTableMarkers(output);
|
|
259
|
+
output = cleanupRenderedOutputHtml(output);
|
|
260
|
+
|
|
261
|
+
this.outputs.push(output);
|
|
262
|
+
}
|
|
263
|
+
this.renderChildren();
|
|
115
264
|
}
|
|
116
265
|
|
|
266
|
+
/**
|
|
267
|
+
* Recursively renders child options.
|
|
268
|
+
*/
|
|
117
269
|
private renderChildren() {
|
|
118
270
|
const option = this.references.options[this.id];
|
|
119
271
|
option.options.forEach((optionId) => {
|
|
272
|
+
const childOption = this.references.options[optionId];
|
|
273
|
+
if (childOption?.meta.tableRole === "table") return;
|
|
274
|
+
|
|
120
275
|
this.outputs = [
|
|
121
276
|
...this.outputs,
|
|
122
277
|
...new OptionRender({
|
package/src/libs/OutputParser.ts
CHANGED
|
@@ -199,20 +199,33 @@ export const parseOutputWithVariables: parseOutputT = (
|
|
|
199
199
|
};
|
|
200
200
|
|
|
201
201
|
/**
|
|
202
|
-
* Parses document name with variables
|
|
203
|
-
*
|
|
202
|
+
* Parses document name with variables.
|
|
203
|
+
* When `index` is provided, each `[var:X]` is resolved using the value
|
|
204
|
+
* at occurrence `index` instead of always using occurrence 0. This is
|
|
205
|
+
* required for document multiples where each occurrence of the document
|
|
206
|
+
* must have its own filename interpolated with the right variable values.
|
|
207
|
+
*
|
|
208
|
+
* @param text Document's name (or filenameTemplate)
|
|
209
|
+
* @param ovc Inputs (parallel arrays)
|
|
210
|
+
* @param references References parsed from the model
|
|
211
|
+
* @param fallback Fallback used when an interpolated variable is empty
|
|
212
|
+
* @param index Occurrence index (defaults to 0)
|
|
204
213
|
*/
|
|
205
214
|
type TParseDocumentNameWithVariables = (
|
|
206
215
|
text: string,
|
|
207
216
|
ovc: InputsType,
|
|
208
217
|
references: Types.ReferencesType,
|
|
209
|
-
fallback?: string
|
|
218
|
+
fallback?: string,
|
|
219
|
+
index?: number,
|
|
220
|
+
contextOptionId?: number
|
|
210
221
|
) => string;
|
|
211
222
|
export const parseDocumentNameWithVariables: TParseDocumentNameWithVariables = (
|
|
212
223
|
text,
|
|
213
224
|
ovc,
|
|
214
225
|
references,
|
|
215
|
-
fallback
|
|
226
|
+
fallback,
|
|
227
|
+
index = 0,
|
|
228
|
+
contextOptionId
|
|
216
229
|
) => {
|
|
217
230
|
/**
|
|
218
231
|
* Variables
|
|
@@ -239,12 +252,30 @@ export const parseDocumentNameWithVariables: TParseDocumentNameWithVariables = (
|
|
|
239
252
|
(c) => ovc !== undefined || references.variables[c] !== undefined
|
|
240
253
|
);
|
|
241
254
|
|
|
242
|
-
// Getting values
|
|
243
255
|
const variablesValues: Record<string, string> = {};
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
|
|
256
|
+
if (typeof contextOptionId === "number") {
|
|
257
|
+
const relatedValues = getRelatedVariablesValues(
|
|
258
|
+
contextOptionId,
|
|
259
|
+
index,
|
|
260
|
+
variables,
|
|
261
|
+
ovc,
|
|
262
|
+
references
|
|
263
|
+
);
|
|
264
|
+
variables.forEach((variableId) => {
|
|
265
|
+
variablesValues[variableId] = (
|
|
266
|
+
relatedValues[variableId] ?? ""
|
|
267
|
+
).toString();
|
|
268
|
+
});
|
|
269
|
+
} else {
|
|
270
|
+
variables.forEach((variableId) => {
|
|
271
|
+
const variableValues = ovc.variables[variableId];
|
|
272
|
+
const valueAtIndex =
|
|
273
|
+
variableValues?.[index] !== undefined
|
|
274
|
+
? variableValues[index]
|
|
275
|
+
: variableValues?.[0] || "";
|
|
276
|
+
variablesValues[variableId] = (valueAtIndex ?? "").toString();
|
|
277
|
+
});
|
|
278
|
+
}
|
|
248
279
|
|
|
249
280
|
// Inserting values
|
|
250
281
|
let parsedText = `${text}`;
|
package/src/libs/Prerender.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ModelV3 } from "@legalplace/models-v3-types";
|
|
2
|
-
import type { Types } from "@legalplace/referencesparser";
|
|
3
|
-
import { ReferencesParser } from "@legalplace/referencesparser";
|
|
4
2
|
import ConditionsRunner from "@legalplace/conditions-runner";
|
|
5
3
|
import OvcConverter from "@legalplace/ovc-converter";
|
|
4
|
+
import {
|
|
5
|
+
ReferencesParser,
|
|
6
|
+
type OutputRegionRole,
|
|
7
|
+
type Types,
|
|
8
|
+
} from "@legalplace/referencesparser";
|
|
6
9
|
import type OvcType from "@legalplace/types/dist/ovc";
|
|
7
10
|
import type InputsType from "@legalplace/types/dist/inputs";
|
|
8
11
|
import crypto from "crypto";
|
|
@@ -16,6 +19,7 @@ import type {
|
|
|
16
19
|
DocumentIndex,
|
|
17
20
|
DocumentOutputIndex,
|
|
18
21
|
DocumentPdfFormIndex,
|
|
22
|
+
DocumentLayout,
|
|
19
23
|
} from "../types/types";
|
|
20
24
|
|
|
21
25
|
class Prerender {
|
|
@@ -88,67 +92,182 @@ class Prerender {
|
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
const index = this.documentsIndex[documentNameHash];
|
|
95
|
+
const occurrenceIndex = index.occurrenceIndex ?? 0;
|
|
91
96
|
|
|
92
97
|
this.renderedDocuments[documentNameHash] = Prerender.isPdfForm(index)
|
|
93
|
-
? this.pdfFiller.fillDocument(index.slug)
|
|
94
|
-
: this.documentRender.renderDocument(index.slug);
|
|
98
|
+
? this.pdfFiller.fillDocument(index.slug, occurrenceIndex)
|
|
99
|
+
: this.documentRender.renderDocument(index.slug, occurrenceIndex);
|
|
95
100
|
|
|
96
101
|
return this.renderedDocuments[documentNameHash];
|
|
97
102
|
}
|
|
98
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Returns rendered header/footer regions and page layout settings for DOCX.
|
|
106
|
+
*/
|
|
107
|
+
public getDocumentLayout(documentNameHash: string): DocumentLayout {
|
|
108
|
+
const index = this.documentsIndex[documentNameHash];
|
|
109
|
+
const occurrenceIndex = index.occurrenceIndex ?? 0;
|
|
110
|
+
const document = this.references.documents[index.slug];
|
|
111
|
+
const layoutParams = document.params?.layout;
|
|
112
|
+
|
|
113
|
+
const renderRegion = (role: OutputRegionRole): string | undefined => {
|
|
114
|
+
const html = this.documentRender.renderRegion(
|
|
115
|
+
index.slug,
|
|
116
|
+
role,
|
|
117
|
+
occurrenceIndex
|
|
118
|
+
);
|
|
119
|
+
return html.trim().length > 0 ? html : undefined;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const header = renderRegion("header");
|
|
123
|
+
const footer = renderRegion("footer");
|
|
124
|
+
const differentFirstPage = layoutParams?.differentFirstPage === true;
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
header,
|
|
128
|
+
footer,
|
|
129
|
+
firstPageHeader: differentFirstPage
|
|
130
|
+
? renderRegion("firstPageHeader")
|
|
131
|
+
: undefined,
|
|
132
|
+
firstPageFooter: differentFirstPage
|
|
133
|
+
? renderRegion("firstPageFooter")
|
|
134
|
+
: undefined,
|
|
135
|
+
differentFirstPage,
|
|
136
|
+
margins: layoutParams?.margins,
|
|
137
|
+
headerDistance: layoutParams?.headerDistance,
|
|
138
|
+
footerDistance: layoutParams?.footerDistance,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Resolves a document occurrence's display name. Interpolates `[var:X]`
|
|
144
|
+
* in `document.name` at the linked occurrence. Optional `filenameTemplate`
|
|
145
|
+
* overrides the title when set and non-empty after interpolation.
|
|
146
|
+
*/
|
|
147
|
+
private resolveDocumentOccurrenceName(
|
|
148
|
+
document: Types.ReferencesDocumentType,
|
|
149
|
+
docMultiple: NonNullable<
|
|
150
|
+
NonNullable<Types.ReferencesDocumentType["params"]>["multiple"]
|
|
151
|
+
>,
|
|
152
|
+
occurrenceIndex: number
|
|
153
|
+
): string {
|
|
154
|
+
const baseName = parseDocumentNameWithVariables(
|
|
155
|
+
document.name,
|
|
156
|
+
this.ovc,
|
|
157
|
+
this.references,
|
|
158
|
+
document.fallbackName,
|
|
159
|
+
occurrenceIndex,
|
|
160
|
+
docMultiple.repeatOption
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const template = docMultiple.filenameTemplate;
|
|
164
|
+
if (typeof template !== "string" || template.trim().length === 0) {
|
|
165
|
+
return baseName;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const resolved = parseDocumentNameWithVariables(
|
|
169
|
+
template,
|
|
170
|
+
this.ovc,
|
|
171
|
+
this.references,
|
|
172
|
+
undefined,
|
|
173
|
+
occurrenceIndex,
|
|
174
|
+
docMultiple.repeatOption
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
if (resolved.trim().length === 0) return baseName;
|
|
178
|
+
return resolved;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Returns the array of occurrences (true = render this occurrence) for a
|
|
183
|
+
* given document slug. For non-multiple documents, returns `[true]`.
|
|
184
|
+
* For multiple documents, returns the parallel array of the source option
|
|
185
|
+
* (`params.multiple.repeatOption`), defaulting to `[true]` if absent.
|
|
186
|
+
*/
|
|
187
|
+
private getDocumentOccurrences(slug: string): boolean[] {
|
|
188
|
+
const document = this.references.documents[slug];
|
|
189
|
+
const docMultiple = document.params?.multiple;
|
|
190
|
+
if (docMultiple?.enabled !== true) return [true];
|
|
191
|
+
|
|
192
|
+
const { repeatOption } = docMultiple;
|
|
193
|
+
if (typeof repeatOption !== "number") return [true];
|
|
194
|
+
|
|
195
|
+
const inputs = this.ovc.options[String(repeatOption)];
|
|
196
|
+
if (!Array.isArray(inputs) || inputs.length === 0) return [true];
|
|
197
|
+
return inputs;
|
|
198
|
+
}
|
|
199
|
+
|
|
99
200
|
private generateDocumentNamesHash() {
|
|
100
201
|
Object.keys(this.references.documents).forEach((slug) => {
|
|
101
202
|
const document = this.references.documents[slug];
|
|
102
|
-
const documentParams =
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
203
|
+
const documentParams = document.params;
|
|
204
|
+
const docMultiple = documentParams?.multiple;
|
|
205
|
+
const isMultipleDocument = docMultiple?.enabled === true;
|
|
206
|
+
|
|
207
|
+
const occurrences = this.getDocumentOccurrences(slug);
|
|
208
|
+
|
|
209
|
+
occurrences.forEach((occurrenceValue, occurrenceIndex) => {
|
|
210
|
+
if (occurrenceValue !== true) return;
|
|
211
|
+
|
|
212
|
+
const conditionObject = this.references.conditions.documents[slug];
|
|
213
|
+
const documentSourceId =
|
|
214
|
+
isMultipleDocument && typeof docMultiple.repeatOption === "number"
|
|
215
|
+
? docMultiple.repeatOption
|
|
216
|
+
: 0;
|
|
217
|
+
const condition =
|
|
218
|
+
conditionObject === undefined
|
|
219
|
+
? true
|
|
220
|
+
: this.conditions.executeCondition(
|
|
221
|
+
conditionObject,
|
|
222
|
+
documentSourceId,
|
|
223
|
+
occurrenceIndex,
|
|
224
|
+
"documents"
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
if (condition !== true) return;
|
|
228
|
+
|
|
229
|
+
const seed = isMultipleDocument ? `${slug}:${occurrenceIndex}` : slug;
|
|
230
|
+
const hash = crypto.createHash("sha1").update(seed).digest("hex");
|
|
231
|
+
|
|
232
|
+
const name =
|
|
233
|
+
isMultipleDocument && docMultiple
|
|
234
|
+
? this.resolveDocumentOccurrenceName(
|
|
235
|
+
document,
|
|
236
|
+
docMultiple,
|
|
237
|
+
occurrenceIndex
|
|
238
|
+
)
|
|
239
|
+
: parseDocumentNameWithVariables(
|
|
240
|
+
document.name,
|
|
241
|
+
this.ovc,
|
|
242
|
+
this.references,
|
|
243
|
+
document.fallbackName
|
|
244
|
+
);
|
|
245
|
+
|
|
119
246
|
if (
|
|
120
247
|
typeof document.pdf === "object" &&
|
|
121
248
|
typeof document.pdf.id === "string" &&
|
|
122
249
|
typeof document.pdf.form === "object"
|
|
123
250
|
) {
|
|
124
251
|
this.documentsIndex[hash] = {
|
|
125
|
-
name
|
|
126
|
-
document.name,
|
|
127
|
-
this.ovc,
|
|
128
|
-
this.references,
|
|
129
|
-
document.fallbackName
|
|
130
|
-
),
|
|
252
|
+
name,
|
|
131
253
|
slug,
|
|
132
254
|
form: true,
|
|
133
255
|
fdf: {
|
|
134
256
|
id: "",
|
|
135
257
|
fields: {},
|
|
136
258
|
},
|
|
259
|
+
...(isMultipleDocument ? { occurrenceIndex } : {}),
|
|
137
260
|
};
|
|
138
261
|
} else {
|
|
139
262
|
this.documentsIndex[hash] = {
|
|
140
|
-
name
|
|
141
|
-
document.name,
|
|
142
|
-
this.ovc,
|
|
143
|
-
this.references,
|
|
144
|
-
document.fallbackName
|
|
145
|
-
),
|
|
263
|
+
name,
|
|
146
264
|
slug,
|
|
147
265
|
pdf: documentParams?.formats?.pdf !== false,
|
|
148
266
|
docx: documentParams?.formats?.docx !== false,
|
|
267
|
+
...(isMultipleDocument ? { occurrenceIndex } : {}),
|
|
149
268
|
};
|
|
150
269
|
}
|
|
151
|
-
}
|
|
270
|
+
});
|
|
152
271
|
});
|
|
153
272
|
}
|
|
154
273
|
|
|
@@ -164,15 +283,15 @@ class Prerender {
|
|
|
164
283
|
private generateAllDocuments() {
|
|
165
284
|
Object.keys(this.documentsIndex).forEach((documentNameHash) => {
|
|
166
285
|
const index = this.documentsIndex[documentNameHash];
|
|
286
|
+
const occurrenceIndex = index.occurrenceIndex ?? 0;
|
|
167
287
|
if (Prerender.isPdfForm(index)) {
|
|
168
288
|
this.renderedDocuments[documentNameHash] = this.pdfFiller.fillDocument(
|
|
169
|
-
|
|
289
|
+
index.slug,
|
|
290
|
+
occurrenceIndex
|
|
170
291
|
);
|
|
171
292
|
} else {
|
|
172
293
|
this.renderedDocuments[documentNameHash] =
|
|
173
|
-
this.documentRender.renderDocument(
|
|
174
|
-
this.documentsIndex[documentNameHash].slug
|
|
175
|
-
);
|
|
294
|
+
this.documentRender.renderDocument(index.slug, occurrenceIndex);
|
|
176
295
|
}
|
|
177
296
|
});
|
|
178
297
|
}
|