beca-ui 2.1.6 → 2.1.7
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/dist/DynamicWorkflowScript.js +1968 -363
- package/dist/beca-ui.js +54541 -48694
- package/dist/components/DebounceSelect/DebounceSelect.d.ts +9 -0
- package/dist/components/DebounceSelect/index.d.ts +1 -0
- package/dist/components/DynamicForm/AttachIcon.d.ts +6 -0
- package/dist/components/DynamicForm/AvatarItem.d.ts +6 -0
- package/dist/components/DynamicForm/DynamicForm.d.ts +4 -0
- package/dist/components/DynamicForm/DynamicFormInfo.d.ts +13 -0
- package/dist/components/DynamicForm/DynamicFormSidebar.d.ts +21 -0
- package/dist/components/DynamicForm/DynamicWorkflowForm.d.ts +19 -0
- package/dist/components/DynamicForm/PropertyFileUpload.d.ts +27 -0
- package/dist/components/DynamicForm/PropertyImageList.d.ts +8 -0
- package/dist/components/DynamicForm/WorkflowForm.d.ts +30 -0
- package/dist/components/DynamicForm/context.d.ts +30 -0
- package/dist/components/DynamicForm/index.d.ts +13 -0
- package/dist/components/DynamicForm/layout.d.ts +9 -0
- package/dist/components/DynamicForm/types.d.ts +241 -0
- package/dist/components/DynamicForm/utils.d.ts +59 -0
- package/dist/components/FormEditor/FormEditor.d.ts +25 -0
- package/dist/components/FormEditor/index.d.ts +2 -0
- package/dist/components/QrBarCodeScanField/QrBarCodeScanField.d.ts +24 -0
- package/dist/components/QrBarCodeScanField/index.d.ts +1 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/index.d.ts +1 -0
- package/dist/main.css +1 -1
- package/package.json +13 -30
- package/README.md +0 -115
- package/dist/styles/components/layout/test.d.ts +0 -0
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
let docTien = typeof DocTienBangChu !== "undefined" ? new DocTienBangChu() : null;
|
|
2
|
+
function getDocTienInstance() {
|
|
3
|
+
if (!docTien && typeof DocTienBangChu !== "undefined") {
|
|
4
|
+
docTien = new DocTienBangChu();
|
|
5
|
+
}
|
|
6
|
+
return docTien;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const DEFAULT_MONEY_UNIT = {
|
|
10
|
+
vi: "đồng",
|
|
11
|
+
en: "dong",
|
|
12
|
+
};
|
|
3
13
|
|
|
4
14
|
var isDetail = false;
|
|
5
15
|
var isMasterDetail = false;
|
|
6
16
|
var colName = undefined;
|
|
7
17
|
var mdCellId = undefined;
|
|
18
|
+
var userWorkflowId = undefined;
|
|
8
19
|
var masterDetailId = undefined;
|
|
9
20
|
|
|
10
|
-
|
|
21
|
+
let previousName = null;
|
|
22
|
+
function capitalize(word = "") {
|
|
11
23
|
word = word.toLowerCase();
|
|
12
24
|
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
13
25
|
}
|
|
@@ -16,204 +28,733 @@ function runScript(scriptString) {
|
|
|
16
28
|
eval(scriptString);
|
|
17
29
|
}
|
|
18
30
|
|
|
31
|
+
function unaccent(str) {
|
|
32
|
+
if (!str) return "";
|
|
33
|
+
return str
|
|
34
|
+
.normalize("NFD")
|
|
35
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
36
|
+
.replace(/[\u02C6\u0306\u031B]/g, "")
|
|
37
|
+
.replace(/đ/g, "d")
|
|
38
|
+
.replace(/Đ/g, "D")
|
|
39
|
+
.replace(
|
|
40
|
+
/!|@|%|\^|\*|\(|\)|\+|\=|\<|\>|\?|\/|,|\.|\:|\;|\'|\"|\&|\#|\[|\]|~|\$|_|`|-|{|}|\||\\/g,
|
|
41
|
+
" ",
|
|
42
|
+
)
|
|
43
|
+
.replace(/\s+/g, " ")
|
|
44
|
+
.trim();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function clearText(text) {
|
|
48
|
+
let clearedText;
|
|
49
|
+
if (text?.length > 0) {
|
|
50
|
+
clearedText = unaccent(text.trim().toLowerCase());
|
|
51
|
+
}
|
|
52
|
+
return clearedText;
|
|
53
|
+
}
|
|
54
|
+
|
|
19
55
|
function isNumeric(n) {
|
|
20
56
|
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
21
57
|
}
|
|
22
58
|
|
|
23
59
|
function getMoneyUnitName(unit) {
|
|
24
60
|
switch (unit) {
|
|
25
|
-
case
|
|
26
|
-
return
|
|
27
|
-
case
|
|
28
|
-
return
|
|
29
|
-
case
|
|
30
|
-
return
|
|
31
|
-
case
|
|
32
|
-
return
|
|
33
|
-
case
|
|
34
|
-
return
|
|
35
|
-
case
|
|
36
|
-
return
|
|
61
|
+
case "VND":
|
|
62
|
+
return "đồng";
|
|
63
|
+
case "USD":
|
|
64
|
+
return "đô la";
|
|
65
|
+
case "EUR":
|
|
66
|
+
return "ơ-rô";
|
|
67
|
+
case "JPY":
|
|
68
|
+
return "yên";
|
|
69
|
+
case "SGD":
|
|
70
|
+
return "đô la Singapore";
|
|
71
|
+
case "KRW":
|
|
72
|
+
return "won";
|
|
37
73
|
default:
|
|
38
|
-
return
|
|
74
|
+
return "";
|
|
39
75
|
}
|
|
40
76
|
}
|
|
41
77
|
|
|
42
|
-
function docSoTien(number, unit
|
|
43
|
-
|
|
44
|
-
|
|
78
|
+
function docSoTien(number, unit, locale = "vi") {
|
|
79
|
+
if (!unit) {
|
|
80
|
+
unit = DEFAULT_MONEY_UNIT[locale];
|
|
81
|
+
}
|
|
82
|
+
let result = "";
|
|
83
|
+
const numbers = number?.toString().split(".");
|
|
45
84
|
|
|
46
|
-
if (locale ===
|
|
85
|
+
if (locale === "en") {
|
|
47
86
|
const numberText1 = numberToTextEN(Number(numbers[0]));
|
|
48
87
|
result = numberText1.trim();
|
|
49
88
|
} else {
|
|
50
|
-
const
|
|
89
|
+
const reader = getDocTienInstance();
|
|
90
|
+
const numberText1 = reader ? reader.doc(numbers[0]) : "";
|
|
51
91
|
result = numberText1.trim();
|
|
52
92
|
}
|
|
53
93
|
|
|
54
94
|
if (numbers.length === 2) {
|
|
55
|
-
if (locale ===
|
|
95
|
+
if (locale === "en") {
|
|
56
96
|
const numberText2 = numberToTextEN(numbers[1]);
|
|
57
97
|
result += ` point ${numberText2.toLowerCase()}`;
|
|
58
98
|
} else {
|
|
59
|
-
const
|
|
99
|
+
const reader = getDocTienInstance();
|
|
100
|
+
const numberText2 = reader ? reader.doc(numbers[1]) : "";
|
|
60
101
|
result += ` phẩy ${numberText2.toLowerCase()}`;
|
|
61
102
|
}
|
|
62
103
|
}
|
|
63
104
|
result = capitalize(result);
|
|
64
|
-
if (unit?.trim() !==
|
|
105
|
+
if (unit?.trim() !== "") {
|
|
65
106
|
result += ` ${unit}`;
|
|
66
107
|
}
|
|
67
108
|
|
|
68
109
|
return result;
|
|
69
110
|
}
|
|
111
|
+
function getShortName(
|
|
112
|
+
name,
|
|
113
|
+
content,
|
|
114
|
+
numberOfWords,
|
|
115
|
+
numberOfChars = 1,
|
|
116
|
+
reverse = false,
|
|
117
|
+
) {
|
|
118
|
+
var rawValue = getValue(name);
|
|
119
|
+
|
|
120
|
+
var noBrackets = rawValue.replace(/\s*[\(\[\{].*?[\)\]\}]\s*/g, " ");
|
|
121
|
+
|
|
122
|
+
var cleanValue = noBrackets.replace(
|
|
123
|
+
/[^\w\sàáạảãâầấậẩẫăằắặẳẵèéẹẻẽêềếệểễìíịỉĩòóọỏõôồốộổỗơờớợởỡùúụủũưừứựửữỳýỵỷỹđ]/gi,
|
|
124
|
+
"",
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
var valueName = cleanValue.trim();
|
|
128
|
+
var words = valueName.split(/\s+/).filter(Boolean);
|
|
129
|
+
var selectedWords = [];
|
|
130
|
+
//get selected words
|
|
131
|
+
if (reverse) {
|
|
132
|
+
var start = Math.max(0, words.length - numberOfWords);
|
|
133
|
+
selectedWords = words.slice(start, words.length);
|
|
134
|
+
} else {
|
|
135
|
+
selectedWords = words.slice(0, numberOfWords);
|
|
136
|
+
}
|
|
137
|
+
//get selected chars
|
|
138
|
+
var result = selectedWords
|
|
139
|
+
.map((word) => word.slice(0, numberOfChars))
|
|
140
|
+
.join("")
|
|
141
|
+
.toUpperCase();
|
|
142
|
+
|
|
143
|
+
setValue(content, result);
|
|
144
|
+
}
|
|
70
145
|
|
|
71
146
|
function fetchDataFromApi(api, from, property, to, fromName) {
|
|
72
|
-
var htmlElement = document.getElementById(
|
|
73
|
-
if (htmlElement && htmlElement.tagName ===
|
|
147
|
+
var htmlElement = document.getElementById("field_" + fromName);
|
|
148
|
+
if (htmlElement && htmlElement.tagName === "SPAN") {
|
|
74
149
|
return;
|
|
75
150
|
}
|
|
76
151
|
if (from && from[property]) {
|
|
77
152
|
if (!isNumeric(from[property])) {
|
|
78
153
|
return;
|
|
79
154
|
}
|
|
80
|
-
api = window.location.origin
|
|
155
|
+
api = `${window.location.origin}${api}${from[property]}?userWorkflowId=${userWorkflowId}`;
|
|
81
156
|
var myHeaders = new Headers();
|
|
82
|
-
myHeaders.append(
|
|
83
|
-
myHeaders.append(
|
|
84
|
-
myHeaders.append(
|
|
85
|
-
myHeaders.append(
|
|
157
|
+
myHeaders.append("Accept", "*/*");
|
|
158
|
+
myHeaders.append("Accept-Language", "en");
|
|
159
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
160
|
+
myHeaders.append("Connection", "keep-alive");
|
|
86
161
|
var requestOptions = {
|
|
87
|
-
method:
|
|
162
|
+
method: "GET",
|
|
88
163
|
headers: myHeaders,
|
|
89
|
-
redirect:
|
|
164
|
+
redirect: "follow",
|
|
90
165
|
};
|
|
91
166
|
|
|
92
167
|
fetch(api, requestOptions)
|
|
93
|
-
.then(response => response.text())
|
|
94
|
-
.then(result => {
|
|
95
|
-
result = result.replace(/"/g,
|
|
168
|
+
.then((response) => response.text())
|
|
169
|
+
.then((result) => {
|
|
170
|
+
result = result.replace(/"/g, "");
|
|
96
171
|
|
|
97
|
-
const textValue = document.getElementById(
|
|
172
|
+
const textValue = document.getElementById("field_" + to);
|
|
98
173
|
|
|
99
174
|
if (textValue) {
|
|
100
175
|
if (result) {
|
|
101
176
|
textValue.innerHTML = result;
|
|
102
177
|
} else {
|
|
103
|
-
textValue.innerHTML =
|
|
178
|
+
textValue.innerHTML = "";
|
|
104
179
|
}
|
|
105
180
|
}
|
|
106
181
|
})
|
|
107
|
-
.catch(error => console.log(
|
|
182
|
+
.catch((error) => console.log("error", error));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
async function fetchDataFromApiWithoutNumber(api, to) {
|
|
186
|
+
try {
|
|
187
|
+
api = `${window.location.origin}${api}`;
|
|
188
|
+
api = api.replaceAll("@UserWorkflowId", userWorkflowId ?? 0);
|
|
189
|
+
const matches = [...api.matchAll(/@([a-zA-Z0-9_]+)/g)];
|
|
190
|
+
|
|
191
|
+
matches.forEach((match) => {
|
|
192
|
+
const variableName = match[1]; // ví dụ: MaPhong
|
|
193
|
+
|
|
194
|
+
const element =
|
|
195
|
+
document.getElementById(`field_${variableName}`) ||
|
|
196
|
+
document.querySelector(`.field_${variableName}`) || // Dùng querySelector ở đây để lấy element đầu tiên, thay vì getElementsByClassName
|
|
197
|
+
document.getElementById(variableName);
|
|
198
|
+
|
|
199
|
+
let value = "";
|
|
200
|
+
|
|
201
|
+
if (element) {
|
|
202
|
+
value =
|
|
203
|
+
element.getAttribute("value") ??
|
|
204
|
+
element.value ??
|
|
205
|
+
element.innerText ??
|
|
206
|
+
"";
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
api = api.replaceAll(`@${variableName}`, encodeURIComponent(value));
|
|
210
|
+
});
|
|
211
|
+
const myHeaders = new Headers();
|
|
212
|
+
myHeaders.append("Accept", "*/*");
|
|
213
|
+
myHeaders.append("Accept-Language", "en");
|
|
214
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
215
|
+
myHeaders.append("Connection", "keep-alive");
|
|
216
|
+
|
|
217
|
+
const requestOptions = {
|
|
218
|
+
method: "GET",
|
|
219
|
+
headers: myHeaders,
|
|
220
|
+
redirect: "follow",
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const response = await fetch(api, requestOptions);
|
|
224
|
+
const resultValue = await response.json();
|
|
225
|
+
setValue(to, resultValue);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
console.log("error", error);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
//tienth1@vntt.com.vn
|
|
232
|
+
async function fetchAndMapDataFromApi(api, to, isMd = false) {
|
|
233
|
+
try {
|
|
234
|
+
api = `${window.location.origin}${api}`;
|
|
235
|
+
api = api.replaceAll("@UserWorkflowId", userWorkflowId ?? 0);
|
|
236
|
+
const matches = [...api.matchAll(/@([a-zA-Z0-9_]+)/g)];
|
|
237
|
+
|
|
238
|
+
matches.forEach((match) => {
|
|
239
|
+
const variableName = match[1]; // ví dụ: MaPhong
|
|
240
|
+
|
|
241
|
+
// ưu tiên lấy từ field_TenBien
|
|
242
|
+
const element =
|
|
243
|
+
document.getElementById(`field_${variableName}`) ||
|
|
244
|
+
document.getElementById(variableName);
|
|
245
|
+
|
|
246
|
+
let value = "";
|
|
247
|
+
|
|
248
|
+
if (element) {
|
|
249
|
+
const selectionItem = element.querySelector(
|
|
250
|
+
".ant-select-selection-item",
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
if (selectionItem) {
|
|
254
|
+
value =
|
|
255
|
+
selectionItem.getAttribute("title") ??
|
|
256
|
+
selectionItem.textContent.trim();
|
|
257
|
+
} else {
|
|
258
|
+
value =
|
|
259
|
+
element.getAttribute("value") ??
|
|
260
|
+
element.value ??
|
|
261
|
+
element.innerText ??
|
|
262
|
+
"";
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const safeValue = String(value).replace(/'/g, "''");
|
|
267
|
+
api = api.replaceAll(
|
|
268
|
+
`@${variableName}`,
|
|
269
|
+
encodeURIComponent(`'${safeValue}'`),
|
|
270
|
+
);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const myHeaders = new Headers();
|
|
274
|
+
myHeaders.append("Accept", "*/*");
|
|
275
|
+
myHeaders.append("Accept-Language", "en");
|
|
276
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
277
|
+
myHeaders.append("Connection", "keep-alive");
|
|
278
|
+
|
|
279
|
+
const requestOptions = {
|
|
280
|
+
method: "GET",
|
|
281
|
+
headers: myHeaders,
|
|
282
|
+
redirect: "follow",
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
const response = await fetch(api, requestOptions);
|
|
286
|
+
const resultValue = await response.json();
|
|
287
|
+
isMd ? mapMdValueIntoMD(to, resultValue) : setValue(to, resultValue, isMd);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
console.log("error", error);
|
|
108
290
|
}
|
|
109
291
|
}
|
|
110
292
|
|
|
293
|
+
//tienth1@vntt.com.vn
|
|
294
|
+
function fetchAndMapDataIntoDropdownFromApi(api, to, isMd = false) {
|
|
295
|
+
const isAbsolute = /^https?:\/\//i.test(api);
|
|
296
|
+
api = `${window.location.origin}${api}`;
|
|
297
|
+
api = api.replaceAll("@UserWorkflowId", userWorkflowId ?? 0);
|
|
298
|
+
const matches = [...api.matchAll(/@([a-zA-Z0-9_]+)/g)];
|
|
299
|
+
|
|
300
|
+
matches.forEach((match) => {
|
|
301
|
+
const variableName = match[1]; // ví dụ: MaPhong
|
|
302
|
+
|
|
303
|
+
// ưu tiên lấy từ field_TenBien
|
|
304
|
+
const element =
|
|
305
|
+
document.getElementById(`field_${variableName}`) ||
|
|
306
|
+
document.getElementById(variableName);
|
|
307
|
+
|
|
308
|
+
let value = "";
|
|
309
|
+
|
|
310
|
+
if (element) {
|
|
311
|
+
const selectionItem = element.querySelector(".ant-select-selection-item");
|
|
312
|
+
|
|
313
|
+
if (selectionItem) {
|
|
314
|
+
value =
|
|
315
|
+
selectionItem.getAttribute("title") ??
|
|
316
|
+
selectionItem.textContent.trim();
|
|
317
|
+
} else {
|
|
318
|
+
value =
|
|
319
|
+
element.getAttribute("value") ??
|
|
320
|
+
element.value ??
|
|
321
|
+
element.innerText ??
|
|
322
|
+
"";
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const safeValue = String(value).replace(/'/g, "''");
|
|
327
|
+
api = api.replaceAll(
|
|
328
|
+
`@${variableName}`,
|
|
329
|
+
encodeURIComponent(`'${safeValue}'`),
|
|
330
|
+
);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const myHeaders = new Headers();
|
|
334
|
+
myHeaders.append("Accept", "*/*");
|
|
335
|
+
myHeaders.append("Accept-Language", "en");
|
|
336
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
337
|
+
myHeaders.append("Connection", "keep-alive");
|
|
338
|
+
|
|
339
|
+
const requestOptions = {
|
|
340
|
+
method: "GET",
|
|
341
|
+
headers: myHeaders,
|
|
342
|
+
redirect: "follow",
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
fetch(api, requestOptions)
|
|
346
|
+
.then((response) => {
|
|
347
|
+
if (response.ok) {
|
|
348
|
+
return response.json();
|
|
349
|
+
}
|
|
350
|
+
console.log("Error: fetchAndMapDataIntoDropdownFromApi");
|
|
351
|
+
throw new Error();
|
|
352
|
+
})
|
|
353
|
+
.then((result) => {
|
|
354
|
+
isMd
|
|
355
|
+
? mapDropDownValueIntoPropertyMD(to, result)
|
|
356
|
+
: mapDropDownValueIntoProperty(to, result);
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
111
360
|
function getPathFromUrl(url) {
|
|
112
361
|
return url.split(/[?#]/)[0];
|
|
113
362
|
}
|
|
114
363
|
|
|
115
|
-
function
|
|
364
|
+
function searchDataByProperyNameWithMultipleMD(
|
|
365
|
+
api,
|
|
366
|
+
from,
|
|
367
|
+
to,
|
|
368
|
+
isMd = false,
|
|
369
|
+
masterDetailIndex = null,
|
|
370
|
+
) {
|
|
116
371
|
api = window.location.origin + api + encodeURIComponent(from.value);
|
|
117
372
|
var myHeaders = new Headers();
|
|
118
|
-
myHeaders.append(
|
|
119
|
-
myHeaders.append(
|
|
120
|
-
myHeaders.append(
|
|
121
|
-
myHeaders.append(
|
|
373
|
+
myHeaders.append("Accept", "*/*");
|
|
374
|
+
myHeaders.append("Accept-Language", "en");
|
|
375
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
376
|
+
myHeaders.append("Connection", "keep-alive");
|
|
122
377
|
var requestOptions = {
|
|
123
|
-
method:
|
|
378
|
+
method: "GET",
|
|
124
379
|
headers: myHeaders,
|
|
125
|
-
redirect:
|
|
380
|
+
redirect: "follow",
|
|
126
381
|
};
|
|
127
382
|
|
|
128
383
|
fetch(api, requestOptions)
|
|
129
|
-
.then(response => response.json())
|
|
130
|
-
.then(result => {
|
|
131
|
-
|
|
384
|
+
.then((response) => response.json())
|
|
385
|
+
.then((result) => {
|
|
386
|
+
isMd
|
|
387
|
+
? mapArrayIntoChoicePropertyInMDMapMultiple[masterDetailIndex](
|
|
388
|
+
to,
|
|
389
|
+
result,
|
|
390
|
+
masterDetailIndex,
|
|
391
|
+
)
|
|
392
|
+
: mapArrayIntoChoiceProperty(to, result, masterDetailIndex);
|
|
132
393
|
})
|
|
133
|
-
.catch(error => console.log(
|
|
394
|
+
.catch((error) => console.log("error", error));
|
|
134
395
|
}
|
|
135
396
|
|
|
136
|
-
function
|
|
137
|
-
|
|
397
|
+
function searchDataByProperyName(
|
|
398
|
+
api,
|
|
399
|
+
from,
|
|
400
|
+
to,
|
|
401
|
+
isMd = false,
|
|
402
|
+
masterDetailIndex = null,
|
|
403
|
+
) {
|
|
404
|
+
api = window.location.origin + api + encodeURIComponent(from.value);
|
|
405
|
+
var myHeaders = new Headers();
|
|
406
|
+
myHeaders.append("Accept", "*/*");
|
|
407
|
+
myHeaders.append("Accept-Language", "en");
|
|
408
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
409
|
+
myHeaders.append("Connection", "keep-alive");
|
|
410
|
+
var requestOptions = {
|
|
411
|
+
method: "GET",
|
|
412
|
+
headers: myHeaders,
|
|
413
|
+
redirect: "follow",
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
fetch(api, requestOptions)
|
|
417
|
+
.then((response) => response.json())
|
|
418
|
+
.then((result) => {
|
|
419
|
+
isMd
|
|
420
|
+
? mapArrayIntoChoicePropertyInMD(to, result, masterDetailIndex)
|
|
421
|
+
: mapArrayIntoChoiceProperty(to, result, masterDetailIndex);
|
|
422
|
+
})
|
|
423
|
+
.catch((error) => console.log("error", error));
|
|
138
424
|
}
|
|
139
425
|
|
|
140
|
-
function searchDataAndMapIntoDropdown(
|
|
426
|
+
function searchDataAndMapIntoDropdown(
|
|
427
|
+
api,
|
|
428
|
+
from,
|
|
429
|
+
to,
|
|
430
|
+
isMD = false,
|
|
431
|
+
isHavingDefaultValue = false,
|
|
432
|
+
) {
|
|
141
433
|
if (from && !isHtmlCollection(from)) {
|
|
142
|
-
api =
|
|
434
|
+
api =
|
|
435
|
+
window.location.origin +
|
|
436
|
+
api +
|
|
437
|
+
(isHavingDefaultValue ? "" : encodeURIComponent(from.value));
|
|
143
438
|
var myHeaders = new Headers();
|
|
144
|
-
myHeaders.append(
|
|
145
|
-
myHeaders.append(
|
|
146
|
-
myHeaders.append(
|
|
147
|
-
myHeaders.append(
|
|
439
|
+
myHeaders.append("Accept", "*/*");
|
|
440
|
+
myHeaders.append("Accept-Language", "en");
|
|
441
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
442
|
+
myHeaders.append("Connection", "keep-alive");
|
|
148
443
|
var requestOptions = {
|
|
149
|
-
method:
|
|
444
|
+
method: "GET",
|
|
150
445
|
headers: myHeaders,
|
|
151
|
-
redirect:
|
|
446
|
+
redirect: "follow",
|
|
152
447
|
};
|
|
153
448
|
|
|
154
449
|
fetch(api, requestOptions)
|
|
155
|
-
.then(response => {
|
|
450
|
+
.then((response) => {
|
|
156
451
|
if (response.ok) {
|
|
157
452
|
return response.json();
|
|
158
453
|
}
|
|
159
|
-
console.log(
|
|
454
|
+
console.log("Error: searchDataAndMapIntoDropdown");
|
|
160
455
|
throw new Error();
|
|
161
456
|
})
|
|
162
|
-
.then(result => {
|
|
163
|
-
|
|
457
|
+
.then((result) => {
|
|
458
|
+
isMD
|
|
459
|
+
? mapDropDownValueIntoPropertyMD(to, result)
|
|
460
|
+
: mapDropDownValueIntoProperty(to, result);
|
|
164
461
|
});
|
|
165
462
|
}
|
|
166
463
|
}
|
|
167
464
|
|
|
168
|
-
function
|
|
169
|
-
|
|
465
|
+
function searchDataAndMapIntoDropdown2(api, from, to, idx = 0, isMD = false) {
|
|
466
|
+
if (from && !isHtmlCollection(from)) {
|
|
467
|
+
const isAbsolute = /^https?:\/\//i.test(api);
|
|
468
|
+
api =
|
|
469
|
+
(isAbsolute ? api : window.location.origin + api) +
|
|
470
|
+
encodeURIComponent(from.value);
|
|
471
|
+
var myHeaders = new Headers();
|
|
472
|
+
myHeaders.append("Accept", "*/*");
|
|
473
|
+
myHeaders.append("Accept-Language", "en");
|
|
474
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
475
|
+
myHeaders.append("Connection", "keep-alive");
|
|
476
|
+
var requestOptions = {
|
|
477
|
+
method: "GET",
|
|
478
|
+
headers: myHeaders,
|
|
479
|
+
redirect: "follow",
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
fetch(api, requestOptions)
|
|
483
|
+
.then((response) => {
|
|
484
|
+
if (response.ok) {
|
|
485
|
+
return response.json();
|
|
486
|
+
}
|
|
487
|
+
console.log("Error: searchDataAndMapIntoDropdown");
|
|
488
|
+
throw new Error();
|
|
489
|
+
})
|
|
490
|
+
.then((result) => {
|
|
491
|
+
isMD
|
|
492
|
+
? mapDropDownValueIntoPropertyMD(to, result)
|
|
493
|
+
: mapDropDownValueIntoProperty(to, result);
|
|
494
|
+
});
|
|
495
|
+
}
|
|
170
496
|
}
|
|
171
497
|
|
|
172
498
|
function mapMDFromMD({
|
|
173
499
|
from,
|
|
174
|
-
fromName =
|
|
500
|
+
fromName = "value",
|
|
175
501
|
masterDetailId,
|
|
176
502
|
fromMDName,
|
|
177
|
-
filterName =
|
|
503
|
+
filterName = "UserWorkflowId",
|
|
178
504
|
toMDName,
|
|
179
505
|
}) {
|
|
180
506
|
searchDataFromMDAndMapIntoMD(
|
|
181
507
|
`/api/v2/workflowReport/SearchDataFromMDAndMapIntoMD/${masterDetailId}?filterName=${filterName}&mdName=${fromMDName}&filterValue=`,
|
|
182
508
|
from,
|
|
183
509
|
`${toMDName}`,
|
|
184
|
-
fromName
|
|
510
|
+
fromName,
|
|
185
511
|
);
|
|
186
512
|
}
|
|
187
513
|
|
|
514
|
+
function mapMDFromMD2({
|
|
515
|
+
from,
|
|
516
|
+
fromName = "value",
|
|
517
|
+
masterDetailId,
|
|
518
|
+
fromMDName,
|
|
519
|
+
filterName = "UserWorkflowId",
|
|
520
|
+
toMDName,
|
|
521
|
+
}) {
|
|
522
|
+
const fromData = window[from];
|
|
523
|
+
searchDataFromMDAndMapIntoMD(
|
|
524
|
+
`/api/v2/workflowReport/SearchDataFromMDAndMapIntoMD/${masterDetailId}?filterName=${filterName}&mdName=${fromMDName}&filterValue=`,
|
|
525
|
+
fromData,
|
|
526
|
+
`${toMDName}`,
|
|
527
|
+
fromName,
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function mapMDFromMDForWareHouse({
|
|
532
|
+
from,
|
|
533
|
+
link,
|
|
534
|
+
dynamic,
|
|
535
|
+
value,
|
|
536
|
+
listHeader,
|
|
537
|
+
fromName = "value",
|
|
538
|
+
workflowId,
|
|
539
|
+
mdName,
|
|
540
|
+
filterName,
|
|
541
|
+
tableNames,
|
|
542
|
+
selectNames,
|
|
543
|
+
searchNames,
|
|
544
|
+
dicNames,
|
|
545
|
+
toMDName,
|
|
546
|
+
}) {
|
|
547
|
+
searchDataFromMDAndMapIntoMD(
|
|
548
|
+
`/api/v2/workflowReport/MapDataFastToMasterDeatail?link=${link}&dynamic=${dynamic}&value=${value}&listHeader=${listHeader}&workflowId=${workflowId}&mdName=${mdName}&filterName=${filterName}&tableNames=${tableNames}&selectNames=${selectNames}&searchNames=${searchNames}&dicNames=${dicNames}&filterValue=`,
|
|
549
|
+
from,
|
|
550
|
+
`${toMDName}`,
|
|
551
|
+
fromName,
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
async function mapMDFromValue(api, items, table, extraSql, where, value, to) {
|
|
556
|
+
try {
|
|
557
|
+
// Split where and value parameters
|
|
558
|
+
const whereFields = where.split("-");
|
|
559
|
+
|
|
560
|
+
const valueFields = value.split("-");
|
|
561
|
+
// Create array of objects based on where and value parameters, filtering out null values
|
|
562
|
+
// const transformedFormValues = whereFields
|
|
563
|
+
// .map((field, index) => ({
|
|
564
|
+
|
|
565
|
+
// name: field,
|
|
566
|
+
// value: getFieldValue(valueFields[index]),
|
|
567
|
+
// }))
|
|
568
|
+
// .filter(item => item.value !== null);
|
|
569
|
+
const transformedFormValues = whereFields
|
|
570
|
+
.map((field, index) => {
|
|
571
|
+
const rawValue = valueFields[index];
|
|
572
|
+
const resolvedValue = getFieldValue(rawValue);
|
|
573
|
+
|
|
574
|
+
return {
|
|
575
|
+
name: field,
|
|
576
|
+
value: resolvedValue ?? rawValue,
|
|
577
|
+
};
|
|
578
|
+
})
|
|
579
|
+
.filter((item) => item.value !== null);
|
|
580
|
+
|
|
581
|
+
// Ensure api is a valid URL by adding origin if it's a relative path
|
|
582
|
+
let baseUrl = api;
|
|
583
|
+
|
|
584
|
+
if (!api.startsWith("http://") && !api.startsWith("https://")) {
|
|
585
|
+
baseUrl = window.location.origin + api;
|
|
586
|
+
}
|
|
587
|
+
const params = new URLSearchParams();
|
|
588
|
+
params.set("items", items); // Let URLSearchParams handle initial structure
|
|
589
|
+
params.set("table", table);
|
|
590
|
+
params.set("extraSql", extraSql);
|
|
591
|
+
|
|
592
|
+
// Rebuild the query string ensuring spaces become %20
|
|
593
|
+
const queryString = Array.from(params.entries())
|
|
594
|
+
.map(
|
|
595
|
+
([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`,
|
|
596
|
+
)
|
|
597
|
+
.join("&");
|
|
598
|
+
|
|
599
|
+
// Construct final URL
|
|
600
|
+
const finalUrl = `${baseUrl}?${queryString}`;
|
|
601
|
+
|
|
602
|
+
// Prepare request options for POST with transformed formValues as body
|
|
603
|
+
const requestOptions = {
|
|
604
|
+
method: "POST",
|
|
605
|
+
headers: {
|
|
606
|
+
"Content-Type": "application/json",
|
|
607
|
+
Accept: "*/*",
|
|
608
|
+
"Accept-Language": "en",
|
|
609
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
610
|
+
Connection: "keep-alive",
|
|
611
|
+
},
|
|
612
|
+
body: JSON.stringify(transformedFormValues),
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// Fetch using the manually constructed URL string
|
|
616
|
+
//const response = await fetch(finalUrl, requestOptions); // Use finalUrl string
|
|
617
|
+
|
|
618
|
+
fetch(finalUrl, requestOptions)
|
|
619
|
+
.then((response) => response.json())
|
|
620
|
+
.then((result) => {
|
|
621
|
+
mapMdValueIntoMD(to, result);
|
|
622
|
+
})
|
|
623
|
+
.catch((error) => console.log("error", error));
|
|
624
|
+
} catch (e) {
|
|
625
|
+
console.log("mapMDFromValueError:", e);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
188
628
|
function searchDataFromMDAndMapIntoMD(api, from, to, fromName) {
|
|
189
629
|
if (from) {
|
|
190
630
|
api =
|
|
191
631
|
window.location.origin +
|
|
192
632
|
api +
|
|
193
|
-
encodeURIComponent(from[fromName ??
|
|
633
|
+
encodeURIComponent(from[fromName ?? "value"]);
|
|
194
634
|
|
|
195
635
|
var myHeaders = new Headers();
|
|
196
636
|
|
|
197
|
-
myHeaders.append(
|
|
198
|
-
myHeaders.append(
|
|
199
|
-
myHeaders.append(
|
|
200
|
-
myHeaders.append(
|
|
637
|
+
myHeaders.append("Accept", "*/*");
|
|
638
|
+
myHeaders.append("Accept-Language", "en");
|
|
639
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
640
|
+
myHeaders.append("Connection", "keep-alive");
|
|
201
641
|
|
|
202
642
|
var requestOptions = {
|
|
203
|
-
method:
|
|
643
|
+
method: "GET",
|
|
204
644
|
headers: myHeaders,
|
|
205
|
-
redirect:
|
|
645
|
+
redirect: "follow",
|
|
206
646
|
};
|
|
207
647
|
|
|
208
648
|
fetch(api, requestOptions)
|
|
209
|
-
.then(response => response.json())
|
|
210
|
-
.then(result => {
|
|
649
|
+
.then((response) => response.json())
|
|
650
|
+
.then((result) => {
|
|
211
651
|
mapMdValueIntoMD(to, result);
|
|
212
652
|
})
|
|
213
|
-
.catch(error => console.log(
|
|
653
|
+
.catch((error) => console.log("error", error));
|
|
214
654
|
}
|
|
215
655
|
}
|
|
656
|
+
function searchDataFromMDAndMapIntoMDAndAddAnotherInfo(
|
|
657
|
+
api,
|
|
658
|
+
to,
|
|
659
|
+
anotherLabel,
|
|
660
|
+
anotherValue,
|
|
661
|
+
) {
|
|
662
|
+
var myHeaders = new Headers();
|
|
663
|
+
|
|
664
|
+
myHeaders.append("Accept", "*/*");
|
|
665
|
+
myHeaders.append("Accept-Language", "en");
|
|
666
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
667
|
+
myHeaders.append("Connection", "keep-alive");
|
|
216
668
|
|
|
669
|
+
var requestOptions = {
|
|
670
|
+
method: "GET",
|
|
671
|
+
headers: myHeaders,
|
|
672
|
+
redirect: "follow",
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
fetch(api, requestOptions)
|
|
676
|
+
.then((response) => response.json())
|
|
677
|
+
.then((result) => {
|
|
678
|
+
let data = [];
|
|
679
|
+
|
|
680
|
+
try {
|
|
681
|
+
data = JSON.parse(result); // Parse chuỗi thành mảng
|
|
682
|
+
} catch (e) {
|
|
683
|
+
console.error("Parse JSON lỗi:", e);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
if (
|
|
687
|
+
anotherLabel &&
|
|
688
|
+
anotherLabel.trim() !== "" &&
|
|
689
|
+
anotherValue &&
|
|
690
|
+
anotherValue.trim() !== ""
|
|
691
|
+
) {
|
|
692
|
+
const labels = anotherLabel.split("-"); // ["a", "b", "c"]
|
|
693
|
+
const values = anotherValue
|
|
694
|
+
.split("-") // ["1", "2", "3"]
|
|
695
|
+
.map((v) => getValue(v));
|
|
696
|
+
|
|
697
|
+
// Thêm Ghichu vào từng phần tử
|
|
698
|
+
data = data.map((item) => {
|
|
699
|
+
const extraFields = {};
|
|
700
|
+
|
|
701
|
+
labels.forEach((label, index) => {
|
|
702
|
+
extraFields[label] = values[index];
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
return {
|
|
706
|
+
...item,
|
|
707
|
+
...extraFields,
|
|
708
|
+
};
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
const newResult = JSON.stringify(data);
|
|
712
|
+
mapMdValueIntoMD(to, newResult);
|
|
713
|
+
})
|
|
714
|
+
.catch((error) => console.log("error", error));
|
|
715
|
+
}
|
|
716
|
+
function mapMDFromMDForWareHouseAndAddAnotherInfo({
|
|
717
|
+
from,
|
|
718
|
+
link,
|
|
719
|
+
dynamic,
|
|
720
|
+
value,
|
|
721
|
+
listHeader,
|
|
722
|
+
workflowId,
|
|
723
|
+
mdName,
|
|
724
|
+
filterName,
|
|
725
|
+
tableNames,
|
|
726
|
+
selectNames,
|
|
727
|
+
searchNames,
|
|
728
|
+
dicNames,
|
|
729
|
+
toMDName,
|
|
730
|
+
anotherLabel,
|
|
731
|
+
anotherValue,
|
|
732
|
+
}) {
|
|
733
|
+
searchDataFromMDAndMapIntoMDAndAddAnotherInfo(
|
|
734
|
+
`/api/v2/workflowReport/MapDataFastToMasterDeatail?link=${link}&dynamic=${dynamic}&value=${value}&listHeader=${listHeader}&workflowId=${workflowId}&mdName=${mdName}&filterName=${filterName}&tableNames=${tableNames}&selectNames=${selectNames}&searchNames=${searchNames}&dicNames=${dicNames}&filterValue=${getValue(
|
|
735
|
+
from,
|
|
736
|
+
)}`,
|
|
737
|
+
`${toMDName}`,
|
|
738
|
+
anotherLabel,
|
|
739
|
+
anotherValue,
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
function clearDataInMDList(from, toMDName, fromName = "value") {
|
|
743
|
+
searchDataFromMDAndMapIntoMD(
|
|
744
|
+
`/api/v2/workflowReport/MapDataFastToMasterDeatail?filterValue=`,
|
|
745
|
+
from,
|
|
746
|
+
`${toMDName}`,
|
|
747
|
+
fromName,
|
|
748
|
+
);
|
|
749
|
+
setTimeout(() => {
|
|
750
|
+
searchDataFromMDAndMapIntoMD(
|
|
751
|
+
`/api/v2/workflowReport/MapDataFastToMasterDeatail?link=clearDataInMDList&filterValue=`,
|
|
752
|
+
from,
|
|
753
|
+
`${toMDName}`,
|
|
754
|
+
fromName,
|
|
755
|
+
);
|
|
756
|
+
}, 1);
|
|
757
|
+
}
|
|
217
758
|
// function reSearchDataByProperyName(api, from, fromProperty, to) {
|
|
218
759
|
// console.log('searchDataByProperyName');
|
|
219
760
|
// api = window.location.origin + api + from[fromProperty];
|
|
@@ -241,33 +782,32 @@ function fromSelectToText(dataFrom, dataTo) {
|
|
|
241
782
|
const selectElement = document.getElementById(dataFrom);
|
|
242
783
|
if (selectElement) {
|
|
243
784
|
const selectItems = selectElement.getElementsByClassName(
|
|
244
|
-
|
|
785
|
+
"ant-select-selection-item",
|
|
245
786
|
);
|
|
787
|
+
|
|
246
788
|
if (selectItems && selectItems.length > 0) {
|
|
247
789
|
const title = selectItems[0].title;
|
|
248
|
-
const textValue = document.getElementById(
|
|
790
|
+
const textValue = document.getElementById("field_" + dataTo);
|
|
249
791
|
if (textValue) {
|
|
250
|
-
const value = title.split(
|
|
792
|
+
const value = title.split("=")[0].trim();
|
|
251
793
|
textValue.innerHTML = value;
|
|
252
794
|
}
|
|
253
795
|
}
|
|
254
796
|
}
|
|
255
797
|
}
|
|
256
798
|
|
|
257
|
-
function
|
|
258
|
-
numberToText(dataFrom, dataTo, unitId, locale);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function numberToText(dataFrom, dataTo, unitId, locale) {
|
|
799
|
+
function numberToText(dataFrom, dataTo, unitId, locale = "vi") {
|
|
262
800
|
if (isDetail === undefined || isDetail === null || isDetail === false) {
|
|
263
801
|
let numberValue = getValue(dataFrom);
|
|
802
|
+
|
|
264
803
|
if (numberValue !== undefined && numberValue !== null) {
|
|
804
|
+
numberValue = Number(numberValue);
|
|
265
805
|
// numberValue = formatNumberString(numberValue, locale);
|
|
266
806
|
|
|
267
807
|
const textValueElement = getElementById(dataTo);
|
|
268
808
|
if (textValueElement) {
|
|
269
809
|
if (numberValue !== undefined && numberValue !== null) {
|
|
270
|
-
let unit = DEFAULT_MONEY_UNIT;
|
|
810
|
+
let unit = DEFAULT_MONEY_UNIT[locale];
|
|
271
811
|
|
|
272
812
|
if (unitId) {
|
|
273
813
|
const unitElement = getElementById(unitId);
|
|
@@ -275,7 +815,7 @@ function numberToText(dataFrom, dataTo, unitId, locale) {
|
|
|
275
815
|
if (unitElement) {
|
|
276
816
|
const itemValue = getValue(unitId);
|
|
277
817
|
if (itemValue !== undefined) {
|
|
278
|
-
const unitName = getMoneyUnitName(itemValue);
|
|
818
|
+
const unitName = getMoneyUnitName(itemValue, locale);
|
|
279
819
|
unit = unitName;
|
|
280
820
|
}
|
|
281
821
|
} else {
|
|
@@ -283,10 +823,12 @@ function numberToText(dataFrom, dataTo, unitId, locale) {
|
|
|
283
823
|
}
|
|
284
824
|
}
|
|
285
825
|
let text = docSoTien(numberValue.toString(), unit, locale);
|
|
286
|
-
text = text?.replaceAll(
|
|
287
|
-
|
|
826
|
+
text = text?.replaceAll(" ", " ");
|
|
827
|
+
setValue(dataTo, text);
|
|
828
|
+
// textValueElement.innerHTML = text;
|
|
288
829
|
} else {
|
|
289
|
-
textValueElement.innerHTML = '';
|
|
830
|
+
// textValueElement.innerHTML = '';
|
|
831
|
+
setValue(dataTo, "");
|
|
290
832
|
}
|
|
291
833
|
}
|
|
292
834
|
}
|
|
@@ -300,69 +842,69 @@ function numberToTextEN(n) {
|
|
|
300
842
|
|
|
301
843
|
// Arrays to hold words for single-digit, double-digit, and below-hundred numbers
|
|
302
844
|
single_digit = [
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
845
|
+
"",
|
|
846
|
+
"One",
|
|
847
|
+
"Two",
|
|
848
|
+
"Three",
|
|
849
|
+
"Four",
|
|
850
|
+
"Five",
|
|
851
|
+
"Six",
|
|
852
|
+
"Seven",
|
|
853
|
+
"Eight",
|
|
854
|
+
"Nine",
|
|
313
855
|
];
|
|
314
856
|
double_digit = [
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
857
|
+
"Ten",
|
|
858
|
+
"Eleven",
|
|
859
|
+
"Twelve",
|
|
860
|
+
"Thirteen",
|
|
861
|
+
"Fourteen",
|
|
862
|
+
"Fifteen",
|
|
863
|
+
"Sixteen",
|
|
864
|
+
"Seventeen",
|
|
865
|
+
"Eighteen",
|
|
866
|
+
"Nineteen",
|
|
325
867
|
];
|
|
326
868
|
below_hundred = [
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
869
|
+
"Twenty",
|
|
870
|
+
"Thirty",
|
|
871
|
+
"Forty",
|
|
872
|
+
"Fifty",
|
|
873
|
+
"Sixty",
|
|
874
|
+
"Seventy",
|
|
875
|
+
"Eighty",
|
|
876
|
+
"Ninety",
|
|
335
877
|
];
|
|
336
878
|
|
|
337
|
-
if (n === 0) return
|
|
879
|
+
if (n === 0) return "Zero";
|
|
338
880
|
|
|
339
881
|
// Recursive function to translate the number into words
|
|
340
882
|
function translate(n) {
|
|
341
|
-
let word =
|
|
883
|
+
let word = "";
|
|
342
884
|
if (n < 10) {
|
|
343
|
-
word = single_digit[n] +
|
|
885
|
+
word = single_digit[n] + " ";
|
|
344
886
|
} else if (n < 20) {
|
|
345
|
-
word = double_digit[n - 10] +
|
|
887
|
+
word = double_digit[n - 10] + " ";
|
|
346
888
|
} else if (n < 100) {
|
|
347
889
|
let rem = translate(n % 10);
|
|
348
|
-
word = below_hundred[(n - (n % 10)) / 10 - 2] +
|
|
890
|
+
word = below_hundred[(n - (n % 10)) / 10 - 2] + " " + rem;
|
|
349
891
|
} else if (n < 1000) {
|
|
350
892
|
word =
|
|
351
|
-
single_digit[Math.trunc(n / 100)] +
|
|
893
|
+
single_digit[Math.trunc(n / 100)] + " Hundred " + translate(n % 100);
|
|
352
894
|
} else if (n < 1000000) {
|
|
353
895
|
word =
|
|
354
896
|
translate(parseInt(n / 1000)).trim() +
|
|
355
|
-
|
|
897
|
+
" Thousand " +
|
|
356
898
|
translate(n % 1000);
|
|
357
899
|
} else if (n < 1000000000) {
|
|
358
900
|
word =
|
|
359
901
|
translate(parseInt(n / 1000000)).trim() +
|
|
360
|
-
|
|
902
|
+
" Million " +
|
|
361
903
|
translate(n % 1000000);
|
|
362
904
|
} else {
|
|
363
905
|
word =
|
|
364
906
|
translate(parseInt(n / 1000000000)).trim() +
|
|
365
|
-
|
|
907
|
+
" Billion " +
|
|
366
908
|
translate(n % 1000000000);
|
|
367
909
|
}
|
|
368
910
|
return word;
|
|
@@ -373,7 +915,7 @@ function numberToTextEN(n) {
|
|
|
373
915
|
return result.trim();
|
|
374
916
|
}
|
|
375
917
|
|
|
376
|
-
function readNumber(dataFrom, unitId, locale, dataTo = colName) {
|
|
918
|
+
function readNumber(dataFrom, unitId, locale = "vi", dataTo = colName) {
|
|
377
919
|
if (isDetail === undefined || isDetail === null || isDetail === false) {
|
|
378
920
|
let numberValue = getFieldValue(dataFrom);
|
|
379
921
|
if (numberValue !== undefined && numberValue !== null) {
|
|
@@ -381,7 +923,7 @@ function readNumber(dataFrom, unitId, locale, dataTo = colName) {
|
|
|
381
923
|
const textValueElement = getElementById(dataTo);
|
|
382
924
|
if (textValueElement) {
|
|
383
925
|
if (numberValue !== undefined && numberValue !== null) {
|
|
384
|
-
let unit = DEFAULT_MONEY_UNIT;
|
|
926
|
+
let unit = DEFAULT_MONEY_UNIT[locale];
|
|
385
927
|
if (unitId) {
|
|
386
928
|
const unitElement = getElementById(unitId);
|
|
387
929
|
if (unitElement) {
|
|
@@ -395,12 +937,12 @@ function readNumber(dataFrom, unitId, locale, dataTo = colName) {
|
|
|
395
937
|
}
|
|
396
938
|
}
|
|
397
939
|
let text = docSoTien(numberValue.toString(), unit, locale);
|
|
398
|
-
text = text?.replaceAll(
|
|
940
|
+
text = text?.replaceAll(" ", " ");
|
|
399
941
|
// textValueElement.innerHTML = text;
|
|
400
942
|
setValue(dataTo, text);
|
|
401
943
|
} else {
|
|
402
944
|
// textValueElement.innerHTML = '';
|
|
403
|
-
setValue(dataTo,
|
|
945
|
+
setValue(dataTo, "");
|
|
404
946
|
}
|
|
405
947
|
}
|
|
406
948
|
}
|
|
@@ -409,13 +951,13 @@ function readNumber(dataFrom, unitId, locale, dataTo = colName) {
|
|
|
409
951
|
}
|
|
410
952
|
}
|
|
411
953
|
|
|
412
|
-
function numberToTextDetail(dataFrom, dataTo, unitId, locale) {
|
|
413
|
-
let unit = DEFAULT_MONEY_UNIT;
|
|
954
|
+
function numberToTextDetail(dataFrom, dataTo, unitId, locale = "vi") {
|
|
955
|
+
let unit = DEFAULT_MONEY_UNIT[locale];
|
|
414
956
|
if (unitId) {
|
|
415
957
|
const unitElement = document.querySelector(`#${unitId}`);
|
|
416
958
|
if (unitElement) {
|
|
417
959
|
const itemElement = unitElement.querySelector(
|
|
418
|
-
|
|
960
|
+
".ant-select-selection-item",
|
|
419
961
|
);
|
|
420
962
|
if (itemElement && itemElement.title) {
|
|
421
963
|
const unitName = getMoneyUnitName(itemElement.title);
|
|
@@ -433,43 +975,43 @@ function numberToTextDetail(dataFrom, dataTo, unitId, locale) {
|
|
|
433
975
|
if (textValue) {
|
|
434
976
|
if (value !== undefined && value !== null) {
|
|
435
977
|
let text = docSoTien(value, unit, locale);
|
|
436
|
-
text = text?.replaceAll(
|
|
978
|
+
text = text?.replaceAll(" ", " ");
|
|
437
979
|
textValue.innerHTML = text;
|
|
438
980
|
} else {
|
|
439
|
-
textValue.innerHTML =
|
|
981
|
+
textValue.innerHTML = "";
|
|
440
982
|
}
|
|
441
983
|
}
|
|
442
984
|
}
|
|
443
985
|
}
|
|
444
986
|
|
|
445
|
-
function textNumberToText(dataFrom, dataTo, unitId) {
|
|
446
|
-
const fromElement = document.getElementById(
|
|
987
|
+
function textNumberToText(dataFrom, dataTo, unitId, locale = "vi") {
|
|
988
|
+
const fromElement = document.getElementById("field_" + dataFrom);
|
|
447
989
|
if (fromElement) {
|
|
448
990
|
const fromValue = fromElement.innerHTML;
|
|
449
991
|
const numberValue = formatNumberString(fromValue);
|
|
450
|
-
const toElement = document.getElementById(
|
|
992
|
+
const toElement = document.getElementById("field_" + dataTo);
|
|
451
993
|
if (toElement) {
|
|
452
994
|
if (numberValue) {
|
|
453
|
-
let unit = DEFAULT_MONEY_UNIT;
|
|
995
|
+
let unit = DEFAULT_MONEY_UNIT[locale];
|
|
454
996
|
if (unitId) {
|
|
455
997
|
const unitElement = document.querySelector(`#${unitId}`);
|
|
456
998
|
if (unitElement) {
|
|
457
999
|
const itemElement = unitElement.querySelector(
|
|
458
|
-
|
|
1000
|
+
".ant-select-selection-item",
|
|
459
1001
|
);
|
|
460
1002
|
if (itemElement && itemElement.title) {
|
|
461
1003
|
const unitName = getMoneyUnitName(itemElement.title);
|
|
462
|
-
unit = unitName;
|
|
1004
|
+
unit = "unitName";
|
|
463
1005
|
}
|
|
464
1006
|
} else {
|
|
465
|
-
unit = unitId;
|
|
1007
|
+
unit = "unitId";
|
|
466
1008
|
}
|
|
467
1009
|
}
|
|
468
1010
|
let text = docSoTien(numberValue.toString(), unit);
|
|
469
|
-
text = text?.replaceAll(
|
|
1011
|
+
text = text?.replaceAll(" ", " ");
|
|
470
1012
|
toElement.innerHTML = text;
|
|
471
1013
|
} else {
|
|
472
|
-
toElement.innerHTML =
|
|
1014
|
+
toElement.innerHTML = "";
|
|
473
1015
|
}
|
|
474
1016
|
}
|
|
475
1017
|
}
|
|
@@ -480,13 +1022,13 @@ function _textNumberToText(dataFrom, dataTo, unitId) {
|
|
|
480
1022
|
}
|
|
481
1023
|
|
|
482
1024
|
// Locale: "vi" | "en"
|
|
483
|
-
function formatNumberString(number, locale =
|
|
484
|
-
let result =
|
|
485
|
-
if (locale ===
|
|
1025
|
+
function formatNumberString(number, locale = "vi") {
|
|
1026
|
+
let result = "0";
|
|
1027
|
+
if (locale === "vi") {
|
|
486
1028
|
result = number
|
|
487
|
-
? number?.toString().replaceAll(
|
|
488
|
-
:
|
|
489
|
-
result = result.replaceAll(
|
|
1029
|
+
? number?.toString().replaceAll(".", "").replaceAll("= ", "")
|
|
1030
|
+
: "0";
|
|
1031
|
+
result = result.replaceAll(",", ".");
|
|
490
1032
|
} else {
|
|
491
1033
|
result = number;
|
|
492
1034
|
}
|
|
@@ -496,7 +1038,9 @@ function formatNumberString(number, locale = 'vi') {
|
|
|
496
1038
|
|
|
497
1039
|
function setValue(toId, value, isMD = false) {
|
|
498
1040
|
const name = toId ?? getCurrentId();
|
|
499
|
-
|
|
1041
|
+
const mdId = isMD ? masterDetailId : undefined;
|
|
1042
|
+
|
|
1043
|
+
window.setFieldValue(name, value, mdId);
|
|
500
1044
|
}
|
|
501
1045
|
|
|
502
1046
|
function setValueMD(name, value, mdId) {
|
|
@@ -530,32 +1074,178 @@ function calc(name, string) {
|
|
|
530
1074
|
}
|
|
531
1075
|
|
|
532
1076
|
const getFieldValue = (name, isMD) => {
|
|
533
|
-
return window.getFieldValue(name, isMD);
|
|
1077
|
+
return window.getFieldValue(name, isMD ? masterDetailId : undefined);
|
|
534
1078
|
};
|
|
535
1079
|
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
1080
|
+
const getFieldContent = (name, isMD) => {
|
|
1081
|
+
return window.getFieldContent(name, isMD ? masterDetailId : false);
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
const getFieldContentMD = (name) => {
|
|
1085
|
+
let value = "";
|
|
1086
|
+
|
|
1087
|
+
const element = document.getElementById(`${masterDetailId}_${name}`);
|
|
1088
|
+
if (element) {
|
|
1089
|
+
const mdField = element.querySelector(".md-field");
|
|
1090
|
+
if (mdField) {
|
|
1091
|
+
const fieldType = mdField.getAttribute("type");
|
|
1092
|
+
const editable = mdField.getAttribute("editable");
|
|
1093
|
+
if (editable === "true") {
|
|
1094
|
+
if (fieldType === "lookup") {
|
|
1095
|
+
const valueFields = mdField.querySelectorAll(
|
|
1096
|
+
".ant-select-selection-item",
|
|
1097
|
+
);
|
|
1098
|
+
if (valueFields.length > 0) {
|
|
1099
|
+
const values = Array.from(valueFields).map(
|
|
1100
|
+
(field) => field.innerText ?? field.textContent ?? "",
|
|
1101
|
+
);
|
|
1102
|
+
value = values.join("; ");
|
|
1103
|
+
}
|
|
1104
|
+
} else if (fieldType === "input") {
|
|
1105
|
+
const inputValue = mdField.querySelector(".ant-input")?.value;
|
|
1106
|
+
if (inputValue) {
|
|
1107
|
+
value = inputValue;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
} else {
|
|
1111
|
+
// View only mode
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return value;
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
const getInnerTextValue = (name, isMD) => {
|
|
1119
|
+
if (!isMD) {
|
|
1120
|
+
// If element is not found, it is editable field, otherwise it is a readonly field
|
|
1121
|
+
let element = document.getElementById(`field_${name}`);
|
|
1122
|
+
if (element) {
|
|
1123
|
+
// Check if element contains an Ant Design checkbox group as a child
|
|
1124
|
+
const checkboxGroup = element.querySelector(".ant-checkbox-group");
|
|
1125
|
+
if (checkboxGroup) {
|
|
1126
|
+
// Get all checked checkboxes within the checkbox group
|
|
1127
|
+
const checkedInputs = checkboxGroup.querySelectorAll(
|
|
1128
|
+
'input[type="checkbox"]:checked',
|
|
1129
|
+
);
|
|
1130
|
+
const checkedLabels = [];
|
|
1131
|
+
|
|
1132
|
+
checkedInputs.forEach((input) => {
|
|
1133
|
+
// Get the label text next to the checkbox
|
|
1134
|
+
const label = input.closest("label");
|
|
1135
|
+
if (label) {
|
|
1136
|
+
// Get the span text (excludes the input)
|
|
1137
|
+
const spans = label.querySelectorAll("span");
|
|
1138
|
+
if (spans.length > 0) {
|
|
1139
|
+
// Usually the text is in the last span
|
|
1140
|
+
const textSpan = spans[spans.length - 1];
|
|
1141
|
+
if (textSpan && textSpan.textContent) {
|
|
1142
|
+
checkedLabels.push(textSpan.textContent.trim());
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
return checkedLabels.join(", ");
|
|
1149
|
+
}
|
|
1150
|
+
return element.innerText ?? element.textContent ?? "";
|
|
1151
|
+
} else {
|
|
1152
|
+
element = document.querySelector(`.field_${name}`);
|
|
1153
|
+
if (element) {
|
|
1154
|
+
// Kiểm tra xem element có phải là select hay lookup không
|
|
1155
|
+
// nếu có thì lấy giá trị của element
|
|
1156
|
+
const type = element.getAttribute("type");
|
|
1157
|
+
if (type === "lookup" || type === "select") {
|
|
1158
|
+
let contentElement = element.querySelector(
|
|
1159
|
+
".ant-select-selection-item-content",
|
|
1160
|
+
);
|
|
1161
|
+
if (!contentElement) {
|
|
1162
|
+
contentElement = element.querySelector(
|
|
1163
|
+
".ant-select-selection-item",
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
if (contentElement) {
|
|
1167
|
+
return contentElement.innerText ?? contentElement.textContent ?? "";
|
|
1168
|
+
}
|
|
1169
|
+
} else if (type === "dateTime") {
|
|
1170
|
+
const inputElement = element.querySelector(
|
|
1171
|
+
`#dynamicWorkflowForm_${name}`,
|
|
1172
|
+
);
|
|
1173
|
+
if (inputElement) {
|
|
1174
|
+
return inputElement.value;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
552
1177
|
}
|
|
553
1178
|
}
|
|
1179
|
+
} else {
|
|
1180
|
+
const fieldContent = getFieldContentMD(name);
|
|
1181
|
+
return fieldContent;
|
|
1182
|
+
}
|
|
1183
|
+
const fieldValue = window.getFieldValue(name, isMD);
|
|
1184
|
+
return fieldValue;
|
|
1185
|
+
};
|
|
1186
|
+
|
|
1187
|
+
const getFieldValueNew = (name, isMasterDetail, IsShowTime) => {
|
|
1188
|
+
return formatDate(getInnerTextValue(name, isMasterDetail), IsShowTime);
|
|
1189
|
+
};
|
|
1190
|
+
|
|
1191
|
+
const formatDate = (value, IsShowTime) => {
|
|
1192
|
+
// 1. Kiểm tra nhanh: Nếu là số, null hoặc không phải chuỗi/đối tượng thì trả về luôn
|
|
1193
|
+
if (!value || typeof parseInt(value, 10) === "number") {
|
|
1194
|
+
return value;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
const date = new Date(value);
|
|
1198
|
+
|
|
1199
|
+
// 2. Kiểm tra tính hợp lệ của Date
|
|
1200
|
+
// isValidDate kiểm tra xem giá trị có chuyển đổi thành mốc thời gian thực hay không
|
|
1201
|
+
const isValidDate = !isNaN(date.getTime());
|
|
1202
|
+
// 3. Kiểm tra bổ sung bằng Regex (Tùy chọn nhưng nên có)
|
|
1203
|
+
// Chỉ xử lý nếu chuỗi có định dạng giống ngày tháng (VD: 2025-12-16 hoặc 12/16/2025)
|
|
1204
|
+
// Điều này ngăn việc một từ ngẫu nhiên bị Date() hiểu nhầm
|
|
1205
|
+
const isDateString = typeof value === "string" && /[\d-:/T]/.test(value);
|
|
1206
|
+
if (isValidDate && isDateString) {
|
|
1207
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
1208
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
1209
|
+
const year = date.getFullYear();
|
|
1210
|
+
const hours = String(date.getHours()).padStart(2, "0");
|
|
1211
|
+
const minutes = String(date.getMinutes()).padStart(2, "0");
|
|
1212
|
+
// Nếu IsShowTime là undefined hoặc false thì chỉ trả về ngày
|
|
1213
|
+
if (!IsShowTime) {
|
|
1214
|
+
return `${day}/${month}/${year}`;
|
|
1215
|
+
} else {
|
|
1216
|
+
return `${day}/${month}/${year} ${hours}:${minutes}`;
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
554
1219
|
|
|
1220
|
+
// Nếu không lọt vào các điều kiện trên, trả về giá trị gốc
|
|
1221
|
+
return value;
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
const handleFormularNew = (formularString, isMD, IsShowTime) => {
|
|
1225
|
+
if (formularString?.length > 0) {
|
|
1226
|
+
const matches = [...formularString.matchAll(/\[([^\]]+)\]/g)];
|
|
1227
|
+
const ids = matches.map((m) => m[1]);
|
|
555
1228
|
let formular = formularString;
|
|
1229
|
+
if (ids.length > 0) {
|
|
1230
|
+
ids.forEach((id) => {
|
|
1231
|
+
const value = getFieldValueNew(id, isMD, IsShowTime) ?? 0;
|
|
1232
|
+
formular = formular.replaceAll(`[${id}]`, value);
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
556
1235
|
|
|
1236
|
+
return { math: formular, elementIds: ids };
|
|
1237
|
+
} else {
|
|
1238
|
+
return { math: formularString, elementIds: [] };
|
|
1239
|
+
}
|
|
1240
|
+
};
|
|
1241
|
+
|
|
1242
|
+
const handleFormular = (formularString, isMD) => {
|
|
1243
|
+
if (formularString?.length > 0) {
|
|
1244
|
+
const matches = [...formularString.matchAll(/\[([^\]]+)\]/g)];
|
|
1245
|
+
const ids = matches.map((m) => m[1]);
|
|
1246
|
+
let formular = formularString;
|
|
557
1247
|
if (ids.length > 0) {
|
|
558
|
-
ids.forEach(id => {
|
|
1248
|
+
ids.forEach((id) => {
|
|
559
1249
|
const value = getFieldValue(id, isMD) ?? 0;
|
|
560
1250
|
formular = formular.replaceAll(`[${id}]`, value);
|
|
561
1251
|
});
|
|
@@ -567,15 +1257,104 @@ const handleFormular = (formularString, isMD) => {
|
|
|
567
1257
|
}
|
|
568
1258
|
};
|
|
569
1259
|
|
|
570
|
-
function
|
|
1260
|
+
function handleCondition(conditionString, isMD) {
|
|
1261
|
+
if (!conditionString) return { math: "false", elementIds: [] };
|
|
1262
|
+
|
|
1263
|
+
let mathResult = conditionString;
|
|
1264
|
+
const ids = [];
|
|
1265
|
+
const matches = conditionString.match(/\[(.*?)\]/g) || [];
|
|
1266
|
+
|
|
1267
|
+
// 1. Thay thế các [ID]
|
|
1268
|
+
matches.forEach((match) => {
|
|
1269
|
+
const id = match.replace(/[\[\]]/g, "");
|
|
1270
|
+
ids.push(id);
|
|
1271
|
+
let value = getFieldValue(id, isMD);
|
|
1272
|
+
// Chỉ bọc nháy nếu giá trị là string và CHƯA bị bọc nháy
|
|
1273
|
+
const safeValue =
|
|
1274
|
+
typeof value === "string"
|
|
1275
|
+
? `'${value.toString().replace(/'/g, "")}'` // Xóa hết nháy cũ đi rồi bọc lại cho chắc
|
|
1276
|
+
: (value ?? "");
|
|
1277
|
+
|
|
1278
|
+
mathResult = mathResult.replaceAll(match, safeValue);
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
// 2. Chuyển = thành ==
|
|
1282
|
+
mathResult = mathResult.replace(/(?<![<>!])=(?!=)/g, "==");
|
|
1283
|
+
|
|
1284
|
+
// 3. Xử lý vế phải (giá trị so sánh cố định)
|
|
1285
|
+
// Xóa bỏ các dấu nháy thừa nếu người dùng lỡ tay nhập: 'Nghỉ phép' -> Nghỉ phép
|
|
1286
|
+
// Sau đó bọc lại một cặp nháy duy nhất cho toàn bộ cụm chữ sau dấu ==
|
|
1287
|
+
mathResult = mathResult.replace(
|
|
1288
|
+
/(==|!=)\s*['"]?(.*?)['"]?$/g,
|
|
1289
|
+
(match, op, content) => {
|
|
1290
|
+
const trimmedContent = content.trim();
|
|
1291
|
+
// Nếu là từ khóa đặc biệt thì không bọc nháy
|
|
1292
|
+
if (["true", "false", "null", "undefined"].includes(trimmedContent))
|
|
1293
|
+
return `${op} ${trimmedContent}`;
|
|
1294
|
+
return `${op} '${trimmedContent.replace(/'/g, "")}'`;
|
|
1295
|
+
},
|
|
1296
|
+
);
|
|
1297
|
+
|
|
1298
|
+
return { math: mathResult, elementIds: ids };
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
function calculateMMD(
|
|
1302
|
+
calcTextTrue,
|
|
1303
|
+
to = colName,
|
|
1304
|
+
conditionText = null,
|
|
1305
|
+
calcTextFalse = null,
|
|
1306
|
+
) {
|
|
1307
|
+
calculate(calcTextTrue, to, conditionText, calcTextFalse, true);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
function calculate(
|
|
1311
|
+
calcTextTrue,
|
|
1312
|
+
to = colName,
|
|
1313
|
+
conditionText = null,
|
|
1314
|
+
calcTextFalse = null,
|
|
1315
|
+
isMasterDetailT = null,
|
|
1316
|
+
) {
|
|
1317
|
+
const isMD = isMasterDetailT !== null ? isMasterDetailT : isMasterDetail;
|
|
571
1318
|
try {
|
|
572
|
-
|
|
573
|
-
|
|
1319
|
+
let isAllowed = true; // Mặc định là true nếu không truyền điều kiện
|
|
1320
|
+
|
|
1321
|
+
// Bước 1: Kiểm tra điều kiện (Nếu có)
|
|
1322
|
+
if (conditionText && conditionText.trim() !== "") {
|
|
1323
|
+
const conditionObj = handleFormular(conditionText, isMD);
|
|
1324
|
+
try {
|
|
1325
|
+
isAllowed = eval(conditionObj.math);
|
|
1326
|
+
} catch (evalError) {
|
|
1327
|
+
console.error("Lỗi cú pháp trong điều kiện:", conditionObj.math);
|
|
1328
|
+
return; // Dừng nếu điều kiện viết sai
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
// Bước 2: Chọn công thức để chạy dựa vào kết quả của isAllowed
|
|
1332
|
+
let finalCalcText = "";
|
|
1333
|
+
|
|
1334
|
+
if (isAllowed) {
|
|
1335
|
+
// Nếu điều kiện ĐÚNG -> Dùng công thức chính
|
|
1336
|
+
finalCalcText = calcTextTrue;
|
|
1337
|
+
} else {
|
|
1338
|
+
// Nếu điều kiện SAI
|
|
1339
|
+
if (calcTextFalse && calcTextFalse.trim() !== "") {
|
|
1340
|
+
// Nếu có truyền công thức dự phòng -> Dùng công thức dự phòng
|
|
1341
|
+
finalCalcText = calcTextFalse;
|
|
1342
|
+
} else {
|
|
1343
|
+
// Nếu sai mà không dặn làm gì tiếp theo -> Bỏ qua, thoát hàm
|
|
1344
|
+
// console.log(
|
|
1345
|
+
// 'Điều kiện sai và không có công thức Else, dừng tính toán.'
|
|
1346
|
+
// );
|
|
1347
|
+
return;
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
// Bước 3: Đưa công thức đã chọn đi tính toán
|
|
1351
|
+
if (finalCalcText && finalCalcText.length > 0) {
|
|
1352
|
+
const formular = handleFormular(finalCalcText, isMD);
|
|
574
1353
|
const result = eval(formular.math);
|
|
575
|
-
setValue(to, result,
|
|
1354
|
+
setValue(to, result, isMD);
|
|
576
1355
|
}
|
|
577
1356
|
} catch (e) {
|
|
578
|
-
console.
|
|
1357
|
+
console.error("Lỗi tổng quát trong calculate:", e);
|
|
579
1358
|
}
|
|
580
1359
|
}
|
|
581
1360
|
|
|
@@ -598,11 +1377,11 @@ function calcSub(name, string) {
|
|
|
598
1377
|
`'${name}'`,
|
|
599
1378
|
`
|
|
600
1379
|
${num}
|
|
601
|
-
|
|
1380
|
+
`,
|
|
602
1381
|
);
|
|
603
1382
|
}
|
|
604
1383
|
} catch (e) {
|
|
605
|
-
console.log(
|
|
1384
|
+
console.log("calcSub", e);
|
|
606
1385
|
}
|
|
607
1386
|
return string;
|
|
608
1387
|
}
|
|
@@ -611,63 +1390,297 @@ function isHtmlCollection(element) {
|
|
|
611
1390
|
return HTMLCollection.prototype.isPrototypeOf(element);
|
|
612
1391
|
}
|
|
613
1392
|
|
|
614
|
-
function mapData(name, from, to,
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
1393
|
+
function mapData(name, from, to, isMD, isDistinct = false) {
|
|
1394
|
+
try {
|
|
1395
|
+
if (isHtmlCollection(from)) return;
|
|
1396
|
+
let isMasterDetailT =
|
|
1397
|
+
isMD !== undefined ? isMD : masterDetailId?.length > 0;
|
|
1398
|
+
if (isMD === false) {
|
|
1399
|
+
isMasterDetailT = false;
|
|
1400
|
+
}
|
|
1401
|
+
if (isMasterDetailT) {
|
|
1402
|
+
// Map data for master detail
|
|
1403
|
+
mapDataMasterDetail(from, name, to, masterDetailId);
|
|
1404
|
+
} else {
|
|
1405
|
+
// Map data for form value
|
|
1406
|
+
//let value = from ? from[name] : undefined;
|
|
1407
|
+
|
|
1408
|
+
//tienth1@vntt.com.vn
|
|
1409
|
+
let value;
|
|
1410
|
+
if (Array.isArray(from)) {
|
|
1411
|
+
// Multiple items selected: join each item's value with ';'
|
|
1412
|
+
const values = from
|
|
1413
|
+
.map((item) => (item ? item[name] : undefined))
|
|
1414
|
+
.filter((v) => v !== undefined && v !== null && v !== "");
|
|
1415
|
+
value = (isDistinct ? [...new Set(values)] : values).join(";");
|
|
1416
|
+
} else {
|
|
1417
|
+
value = from ? from[name] : undefined;
|
|
1418
|
+
}
|
|
618
1419
|
|
|
619
|
-
|
|
620
|
-
|
|
1420
|
+
// if (value && type === 'number') {
|
|
1421
|
+
// value = convertNumberToText(value);
|
|
1422
|
+
// }
|
|
1423
|
+
setValue(to, value);
|
|
1424
|
+
}
|
|
1425
|
+
} catch (e) {
|
|
1426
|
+
console.log("mapData", e);
|
|
621
1427
|
}
|
|
622
|
-
setValue(to, value);
|
|
623
|
-
// const toElement = getElementById(to);
|
|
624
|
-
// if (toElement) {
|
|
625
|
-
// toElement.innerHTML = value ?? '';
|
|
626
|
-
// }
|
|
627
1428
|
}
|
|
628
1429
|
|
|
629
1430
|
function _mapData(name, from, to, type) {
|
|
630
1431
|
mapData(name, from, to, type);
|
|
631
1432
|
}
|
|
632
1433
|
|
|
633
|
-
|
|
1434
|
+
/**
|
|
1435
|
+
* Chuyển đổi ngày UTC sang giờ Local tuỳ chỉnh và format
|
|
1436
|
+
* @param {string} dateStr - Chuỗi ngày (ví dụ: "2/9/2025 5:00:00 PM")
|
|
1437
|
+
* @param {number} offsetHour - Múi giờ muốn chuyển (ví dụ: 7 cho VN, 8 cho TQ)
|
|
1438
|
+
* @param {string} formatStr - Định dạng output (ví dụ: 'DD/MM/YYYY HH:mm:ss')
|
|
1439
|
+
*/
|
|
1440
|
+
|
|
1441
|
+
function mapDateDataMasterDetail(
|
|
1442
|
+
from,
|
|
1443
|
+
fromName,
|
|
1444
|
+
to,
|
|
1445
|
+
id,
|
|
1446
|
+
offsetHour = 7,
|
|
1447
|
+
formatStr = "DD/MM/YYYY",
|
|
1448
|
+
) {
|
|
1449
|
+
try {
|
|
1450
|
+
if (fromName && to && id !== undefined) {
|
|
1451
|
+
let dateStr = "";
|
|
1452
|
+
if (from) {
|
|
1453
|
+
dateStr = from[fromName];
|
|
1454
|
+
// 1. Tạo đối tượng Date, ép kiểu hiểu là UTC bằng cách thêm chuỗi 'UTC' nếu chưa có
|
|
1455
|
+
// Lưu ý: new Date("... UTC") sẽ trả về thời gian chính xác tại mốc UTC đó
|
|
1456
|
+
const isExplicitUTC =
|
|
1457
|
+
dateStr.toUpperCase().includes("Z") ||
|
|
1458
|
+
dateStr.toUpperCase().includes("UTC");
|
|
1459
|
+
const utcDate = new Date(isExplicitUTC ? dateStr : `${dateStr} UTC`);
|
|
1460
|
+
|
|
1461
|
+
if (isNaN(utcDate.getTime())) return "Invalid Date";
|
|
1462
|
+
|
|
1463
|
+
// 2. Cộng thêm giờ offset (đổi ra milliseconds)
|
|
1464
|
+
// Logic: Lấy thời gian UTC gốc + (số giờ * 60 phút * 60 giây * 1000 ms)
|
|
1465
|
+
const targetTime = new Date(
|
|
1466
|
+
utcDate.getTime() + offsetHour * 60 * 60 * 1000,
|
|
1467
|
+
);
|
|
1468
|
+
|
|
1469
|
+
// 3. Chuẩn bị các thành phần để map vào format
|
|
1470
|
+
// Lưu ý: Ta dùng getUTC...() vì ta đã cộng thẳng giờ vào timestamp rồi
|
|
1471
|
+
const map = {
|
|
1472
|
+
DD: String(targetTime.getUTCDate()).padStart(2, "0"),
|
|
1473
|
+
MM: String(targetTime.getUTCMonth() + 1).padStart(2, "0"),
|
|
1474
|
+
YYYY: targetTime.getUTCFullYear(),
|
|
1475
|
+
HH: String(targetTime.getUTCHours()).padStart(2, "0"), // 24h format
|
|
1476
|
+
mm: String(targetTime.getUTCMinutes()).padStart(2, "0"),
|
|
1477
|
+
ss: String(targetTime.getUTCSeconds()).padStart(2, "0"),
|
|
1478
|
+
// Thêm xử lý AM/PM nếu cần thiết (cho định dạng hh)
|
|
1479
|
+
};
|
|
1480
|
+
|
|
1481
|
+
// 4. Replace format
|
|
1482
|
+
dateStr = formatStr.replace(
|
|
1483
|
+
/DD|MM|YYYY|HH|mm|ss/g,
|
|
1484
|
+
(match) => map[match],
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// toElement.innerHTML = value ?? '';
|
|
1489
|
+
setValueMD(to, dateStr ?? "", id);
|
|
1490
|
+
from = undefined;
|
|
1491
|
+
}
|
|
1492
|
+
} catch (e) {
|
|
1493
|
+
console.log("mapDateDataMasterDetailError:", e);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
function mapDataMasterDetail(from, fromName, to, id) {
|
|
1498
|
+
try {
|
|
1499
|
+
if (fromName && to && id !== undefined) {
|
|
1500
|
+
let value = "";
|
|
1501
|
+
// if (from) {
|
|
1502
|
+
|
|
1503
|
+
//tienth1@vntt.com.vn
|
|
1504
|
+
if (Array.isArray(from)) {
|
|
1505
|
+
// Multiple items selected: join each item's value with ';'
|
|
1506
|
+
value = from
|
|
1507
|
+
.map((item) => (item ? item[fromName] : undefined))
|
|
1508
|
+
.filter((v) => v !== undefined && v !== null && v !== "")
|
|
1509
|
+
.join(";");
|
|
1510
|
+
} else if (from) {
|
|
1511
|
+
value = from[fromName];
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
setValueMD(to, value ?? "", id);
|
|
1515
|
+
from = undefined;
|
|
1516
|
+
}
|
|
1517
|
+
} catch (e) {
|
|
1518
|
+
console.log("mapDataMasterDetailError:", e);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
function mapDataMasterDetailWhenDataChange(
|
|
634
1523
|
from,
|
|
635
|
-
|
|
1524
|
+
fromName,
|
|
636
1525
|
to,
|
|
637
1526
|
id,
|
|
638
1527
|
type,
|
|
639
|
-
isEditMasterDetail
|
|
1528
|
+
isEditMasterDetail,
|
|
640
1529
|
) {
|
|
641
1530
|
try {
|
|
642
|
-
if (
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
1531
|
+
if (from[fromName] !== previousName) {
|
|
1532
|
+
// Update the previousName to the current name
|
|
1533
|
+
previousName = from[fromName];
|
|
1534
|
+
if (!isEditMasterDetail) {
|
|
1535
|
+
if (fromName && to && id !== undefined) {
|
|
1536
|
+
let value = "";
|
|
1537
|
+
if (from) {
|
|
1538
|
+
value = from[fromName];
|
|
1539
|
+
// if (value && type === 'number') {
|
|
1540
|
+
// value = '= ' + convertNumberToText(value);
|
|
1541
|
+
// }
|
|
1542
|
+
}
|
|
1543
|
+
// toElement.innerHTML = value ?? '';
|
|
1544
|
+
setValueMD(to, value ?? "", id);
|
|
650
1545
|
}
|
|
651
|
-
// toElement.innerHTML = value ?? '';
|
|
652
|
-
setValueMD(to, value ?? '', id);
|
|
653
1546
|
}
|
|
1547
|
+
} else {
|
|
1548
|
+
// console.log('du lieu ko thay đổi', from[fromName], previousName);
|
|
654
1549
|
}
|
|
655
1550
|
} catch (e) {
|
|
656
|
-
console.log(
|
|
1551
|
+
console.log("mapDataMasterDetailWhenDataChangeError:", e);
|
|
657
1552
|
}
|
|
658
1553
|
}
|
|
659
1554
|
|
|
660
|
-
function
|
|
661
|
-
|
|
1555
|
+
async function mapDataMasterDetailWithFormValue(
|
|
1556
|
+
api,
|
|
1557
|
+
items,
|
|
1558
|
+
table,
|
|
1559
|
+
where,
|
|
1560
|
+
value,
|
|
1561
|
+
target,
|
|
1562
|
+
id,
|
|
1563
|
+
formValues,
|
|
1564
|
+
) {
|
|
1565
|
+
try {
|
|
1566
|
+
// Split where and value parameters
|
|
1567
|
+
const whereFields = where.split("-");
|
|
1568
|
+
|
|
1569
|
+
const valueFields = value.split("-");
|
|
1570
|
+
|
|
1571
|
+
// Create array of objects based on where and value parameters, filtering out null values
|
|
1572
|
+
const transformedFormValues = whereFields
|
|
1573
|
+
.map((field, index) => ({
|
|
1574
|
+
name: field,
|
|
1575
|
+
value: valueFields[index].includes(".")
|
|
1576
|
+
? formValues[valueFields[index].split(".")[0]][
|
|
1577
|
+
valueFields[index].split(".")[1]
|
|
1578
|
+
]
|
|
1579
|
+
: formValues[valueFields[index]],
|
|
1580
|
+
}))
|
|
1581
|
+
.filter((item) => item.value !== null);
|
|
1582
|
+
|
|
1583
|
+
// Ensure api is a valid URL by adding origin if it's a relative path
|
|
1584
|
+
let baseUrl = api;
|
|
1585
|
+
|
|
1586
|
+
if (!api.startsWith("http://") && !api.startsWith("https://")) {
|
|
1587
|
+
baseUrl = window.location.origin + api;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
const params = new URLSearchParams();
|
|
1591
|
+
params.set("items", items); // Let URLSearchParams handle initial structure
|
|
1592
|
+
params.set("table", table);
|
|
1593
|
+
|
|
1594
|
+
// Rebuild the query string ensuring spaces become %20
|
|
1595
|
+
const queryString = Array.from(params.entries())
|
|
1596
|
+
.map(
|
|
1597
|
+
([key, val]) => `${encodeURIComponent(key)}=${encodeURIComponent(val)}`,
|
|
1598
|
+
)
|
|
1599
|
+
.join("&");
|
|
1600
|
+
|
|
1601
|
+
// Construct final URL
|
|
1602
|
+
const finalUrl = `${baseUrl}?${queryString}`;
|
|
1603
|
+
|
|
1604
|
+
// Prepare request options for POST with transformed formValues as body
|
|
1605
|
+
const requestOptions = {
|
|
1606
|
+
method: "POST",
|
|
1607
|
+
headers: {
|
|
1608
|
+
"Content-Type": "application/json",
|
|
1609
|
+
Accept: "*/*",
|
|
1610
|
+
"Accept-Language": "en",
|
|
1611
|
+
"Accept-Encoding": "gzip, deflate, br",
|
|
1612
|
+
Connection: "keep-alive",
|
|
1613
|
+
},
|
|
1614
|
+
body: JSON.stringify(transformedFormValues),
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
// Fetch using the manually constructed URL string
|
|
1618
|
+
const response = await fetch(finalUrl, requestOptions); // Use finalUrl string
|
|
1619
|
+
|
|
1620
|
+
if (!response.ok) {
|
|
1621
|
+
throw new Error("Network response was not ok");
|
|
1622
|
+
}
|
|
1623
|
+
const data = await response.json();
|
|
1624
|
+
if (data) {
|
|
1625
|
+
setValueMD(target, data.value, id);
|
|
1626
|
+
}
|
|
1627
|
+
} catch (e) {
|
|
1628
|
+
console.log("mapDataMasterDetailWithFormValueError:", e);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
async function mapDataMasterDetailForBIDV(api, target, id, formValues) {
|
|
1632
|
+
try {
|
|
1633
|
+
// Ensure api is a valid URL by adding origin if it's a relative path
|
|
1634
|
+
const matches = api.match(/@[a-zA-Z0-9_]+/g);
|
|
1635
|
+
if (matches) {
|
|
1636
|
+
api = api.replace(/@[a-zA-Z0-9_]+/g, (match) => {
|
|
1637
|
+
const key = match.slice(1);
|
|
1638
|
+
return formValues[key] ?? "0";
|
|
1639
|
+
});
|
|
1640
|
+
}
|
|
1641
|
+
let baseUrl = api;
|
|
1642
|
+
|
|
1643
|
+
if (!api.startsWith("http://") && !api.startsWith("https://")) {
|
|
1644
|
+
baseUrl = window.location.origin + api;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// Construct final URL
|
|
1648
|
+
const finalUrl = `${baseUrl}`;
|
|
1649
|
+
|
|
1650
|
+
// Prepare request options for POST with transformed formValues as body
|
|
1651
|
+
var myHeaders = new Headers();
|
|
1652
|
+
myHeaders.append("Accept", "*/*");
|
|
1653
|
+
myHeaders.append("Accept-Language", "en");
|
|
1654
|
+
myHeaders.append("Accept-Encoding", "gzip, deflate, br");
|
|
1655
|
+
myHeaders.append("Connection", "keep-alive");
|
|
1656
|
+
var requestOptions = {
|
|
1657
|
+
method: "GET",
|
|
1658
|
+
headers: myHeaders,
|
|
1659
|
+
redirect: "follow",
|
|
1660
|
+
};
|
|
1661
|
+
|
|
1662
|
+
// Fetch using the manually constructed URL string
|
|
1663
|
+
const response = await fetch(finalUrl, requestOptions); // Use finalUrl string
|
|
1664
|
+
|
|
1665
|
+
if (!response.ok) {
|
|
1666
|
+
throw new Error("Network response was not ok");
|
|
1667
|
+
}
|
|
1668
|
+
const data = await response.json();
|
|
1669
|
+
if (data) {
|
|
1670
|
+
setValueMD(target, data.value, id);
|
|
1671
|
+
}
|
|
1672
|
+
} catch (e) {
|
|
1673
|
+
console.log("mapDataMasterDetailForBIDVError:", e);
|
|
1674
|
+
}
|
|
662
1675
|
}
|
|
663
1676
|
|
|
664
1677
|
function sendLinkToWebView(path, fileName) {
|
|
665
1678
|
try {
|
|
666
1679
|
let pathSendToWebView =
|
|
667
1680
|
window.location.protocol +
|
|
668
|
-
|
|
1681
|
+
"//" +
|
|
669
1682
|
window.location.host +
|
|
670
|
-
path.replace(
|
|
1683
|
+
path.replace("downloadFile", "downloadFileAnonymous");
|
|
671
1684
|
let data = {
|
|
672
1685
|
path: pathSendToWebView,
|
|
673
1686
|
fileName: fileName,
|
|
@@ -680,15 +1693,18 @@ function sendLinkToWebView(path, fileName) {
|
|
|
680
1693
|
}
|
|
681
1694
|
}
|
|
682
1695
|
|
|
1696
|
+
// Dùng riêng cho VNTT để làm Đơn xin nghỉ phép
|
|
1697
|
+
// Tách ký hiệu để map. Ví dụ 4X4P, 4X4K
|
|
683
1698
|
function fromSelectToTextMasterDetail(from, fromProperty, to, id) {
|
|
684
1699
|
if (from) {
|
|
685
1700
|
let value = from[fromProperty];
|
|
686
|
-
const toId = id.toString() +
|
|
1701
|
+
const toId = id.toString() + "_" + to;
|
|
687
1702
|
const toElement = document.getElementById(toId);
|
|
688
1703
|
if (toElement) {
|
|
689
1704
|
if (value) {
|
|
690
|
-
const textValue = value.split(
|
|
691
|
-
|
|
1705
|
+
const textValue = value.split("=")[0].trim();
|
|
1706
|
+
setValueMD(to, textValue, id);
|
|
1707
|
+
// toElement.innerHTML = textValue;
|
|
692
1708
|
}
|
|
693
1709
|
}
|
|
694
1710
|
}
|
|
@@ -699,43 +1715,43 @@ function isStringValid(string) {
|
|
|
699
1715
|
return !format.test(string);
|
|
700
1716
|
}
|
|
701
1717
|
|
|
702
|
-
function _mapNumberMD2F(fromId, toNumberId, toTextId) {
|
|
703
|
-
|
|
1718
|
+
// function _mapNumberMD2F(fromId, toNumberId, toTextId) {
|
|
1719
|
+
// const num = _mapFromMasterDetail(fromId, toNumberId);
|
|
704
1720
|
|
|
705
|
-
|
|
706
|
-
}
|
|
1721
|
+
// _numberToTextFromMD(num, toTextId);
|
|
1722
|
+
// }
|
|
707
1723
|
|
|
708
|
-
function _mapFromMasterDetail(fromId, toId) {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
}
|
|
1724
|
+
// function _mapFromMasterDetail(fromId, toId) {
|
|
1725
|
+
// let result;
|
|
1726
|
+
// const fromElement = getElementById(fromId, true);
|
|
1727
|
+
// if (fromElement) {
|
|
1728
|
+
// const fromValue = getValueMD(fromId) ?? 0;
|
|
1729
|
+
// const toElement = document.querySelector(`#field_${toId}`);
|
|
1730
|
+
// if (toElement) {
|
|
1731
|
+
// toElement.innerHTML = fromValue;
|
|
1732
|
+
// result = fromValue;
|
|
1733
|
+
// }
|
|
1734
|
+
// }
|
|
1735
|
+
// return result;
|
|
1736
|
+
// }
|
|
721
1737
|
|
|
722
|
-
function _numberToTextFromMD(num, dataTo) {
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
}
|
|
1738
|
+
// function _numberToTextFromMD(num, dataTo) {
|
|
1739
|
+
// const toElement = document.querySelector(`#field_${dataTo}`);
|
|
1740
|
+
// if (toElement) {
|
|
1741
|
+
// if (num) {
|
|
1742
|
+
// let number = formatNumberString(num);
|
|
1743
|
+
// if (toElement) {
|
|
1744
|
+
// if (number) {
|
|
1745
|
+
// toElement.innerHTML = docTien.doc(number);
|
|
1746
|
+
// } else {
|
|
1747
|
+
// toElement.innerHTML = '';
|
|
1748
|
+
// }
|
|
1749
|
+
// }
|
|
1750
|
+
// } else {
|
|
1751
|
+
// toElement.innerHTML = '';
|
|
1752
|
+
// }
|
|
1753
|
+
// }
|
|
1754
|
+
// }
|
|
739
1755
|
|
|
740
1756
|
function removeDuplicates(arr) {
|
|
741
1757
|
return arr.filter((item, index) => arr.indexOf(item) === index);
|
|
@@ -745,24 +1761,24 @@ function calcMD(calcText, to, id) {
|
|
|
745
1761
|
try {
|
|
746
1762
|
// const toElement = document.getElementById(`${id}_${to}`);
|
|
747
1763
|
const toElement = to ? getElementById(`${to}`, true) : getCurrentElement();
|
|
748
|
-
let text = calcText.replace(/[^a-zA-Z0-9 ]/g,
|
|
1764
|
+
let text = calcText.replace(/[^a-zA-Z0-9 ]/g, " ");
|
|
749
1765
|
|
|
750
|
-
text = text.trim().replace(/\s+/g,
|
|
751
|
-
let variables = text.split(
|
|
1766
|
+
text = text.trim().replace(/\s+/g, " ");
|
|
1767
|
+
let variables = text.split(" ");
|
|
752
1768
|
|
|
753
1769
|
if (toElement && variables.length > 0) {
|
|
754
1770
|
variables = removeDuplicates(variables);
|
|
755
1771
|
|
|
756
1772
|
const names = [];
|
|
757
1773
|
|
|
758
|
-
variables.forEach(variable => {
|
|
1774
|
+
variables.forEach((variable) => {
|
|
759
1775
|
if (isNaN(variable)) {
|
|
760
1776
|
names.push(variable);
|
|
761
1777
|
}
|
|
762
1778
|
});
|
|
763
1779
|
|
|
764
1780
|
let calc = calcText;
|
|
765
|
-
names.forEach(name => {
|
|
1781
|
+
names.forEach((name) => {
|
|
766
1782
|
try {
|
|
767
1783
|
const numberValue = getValueMD(name) ?? 0;
|
|
768
1784
|
// const num = numberElement
|
|
@@ -775,7 +1791,7 @@ function calcMD(calcText, to, id) {
|
|
|
775
1791
|
|
|
776
1792
|
calc = calc.replaceAll(`'${name}'`, numberValue);
|
|
777
1793
|
} catch (e) {
|
|
778
|
-
throw
|
|
1794
|
+
throw "Not found element: " + name;
|
|
779
1795
|
}
|
|
780
1796
|
});
|
|
781
1797
|
const result = eval(calc);
|
|
@@ -804,51 +1820,49 @@ function calcMMD(calcText, to) {
|
|
|
804
1820
|
|
|
805
1821
|
// const calcText = `('num1_mdsum' - 'num2_mdsum')`;
|
|
806
1822
|
let calc = calcText;
|
|
807
|
-
let text = calcText.replaceAll(
|
|
808
|
-
text = text.replaceAll(
|
|
809
|
-
text = text.replaceAll(
|
|
810
|
-
text = text.replaceAll(
|
|
811
|
-
text = text.replaceAll(
|
|
812
|
-
text = text.replaceAll(
|
|
813
|
-
text = text.replaceAll("'",
|
|
814
|
-
text = text.replaceAll('"',
|
|
1823
|
+
let text = calcText.replaceAll("+", " ");
|
|
1824
|
+
text = text.replaceAll("-", " ");
|
|
1825
|
+
text = text.replaceAll("*", " ");
|
|
1826
|
+
text = text.replaceAll("/", " ");
|
|
1827
|
+
text = text.replaceAll("(", " ");
|
|
1828
|
+
text = text.replaceAll(")", " ");
|
|
1829
|
+
text = text.replaceAll("'", " ");
|
|
1830
|
+
text = text.replaceAll('"', " ");
|
|
815
1831
|
|
|
816
|
-
text = text.trim().replace(/\s+/g,
|
|
1832
|
+
text = text.trim().replace(/\s+/g, " ");
|
|
817
1833
|
|
|
818
|
-
let variables = text.split(
|
|
1834
|
+
let variables = text.split(" ");
|
|
819
1835
|
|
|
820
1836
|
if (toElement && variables.length > 0) {
|
|
821
1837
|
variables = removeDuplicates(variables);
|
|
822
1838
|
|
|
823
1839
|
const names = [];
|
|
824
1840
|
|
|
825
|
-
variables.forEach(variable => {
|
|
1841
|
+
variables.forEach((variable) => {
|
|
826
1842
|
if (isNaN(variable)) {
|
|
827
1843
|
names.push(variable);
|
|
828
1844
|
}
|
|
829
1845
|
});
|
|
830
|
-
|
|
831
|
-
names.forEach(name => {
|
|
1846
|
+
names.forEach((name) => {
|
|
832
1847
|
let val = 0;
|
|
833
|
-
const nameSplit = name.split(
|
|
1848
|
+
const nameSplit = name.split("_");
|
|
834
1849
|
if (nameSplit.length === 2) {
|
|
835
1850
|
const tableElement = document.getElementById(`${nameSplit[0]}`);
|
|
836
1851
|
if (tableElement) {
|
|
837
|
-
const
|
|
838
|
-
|
|
839
|
-
);
|
|
840
|
-
|
|
1852
|
+
const sumClass = `.sum-${nameSplit[1]}`;
|
|
1853
|
+
const valueElement = tableElement.querySelector(sumClass);
|
|
841
1854
|
if (valueElement) {
|
|
842
1855
|
val = valueElement.innerHTML;
|
|
843
1856
|
}
|
|
844
1857
|
}
|
|
845
1858
|
}
|
|
846
1859
|
if (val !== 0) {
|
|
847
|
-
val = formatNumberString(val);
|
|
1860
|
+
val = formatNumberString(val.replace(" %", ""));
|
|
848
1861
|
}
|
|
849
1862
|
calc = calc.replaceAll(`'${name}'`, val ?? 0);
|
|
850
1863
|
});
|
|
851
|
-
const result = eval(calc !==
|
|
1864
|
+
const result = eval(calc !== "()" ? calc : "");
|
|
1865
|
+
|
|
852
1866
|
setValue(to, result);
|
|
853
1867
|
// toElement.innerHTML = Intl.NumberFormat('vi-VN', {}).format(result);
|
|
854
1868
|
}
|
|
@@ -861,14 +1875,14 @@ const getElementById = (name, isMD) => {
|
|
|
861
1875
|
if (name) {
|
|
862
1876
|
let isMasterDetailT = isMD;
|
|
863
1877
|
if (!isMasterDetailT) {
|
|
864
|
-
const splitName = name.split(
|
|
865
|
-
if (splitName.length === 2 && splitName[0] ===
|
|
1878
|
+
const splitName = name.split("-");
|
|
1879
|
+
if (splitName.length === 2 && splitName[0] === "md") {
|
|
866
1880
|
isMasterDetailT = true;
|
|
867
1881
|
}
|
|
868
1882
|
}
|
|
869
1883
|
// check if master detail
|
|
870
1884
|
if (isMasterDetailT && mdCellId) {
|
|
871
|
-
const ids = mdCellId.split(
|
|
1885
|
+
const ids = mdCellId.split("_");
|
|
872
1886
|
if (ids.length === 3) {
|
|
873
1887
|
const id = `${ids[0]}_${ids[1]}_${name}`;
|
|
874
1888
|
const ele = document.getElementById(id);
|
|
@@ -882,7 +1896,7 @@ const getElementById = (name, isMD) => {
|
|
|
882
1896
|
}
|
|
883
1897
|
}
|
|
884
1898
|
} else {
|
|
885
|
-
const ele = document.getElementById(
|
|
1899
|
+
const ele = document.getElementById("field_" + name);
|
|
886
1900
|
|
|
887
1901
|
if (ele) {
|
|
888
1902
|
return ele;
|
|
@@ -891,7 +1905,7 @@ const getElementById = (name, isMD) => {
|
|
|
891
1905
|
if (classEle) {
|
|
892
1906
|
return classEle;
|
|
893
1907
|
}
|
|
894
|
-
return document.getElementById(
|
|
1908
|
+
return document.getElementById("dynamicWorkflowForm_" + name);
|
|
895
1909
|
}
|
|
896
1910
|
}
|
|
897
1911
|
}
|
|
@@ -925,7 +1939,7 @@ const getValue = (name, mdId) => {
|
|
|
925
1939
|
// }
|
|
926
1940
|
};
|
|
927
1941
|
|
|
928
|
-
const getCurrentElement = isMD => {
|
|
1942
|
+
const getCurrentElement = (isMD) => {
|
|
929
1943
|
let isMasterDetailT = isMasterDetail;
|
|
930
1944
|
if (isMD !== undefined) {
|
|
931
1945
|
isMasterDetailT = isMD;
|
|
@@ -933,24 +1947,53 @@ const getCurrentElement = isMD => {
|
|
|
933
1947
|
if (isMasterDetailT) {
|
|
934
1948
|
return document.getElementById(mdCellId);
|
|
935
1949
|
} else {
|
|
936
|
-
return document.getElementById(
|
|
1950
|
+
return document.getElementById("field_" + colName);
|
|
937
1951
|
}
|
|
938
1952
|
};
|
|
939
1953
|
|
|
940
|
-
const getCurrentId = isMD => {
|
|
1954
|
+
const getCurrentId = (isMD) => {
|
|
941
1955
|
let isMasterDetailT = isMasterDetail;
|
|
942
1956
|
if (isMD !== undefined) {
|
|
943
1957
|
isMasterDetailT = isMD;
|
|
944
1958
|
}
|
|
945
1959
|
if (isMasterDetailT) {
|
|
946
|
-
|
|
1960
|
+
if (mdCellId) {
|
|
1961
|
+
const mdCells = mdCellId.split("_");
|
|
1962
|
+
if (mdCells.length === 3) {
|
|
1963
|
+
return mdCells[2];
|
|
1964
|
+
}
|
|
1965
|
+
return mdCellId;
|
|
1966
|
+
}
|
|
947
1967
|
} else {
|
|
948
1968
|
return colName;
|
|
949
1969
|
}
|
|
950
1970
|
};
|
|
951
1971
|
|
|
952
1972
|
function isValidDate(str) {
|
|
953
|
-
return dayjs(str,
|
|
1973
|
+
return dayjs(str, "YYYY-MM-DD", true).isValid();
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
function connectTextMD(name, formularString, IsShowTime = undefined) {
|
|
1977
|
+
try {
|
|
1978
|
+
if (formularString && formularString.length > 0) {
|
|
1979
|
+
var connect = handleFormularNew(formularString, true, IsShowTime);
|
|
1980
|
+
setValue(name, connect.math, true);
|
|
1981
|
+
}
|
|
1982
|
+
} catch (e) {
|
|
1983
|
+
console.log(e);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
function connectText(name, formularString, IsShowTime = undefined) {
|
|
1988
|
+
try {
|
|
1989
|
+
if (formularString && formularString.length > 0) {
|
|
1990
|
+
var connect = handleFormularNew(formularString, false, IsShowTime);
|
|
1991
|
+
|
|
1992
|
+
setValue(name, connect.math, false);
|
|
1993
|
+
}
|
|
1994
|
+
} catch (e) {
|
|
1995
|
+
console.log(e);
|
|
1996
|
+
}
|
|
954
1997
|
}
|
|
955
1998
|
|
|
956
1999
|
const datetime = {
|
|
@@ -961,119 +2004,166 @@ const datetime = {
|
|
|
961
2004
|
}
|
|
962
2005
|
let dateTime;
|
|
963
2006
|
if (date.length === 10) {
|
|
964
|
-
let dateSplit = date.split(
|
|
2007
|
+
let dateSplit = date.split("/");
|
|
965
2008
|
|
|
966
2009
|
if (dateSplit[2].length === 4) {
|
|
967
2010
|
dateSplit = dateSplit.reverse();
|
|
968
2011
|
}
|
|
969
|
-
dateTime = dateSplit.join(
|
|
2012
|
+
dateTime = dateSplit.join("/") + " 00:00";
|
|
970
2013
|
} else if (date.length === 16) {
|
|
971
|
-
const dateTimes = date.split(
|
|
972
|
-
const dateSplit = dateTimes[0].split(
|
|
973
|
-
dateTime = dateSplit.reverse().join(
|
|
2014
|
+
const dateTimes = date.split(" ");
|
|
2015
|
+
const dateSplit = dateTimes[0].split("/");
|
|
2016
|
+
dateTime = dateSplit.reverse().join("/") + ` ${dateTimes[1]}`;
|
|
974
2017
|
} else if (date.length === 19) {
|
|
975
|
-
return dayjs(date,
|
|
2018
|
+
return dayjs(date, "YYYY-MM-DDTHH:mm:ss");
|
|
976
2019
|
}
|
|
977
2020
|
if (dateTime) {
|
|
978
|
-
return dayjs(dateTime,
|
|
2021
|
+
return dayjs(dateTime, "YYYY/MM/DD HH:mm");
|
|
979
2022
|
} else if (isValidDate(date)) {
|
|
980
2023
|
return dayjs(date);
|
|
981
2024
|
}
|
|
982
2025
|
}
|
|
983
2026
|
},
|
|
984
2027
|
|
|
985
|
-
count(type, fromDateName, toDateName,
|
|
986
|
-
|
|
2028
|
+
count(type, fromDateName, toDateName, isMD) {
|
|
2029
|
+
const isMDValue = isMD !== undefined ? isMD : isMasterDetail;
|
|
2030
|
+
let result = "";
|
|
987
2031
|
|
|
988
|
-
|
|
989
|
-
|
|
2032
|
+
let fromDateValue = getValue(fromDateName, isMDValue);
|
|
2033
|
+
let toDateValue = getValue(toDateName, isMDValue);
|
|
2034
|
+
|
|
2035
|
+
if (!fromDateValue && fromDateName.toLowerCase() === "today") {
|
|
2036
|
+
fromDateValue = new Date();
|
|
2037
|
+
}
|
|
2038
|
+
if (!toDateValue && toDateName.toLowerCase() === "today") {
|
|
2039
|
+
toDateValue = new Date();
|
|
2040
|
+
}
|
|
990
2041
|
|
|
991
2042
|
if (
|
|
992
2043
|
fromDateValue &&
|
|
993
|
-
fromDateValue !==
|
|
2044
|
+
fromDateValue !== "Invalid Date" &&
|
|
994
2045
|
toDateValue &&
|
|
995
|
-
toDateValue !==
|
|
2046
|
+
toDateValue !== "Invalid Date"
|
|
996
2047
|
) {
|
|
997
2048
|
let fromDate = this.formatDayjs(fromDateValue);
|
|
998
2049
|
let toDate = this.formatDayjs(toDateValue);
|
|
999
2050
|
|
|
1000
|
-
let diff =
|
|
2051
|
+
let diff = "";
|
|
1001
2052
|
switch (type) {
|
|
1002
|
-
case
|
|
1003
|
-
diff = toDate.diff(fromDate,
|
|
2053
|
+
case "minutes":
|
|
2054
|
+
diff = toDate.diff(fromDate, "minutes");
|
|
1004
2055
|
break;
|
|
1005
|
-
case
|
|
1006
|
-
diff = toDate.diff(fromDate,
|
|
2056
|
+
case "hours":
|
|
2057
|
+
diff = toDate.diff(fromDate, "hours");
|
|
1007
2058
|
break;
|
|
1008
|
-
case
|
|
1009
|
-
diff = toDate.diff(fromDate,
|
|
2059
|
+
case "days":
|
|
2060
|
+
diff = toDate.diff(fromDate, "days") + 1;
|
|
1010
2061
|
break;
|
|
1011
|
-
case
|
|
1012
|
-
diff = toDate.diff(fromDate,
|
|
2062
|
+
case "months":
|
|
2063
|
+
diff = toDate.diff(fromDate, "months");
|
|
1013
2064
|
break;
|
|
1014
|
-
case
|
|
1015
|
-
diff = toDate.diff(fromDate,
|
|
2065
|
+
case "years":
|
|
2066
|
+
diff = toDate.diff(fromDate, "years");
|
|
1016
2067
|
break;
|
|
1017
2068
|
|
|
1018
2069
|
default:
|
|
1019
2070
|
break;
|
|
1020
2071
|
}
|
|
2072
|
+
|
|
1021
2073
|
result = diff.toString();
|
|
1022
2074
|
}
|
|
1023
|
-
|
|
1024
|
-
setValue(toId, result, isMasterDetail);
|
|
2075
|
+
return result;
|
|
1025
2076
|
// toElement.innerHTML = result;
|
|
1026
2077
|
},
|
|
1027
2078
|
};
|
|
1028
2079
|
|
|
1029
|
-
const countYears = (fromDateName, toDateName, toId) => {
|
|
1030
|
-
datetime.count(
|
|
2080
|
+
const countYears = (fromDateName, toDateName, toId, isMD) => {
|
|
2081
|
+
const result = datetime.count("years", fromDateName, toDateName, isMD);
|
|
2082
|
+
setValue(toId, result, isMasterDetail);
|
|
1031
2083
|
};
|
|
1032
2084
|
|
|
1033
|
-
const countMonths = (fromDateName, toDateName, toId) => {
|
|
1034
|
-
datetime.count(
|
|
2085
|
+
const countMonths = (fromDateName, toDateName, toId, isMD) => {
|
|
2086
|
+
const result = datetime.count("months", fromDateName, toDateName, isMD);
|
|
2087
|
+
setValue(toId, result, isMasterDetail);
|
|
1035
2088
|
};
|
|
1036
2089
|
|
|
1037
|
-
const countDays = (fromDateName, toDateName, toId) => {
|
|
1038
|
-
datetime.count(
|
|
2090
|
+
const countDays = (fromDateName, toDateName, toId, isMD) => {
|
|
2091
|
+
const result = datetime.count("days", fromDateName, toDateName, isMD);
|
|
2092
|
+
setValue(toId, result, isMasterDetail);
|
|
1039
2093
|
};
|
|
1040
2094
|
|
|
1041
|
-
const countHours = (fromDateName, toDateName, toId) => {
|
|
1042
|
-
datetime.count(
|
|
2095
|
+
const countHours = (fromDateName, toDateName, toId, isMD) => {
|
|
2096
|
+
const result = datetime.count("hours", fromDateName, toDateName, isMD);
|
|
2097
|
+
setValue(toId, result, isMasterDetail);
|
|
1043
2098
|
};
|
|
1044
2099
|
|
|
1045
|
-
const countMinutes = (fromDateName, toDateName, toId) => {
|
|
1046
|
-
datetime.count(
|
|
2100
|
+
const countMinutes = (fromDateName, toDateName, toId, isMD) => {
|
|
2101
|
+
const result = datetime.count("minutes", fromDateName, toDateName, isMD);
|
|
2102
|
+
setValue(toId, result, isMasterDetail);
|
|
1047
2103
|
};
|
|
1048
2104
|
|
|
1049
2105
|
const validateDatesRange = (startDate, toDate, dateType, count) => {
|
|
1050
2106
|
window.validateDatesRange(startDate, toDate, dateType, count);
|
|
1051
2107
|
};
|
|
1052
2108
|
|
|
1053
|
-
const validatePhoneNumber = name => {
|
|
2109
|
+
const validatePhoneNumber = (name) => {
|
|
1054
2110
|
window.validatePhoneNumber(name);
|
|
1055
2111
|
};
|
|
1056
2112
|
|
|
1057
|
-
const validateEmail = name => {
|
|
2113
|
+
const validateEmail = (name) => {
|
|
1058
2114
|
window.validateEmail(name);
|
|
1059
2115
|
};
|
|
1060
2116
|
|
|
1061
|
-
const
|
|
1062
|
-
|
|
2117
|
+
const validateField = (fieldName, scriptString, message, isMasterDetailT) => {
|
|
2118
|
+
let mdId = masterDetailId;
|
|
2119
|
+
if (isMasterDetailT === false) {
|
|
2120
|
+
mdId = undefined;
|
|
2121
|
+
}
|
|
2122
|
+
const value = getValue(fieldName, mdId);
|
|
2123
|
+
if (value !== "null") {
|
|
2124
|
+
const formular = handleFormularString(scriptString, mdId);
|
|
2125
|
+
|
|
2126
|
+
const isValid = eval(formular.math);
|
|
2127
|
+
|
|
2128
|
+
window.handleActionButtons(isValid, formular.elementIds, mdId, message);
|
|
2129
|
+
} else {
|
|
2130
|
+
window.handleActionButtons(true, [fieldName], mdId);
|
|
2131
|
+
}
|
|
1063
2132
|
};
|
|
1064
2133
|
|
|
1065
|
-
const
|
|
2134
|
+
const validateNumber = (fieldName, formularText, message) => {
|
|
2135
|
+
const value = getValue(fieldName);
|
|
2136
|
+
if (value !== "null" && value !== undefined && value !== null) {
|
|
2137
|
+
const formular = handleFormularString(formularText, isMasterDetail);
|
|
2138
|
+
const isValid = eval(formular.math);
|
|
2139
|
+
|
|
2140
|
+
window.handleActionButtons(
|
|
2141
|
+
isValid,
|
|
2142
|
+
formular.elementIds,
|
|
2143
|
+
isMasterDetail,
|
|
2144
|
+
message,
|
|
2145
|
+
);
|
|
2146
|
+
} else {
|
|
2147
|
+
window.handleActionButtons(true, [fieldName], isMasterDetail);
|
|
2148
|
+
}
|
|
2149
|
+
};
|
|
2150
|
+
|
|
2151
|
+
const convertNumberToText = (value) => {
|
|
2152
|
+
return new Intl.NumberFormat("vi-VN").format(Number(value));
|
|
2153
|
+
};
|
|
2154
|
+
|
|
2155
|
+
const displayNumber = (elementId) => {
|
|
1066
2156
|
const id = elementId ?? colName;
|
|
1067
2157
|
const element = getElementById(id);
|
|
1068
2158
|
if (element) {
|
|
1069
2159
|
const value = getValue(id) ?? 0;
|
|
1070
|
-
if (!value.includes(
|
|
1071
|
-
element.innerHTML = new Intl.NumberFormat(
|
|
2160
|
+
if (!value.includes(".")) {
|
|
2161
|
+
element.innerHTML = new Intl.NumberFormat("vi-VN").format(Number(value));
|
|
1072
2162
|
}
|
|
1073
2163
|
}
|
|
1074
2164
|
};
|
|
1075
2165
|
|
|
1076
|
-
const _displayNumber = elementId => {
|
|
2166
|
+
const _displayNumber = (elementId) => {
|
|
1077
2167
|
displayNumber(elementId);
|
|
1078
2168
|
};
|
|
1079
2169
|
|
|
@@ -1081,61 +2171,576 @@ const validateForm = (isValid, elementIds, isMD) => {
|
|
|
1081
2171
|
window.handleActionButtons(isValid, elementIds, isMD);
|
|
1082
2172
|
};
|
|
1083
2173
|
|
|
1084
|
-
const compareNumbers = (compareString, isMD) => {
|
|
2174
|
+
const compareNumbers = (compareString, isMD, error) => {
|
|
1085
2175
|
const formular = handleFormularString(compareString, isMD);
|
|
1086
|
-
|
|
1087
|
-
|
|
2176
|
+
window.handleActionButtons(
|
|
2177
|
+
eval(formular.math),
|
|
2178
|
+
formular.elementIds,
|
|
2179
|
+
isMD ? masterDetailId : false,
|
|
2180
|
+
error,
|
|
2181
|
+
);
|
|
1088
2182
|
};
|
|
1089
2183
|
|
|
1090
|
-
const compareNumbersMD = compareString => {
|
|
2184
|
+
const compareNumbersMD = (compareString) => {
|
|
1091
2185
|
compareNumbers(compareString, true);
|
|
1092
2186
|
};
|
|
1093
2187
|
|
|
1094
2188
|
const handleFormularString = (formularString, isMD) => {
|
|
1095
|
-
|
|
1096
|
-
formularString
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
adding = false;
|
|
1107
|
-
name = '';
|
|
1108
|
-
} else if (adding === true) {
|
|
1109
|
-
name += formularString[i];
|
|
2189
|
+
try {
|
|
2190
|
+
if (formularString?.length > 0) {
|
|
2191
|
+
const cleanFormular = formularString.replace(/\s/g, "");
|
|
2192
|
+
const matches = [...cleanFormular.matchAll(/\[([^\]]+)\]/g)];
|
|
2193
|
+
const ids = matches.map((m) => m[1]);
|
|
2194
|
+
let formular = cleanFormular;
|
|
2195
|
+
if (ids.length > 0) {
|
|
2196
|
+
ids.forEach((id) => {
|
|
2197
|
+
const value = isMD ? getValueMD(id) : getValue(id);
|
|
2198
|
+
formular = formular.replaceAll(`[${id}]`, value);
|
|
2199
|
+
});
|
|
1110
2200
|
}
|
|
2201
|
+
return { math: formular, elementIds: ids };
|
|
1111
2202
|
}
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
ids.forEach(id => {
|
|
1116
|
-
const value = formatNumberString(
|
|
1117
|
-
isMD ? getValueMD(id) : getValue(id) ?? '0'
|
|
1118
|
-
);
|
|
1119
|
-
formular = formular.replaceAll(`[${id}]`, value);
|
|
1120
|
-
});
|
|
1121
|
-
}
|
|
1122
|
-
return { math: formular, elementIds: ids };
|
|
1123
|
-
} else {
|
|
2203
|
+
return { math: formularString, elementIds: [] };
|
|
2204
|
+
} catch (e) {
|
|
2205
|
+
console.error("handleFormularString error:", e);
|
|
1124
2206
|
return { math: formularString, elementIds: [] };
|
|
1125
2207
|
}
|
|
1126
2208
|
};
|
|
1127
2209
|
|
|
1128
2210
|
const valid = () => {
|
|
1129
|
-
window.handleActionButtons
|
|
2211
|
+
if (typeof window !== "undefined" && typeof window.handleActionButtons === "function") {
|
|
2212
|
+
window.handleActionButtons(true);
|
|
2213
|
+
}
|
|
1130
2214
|
};
|
|
1131
2215
|
|
|
1132
2216
|
const invalid = () => {
|
|
1133
|
-
window.handleActionButtons
|
|
2217
|
+
if (typeof window !== "undefined" && typeof window.handleActionButtons === "function") {
|
|
2218
|
+
window.handleActionButtons(false);
|
|
2219
|
+
}
|
|
1134
2220
|
};
|
|
1135
2221
|
|
|
1136
|
-
const convertVIToENNumber = num => {
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
num = num.replaceAll('-', '.');
|
|
1140
|
-
return num;
|
|
2222
|
+
const convertVIToENNumber = (num) => {
|
|
2223
|
+
if (typeof num !== "string") return num;
|
|
2224
|
+
return num.replace(/[.,]/g, (m) => (m === "," ? "." : ","));
|
|
1141
2225
|
};
|
|
2226
|
+
|
|
2227
|
+
function mapDayOfWeek(date, target, isMD) {
|
|
2228
|
+
const value = getValueMD(date, isMD);
|
|
2229
|
+
let val;
|
|
2230
|
+
if (value !== undefined) {
|
|
2231
|
+
val = dayjs(value).day()?.toString();
|
|
2232
|
+
}
|
|
2233
|
+
setValue(target, val, isMD);
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
function validateDate(startDate, endDate, condition, isMD, isDateOnly = false) {
|
|
2237
|
+
const startDateValue = getValue(startDate, isMD);
|
|
2238
|
+
const endDateValue = getValue(endDate, isMD);
|
|
2239
|
+
|
|
2240
|
+
let isValid = false;
|
|
2241
|
+
if (startDateValue && endDateValue) {
|
|
2242
|
+
let startDatejs = dayjs(startDateValue);
|
|
2243
|
+
let endDatejs = dayjs(endDateValue);
|
|
2244
|
+
|
|
2245
|
+
if (isDateOnly) {
|
|
2246
|
+
startDatejs = startDatejs.startOf("day");
|
|
2247
|
+
endDatejs = endDatejs.startOf("day");
|
|
2248
|
+
}
|
|
2249
|
+
|
|
2250
|
+
switch (condition) {
|
|
2251
|
+
case ">=":
|
|
2252
|
+
case "=>":
|
|
2253
|
+
isValid =
|
|
2254
|
+
startDatejs.isAfter(endDatejs) || startDatejs.isSame(endDatejs);
|
|
2255
|
+
break;
|
|
2256
|
+
|
|
2257
|
+
case "<=":
|
|
2258
|
+
case "=<":
|
|
2259
|
+
isValid =
|
|
2260
|
+
startDatejs.isBefore(endDatejs) || startDatejs.isSame(endDatejs);
|
|
2261
|
+
break;
|
|
2262
|
+
case ">":
|
|
2263
|
+
isValid = startDatejs.isAfter(endDatejs);
|
|
2264
|
+
break;
|
|
2265
|
+
case "<":
|
|
2266
|
+
isValid = startDatejs.isBefore(endDatejs);
|
|
2267
|
+
break;
|
|
2268
|
+
|
|
2269
|
+
case "=":
|
|
2270
|
+
isValid = startDatejs.isSame(endDatejs);
|
|
2271
|
+
break;
|
|
2272
|
+
|
|
2273
|
+
default:
|
|
2274
|
+
isValid = false;
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
window.handleActionButtons(isValid, [endDate], isMD, "Date is not valid");
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
function getYearFromDateTime(from, to, isShortYear = false, isMD = false) {
|
|
2281
|
+
const value = getValue(from, isMD);
|
|
2282
|
+
const year = dayjs(value).year();
|
|
2283
|
+
let yearString = year.toString();
|
|
2284
|
+
|
|
2285
|
+
if (isShortYear) {
|
|
2286
|
+
yearString = yearString.slice(2);
|
|
2287
|
+
}
|
|
2288
|
+
setValue(to, yearString, isMD);
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
function getNumberInSingleProperty(from, to, isMD = false) {
|
|
2292
|
+
const value = getValue(from, isMD);
|
|
2293
|
+
if (value) {
|
|
2294
|
+
const number = value.replace(/[^0-9]/g, "");
|
|
2295
|
+
const numberValue = Number(number);
|
|
2296
|
+
setValue(to, numberValue, isMD);
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
/**
|
|
2301
|
+
* Kiểu selector cho DynamicFieldResolver:
|
|
2302
|
+
* có thể truyền tên field (string) hoặc 1 callback function nhận (item, lang)
|
|
2303
|
+
*
|
|
2304
|
+
* @param {Record<string, any>} item
|
|
2305
|
+
* @param {string | ((item: Record<string, any>, lang: string) => any)} resolver
|
|
2306
|
+
* @param {string} lang
|
|
2307
|
+
* @returns {any}
|
|
2308
|
+
*/
|
|
2309
|
+
function resolveFieldValue(item, resolver, lang) {
|
|
2310
|
+
if (!item) return undefined;
|
|
2311
|
+
|
|
2312
|
+
if (typeof resolver === "function") {
|
|
2313
|
+
return resolver(item, lang);
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
if (typeof resolver !== "string") {
|
|
2317
|
+
return undefined;
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
// Convention tự động: nếu lang === 'en', ưu tiên tìm `${fieldName}EN`, không có thì fallback về fieldName gốc
|
|
2321
|
+
if (lang === "en") {
|
|
2322
|
+
const enKey = `${resolver}EN`;
|
|
2323
|
+
if (item[enKey] !== undefined && item[enKey] !== null) {
|
|
2324
|
+
return item[enKey];
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
return item[resolver];
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
/**
|
|
2332
|
+
* Trích xuất data từ Master Detail và map thành options cho dropdown/choice field trên DynamicForm
|
|
2333
|
+
*
|
|
2334
|
+
* @param {string} fromMasterDetailName - Tên của Master Detail nguồn
|
|
2335
|
+
* @param {string | ((item: Record<string, any>, lang: string) => any)} valueName - Tên field hoặc function resolver lấy value
|
|
2336
|
+
* @param {string | ((item: Record<string, any>, lang: string) => any)} labelName - Tên field hoặc function resolver lấy label
|
|
2337
|
+
* @param {string} [toName] - Tên trường dropdown đích trên DynamicForm hoặc MasterDetail (mặc định lấy trường đang chạy script)
|
|
2338
|
+
* @param {boolean} [isMD] - Chỉ định rõ ràng đích đến là Master Detail (nếu không truyền sẽ tự động nhận diện)
|
|
2339
|
+
*/
|
|
2340
|
+
/**
|
|
2341
|
+
* Kho đăng ký toàn cục cho các mapping options từ Master Detail
|
|
2342
|
+
* key: `${fromMasterDetailName}::${toName}`
|
|
2343
|
+
* value: { fromMasterDetailName, valueName, labelName, toName, isMD }
|
|
2344
|
+
*/
|
|
2345
|
+
if (typeof window !== "undefined") {
|
|
2346
|
+
if (!window.masterDetailOptionMappings) {
|
|
2347
|
+
window.masterDetailOptionMappings = new Map();
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
/**
|
|
2352
|
+
* Đăng ký một quy tắc mapping options từ Master Detail vào Registry
|
|
2353
|
+
*/
|
|
2354
|
+
function registerMasterDetailOptionMapping(
|
|
2355
|
+
fromMasterDetailName,
|
|
2356
|
+
valueName,
|
|
2357
|
+
labelName,
|
|
2358
|
+
toName,
|
|
2359
|
+
isMD,
|
|
2360
|
+
) {
|
|
2361
|
+
if (typeof window === "undefined") return;
|
|
2362
|
+
if (!window.masterDetailOptionMappings) {
|
|
2363
|
+
window.masterDetailOptionMappings = new Map();
|
|
2364
|
+
}
|
|
2365
|
+
const key = `${fromMasterDetailName}::${toName}`;
|
|
2366
|
+
const mapping = {
|
|
2367
|
+
fromMasterDetailName,
|
|
2368
|
+
valueName,
|
|
2369
|
+
labelName,
|
|
2370
|
+
toName,
|
|
2371
|
+
isMD,
|
|
2372
|
+
};
|
|
2373
|
+
window.masterDetailOptionMappings.set(key, mapping);
|
|
2374
|
+
return mapping;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
/**
|
|
2378
|
+
* Thực thi mapping options từ Master Detail vào trường đích
|
|
2379
|
+
*/
|
|
2380
|
+
function applyMasterDetailOptionMapping(mapping) {
|
|
2381
|
+
if (!mapping || !mapping.fromMasterDetailName || !mapping.toName) return;
|
|
2382
|
+
const { fromMasterDetailName, valueName, labelName, toName, isMD } = mapping;
|
|
2383
|
+
|
|
2384
|
+
// 1. Lấy data của Master Detail nguồn
|
|
2385
|
+
let mdData = [];
|
|
2386
|
+
if (typeof window.getMasterDetailData === "function") {
|
|
2387
|
+
mdData = window.getMasterDetailData(fromMasterDetailName);
|
|
2388
|
+
}
|
|
2389
|
+
if ((!mdData || mdData.length === 0) && window.masterDetailData) {
|
|
2390
|
+
mdData = window.masterDetailData[fromMasterDetailName];
|
|
2391
|
+
}
|
|
2392
|
+
if ((!mdData || mdData.length === 0) && window.masterDetailValues) {
|
|
2393
|
+
mdData = window.masterDetailValues[fromMasterDetailName];
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
// Fallback: Tìm records từ DOM container nếu có
|
|
2397
|
+
if (!mdData || mdData.length === 0) {
|
|
2398
|
+
const mdElement =
|
|
2399
|
+
typeof document !== "undefined"
|
|
2400
|
+
? document.querySelector(`[data-name="${fromMasterDetailName}"]`) ||
|
|
2401
|
+
document.getElementById(`master-detail-${fromMasterDetailName}`)
|
|
2402
|
+
: null;
|
|
2403
|
+
if (mdElement) {
|
|
2404
|
+
const cellElements = mdElement.querySelectorAll("td[record], [record]");
|
|
2405
|
+
const recordsMap = new Map();
|
|
2406
|
+
cellElements.forEach((cell) => {
|
|
2407
|
+
try {
|
|
2408
|
+
const recordStr = cell.getAttribute("record");
|
|
2409
|
+
if (recordStr) {
|
|
2410
|
+
const rec = JSON.parse(recordStr);
|
|
2411
|
+
if (rec && rec.id !== undefined && !recordsMap.has(rec.id)) {
|
|
2412
|
+
recordsMap.set(rec.id, rec);
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
} catch (e) {
|
|
2416
|
+
// ignore parse error
|
|
2417
|
+
}
|
|
2418
|
+
});
|
|
2419
|
+
if (recordsMap.size > 0) {
|
|
2420
|
+
mdData = Array.from(recordsMap.values());
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
// 2. Xác định ngôn ngữ hiện tại
|
|
2426
|
+
const lang = (
|
|
2427
|
+
(typeof localStorage !== "undefined" &&
|
|
2428
|
+
(localStorage.getItem("locale") || localStorage.getItem("i18nextLng"))) ||
|
|
2429
|
+
"vi"
|
|
2430
|
+
)
|
|
2431
|
+
.toLowerCase()
|
|
2432
|
+
.startsWith("en")
|
|
2433
|
+
? "en"
|
|
2434
|
+
: "vi";
|
|
2435
|
+
|
|
2436
|
+
// 3. Map các items thành mảng options, deduplicate theo value
|
|
2437
|
+
const seen = new Set();
|
|
2438
|
+
const options = [];
|
|
2439
|
+
|
|
2440
|
+
if (Array.isArray(mdData)) {
|
|
2441
|
+
mdData.forEach((item) => {
|
|
2442
|
+
if (!item) return;
|
|
2443
|
+
|
|
2444
|
+
const val = resolveFieldValue(item, valueName, lang);
|
|
2445
|
+
const lbl = resolveFieldValue(item, labelName, lang);
|
|
2446
|
+
|
|
2447
|
+
if (val !== undefined && val !== null && val !== "") {
|
|
2448
|
+
if (!seen.has(val)) {
|
|
2449
|
+
seen.add(val);
|
|
2450
|
+
options.push({
|
|
2451
|
+
...item,
|
|
2452
|
+
value: val,
|
|
2453
|
+
label: lbl !== undefined && lbl !== null && lbl !== "" ? lbl : val,
|
|
2454
|
+
userStatus: 1,
|
|
2455
|
+
item,
|
|
2456
|
+
});
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
// 4. Map options vào field dropdown (tự động nhận diện Master Detail hoặc DynamicForm)
|
|
2463
|
+
const fnMD =
|
|
2464
|
+
(typeof window !== "undefined" && window.mapDropDownValueIntoPropertyMD) ||
|
|
2465
|
+
(typeof mapDropDownValueIntoPropertyMD === "function"
|
|
2466
|
+
? mapDropDownValueIntoPropertyMD
|
|
2467
|
+
: null);
|
|
2468
|
+
|
|
2469
|
+
const fnForm =
|
|
2470
|
+
(typeof window !== "undefined" && window.mapDropDownValueIntoProperty) ||
|
|
2471
|
+
(typeof mapDropDownValueIntoProperty === "function"
|
|
2472
|
+
? mapDropDownValueIntoProperty
|
|
2473
|
+
: null);
|
|
2474
|
+
|
|
2475
|
+
const isTargetMD =
|
|
2476
|
+
Boolean(isMD) ||
|
|
2477
|
+
(typeof isMasterDetail !== "undefined" && Boolean(isMasterDetail)) ||
|
|
2478
|
+
(typeof masterDetailIndex !== "undefined" && masterDetailIndex !== null);
|
|
2479
|
+
|
|
2480
|
+
if (isTargetMD) {
|
|
2481
|
+
if (fnMD) {
|
|
2482
|
+
fnMD(toName, options);
|
|
2483
|
+
} else if (fnForm) {
|
|
2484
|
+
fnForm(toName, options);
|
|
2485
|
+
}
|
|
2486
|
+
} else {
|
|
2487
|
+
// Thử map vào DynamicForm trước
|
|
2488
|
+
if (fnForm) {
|
|
2489
|
+
fnForm(toName, options);
|
|
2490
|
+
}
|
|
2491
|
+
// Fallback sang Master Detail nếu target chứa '_' hoặc không có element trên Form
|
|
2492
|
+
if (
|
|
2493
|
+
fnMD &&
|
|
2494
|
+
toName &&
|
|
2495
|
+
(toName.includes("_") ||
|
|
2496
|
+
(typeof document !== "undefined" &&
|
|
2497
|
+
!document.getElementById(`field_${toName}`) &&
|
|
2498
|
+
!document.getElementById(toName)))
|
|
2499
|
+
) {
|
|
2500
|
+
fnMD(toName, options);
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
/**
|
|
2506
|
+
* Kích hoạt cập nhật options cho tất cả các mapping phụ thuộc vào Master Detail nguồn
|
|
2507
|
+
* @param {string} [fromMasterDetailName] - Tên Master Detail nguồn, nếu không truyền sẽ trigger toàn bộ
|
|
2508
|
+
*/
|
|
2509
|
+
function triggerMasterDetailOptionMappings(fromMasterDetailName) {
|
|
2510
|
+
if (
|
|
2511
|
+
typeof window === "undefined" ||
|
|
2512
|
+
!window.masterDetailOptionMappings ||
|
|
2513
|
+
window.masterDetailOptionMappings.size === 0
|
|
2514
|
+
) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
window.masterDetailOptionMappings.forEach((mapping) => {
|
|
2518
|
+
if (
|
|
2519
|
+
!fromMasterDetailName ||
|
|
2520
|
+
mapping.fromMasterDetailName === fromMasterDetailName
|
|
2521
|
+
) {
|
|
2522
|
+
try {
|
|
2523
|
+
applyMasterDetailOptionMapping(mapping);
|
|
2524
|
+
} catch (err) {
|
|
2525
|
+
console.error(
|
|
2526
|
+
"triggerMasterDetailOptionMappings error for mapping:",
|
|
2527
|
+
mapping,
|
|
2528
|
+
err,
|
|
2529
|
+
);
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
});
|
|
2533
|
+
}
|
|
2534
|
+
|
|
2535
|
+
/**
|
|
2536
|
+
* Quét toàn bộ scripts trong workflow để phát hiện các hàm mapOptionsFromMasterDetail
|
|
2537
|
+
* và đăng ký trước vào Registry, giúp tự động hiển thị options ngay khi tải lại trang
|
|
2538
|
+
* @param {any} workflow - Model workflow cần quét
|
|
2539
|
+
*/
|
|
2540
|
+
function scanAndRegisterMasterDetailOptionMappings(workflow) {
|
|
2541
|
+
if (!workflow || typeof window === "undefined") return;
|
|
2542
|
+
try {
|
|
2543
|
+
const scriptsToScan = [];
|
|
2544
|
+
|
|
2545
|
+
// 1. Quét properties của workflow
|
|
2546
|
+
if (Array.isArray(workflow.properties)) {
|
|
2547
|
+
workflow.properties.forEach((prop) => {
|
|
2548
|
+
if (prop && prop.executeJS) {
|
|
2549
|
+
scriptsToScan.push({ script: prop.executeJS, target: prop.name });
|
|
2550
|
+
}
|
|
2551
|
+
});
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
// 2. Quét workflowSteps nếu có
|
|
2555
|
+
if (Array.isArray(workflow.workflowSteps)) {
|
|
2556
|
+
workflow.workflowSteps.forEach((step) => {
|
|
2557
|
+
if (Array.isArray(step.stepFields)) {
|
|
2558
|
+
step.stepFields.forEach((field) => {
|
|
2559
|
+
if (field && field.executeJS) {
|
|
2560
|
+
scriptsToScan.push({
|
|
2561
|
+
script: field.executeJS,
|
|
2562
|
+
target: field.name,
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
|
|
2570
|
+
// 3. Quét masterDetails nếu có
|
|
2571
|
+
if (Array.isArray(workflow.masterDetails)) {
|
|
2572
|
+
workflow.masterDetails.forEach((md) => {
|
|
2573
|
+
const checkRows = (rows) => {
|
|
2574
|
+
if (Array.isArray(rows)) {
|
|
2575
|
+
rows.forEach((r) => {
|
|
2576
|
+
const cols = Array.isArray(r) ? r : r.row || [];
|
|
2577
|
+
cols.forEach((col) => {
|
|
2578
|
+
if (col && (col.executeJs || col.executeJS)) {
|
|
2579
|
+
scriptsToScan.push({
|
|
2580
|
+
script: col.executeJs || col.executeJS,
|
|
2581
|
+
target: col.name,
|
|
2582
|
+
isMD: true,
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
});
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
};
|
|
2589
|
+
checkRows(md.masterDetailRows);
|
|
2590
|
+
checkRows(md.row);
|
|
2591
|
+
});
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
const regex =
|
|
2595
|
+
/mapOptionsFromMasterDetail\s*\(\s*(['"][^'"]+['"])\s*,\s*(['"][^'"]+['"])\s*,\s*(['"][^'"]+['"])(?:\s*,\s*(['"][^'"]+['"]))?(?:\s*,\s*(true|false))?\s*\)/g;
|
|
2596
|
+
|
|
2597
|
+
scriptsToScan.forEach(({ script, target, isMD }) => {
|
|
2598
|
+
let match;
|
|
2599
|
+
regex.lastIndex = 0;
|
|
2600
|
+
while ((match = regex.exec(script)) !== null) {
|
|
2601
|
+
const fromMD = match[1].replace(/['"]/g, "").trim();
|
|
2602
|
+
const valName = match[2].replace(/['"]/g, "").trim();
|
|
2603
|
+
const lblName = match[3].replace(/['"]/g, "").trim();
|
|
2604
|
+
const explicitTarget = match[4]
|
|
2605
|
+
? match[4].replace(/['"]/g, "").trim()
|
|
2606
|
+
: target;
|
|
2607
|
+
const explicitIsMD = match[5] ? match[5] === "true" : isMD;
|
|
2608
|
+
|
|
2609
|
+
if (fromMD && explicitTarget) {
|
|
2610
|
+
registerMasterDetailOptionMapping(
|
|
2611
|
+
fromMD,
|
|
2612
|
+
valName,
|
|
2613
|
+
lblName,
|
|
2614
|
+
explicitTarget,
|
|
2615
|
+
explicitIsMD,
|
|
2616
|
+
);
|
|
2617
|
+
|
|
2618
|
+
// Nếu MD nguồn đã có data sẵn thì map ngay
|
|
2619
|
+
if (
|
|
2620
|
+
(window.masterDetailData &&
|
|
2621
|
+
window.masterDetailData[fromMD] &&
|
|
2622
|
+
window.masterDetailData[fromMD].length > 0) ||
|
|
2623
|
+
(typeof window.getMasterDetailData === "function" &&
|
|
2624
|
+
window.getMasterDetailData(fromMD)?.length > 0)
|
|
2625
|
+
) {
|
|
2626
|
+
triggerMasterDetailOptionMappings(fromMD);
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
}
|
|
2630
|
+
});
|
|
2631
|
+
} catch (e) {
|
|
2632
|
+
console.error("scanAndRegisterMasterDetailOptionMappings error:", e);
|
|
2633
|
+
}
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
/**
|
|
2637
|
+
* Trích xuất dữ liệu từ một Master Detail và ánh xạ thành danh sách Options (value, label)
|
|
2638
|
+
* cho trường dropdown trên DynamicForm hoặc Master Detail khác.
|
|
2639
|
+
* Đồng thời tự động đăng ký vào Registry để tự động đồng bộ khi Master Detail nguồn có dữ liệu mới.
|
|
2640
|
+
*
|
|
2641
|
+
* @param {string} fromMasterDetailName - Tên Master Detail nguồn (ví dụ: "DS_SanPham")
|
|
2642
|
+
* @param {string | ((item: Record<string, any>, lang: string) => any)} valueName - Tên field hoặc function resolver lấy value
|
|
2643
|
+
* @param {string | ((item: Record<string, any>, lang: string) => any)} labelName - Tên field hoặc function resolver lấy label
|
|
2644
|
+
* @param {string} [toName] - Tên trường dropdown đích trên DynamicForm hoặc MasterDetail (mặc định lấy trường đang chạy script)
|
|
2645
|
+
* @param {boolean} [isMD] - Chỉ định rõ ràng đích đến là Master Detail (nếu không truyền sẽ tự động nhận diện)
|
|
2646
|
+
*/
|
|
2647
|
+
function mapOptionsFromMasterDetail(
|
|
2648
|
+
fromMasterDetailName,
|
|
2649
|
+
valueName,
|
|
2650
|
+
labelName,
|
|
2651
|
+
toName,
|
|
2652
|
+
isMD,
|
|
2653
|
+
) {
|
|
2654
|
+
try {
|
|
2655
|
+
const target =
|
|
2656
|
+
toName ||
|
|
2657
|
+
(typeof colName !== "undefined" ? colName : undefined) ||
|
|
2658
|
+
(typeof getCurrentId === "function" ? getCurrentId() : undefined);
|
|
2659
|
+
|
|
2660
|
+
if (!target) {
|
|
2661
|
+
console.warn(
|
|
2662
|
+
"mapOptionsFromMasterDetail: target field (toName) is required.",
|
|
2663
|
+
);
|
|
2664
|
+
return;
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
if (!fromMasterDetailName) {
|
|
2668
|
+
console.warn(
|
|
2669
|
+
"mapOptionsFromMasterDetail: fromMasterDetailName is required.",
|
|
2670
|
+
);
|
|
2671
|
+
return;
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
// Luôn đăng ký vào Registry để tự động kích hoạt lại khi Master Detail nguồn cập nhật dữ liệu
|
|
2675
|
+
registerMasterDetailOptionMapping(
|
|
2676
|
+
fromMasterDetailName,
|
|
2677
|
+
valueName,
|
|
2678
|
+
labelName,
|
|
2679
|
+
target,
|
|
2680
|
+
isMD,
|
|
2681
|
+
);
|
|
2682
|
+
|
|
2683
|
+
// Tiến hành ánh xạ dữ liệu
|
|
2684
|
+
applyMasterDetailOptionMapping({
|
|
2685
|
+
fromMasterDetailName,
|
|
2686
|
+
valueName,
|
|
2687
|
+
labelName,
|
|
2688
|
+
toName: target,
|
|
2689
|
+
isMD,
|
|
2690
|
+
});
|
|
2691
|
+
} catch (e) {
|
|
2692
|
+
console.error("mapOptionsFromMasterDetail error:", e);
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
|
|
2696
|
+
// Expose ra window để đảm bảo gọi được ở mọi nơi
|
|
2697
|
+
window.resolveFieldValue = resolveFieldValue;
|
|
2698
|
+
window.mapOptionsFromMasterDetail = mapOptionsFromMasterDetail;
|
|
2699
|
+
window.registerMasterDetailOptionMapping = registerMasterDetailOptionMapping;
|
|
2700
|
+
window.applyMasterDetailOptionMapping = applyMasterDetailOptionMapping;
|
|
2701
|
+
window.triggerMasterDetailOptionMappings = triggerMasterDetailOptionMappings;
|
|
2702
|
+
window.scanAndRegisterMasterDetailOptionMappings =
|
|
2703
|
+
scanAndRegisterMasterDetailOptionMappings;
|
|
2704
|
+
|
|
2705
|
+
/**
|
|
2706
|
+
* Khi xóa bất kỳ dòng nào trong Master Detail có liên quan đến mapOptions,
|
|
2707
|
+
* tìm tất cả các trường đang sử dụng options được trích xuất từ Master Detail này
|
|
2708
|
+
* và set giá trị của chúng về undefined.
|
|
2709
|
+
* @param {string} fromMasterDetailName - Tên Master Detail nguồn
|
|
2710
|
+
* @param {any} [formInstance] - Form chính (userWorkflowForm)
|
|
2711
|
+
*/
|
|
2712
|
+
function resetDependentFieldsOfMasterDetail(
|
|
2713
|
+
fromMasterDetailName,
|
|
2714
|
+
formInstance,
|
|
2715
|
+
) {
|
|
2716
|
+
if (!fromMasterDetailName || typeof window === "undefined") return;
|
|
2717
|
+
try {
|
|
2718
|
+
if (
|
|
2719
|
+
window.masterDetailOptionMappings &&
|
|
2720
|
+
window.masterDetailOptionMappings.size > 0
|
|
2721
|
+
) {
|
|
2722
|
+
window.masterDetailOptionMappings.forEach((mapping) => {
|
|
2723
|
+
if (
|
|
2724
|
+
mapping &&
|
|
2725
|
+
mapping.fromMasterDetailName === fromMasterDetailName &&
|
|
2726
|
+
mapping.toName
|
|
2727
|
+
) {
|
|
2728
|
+
const target = mapping.toName;
|
|
2729
|
+
const targetForm =
|
|
2730
|
+
formInstance ||
|
|
2731
|
+
(typeof window.userWorkflowForm !== "undefined"
|
|
2732
|
+
? window.userWorkflowForm
|
|
2733
|
+
: null);
|
|
2734
|
+
|
|
2735
|
+
if (targetForm && typeof targetForm.setFieldValue === "function") {
|
|
2736
|
+
targetForm.setFieldValue(target, undefined);
|
|
2737
|
+
}
|
|
2738
|
+
}
|
|
2739
|
+
});
|
|
2740
|
+
}
|
|
2741
|
+
} catch (err) {
|
|
2742
|
+
console.error("resetDependentFieldsOfMasterDetail error:", err);
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
window.resetDependentFieldsOfMasterDetail = resetDependentFieldsOfMasterDetail;
|