From 792227fcd67f292a01c0b403e73543a5b4dec9a5 Mon Sep 17 00:00:00 2001 From: Away <010109ali@gmail.com> Date: Mon, 1 Dec 2025 11:58:05 +0000 Subject: [PATCH] push from btp --- taxes_az/fixtures/client_script.json | 11 ----------- .../land_tax_declaration.json | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/taxes_az/fixtures/client_script.json b/taxes_az/fixtures/client_script.json index 5a43292..fb18fe2 100644 --- a/taxes_az/fixtures/client_script.json +++ b/taxes_az/fixtures/client_script.json @@ -21,17 +21,6 @@ "script": "// Generated by Formula Editor\nfrappe.ui.form.on(\"Tax return withheld at source of payment\", {\n \"ay\": async function(frm) {\n // Вычисляем универсальные поля только когда меняется ay\n await calculate_universal_fields(frm);\n },\n \"il\": async function(frm) {\n // Вычисляем универсальные поля только когда меняется il\n await calculate_universal_fields(frm);\n },\n});\n\n\n // Функция для обработки и выполнения формул\n async function processFormula(frm, formula) {\n try {\n // Проверяем наличие вызовов отчетов\n if (formula.includes(\"report(\")) {\n // Извлекаем все вызовы report() из формулы\n const reportRegex = /report\\(([^)]+)\\)/g;\n let match;\n let processedFormula = formula;\n \n // Массив для хранения промисов запросов к отчетам\n const reportPromises = [];\n const reportPlaceholders = [];\n \n // Находим все вызовы report() и заменяем их на плейсхолдеры\n while ((match = reportRegex.exec(formula)) !== null) {\n const fullMatch = match[0]; // report(fieldName)\n const fieldName = match[1]; // fieldName\n \n // Создаем уникальный плейсхолдер\n const placeholder = `__REPORT_PLACEHOLDER_${reportPromises.length}__`;\n \n // Ищем данные отчета в хранилище\n const reportFields = JSON.parse(frm.doc.report_fields_storage || \"{}\");\n const reportField = Object.values(reportFields).find(f => f.name === fieldName);\n \n if (reportField) {\n // Добавляем промис запроса к отчету\n reportPromises.push(\n getReportFieldValue(\n frm,\n reportField.report, \n JSON.parse(reportField.filters_json || \"{}\"), \n reportField.field_name, \n reportField.aggregation || 'sum'\n )\n );\n \n // Запоминаем соответствие плейсхолдера и вызова\n reportPlaceholders.push({\n placeholder: placeholder,\n original: fullMatch\n });\n \n // Заменяем вызов отчета на плейсхолдер\n processedFormula = processedFormula.replace(fullMatch, placeholder);\n }\n }\n \n // Если нашли вызовы отчетов, ждем выполнения всех запросов\n if (reportPromises.length > 0) {\n const reportResults = await Promise.all(reportPromises);\n \n // Заменяем плейсхолдеры на результаты\n for (let i = 0; i < reportPlaceholders.length; i++) {\n // Убедимся, что результат - числовое значение\n const reportValue = reportResults[i] !== undefined ? Number(reportResults[i]) : 0;\n processedFormula = processedFormula.replace(\n reportPlaceholders[i].placeholder, \n reportValue\n );\n }\n \n // Теперь формула не содержит вызовов report(), обрабатываем её обычным способом\n let transformResult = transform_expression(processedFormula, false);\n \n // Если есть внешняя трансформация, применяем её\n let result;\n if (transformResult && transformResult.transformed) {\n result = eval(transformResult.transformed);\n } else {\n // Иначе просто вычисляем выражение\n result = eval(processedFormula);\n }\n \n // Возвращаем результат\n return isNaN(result) ? 0 : result;\n }\n }\n \n // Стандартная обработка для формул без отчетов\n let transformResult = transform_expression(formula, false);\n let result = eval(transformResult.transformed);\n return isNaN(result) ? 0 : result;\n } catch (e) {\n return 0;\n }\n }\n\n\n // Функция для расчета универсальных полей\n async function calculate_universal_fields(frm) {\n \n }\n \n // Функция для обработки квартала (rüb) и года (il)\n function processQuarterDateField(docValues) {\n // Проверяем, есть ли у нас значения rüb и il\n if (docValues && docValues.rüb && docValues.il) {\n // Получаем номер квартала (преобразуем из строки вида \"1 Rüb\" в число 1)\n const quarterStr = docValues.rüb;\n const quarterMatch = quarterStr.match(/(\\d+)/);\n \n if (!quarterMatch) {\n return null;\n }\n \n const quarterNum = parseInt(quarterMatch[1]);\n \n // Получаем год из поля il или используем текущий год\n let year = docValues.il || new Date().getFullYear();\n \n // Определяем начальный и конечный месяц квартала\n let startMonth, endMonth;\n \n switch(quarterNum) {\n case 1:\n startMonth = 1; // Январь\n endMonth = 3; // Март\n break;\n case 2:\n startMonth = 4; // Апрель\n endMonth = 6; // Июнь\n break;\n case 3:\n startMonth = 7; // Июль\n endMonth = 9; // Сентябрь\n break;\n case 4:\n startMonth = 10; // Октябрь\n endMonth = 12; // Декабрь\n break;\n default:\n return null;\n }\n \n // Определяем первый день квартала (1-е число начального месяца)\n const firstDay = new Date(year, startMonth - 1, 1);\n \n // Последний день квартала (последнее число конечного месяца)\n const lastDay = new Date(year, endMonth, 0);\n \n // Форматируем даты в формате YYYY-MM-DD\n const firstDayStr = `${year}-${String(startMonth).padStart(2, '0')}-01`;\n const lastDayStr = `${year}-${String(endMonth).padStart(2, '0')}-${String(lastDay.getDate()).padStart(2, '0')}`; \n \n // Возвращаем объект с диапазоном дат\n return {\n start: firstDayStr,\n end: lastDayStr\n };\n }\n \n // Если это не специальное поле или значение не распознано, возвращаем null\n return null;\n }\n \n // Функция для обработки специальных полей с азербайджанскими месяцами\n function processAzerbayjanDateField(docValues) {\n // Словарь для сопоставления названий месяцев с их номерами\n const monthNames = {\n \"Yanvar\": 1,\n \"Fevral\": 2,\n \"Mart\": 3,\n \"Aprel\": 4,\n \"May\": 5,\n \"İyun\": 6,\n \"İyul\": 7,\n \"Avqust\": 8,\n \"Sentyabr\": 9,\n \"Oktyabr\": 10,\n \"Noyabr\": 11,\n \"Dekabr\": 12\n };\n \n // Проверяем наличие квартала (rüb) и года (il)\n if (docValues && docValues.rüb && docValues.il) {\n // Вызываем специальную функцию для обработки квартала\n return processQuarterDateField(docValues);\n }\n \n // Проверяем, есть ли у нас значения ay и il\n if (docValues && docValues.ay && monthNames[docValues.ay]) {\n // Получаем номер месяца\n const monthNum = monthNames[docValues.ay];\n \n // Получаем год из поля il или используем текущий год\n let year = docValues.il || new Date().getFullYear();\n \n // Определяем первый день месяца (1-е число)\n const firstDay = new Date(year, monthNum - 1, 1);\n \n // Последний день месяца (получаем последнее число месяца)\n const lastDay = new Date(year, monthNum, 0);\n \n // Форматируем даты в формате YYYY-MM-DD\n const firstDayStr = `${year}-${String(monthNum).padStart(2, '0')}-01`;\n const lastDayStr = `${year}-${String(monthNum).padStart(2, '0')}-${String(lastDay.getDate()).padStart(2, '0')}`; \n \n // Возвращаем объект с диапазоном дат\n return {\n start: firstDayStr,\n end: lastDayStr\n };\n }\n \n // Если это не специальное поле или значение не распознано, возвращаем null\n return null;\n }\n \n\n\n function transform_expression(expression, for_child_table = false, table_name = null) {\n let transformed = expression;\n let needsAsync = false;\n let reportCalls = [];\n\n // === Обработка report(...) ===\n const reportRegex = /report\\\\(([^)]+)\\\\)/g;\n let match;\n \n // Находим все вызовы report() в выражении\n while ((match = reportRegex.exec(expression)) !== null) {\n const reportName = match[1]; // Имя поля отчета\n const fullMatch = match[0]; // Полный текст match - \"report(xyz)\"\n needsAsync = true;\n \n // Создаем уникальный плейсхолдер\n const placeholder = `__REPORT_${reportCalls.length}__`;\n \n // Ищем соответствующее поле отчета по имени\n \n }\n\n // === Обработка universal полей ===\n \n\n // === Обработка val() и row() ===\n\n for (const prefix of tablePrefixes) {\n const sumPattern = new RegExp(`sum\\\\\\\\(${prefix}_([^)]+)\\\\\\\\)`, 'g');\n transformed = transformed.replace(sumPattern, (m, f) => \n `(frm.doc[\"${prefix}\"] && frm.doc[\"${prefix}\"].reduce((s, r) => s + (parseFloat(r[\"${f}\"]) || 0), 0) || 0)`\n );\n\n const valPattern = new RegExp(`val\\\\\\\\(${prefix}_([^)]+)\\\\\\\\)`, 'g');\n transformed = transformed.replace(valPattern, (m, f) => \n `(frm.doc[\"${prefix}\"] && frm.doc[\"${prefix}\"].reduce((s, r) => s + (parseFloat(r[\"${f}\"]) || 0), 0) || 0)`\n );\n }\n\n if (for_child_table) {\n // ИСПРАВЛЕНО: корректная обработка row() в контексте табличной части\n transformed = transformed.replace(/row\\\\(([^)]+)\\\\)/g, (m, f) => \n `(parseFloat(row[\"${f}\"]) || 0)`\n );\n\n transformed = transformed.replace(/val\\\\(([^)]+)\\\\)/g, (m, f) => {\n if (f.startsWith(table_name + '_')) {\n return `(parseFloat(row[\"${f.slice(table_name.length + 1)}\"]) || 0)`;\n }\n return `(parseFloat(frm.doc[\"${f}\"]) || 0)`;\n });\n } else {\n transformed = transformed.replace(/val\\\\(([^)]+)\\\\)/g, (m, f) => \n `(parseFloat(frm.doc[\"${f}\"]) || 0)`\n );\n\n transformed = transformed.replace(/row\\\\(([^)]+)\\\\)/g, () => \n `0 /* ERROR: row() вне табличной части */`\n );\n }\n\n transformed = transformed.replace(/(frm\\\\.doc\\\\[\"[^\"]+\"]|\\\\d+|row\\\\[\"[^\"]+\"])\\\\s*([+\\\\-])\\\\s*(\\\\d+(\\\\.\\\\d+)?)%/g, \n (match, base, op, percent) => `${base} ${op} ((${base} * ${percent}) / 100)`\n );\n\n return { transformed, needsAsync, reportCalls };\n }\n\n\n// Функция для получения индекса строки по её имени (name/cdn)\nfunction get_row_index(frm, tableName, rowName) {\n \n // Проверяем наличие таблицы\n if (!frm.doc[tableName]) {\n return -1;\n }\n \n // Выводим все строки для отладки\n frm.doc[tableName].forEach((row, idx) => {\n });\n \n // Ищем индекс строки по имени\n for (let i = 0; i < frm.doc[tableName].length; i++) {\n if (frm.doc[tableName][i].name === rowName) {\n return i;\n }\n }\n return -1;\n}\n\n// Функция для доступа к полям конкретных строк таблицы\nfunction table_row(tableName, rowIndex, fieldName) {\n // Проверяем наличие необходимых аргументов\n if (!tableName || rowIndex === undefined || !fieldName) {\n return 0;\n }\n \n // Преобразуем rowIndex в число (на случай, если передана строка)\n const rowIdx = parseInt(rowIndex) - 1; // Индексация с 1 для пользователя, но с 0 в массиве\n \n try {\n // Проверяем наличие таблицы и строки\n if (!frm.doc[tableName] || !frm.doc[tableName][rowIdx]) {\n return 0;\n }\n \n // Получаем значение поля\n const value = frm.doc[tableName][rowIdx][fieldName];\n \n // Проверяем на NaN и возвращаем значение или 0\n const numValue = parseFloat(value);\n if (isNaN(numValue)) {\n return 0;\n }\n return numValue;\n } catch (error) {\n return 0;\n }\n}\n\n// Функция для обновления значения поля конкретной строки таблицы\nfunction set_table_row(frm, tableName, rowIndex, fieldName, value) {\n // Проверяем наличие необходимых аргументов\n if (!frm || !tableName || rowIndex === undefined || !fieldName) {\n return false;\n }\n \n // Преобразуем rowIndex в число (на случай, если передана строка)\n const rowIdx = parseInt(rowIndex) - 1; // Индексация с 1 для пользователя, но с 0 в массиве\n \n try {\n // Проверяем наличие таблицы и строки\n if (!frm.doc[tableName] || !frm.doc[tableName][rowIdx]) {\n return false;\n }\n \n // Получаем строку таблицы\n const row = frm.doc[tableName][rowIdx];\n \n // Получаем сетевое имя cdt и cdn для Frappe\n const cdt = frm.fields_dict[tableName].grid.doctype;\n const cdn = row.name;\n \n // Проверяем, является ли значение NaN, и если да, заменяем его на 0\n let finalValue = value;\n if (typeof value === 'number' && isNaN(value)) {\n finalValue = 0;\n }\n \n // Обновляем значение поля\n frappe.model.set_value(cdt, cdn, fieldName, finalValue);\n \n return true;\n } catch (error) {\n return false;\n }\n}\n\n// Функция для поиска строки таблицы по значению поля\nfunction find_table_row(frm, tableName, fieldName, value) {\n // Проверяем наличие необходимых аргументов\n if (!frm || !tableName || !fieldName) {\n return -1;\n }\n \n try {\n // Проверяем наличие таблицы\n if (!frm.doc[tableName]) {\n return -1;\n }\n \n // Ищем строку с указанным значением поля\n for (let i = 0; i < frm.doc[tableName].length; i++) {\n if (frm.doc[tableName][i][fieldName] == value) {\n // Возвращаем индекс строки (начиная с 1 для пользователя)\n return i + 1;\n }\n }\n \n // Если строка не найдена\n return -1;\n } catch (error) {\n return -1;\n }\n}", "view": "Form" }, - { - "docstatus": 0, - "doctype": "Client Script", - "dt": "Land tax declaration", - "enabled": 1, - "modified": "2025-10-28 14:44:35.164997", - "module": "Taxes Az", - "name": "Formula_Editor_Script_for_Land tax declaration", - "script": "// Generated by Formula Editor\nfrappe.ui.form.on(\"Land tax declaration\", {\n \"refresh\": function(frm) {\n initialize_table_row_formulas(frm);\n },\n \"il\": async function(frm) {\n await calculate_mobnömrə1(frm); // order: 10\n \n // Формула строки 1 в таблице verginin_hesablanması (порядок: 10)\n if (frm.doc.verginin_hesablanması && frm.doc.verginin_hesablanması.length >= 1) {\n try {\n // Индекс строки на 1 меньше номера строки\n const rowIndex = 0;\n const formula = \"val(əlavə_1_vergiməbləğimanatla)\";\n \n calculate_table_row_field(frm, \"verginin_hesablanması\", rowIndex, \"vergiməbləğimanatla\", formula, \"il\");\n } catch (error) {\n }\n }\n },\n \"vöen\": function(frm) {\n \n // Формула строки 1 в таблице verginin_hesablanması (порядок: 10)\n if (frm.doc.verginin_hesablanması && frm.doc.verginin_hesablanması.length >= 1) {\n try {\n // Индекс строки на 1 меньше номера строки\n const rowIndex = 0;\n const formula = \"val(əlavə_1_vergiməbləğimanatla)\";\n \n calculate_table_row_field(frm, \"verginin_hesablanması\", rowIndex, \"vergiməbləğimanatla\", formula, \"vöen\");\n } catch (error) {\n }\n }\n },\n \"verginin_hesablanması_add\": function(frm) {\n // Обновляем индексы строк после добавления новой строки\n setTimeout(() => initialize_table_row_formulas(frm), 100);\n },\n \"verginin_hesablanması_remove\": function(frm) {\n // Обновляем индексы строк после удаления строки\n setTimeout(() => initialize_table_row_formulas(frm), 100);\n },\n});\n\n\n // Функция для обработки и выполнения формул\n async function processFormula(frm, formula) {\n try {\n // Проверяем наличие вызовов отчетов\n if (formula.includes(\"report(\")) {\n // Извлекаем все вызовы report() из формулы\n const reportRegex = /report\\(([^)]+)\\)/g;\n let match;\n let processedFormula = formula;\n \n // Массив для хранения промисов запросов к отчетам\n const reportPromises = [];\n const reportPlaceholders = [];\n \n // Находим все вызовы report() и заменяем их на плейсхолдеры\n while ((match = reportRegex.exec(formula)) !== null) {\n const fullMatch = match[0]; // report(fieldName)\n const fieldName = match[1]; // fieldName\n \n // Создаем уникальный плейсхолдер\n const placeholder = `__REPORT_PLACEHOLDER_${reportPromises.length}__`;\n \n // Ищем данные отчета в хранилище\n const reportFields = JSON.parse(frm.doc.report_fields_storage || \"{}\");\n const reportField = Object.values(reportFields).find(f => f.name === fieldName);\n \n if (reportField) {\n // Добавляем промис запроса к отчету\n reportPromises.push(\n getReportFieldValue(\n frm,\n reportField.report, \n JSON.parse(reportField.filters_json || \"{}\"), \n reportField.field_name, \n reportField.aggregation || 'sum'\n )\n );\n \n // Запоминаем соответствие плейсхолдера и вызова\n reportPlaceholders.push({\n placeholder: placeholder,\n original: fullMatch\n });\n \n // Заменяем вызов отчета на плейсхолдер\n processedFormula = processedFormula.replace(fullMatch, placeholder);\n }\n }\n \n // Если нашли вызовы отчетов, ждем выполнения всех запросов\n if (reportPromises.length > 0) {\n const reportResults = await Promise.all(reportPromises);\n \n // Заменяем плейсхолдеры на результаты\n for (let i = 0; i < reportPlaceholders.length; i++) {\n // Убедимся, что результат - числовое значение\n const reportValue = reportResults[i] !== undefined ? Number(reportResults[i]) : 0;\n processedFormula = processedFormula.replace(\n reportPlaceholders[i].placeholder, \n reportValue\n );\n }\n \n // Теперь формула не содержит вызовов report(), обрабатываем её обычным способом\n let transformResult = transform_expression(processedFormula, false);\n \n // Если есть внешняя трансформация, применяем её\n let result;\n if (transformResult && transformResult.transformed) {\n result = eval(transformResult.transformed);\n } else {\n // Иначе просто вычисляем выражение\n result = eval(processedFormula);\n }\n \n // Возвращаем результат\n return isNaN(result) ? 0 : result;\n }\n }\n \n // Стандартная обработка для формул без отчетов\n let transformResult = transform_expression(formula, false);\n let result = eval(transformResult.transformed);\n return isNaN(result) ? 0 : result;\n } catch (e) {\n return 0;\n }\n }\n\n\n // Функция для расчета универсальных полей\n async function calculate_universal_fields(frm) {\n \n }\n \n // Функция для обработки квартала (rüb) и года (il)\n function processQuarterDateField(docValues) {\n // Проверяем, есть ли у нас значения rüb и il\n if (docValues && docValues.rüb && docValues.il) {\n // Получаем номер квартала (преобразуем из строки вида \"1 Rüb\" в число 1)\n const quarterStr = docValues.rüb;\n const quarterMatch = quarterStr.match(/(\\d+)/);\n \n if (!quarterMatch) {\n return null;\n }\n \n const quarterNum = parseInt(quarterMatch[1]);\n \n // Получаем год из поля il или используем текущий год\n let year = docValues.il || new Date().getFullYear();\n \n // Определяем начальный и конечный месяц квартала\n let startMonth, endMonth;\n \n switch(quarterNum) {\n case 1:\n startMonth = 1; // Январь\n endMonth = 3; // Март\n break;\n case 2:\n startMonth = 4; // Апрель\n endMonth = 6; // Июнь\n break;\n case 3:\n startMonth = 7; // Июль\n endMonth = 9; // Сентябрь\n break;\n case 4:\n startMonth = 10; // Октябрь\n endMonth = 12; // Декабрь\n break;\n default:\n return null;\n }\n \n // Определяем первый день квартала (1-е число начального месяца)\n const firstDay = new Date(year, startMonth - 1, 1);\n \n // Последний день квартала (последнее число конечного месяца)\n const lastDay = new Date(year, endMonth, 0);\n \n // Форматируем даты в формате YYYY-MM-DD\n const firstDayStr = `${year}-${String(startMonth).padStart(2, '0')}-01`;\n const lastDayStr = `${year}-${String(endMonth).padStart(2, '0')}-${String(lastDay.getDate()).padStart(2, '0')}`; \n \n // Возвращаем объект с диапазоном дат\n return {\n start: firstDayStr,\n end: lastDayStr\n };\n }\n \n // Если это не специальное поле или значение не распознано, возвращаем null\n return null;\n }\n \n // Функция для обработки специальных полей с азербайджанскими месяцами\n function processAzerbayjanDateField(docValues) {\n // Словарь для сопоставления названий месяцев с их номерами\n const monthNames = {\n \"Yanvar\": 1,\n \"Fevral\": 2,\n \"Mart\": 3,\n \"Aprel\": 4,\n \"May\": 5,\n \"İyun\": 6,\n \"İyul\": 7,\n \"Avqust\": 8,\n \"Sentyabr\": 9,\n \"Oktyabr\": 10,\n \"Noyabr\": 11,\n \"Dekabr\": 12\n };\n \n // Проверяем наличие квартала (rüb) и года (il)\n if (docValues && docValues.rüb && docValues.il) {\n // Вызываем специальную функцию для обработки квартала\n return processQuarterDateField(docValues);\n }\n \n // Проверяем, есть ли у нас значения ay и il\n if (docValues && docValues.ay && monthNames[docValues.ay]) {\n // Получаем номер месяца\n const monthNum = monthNames[docValues.ay];\n \n // Получаем год из поля il или используем текущий год\n let year = docValues.il || new Date().getFullYear();\n \n // Определяем первый день месяца (1-е число)\n const firstDay = new Date(year, monthNum - 1, 1);\n \n // Последний день месяца (получаем последнее число месяца)\n const lastDay = new Date(year, monthNum, 0);\n \n // Форматируем даты в формате YYYY-MM-DD\n const firstDayStr = `${year}-${String(monthNum).padStart(2, '0')}-01`;\n const lastDayStr = `${year}-${String(monthNum).padStart(2, '0')}-${String(lastDay.getDate()).padStart(2, '0')}`; \n \n // Возвращаем объект с диапазоном дат\n return {\n start: firstDayStr,\n end: lastDayStr\n };\n }\n \n // Если это не специальное поле или значение не распознано, возвращаем null\n return null;\n }\n \n\n\n function transform_expression(expression, for_child_table = false, table_name = null) {\n let transformed = expression;\n let needsAsync = false;\n let reportCalls = [];\n\n // === Обработка report(...) ===\n const reportRegex = /report\\(([^)]+)\\)/g;\n let match;\n \n // Находим все вызовы report() в выражении\n while ((match = reportRegex.exec(expression)) !== null) {\n const reportName = match[1]; // Имя поля отчета\n const fullMatch = match[0]; // Полный текст match - \"report(xyz)\"\n needsAsync = true;\n \n // Создаем уникальный плейсхолдер\n const placeholder = `__REPORT_PLACEHOLDER_${reportCalls.length}__`;\n \n // Ищем соответствующее поле отчета по имени\n \n }\n\n // === Обработка table(...) ===\n const tableRegex = /table\\(([^,)]+),\\s*([^,)]+),\\s*([^)]+)\\)/g;\n let tableMatch;\n while ((tableMatch = tableRegex.exec(expression)) !== null) {\n const fullMatch = tableMatch[0];\n const tableName = tableMatch[1].trim().replace(/['\"]/g, '');\n const rowNumber = tableMatch[2].trim();\n const fieldName = tableMatch[3].trim().replace(/['\"]/g, '');\n \n const replacement = `(function() { \n try {\n if (!frm.doc[\"${tableName}\"] || !frm.doc[\"${tableName}\"][${rowNumber} - 1]) {\n return 0;\n }\n const value = frm.doc[\"${tableName}\"][${rowNumber} - 1][\"${fieldName}\"];\n return parseFloat(value) || 0;\n } catch (e) {\n return 0;\n }\n })()`;\n \n transformed = transformed.replace(fullMatch, replacement);\n }\n\n // === Обработка universal полей ===\n \n\n // === Обработка val() и row() ===\n // Динамически получаем список всех табличных частей из DocType\n const tablePrefixes = [\"verginin_hesablanması\",\"əlavə_1\",\"əlavə_2\"];\n\n for (const prefix of tablePrefixes) {\n const sumPattern = new RegExp(`sum\\\\(${prefix}_([^)]+)\\\\)`, 'g');\n transformed = transformed.replace(sumPattern, (m, f) => \n `(frm.doc[\"${prefix}\"] && frm.doc[\"${prefix}\"].reduce((s, r) => s + (parseFloat(r[\"${f}\"]) || 0), 0) || 0)`\n );\n\n const valPattern = new RegExp(`val\\\\(${prefix}_([^)]+)\\\\)`, 'g');\n transformed = transformed.replace(valPattern, (m, f) => \n `(frm.doc[\"${prefix}\"] && frm.doc[\"${prefix}\"].reduce((s, r) => s + (parseFloat(r[\"${f}\"]) || 0), 0) || 0)`\n );\n }\n\n if (for_child_table) {\n transformed = transformed.replace(/row\\(([^)]+)\\)/g, (m, f) => \n `(parseFloat(row[\"${f}\"]) || 0)`\n );\n\n transformed = transformed.replace(/val\\(([^)]+)\\)/g, (m, f) => {\n if (f.startsWith(table_name + '_')) {\n return `(parseFloat(row[\"${f.slice(table_name.length + 1)}\"]) || 0)`;\n }\n return `(parseFloat(frm.doc[\"${f}\"]) || 0)`;\n });\n } else {\n transformed = transformed.replace(/val\\(([^)]+)\\)/g, (m, f) => \n `(parseFloat(frm.doc[\"${f}\"]) || 0)`\n );\n\n transformed = transformed.replace(/row\\(([^)]+)\\)/g, () => \n `0 /* ERROR: row() вне табличной части */`\n );\n }\n\n return { transformed, needsAsync, reportCalls };\n }\n\n\n// Функция для получения индекса строки по её имени (name/cdn)\nfunction get_row_index(frm, tableName, rowName) {\n \n // Проверяем наличие таблицы\n if (!frm.doc[tableName]) {\n return -1;\n }\n \n // Выводим все строки для отладки\n frm.doc[tableName].forEach((row, idx) => {\n });\n \n // Ищем индекс строки по имени\n for (let i = 0; i < frm.doc[tableName].length; i++) {\n if (frm.doc[tableName][i].name === rowName) {\n return i;\n }\n }\n return -1;\n}\n\n// Функция для доступа к полям конкретных строк таблицы\nfunction table_row(tableName, rowIndex, fieldName) {\n // Проверяем наличие необходимых аргументов\n if (!tableName || rowIndex === undefined || !fieldName) {\n return 0;\n }\n \n // Преобразуем rowIndex в число (на случай, если передана строка)\n const rowIdx = parseInt(rowIndex) - 1; // Индексация с 1 для пользователя, но с 0 в массиве\n \n try {\n // Проверяем наличие таблицы и строки\n if (!frm.doc[tableName] || !frm.doc[tableName][rowIdx]) {\n return 0;\n }\n \n // Получаем значение поля\n const value = frm.doc[tableName][rowIdx][fieldName];\n \n // Проверяем на NaN и возвращаем значение или 0\n const numValue = parseFloat(value);\n if (isNaN(numValue)) {\n return 0;\n }\n return numValue;\n } catch (error) {\n return 0;\n }\n}\n\n// Функция для обновления значения поля конкретной строки таблицы\nfunction set_table_row(frm, tableName, rowIndex, fieldName, value) {\n // Проверяем наличие необходимых аргументов\n if (!frm || !tableName || rowIndex === undefined || !fieldName) {\n return false;\n }\n \n // Преобразуем rowIndex в число (на случай, если передана строка)\n const rowIdx = parseInt(rowIndex) - 1; // Индексация с 1 для пользователя, но с 0 в массиве\n \n try {\n // Проверяем наличие таблицы и строки\n if (!frm.doc[tableName] || !frm.doc[tableName][rowIdx]) {\n return false;\n }\n \n // Получаем строку таблицы\n const row = frm.doc[tableName][rowIdx];\n \n // Получаем сетевое имя cdt и cdn для Frappe\n const cdt = frm.fields_dict[tableName].grid.doctype;\n const cdn = row.name;\n \n // Проверяем, является ли значение NaN, и если да, заменяем его на 0\n let finalValue = value;\n if (typeof value === 'number' && isNaN(value)) {\n finalValue = 0;\n }\n \n // Обновляем значение поля\n frappe.model.set_value(cdt, cdn, fieldName, finalValue);\n \n return true;\n } catch (error) {\n return false;\n }\n}\n\n// Функция для поиска строки таблицы по значению поля\nfunction find_table_row(frm, tableName, fieldName, value) {\n // Проверяем наличие необходимых аргументов\n if (!frm || !tableName || !fieldName) {\n return -1;\n }\n \n try {\n // Проверяем наличие таблицы\n if (!frm.doc[tableName]) {\n return -1;\n }\n \n // Ищем строку с указанным значением поля\n for (let i = 0; i < frm.doc[tableName].length; i++) {\n if (frm.doc[tableName][i][fieldName] == value) {\n // Возвращаем индекс строки (начиная с 1 для пользователя)\n return i + 1;\n }\n }\n \n // Если строка не найдена\n return -1;\n } catch (error) {\n return -1;\n }\n}\n\n\n async function calculate_mobnömrə1(frm) {\n try {\n \n const { transformed, needsAsync, reportCalls } = transform_expression(\"val(əlavə_1_vergiməbləğimanatla)\", false);\n \n // Обработка вызовов отчетов, если они есть\n let finalExpression = transformed;\n if (reportCalls && reportCalls.length > 0) {\n // Собираем все результаты отчетов\n const reportResults = [];\n for (const call of reportCalls) {\n const result = await getReportFieldValue(\n frm,\n call.reportName,\n call.filters,\n call.fieldName,\n call.aggregation\n );\n reportResults.push({ placeholder: call.placeholder, value: result });\n }\n \n // Заменяем плейсхолдеры на полученные значения\n for (const result of reportResults) {\n finalExpression = finalExpression.replace(result.placeholder, result.value);\n }\n }\n \n // Теперь можно безопасно вычислить выражение\n const result = eval(finalExpression);\n frm.set_value(\"mobnömrə1\", isNaN(result) ? 0 : result);\n } catch (e) {\n }\n }\n\n\n // Функция для инициализации формул строк табличных частей\n function initialize_table_row_formulas(frm) {\n try {\n // Получаем информацию о формулах для строк табличных частей\n const formulas = JSON.parse(frm.doc.table_row_formulas_storage || \"{}\");\n \n // ВАЖНО: Преобразуем объект в массив и сортируем его по порядку выполнения\n const sortedFormulas = Object.entries(formulas).map(([id, formula]) => {\n return { id, ...formula };\n }).sort((a, b) => {\n // Сортировка по значению поля order (если порядок не задан, используем 10 по умолчанию)\n const orderA = a.order !== undefined ? parseInt(a.order) : 10;\n const orderB = b.order !== undefined ? parseInt(b.order) : 10;\n return orderA - orderB;\n });\n \n // Группируем формулы по табличным частям и строкам\n const formulasByTableAndRow = {};\n \n sortedFormulas.forEach(formula => {\n const tableName = formula.table_name;\n const rowIndex = parseInt(formula.row_index);\n \n if (!formulasByTableAndRow[tableName]) {\n formulasByTableAndRow[tableName] = {};\n }\n \n if (!formulasByTableAndRow[tableName][rowIndex]) {\n formulasByTableAndRow[tableName][rowIndex] = [];\n }\n \n formulasByTableAndRow[tableName][rowIndex].push(formula);\n });\n \n // Устанавливаем обработчики для каждой табличной части\n Object.keys(formulasByTableAndRow).forEach(tableName => {\n if (!frm.doc[tableName]) {\n return;\n }\n \n // Для каждой строки таблицы\n frm.doc[tableName].forEach((row, rowIdx) => {\n // Получаем отсортированные формулы для текущей строки (строки нумеруются с 1, а индексы с 0)\n const rowFormulas = formulasByTableAndRow[tableName][rowIdx + 1] || [];\n \n // Выполняем формулы в порядке сортировки (по полю order)\n for (const formula of rowFormulas) {\n try {\n // Определяем необходимость асинхронного вызова\n const usesUniversal = formula.formula && formula.formula.includes(\"univ(\");\n const usesReports = formula.formula && formula.formula.includes(\"report(\");\n \n // Вызываем расчет формулы согласно установленному порядку\n if (usesUniversal || usesReports) {\n // Для асинхронных вызовов используем await в асинхронной IIFE\n (async () => {\n try {\n await calculate_table_row_field(frm, tableName, rowIdx, formula.field_name, formula.formula, \"refresh\");\n } catch (asyncError) {\n }\n })();\n } else {\n // Для синхронных вызовов выполняем напрямую\n calculate_table_row_field(frm, tableName, rowIdx, formula.field_name, formula.formula, \"refresh\");\n }\n } catch (error) {\n }\n }\n });\n });\n } catch (error) {\n }\n }\n\n\n// Функция для расчета значения поля в строке табличной части\nasync function calculate_table_row_field(frm, tableName, rowIndex, fieldName, formula, triggerField) {\n try {\n \n if (!frm.doc[tableName] || !frm.doc[tableName][rowIndex]) {\n return;\n }\n\n const row = frm.doc[tableName][rowIndex];\n const cdt = frm.fields_dict[tableName].grid.doctype;\n const cdn = row.name;\n \n // === ЕДИНАЯ ТОЧКА ОБРАБОТКИ ВСЕХ ФУНКЦИЙ ===\n let finalFormula, reportCalls;\n \n try {\n // Используем transform_expression как единственную точку преобразования\n const transformResult = transform_expression(formula, true, tableName);\n \n if (transformResult) {\n finalFormula = transformResult.transformed;\n reportCalls = transformResult.reportCalls || [];\n } else {\n finalFormula = formula;\n reportCalls = [];\n }\n } catch (transformError) {\n console.error('Ошибка transform_expression:', transformError);\n finalFormula = formula;\n reportCalls = [];\n }\n \n // === ОБРАБОТКА АСИНХРОННЫХ ВЫЗОВОВ ОТЧЕТОВ ===\n if (reportCalls && reportCalls.length > 0) {\n try {\n // Выполняем все асинхронные запросы к отчетам параллельно\n const reportResults = await Promise.all(\n reportCalls.map(call => \n getReportFieldValue(\n frm,\n call.reportName,\n call.filters,\n call.fieldName,\n call.aggregation\n )\n )\n );\n \n // Заменяем плейсхолдеры на реальные значения\n for (let i = 0; i < reportCalls.length; i++) {\n const reportValue = reportResults[i] !== undefined ? Number(reportResults[i]) : 0;\n finalFormula = finalFormula.replace(\n reportCalls[i].placeholder, \n reportValue\n );\n }\n } catch (reportError) {\n console.error('Ошибка получения данных отчетов:', reportError);\n // В случае ошибки заменяем все плейсхолдеры на 0\n for (const call of reportCalls) {\n finalFormula = finalFormula.replace(call.placeholder, 0);\n }\n }\n }\n \n // === ВЫЧИСЛЕНИЕ ФИНАЛЬНОЙ ФОРМУЛЫ ===\n let result = 0;\n try {\n // Создаем функцию с доступом к контексту row и frm\n const evalFunc = new Function('row', 'frm', `return ${finalFormula};`);\n result = evalFunc(row, frm);\n } catch (evalError) {\n console.error('Ошибка вычисления формулы:', evalError, 'Formula:', finalFormula);\n }\n\n if (isNaN(result)) {\n result = 0;\n }\n\n // Устанавливаем вычисленное значение\n frappe.model.set_value(cdt, cdn, fieldName, result);\n\n // Обновляем таблицу\n frm.refresh_field(tableName);\n } catch (err) {\n console.error('Ошибка в calculate_table_row_field:', err);\n }\n}\n\n\n// Функция для получения индекса строки по её имени\nfunction get_row_index(frm, tableName, rowName) {\n \n // Проверяем наличие таблицы\n if (!frm.doc[tableName]) {\n return -1;\n }\n \n // Ищем индекс строки по имени\n for (let i = 0; i < frm.doc[tableName].length; i++) {\n if (frm.doc[tableName][i].name === rowName) {\n return i;\n }\n }\n return -1;\n}", - "view": "Form" - }, { "docstatus": 0, "doctype": "Client Script", diff --git a/taxes_az/taxes_az/doctype/land_tax_declaration/land_tax_declaration.json b/taxes_az/taxes_az/doctype/land_tax_declaration/land_tax_declaration.json index 12d49a1..4e22775 100644 --- a/taxes_az/taxes_az/doctype/land_tax_declaration/land_tax_declaration.json +++ b/taxes_az/taxes_az/doctype/land_tax_declaration/land_tax_declaration.json @@ -306,19 +306,16 @@ "fieldname": "column_break_lhqj", "fieldtype": "Column Break" }, - { - "fieldname": "verginin_hesablanmas\u0131", - "fieldtype": "Table", - "options": "Land tax declaration Vergi Hesab" - }, { "fieldname": "\u0259lav\u0259_1", "fieldtype": "Table", + "label": "\u018flav\u0259 1", "options": "Land tax declaration Elave 1" }, { "fieldname": "\u0259lav\u0259_2", "fieldtype": "Table", + "label": "\u018flav\u0259 2", "options": "Land tax declaration Elave 2" }, { @@ -380,11 +377,17 @@ "fieldtype": "Float", "hidden": 1, "read_only": 1 + }, + { + "fieldname": "verginin_hesablanmas\u0131", + "fieldtype": "Table", + "label": "Verginin hesablanmas\u0131", + "options": "Land tax declaration Vergi Hesab" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2025-10-28 15:07:19.669667", + "modified": "2025-11-26 14:06:08.803709", "modified_by": "Administrator", "module": "Taxes Az", "name": "Land tax declaration", @@ -403,8 +406,7 @@ "write": 1 } ], - "row_format": "Dynamic", "sort_field": "modified", "sort_order": "DESC", "states": [] -} +} \ No newline at end of file