From 34171eaa3c52757b0482954aaf05928ecb914a94 Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Tue, 19 Aug 2025 14:23:01 +0400 Subject: [PATCH] fixed bug with duplicate in script --- .../doctype/formula_editor/formula_editor.js | 311 ++++++++++-------- 1 file changed, 178 insertions(+), 133 deletions(-) diff --git a/formula_editor/formula_editor/doctype/formula_editor/formula_editor.js b/formula_editor/formula_editor/doctype/formula_editor/formula_editor.js index 721164d..99971d6 100644 --- a/formula_editor/formula_editor/doctype/formula_editor/formula_editor.js +++ b/formula_editor/formula_editor/doctype/formula_editor/formula_editor.js @@ -246,94 +246,94 @@ frappe.require([ // Добавляем функцию для обработки и выполнения формул functions.push(` - // Функция для обработки и выполнения формул - async function processFormula(frm, formula) { - try { - // Проверяем наличие вызовов отчетов - if (formula.includes("report(")) { - // Извлекаем все вызовы report() из формулы - const reportRegex = /report\\(([^)]+)\\)/g; - let match; - let processedFormula = formula; - - // Массив для хранения промисов запросов к отчетам - const reportPromises = []; - const reportPlaceholders = []; - - // Находим все вызовы report() и заменяем их на плейсхолдеры - while ((match = reportRegex.exec(formula)) !== null) { - const fullMatch = match[0]; // report(fieldName) - const fieldName = match[1]; // fieldName + // Функция для обработки и выполнения формул + async function processFormula(frm, formula) { + try { + // Проверяем наличие вызовов отчетов + if (formula.includes("report(")) { + // Извлекаем все вызовы report() из формулы + const reportRegex = /report\\(([^)]+)\\)/g; + let match; + let processedFormula = formula; - // Создаем уникальный плейсхолдер - const placeholder = \`__REPORT_PLACEHOLDER_\${reportPromises.length}__\`; + // Массив для хранения промисов запросов к отчетам + const reportPromises = []; + const reportPlaceholders = []; - // Ищем данные отчета в хранилище - const reportFields = JSON.parse(frm.doc.report_fields_storage || "{}"); - const reportField = Object.values(reportFields).find(f => f.name === fieldName); - - if (reportField) { - // Добавляем промис запроса к отчету - reportPromises.push( - getReportFieldValue( - frm, - reportField.report, - JSON.parse(reportField.filters_json || "{}"), - reportField.field_name, - reportField.aggregation || 'sum' - ) - ); + // Находим все вызовы report() и заменяем их на плейсхолдеры + while ((match = reportRegex.exec(formula)) !== null) { + const fullMatch = match[0]; // report(fieldName) + const fieldName = match[1]; // fieldName - // Запоминаем соответствие плейсхолдера и вызова - reportPlaceholders.push({ - placeholder: placeholder, - original: fullMatch - }); + // Создаем уникальный плейсхолдер + const placeholder = \`__REPORT_PLACEHOLDER_\${reportPromises.length}__\`; - // Заменяем вызов отчета на плейсхолдер - processedFormula = processedFormula.replace(fullMatch, placeholder); + // Ищем данные отчета в хранилище + const reportFields = JSON.parse(frm.doc.report_fields_storage || "{}"); + const reportField = Object.values(reportFields).find(f => f.name === fieldName); + + if (reportField) { + // Добавляем промис запроса к отчету + reportPromises.push( + getReportFieldValue( + frm, + reportField.report, + JSON.parse(reportField.filters_json || "{}"), + reportField.field_name, + reportField.aggregation || 'sum' + ) + ); + + // Запоминаем соответствие плейсхолдера и вызова + reportPlaceholders.push({ + placeholder: placeholder, + original: fullMatch + }); + + // Заменяем вызов отчета на плейсхолдер + processedFormula = processedFormula.replace(fullMatch, placeholder); + } + } + + // Если нашли вызовы отчетов, ждем выполнения всех запросов + if (reportPromises.length > 0) { + const reportResults = await Promise.all(reportPromises); + + // Заменяем плейсхолдеры на результаты + for (let i = 0; i < reportPlaceholders.length; i++) { + // Убедимся, что результат - числовое значение + const reportValue = reportResults[i] !== undefined ? Number(reportResults[i]) : 0; + processedFormula = processedFormula.replace( + reportPlaceholders[i].placeholder, + reportValue + ); + } + + // Теперь формула не содержит вызовов report(), обрабатываем её обычным способом + let transformResult = transform_expression(processedFormula, false); + + // Если есть внешняя трансформация, применяем её + let result; + if (transformResult && transformResult.transformed) { + result = eval(transformResult.transformed); + } else { + // Иначе просто вычисляем выражение + result = eval(processedFormula); + } + + // Возвращаем результат + return isNaN(result) ? 0 : result; } } - // Если нашли вызовы отчетов, ждем выполнения всех запросов - if (reportPromises.length > 0) { - const reportResults = await Promise.all(reportPromises); - - // Заменяем плейсхолдеры на результаты - for (let i = 0; i < reportPlaceholders.length; i++) { - // Убедимся, что результат - числовое значение - const reportValue = reportResults[i] !== undefined ? Number(reportResults[i]) : 0; - processedFormula = processedFormula.replace( - reportPlaceholders[i].placeholder, - reportValue - ); - } - - // Теперь формула не содержит вызовов report(), обрабатываем её обычным способом - let transformResult = transform_expression(processedFormula, false); - - // Если есть внешняя трансформация, применяем её - let result; - if (transformResult && transformResult.transformed) { - result = eval(transformResult.transformed); - } else { - // Иначе просто вычисляем выражение - result = eval(processedFormula); - } - - // Возвращаем результат - return isNaN(result) ? 0 : result; - } + // Стандартная обработка для формул без отчетов + let transformResult = transform_expression(formula, false); + let result = eval(transformResult.transformed); + return isNaN(result) ? 0 : result; + } catch (e) { + return 0; } - - // Стандартная обработка для формул без отчетов - let transformResult = transform_expression(formula, false); - let result = eval(transformResult.transformed); - return isNaN(result) ? 0 : result; - } catch (e) { - return 0; - } - }`); + }`); // Добавляем функцию для расчета универсальных полей functions.push(generateUniversalFieldsFunction(universal_fields)); @@ -343,16 +343,8 @@ frappe.require([ functions.push(generateReportFieldsFunction(report_fields)); } - // Добавляем обработчики для ay и il - для расчета универсальных полей - triggerHandlers["ay"] = { - isAsync: true, - handlers: ["// Вычисляем универсальные поля только когда меняется ay", "await calculate_universal_fields(frm);"] - }; - - triggerHandlers["il"] = { - isAsync: true, - handlers: ["// Вычисляем универсальные поля только когда меняется il", "await calculate_universal_fields(frm);"] - }; + // НЕ добавляем обработчики для ay и il здесь отдельно + // Они будут добавлены позже вместе с другими обработчиками // Добавляем функцию преобразования выражений functions.push(generateTransformExpressionFunction(universal_fields, report_fields)); @@ -440,39 +432,39 @@ frappe.require([ } else { // Используем единый подход для всех формул через transform_expression functions.push(` - async function calculate_${fieldname}(frm) { - try { - ${formula.includes("univ(") ? 'await calculate_universal_fields(frm);' : ''} - const { transformed, needsAsync, reportCalls } = transform_expression(${JSON.stringify(formula)}, false); - - // Обработка вызовов отчетов, если они есть - let finalExpression = transformed; - if (reportCalls && reportCalls.length > 0) { - // Собираем все результаты отчетов - const reportResults = []; - for (const call of reportCalls) { - const result = await getReportFieldValue( - frm, - call.reportName, - call.filters, - call.fieldName, - call.aggregation - ); - reportResults.push({ placeholder: call.placeholder, value: result }); + async function calculate_${fieldname}(frm) { + try { + ${formula.includes("univ(") ? 'await calculate_universal_fields(frm);' : ''} + const { transformed, needsAsync, reportCalls } = transform_expression(${JSON.stringify(formula)}, false); + + // Обработка вызовов отчетов, если они есть + let finalExpression = transformed; + if (reportCalls && reportCalls.length > 0) { + // Собираем все результаты отчетов + const reportResults = []; + for (const call of reportCalls) { + const result = await getReportFieldValue( + frm, + call.reportName, + call.filters, + call.fieldName, + call.aggregation + ); + reportResults.push({ placeholder: call.placeholder, value: result }); + } + + // Заменяем плейсхолдеры на полученные значения + for (const result of reportResults) { + finalExpression = finalExpression.replace(result.placeholder, result.value); + } } - // Заменяем плейсхолдеры на полученные значения - for (const result of reportResults) { - finalExpression = finalExpression.replace(result.placeholder, result.value); - } + // Теперь можно безопасно вычислить выражение + const result = eval(finalExpression); + frm.set_value("${fieldname}", isNaN(result) ? 0 : result); + } catch (e) { } - - // Теперь можно безопасно вычислить выражение - const result = eval(finalExpression); - frm.set_value("${fieldname}", isNaN(result) ? 0 : result); - } catch (e) { - } - }`); + }`); // Проверяем триггеры if (field_info.triggers) { @@ -577,23 +569,76 @@ frappe.require([ 'frappe.ui.form.on("' + target_doctype + '", {\n' + refreshHandlerBody); } - // Теперь создаем обработчики для каждого триггера - Object.keys(triggerHandlers).forEach(trigger => { - const handler = triggerHandlers[trigger]; - main_script += ` "${trigger}": ${handler.isAsync ? 'async ' : ''}function(frm) { - ${handler.handlers.join('\n ')} - },\n`; - }); - // Обработчики для табличных частей и триггеры, связанные с ними const tableRowFormulaHandlers = processTableRowFormulas(table_row_formulas, table_doctypes); + + // ВАЖНО: Объединяем обработчики формул строк таблиц с основными обработчиками Object.keys(tableRowFormulaHandlers).forEach(trigger => { const handler = tableRowFormulaHandlers[trigger]; - main_script += ` "${trigger}": ${handler.isAsync ? 'async ' : ''}function(frm) { - ${handler.isAsync ? 'await calculate_universal_fields(frm);' : '// Универсальные поля не используются'} - - ${handler.handlers.join('\n ')} - },\n`; + + // Проверяем, есть ли уже обработчик для этого триггера + if (triggerHandlers[trigger]) { + // Объединяем обработчики + // Если хотя бы один требует async, делаем весь обработчик async + if (handler.isAsync) { + triggerHandlers[trigger].isAsync = true; + } + // Добавляем новые обработчики к существующим + triggerHandlers[trigger].handlers = triggerHandlers[trigger].handlers.concat(handler.handlers); + } else { + // Если обработчика еще нет, создаем новый + triggerHandlers[trigger] = handler; + } + }); + + // Проверяем, нужны ли обработчики для ay и il для универсальных полей и основных формул + const needsAyIlHandlers = fieldsUsingUniversalOrReports.some(f => f.uses_universal) || + Object.keys(table_row_formulas).some(id => { + const formula = table_row_formulas[id]; + return formula.formula && formula.formula.includes("univ("); + }); + + if (needsAyIlHandlers) { + // Добавляем или обновляем обработчики для ay и il + ['ay', 'il'].forEach(field => { + if (!triggerHandlers[field]) { + triggerHandlers[field] = { + isAsync: true, + handlers: [] + }; + } else { + triggerHandlers[field].isAsync = true; + } + + // Добавляем вызов calculate_universal_fields в начало обработчика + const hasUniversalCall = triggerHandlers[field].handlers.some(h => + h.includes('calculate_universal_fields') + ); + + if (!hasUniversalCall) { + triggerHandlers[field].handlers.unshift('await calculate_universal_fields(frm);'); + } + }); + } + + // Теперь создаем обработчики для каждого триггера (без дубликатов) + Object.keys(triggerHandlers).forEach(trigger => { + const handler = triggerHandlers[trigger]; + main_script += ` "${trigger}": ${handler.isAsync ? 'async ' : ''}function(frm) {\n`; + + // Для ay и il добавляем комментарий о вычислении универсальных полей + if (trigger === 'ay' || trigger === 'il') { + if (handler.handlers.some(h => h.includes('calculate_universal_fields'))) { + main_script += ` // Вычисляем универсальные поля только когда меняется ${trigger}\n`; + } + } + + // Добавляем все обработчики + handler.handlers.forEach(handlerCode => { + main_script += ` ${handlerCode}\n`; + }); + + main_script += ' },\n'; }); main_script += `});`;