@legalplace/prerenderx 3.8.17 → 3.8.18-staging.2

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.
@@ -6,7 +6,13 @@ type FDFType = {
6
6
  fields: Record<string, string>;
7
7
  };
8
8
  class FillPdfForm extends FillPdfFormBase {
9
- public fillDocument(documentName: string) {
9
+ /**
10
+ * Fills a PDF form template with values from `ovc`. When `baseIndex` is
11
+ * provided (document multiple), variables and options are read at that
12
+ * occurrence index instead of always reading slot 0. Each occurrence of
13
+ * a multiple PDF form document yields its own filled FDF.
14
+ */
15
+ public fillDocument(documentName: string, baseIndex = 0) {
10
16
  const result: FDFType = {
11
17
  id: "",
12
18
  fields: {},
@@ -24,13 +30,13 @@ class FillPdfForm extends FillPdfFormBase {
24
30
  // Filling inputs
25
31
  form.inputs.forEach((input) => {
26
32
  const id = `${input.name}`;
27
- result.fields[id] = this.getInputValue(input);
33
+ result.fields[id] = this.getInputValue(input, baseIndex);
28
34
  });
29
35
 
30
36
  // Filling boxes
31
37
  form.boxes.forEach((box) => {
32
38
  const id = `${box.name}`;
33
- const value = this.getBoxValue(box);
39
+ const value = this.getBoxValue(box, baseIndex);
34
40
  if (
35
41
  !(
36
42
  value === "" &&
@@ -44,13 +50,14 @@ class FillPdfForm extends FillPdfFormBase {
44
50
  }
45
51
 
46
52
  /**
47
- * Returns an input value
53
+ * Returns an input value at the given occurrence `index`.
48
54
  * @param input Input
55
+ * @param index Occurrence index (defaults to 0)
49
56
  */
50
- private getInputValue(input: FDFInputsV3): string {
57
+ private getInputValue(input: FDFInputsV3, index = 0): string {
51
58
  for (let i = 0; i < input.variables.length; i += 1) {
52
59
  const variable = input.variables[i];
53
- const currentValue = this.getVariableValue(variable.id);
60
+ const currentValue = this.getVariableValue(variable.id, index);
54
61
 
55
62
  if (currentValue !== false) {
56
63
  // Date values
@@ -110,13 +117,14 @@ class FillPdfForm extends FillPdfFormBase {
110
117
  }
111
118
 
112
119
  /**
113
- * Return a box value
120
+ * Return a box value at the given occurrence `index`.
114
121
  * @param box Box
122
+ * @param index Occurrence index (defaults to 0)
115
123
  */
116
- private getBoxValue(box: FDFBoxesV3): string {
124
+ private getBoxValue(box: FDFBoxesV3, index = 0): string {
117
125
  for (let i = 0; i < box.options.length; i += 1) {
118
126
  const option = box.options[i];
119
- const currentValue = this.getOptionValue(option.id);
127
+ const currentValue = this.getOptionValue(option.id, index);
120
128
 
121
129
  if (currentValue === true) {
122
130
  if (
@@ -132,25 +140,34 @@ class FillPdfForm extends FillPdfFormBase {
132
140
  }
133
141
 
134
142
  /**
135
- * Returns a variable's value
143
+ * Returns a variable's value at the given occurrence `index`. Falls back
144
+ * to slot 0 when the requested index is missing (e.g. a non-multiple
145
+ * variable inside a multiple document).
136
146
  */
137
- private getVariableValue(variableId: number) {
138
- // Checking if variable is displayed
139
- if (this.isVariableDisplayed(variableId, 0) === false) return false;
147
+ private getVariableValue(variableId: number, index = 0) {
148
+ // Checking if variable is displayed at the given occurrence
149
+ if (this.isVariableDisplayed(variableId, index) === false) return false;
140
150
 
141
- // Returning variable's value
142
- return (this.ovc.variables[variableId]?.[0] || "").toString();
151
+ const variableValues = this.ovc.variables[variableId];
152
+ const value =
153
+ variableValues?.[index] !== undefined
154
+ ? variableValues[index]
155
+ : variableValues?.[0] || "";
156
+
157
+ return (value ?? "").toString();
143
158
  }
144
159
 
145
160
  /**
146
- * Returns a option's value
161
+ * Returns an option's boolean value at the given occurrence `index`.
147
162
  */
148
- private getOptionValue(optionId: number) {
149
- // Checking if option is displayed
150
- if (this.isOptionDisplayed(optionId, 0) === false) return false;
151
-
152
- // Returning option's value
153
- return this.ovc.options[optionId]?.[0] === true;
163
+ private getOptionValue(optionId: number, index = 0) {
164
+ // Checking if option is displayed at the given occurrence
165
+ if (this.isOptionDisplayed(optionId, index) === false) return false;
166
+
167
+ const optionValues = this.ovc.options[optionId];
168
+ return optionValues?.[index] !== undefined
169
+ ? optionValues[index] === true
170
+ : optionValues?.[0] === true;
154
171
  }
155
172
  }
156
173
 
@@ -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 conditionObject = this.references.conditions.options[this.id];
50
- const condition =
51
- conditionObject === undefined
52
- ? true
53
- : this.conditions.executeCondition(
54
- conditionObject,
55
- this.id,
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
- // variables
87
- const variables = getRelatedVariablesValues(
88
- this.id,
89
- this.index,
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
- this.renderChildren();
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({
@@ -199,20 +199,33 @@ export const parseOutputWithVariables: parseOutputT = (
199
199
  };
200
200
 
201
201
  /**
202
- * Parses document name with variables
203
- * @param text Document's name
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
- variables.forEach((variableId) => {
245
- const value = ovc.variables[variableId]?.[0] || "";
246
- variablesValues[variableId] = value.toString();
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}`;