#include "vedomostcontroller.h" #include "../model/componentmodel.h" #include "../model/projectparamtablemodel.h" #include "../model/titleinscriptionsmodel.h" #include "../controller/maincontroller.h" #include #include VedomostController::VedomostController(QObject *parent) : QObject(parent) , m_tableModel(new VedomostTableModel(this)) , m_designatorMappingModel(nullptr) , m_projectSettingsModel(nullptr) , m_mainController(nullptr) { // Устанавливаем маппинг по умолчанию для ведомости покупных изделий // Только поля, которые можно маппить на свойства компонентов m_columnMappings << "Name" << "ProductCode" << "DocumentCode" << "Supplier" << "WhereUsed" << "Note"; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // connect(m_tableModel, &VedomostTableModel::dataChanged, // this, &VedomostController::onRowDataChanged); } VedomostController::~VedomostController() { // m_tableModel удалится автоматически, так как он является дочерним объектом } void VedomostController::setDesignatorMappingModel(DesignatorMappingModel *mappingModel) { m_designatorMappingModel = mappingModel; } void VedomostController::setModel(VedomostTableModel *model) { if (m_tableModel != model) { qDebug() << "VedomostController::setModel: Заменяем модель таблицы"; m_tableModel = model; // Если модель не пуста, загружаем из неё настройки колонок if (m_tableModel) { QStringList columnMappings = m_tableModel->columnMappings(); if (!columnMappings.isEmpty()) { qDebug() << "VedomostController::setModel: Загружаем настройки колонок из модели:" << columnMappings; m_columnMappings = columnMappings; } } } } void VedomostController::setProjectSettingsModel(ProjectSettingsModel *model) { m_projectSettingsModel = model; qDebug() << "VedomostController::setProjectSettingsModel: Установлена модель настроек проекта"; } void VedomostController::setMainController(MainController *controller) { m_mainController = controller; qDebug() << "VedomostController::setMainController: Установлен главный контроллер"; } void VedomostController::setComponents(const QList &components) { qDebug() << "VedomostController::setComponents: Устанавливаем" << components.size() << "компонентов"; m_components = components; // Собираем доступные свойства из компонентов m_propertyNames = collectPropertyNames(); qDebug() << "VedomostController::setComponents: Собрано свойств:" << m_propertyNames.size(); qDebug() << "VedomostController::setComponents: Свойства:" << m_propertyNames; } void VedomostController::generateTableFromComponents() { if (!m_tableModel) { return; } qDebug() << "VedomostController::generateTableFromComponents: Начинаем генерацию таблицы"; qDebug() << "VedomostController::generateTableFromComponents: Текущие настройки колонок:" << m_columnMappings; qDebug() << "VedomostController::generateTableFromComponents: Количество компонентов:" << m_components.size(); // Очищаем таблицу m_tableModel->clear(); // Добавляем заголовок таблицы // VedomostRowData headerRow; // headerRow.isHeader = true; // headerRow.name = VedomostCellData("Наименование", 0, 1, true); // headerRow.productCode = VedomostCellData("Код продукции", 0, 1, true); // headerRow.documentCode = VedomostCellData("Обозначение документа на поставку", 0, 1, true); // headerRow.supplier = VedomostCellData("Поставщик", 0, 1, true); // headerRow.whereUsed = VedomostCellData("Куда входит (обозначение)", 0, 1, true); // headerRow.quantityPerItem = VedomostCellData("Количество на изделие", 0, 1, true); // headerRow.quantityInSet = VedomostCellData("Количество в комплекте", 0, 1, true); // headerRow.quantityForReg = VedomostCellData("Количество на регулир", 0, 1, true); // headerRow.totalQuantity = VedomostCellData("Количество всего", 0, 1, true); // headerRow.note = VedomostCellData("Примечание", 0, 1, true); // m_tableModel->addRow(headerRow); // // Добавляем пустую строку после заголовка // m_tableModel->addEmptyRow(); if (m_components.isEmpty()) { qDebug() << "VedomostController::generateTableFromComponents: Нет компонентов для генерации"; return; } try { // Сортируем компоненты по десигнатору std::sort(m_components.begin(), m_components.end(), [this](ComponentModel* a, ComponentModel* b) { if (!a || !b) return false; return a->designator() < b->designator(); }); // Группируем компоненты по буквенной части десигнатора QMap> groupedByLetter; for (ComponentModel *component : m_components) { QPair designatorParts = splitDesignator(component->designator()); QString desLetter = designatorParts.first; groupedByLetter[desLetter].append(component); } // Обрабатываем каждую группу for (auto it = groupedByLetter.begin(); it != groupedByLetter.end(); ++it) { QString desLetter = it.key(); QList componentsInGroup = it.value(); if (componentsInGroup.size() > 1) { // Если компонентов больше одного, группируем компоненты по наименованию QList>> groupedByName; for (ComponentModel *component : componentsInGroup) { QString componentName = getMappedValue(component, "Name"); // Ищем существующую группу bool found = false; for (auto &group : groupedByName) { if (group.first == componentName) { group.second.append(component); found = true; break; } } // Если группа не найдена, создаем новую if (!found) { QList newGroup; newGroup.append(component); groupedByName.append(qMakePair(componentName, newGroup)); } } // Если несколько разных названий в группе - добавляем заголовок типа if (groupedByName.size() > 1) { // Добавляем заголовок типа (множественное число) QString componentTypeHeader = getComponentType(componentsInGroup.first()); if (!componentTypeHeader.isEmpty()) { VedomostRowData headerRow; // Заголовок должен иметь isHeader=true и isUnderline=true для центрирования и подчеркивания в PDF headerRow.name = VedomostCellData(componentTypeHeader, 0, 1, true, true, false, 100, true); headerRow.productCode = VedomostCellData("", 0, 1, false, true, false, 100, false); headerRow.documentCode = VedomostCellData("", 0, 1, false, true, false, 100, false); headerRow.supplier = VedomostCellData("", 0, 1, false, true, false, 100, false); headerRow.whereUsed = VedomostCellData("", 0, 1, false, true, false, 100, false); headerRow.quantityPerItem = VedomostCellData("", 0, 1, false, true, false, 100, false); headerRow.quantityInSet = VedomostCellData("", 0, 1, false, true, false, 100, false); headerRow.quantityForReg = VedomostCellData("", 0, 1, false, true, false, 100, false); headerRow.totalQuantity = VedomostCellData("", 0, 1, false, true, false, 100, false); headerRow.note = VedomostCellData("", 0, 1, false, true, false, 100, false); headerRow.isHeader = true; // Помечаем всю строку как заголовок m_tableModel->addRow(headerRow); } // Добавляем пустую строку m_tableModel->addEmptyRow(); } // Теперь создаем строки для каждой группы по наименованию for (const auto &nameGroup : groupedByName) { QString componentName = nameGroup.first; QList componentsWithSameName = nameGroup.second; // Подсчитываем общее количество компонентов int totalQuantity = componentsWithSameName.size(); // Создаем строку для этой группы компонентов ComponentModel *firstComponent = componentsWithSameName.first(); // Получаем оригинальные строки для правильной обработки выражений QStringList mappings = getColumnMappings(); QString nameProperty = mappings.size() > 0 ? mappings[0] : "Name"; QString productCodeProperty = mappings.size() > 1 ? mappings[1] : "ProductCode"; QString documentCodeProperty = mappings.size() > 2 ? mappings[2] : "DocumentCode"; QString supplierProperty = mappings.size() > 3 ? mappings[3] : "Supplier"; // Получаем СЫРЫЕ значения свойств БЕЗ вычисления выражений для правильного разделения QString nameOriginalString = getComponentRawValue(firstComponent, nameProperty); QString productCodeOriginalString = getComponentRawValue(firstComponent, productCodeProperty); QString documentCodeOriginalString = getComponentRawValue(firstComponent, documentCodeProperty); QString supplierOriginalString = getComponentRawValue(firstComponent, supplierProperty); // Получаем данные с разделением длинных строк QString nameValue = getMappedValue(firstComponent, "Name"); // Определяем, нужно ли добавлять название элемента // Если несколько разных названий в группе - только название без типа // Если несколько одинаковых элементов - множественное число + название // Если один элемент - единственное число + название bool addComponentType = false; QString componentTypeName; if (groupedByName.size() > 1) { // Несколько разных названий - только название без типа addComponentType = false; } else if (totalQuantity > 1) { // Несколько одинаковых элементов - множественное число componentTypeName = getComponentType(firstComponent); addComponentType = true; } else { // Один элемент - единственное число componentTypeName = getComponentTypeName(firstComponent); addComponentType = true; } QString productCodeValue = getMappedValue(firstComponent, "ProductCode"); QString documentCodeValue = getMappedValue(firstComponent, "DocumentCode"); QString supplierValue = getMappedValue(firstComponent, "Supplier"); QString whereUsedValue = getWhereUsed(); // Получаем из titleInscriptionsModel или ProjectParamTable QString noteValue = getMappedValue(firstComponent, "Note"); qDebug() << "[SPLIT_CALL] ========== ВЫЗОВ splitLongDesignation для Name =========="; qDebug() << "[SPLIT_CALL] nameValue:" << nameValue; qDebug() << "[SPLIT_CALL] nameOriginalString:" << nameOriginalString; qDebug() << "[SPLIT_CALL] component:" << (firstComponent ? "не nullptr" : "nullptr"); qDebug() << "[SPLIT_CALL] maxLength: 32"; // Разделяем длинные строки (используем оригинальные строки для обработки выражений) // ВАЖНО: сначала разделяем, потом добавляем название элемента к первой части QList nameParts = splitLongDesignation(nameValue, nameOriginalString, firstComponent, 32); qDebug() << "[SPLIT_CALL] Результат nameParts:" << nameParts; // Добавляем название элемента к первой части, если нужно if (addComponentType && !componentTypeName.isEmpty() && !nameParts.isEmpty()) { if (!nameParts.first().isEmpty()) { nameParts.first() = componentTypeName + " " + nameParts.first(); } else { nameParts.first() = componentTypeName; } } QList productCodeParts = splitLongDesignation(productCodeValue, productCodeOriginalString, firstComponent, 25); QList documentCodeParts = splitLongDesignation(documentCodeValue, documentCodeOriginalString, firstComponent, 35); QList supplierParts = splitLongDesignation(supplierValue, supplierOriginalString, firstComponent, 25); QList whereUsedParts = splitLongDesignation(whereUsedValue, whereUsedValue, firstComponent, 35); QList noteParts = splitLongDesignation(noteValue, noteValue, firstComponent, 16); // Группируем десигнаторы только для компонентов с этим наименованием // Сначала сортируем компоненты по десигнатору (численно) std::sort(componentsWithSameName.begin(), componentsWithSameName.end(), [this](ComponentModel* a, ComponentModel* b) { if (!a || !b) return false; QString designatorA = a->designator(); QString designatorB = b->designator(); // Разделяем десигнаторы на буквенную и числовую части QPair partsA = splitDesignator(designatorA); QPair partsB = splitDesignator(designatorB); QString letterA = partsA.first; QString letterB = partsB.first; QString numberA = partsA.second; QString numberB = partsB.second; // Сначала сравниваем буквенные части if (letterA != letterB) { return letterA < letterB; } // Если буквенные части одинаковые, сравниваем числовые части bool okA, okB; int numA = numberA.toInt(&okA); int numB = numberB.toInt(&okB); if (okA && okB) { return numA < numB; } // Fallback: если не удалось преобразовать в числа, используем строковое сравнение return numberA < numberB; }); QList designatorGroupsForThisName; QString currentGroup = componentsWithSameName.first()->designator(); QString lastDesignator = currentGroup; for (int i = 1; i < componentsWithSameName.size(); ++i) { QString currentDesignator = componentsWithSameName[i]->designator(); QPair currentParts = splitDesignator(currentDesignator); QPair lastParts = splitDesignator(lastDesignator); // Проверяем, является ли текущий десигнатор следующим по порядку if (currentParts.first == lastParts.first) { bool ok1, ok2; int currentNum = currentParts.second.toInt(&ok1); int lastNum = lastParts.second.toInt(&ok2); if (ok1 && ok2 && currentNum == lastNum + 1) { // Продолжаем группу lastDesignator = currentDesignator; } else { // Завершаем текущую группу и начинаем новую if (currentGroup == lastDesignator) { designatorGroupsForThisName.append(currentGroup); } else { designatorGroupsForThisName.append(currentGroup + "-" + lastDesignator); } currentGroup = currentDesignator; lastDesignator = currentDesignator; } } else { // Завершаем текущую группу и начинаем новую if (currentGroup == lastDesignator) { designatorGroupsForThisName.append(currentGroup); } else { designatorGroupsForThisName.append(currentGroup + "-" + lastDesignator); } currentGroup = currentDesignator; lastDesignator = currentDesignator; } } // Добавляем последнюю группу if (currentGroup == lastDesignator) { designatorGroupsForThisName.append(currentGroup); } else { designatorGroupsForThisName.append(currentGroup + "-" + lastDesignator); } // Разбиваем длинные группы десигнаторов (35 символов на строку для whereUsed) QStringList designatorsSplit = splitDesignatorGroups(designatorGroupsForThisName, 35); // Если несколько одинаковых элементов или один элемент - добавляем пустую строку перед if (groupedByName.size() == 1) { m_tableModel->addEmptyRow(); } // Определяем максимальное количество строк для всех полей int maxRows = qMax(qMax(qMax(nameParts.size(), productCodeParts.size()), qMax(documentCodeParts.size(), supplierParts.size())), qMax(qMax(whereUsedParts.size(), noteParts.size()), designatorsSplit.size())); // Создаем строки для каждой части for (int i = 0; i < maxRows; ++i) { VedomostRowData row; // Заполняем данные с учетом разделения row.name = VedomostCellData(i < nameParts.size() ? nameParts[i] : "", 0, 1, false, true, false, 100, false); row.productCode = VedomostCellData(i < productCodeParts.size() ? productCodeParts[i] : "", 0, 1, false, true, false, 100, false); row.documentCode = VedomostCellData(i < documentCodeParts.size() ? documentCodeParts[i] : "", 0, 1, false, true, false, 100, false); row.supplier = VedomostCellData(i < supplierParts.size() ? supplierParts[i] : "", 0, 1, false, true, false, 100, false); // В whereUsed показываем значение из titleInscriptionsModel или ProjectParamTable if (i < whereUsedParts.size()) { row.whereUsed = VedomostCellData(whereUsedParts[i], 0, 1, false, true, false, 100, false); } else { row.whereUsed = VedomostCellData("", 0, 1, false, true, false, 100, false); } // Количества только в первой строке if (i == 0) { row.quantityPerItem = VedomostCellData(QString::number(totalQuantity), 0, 1, false, true, false, 100, false); row.quantityInSet = VedomostCellData("", 0, 1, false, true, false, 100, false); row.quantityForReg = VedomostCellData("", 0, 1, false, true, false, 100, false); // totalQuantity должен быть автоматически сгенерирован для автоматического пересчета row.totalQuantity = VedomostCellData(QString::number(totalQuantity), 0, 1, false, true, true, 100, false); } else { row.quantityPerItem = VedomostCellData("", 0, 1, false, true, false, 100, false); row.quantityInSet = VedomostCellData("", 0, 1, false, true, false, 100, false); row.quantityForReg = VedomostCellData("", 0, 1, false, true, false, 100, false); row.totalQuantity = VedomostCellData("", 0, 1, false, true, false, 100, false); } row.note = VedomostCellData(i < noteParts.size() ? noteParts[i] : "", 0, 1, false, true, false, 100, false); m_tableModel->addRow(row); } // Если несколько одинаковых элементов или один элемент - добавляем пустую строку после if (groupedByName.size() == 1) { m_tableModel->addEmptyRow(); } } } else { // Если компонент один, добавляем его как отдельную строку ComponentModel *component = componentsInGroup.first(); // Получаем оригинальные строки для правильной обработки выражений QStringList mappings = getColumnMappings(); QString nameProperty = mappings.size() > 0 ? mappings[0] : "Name"; QString productCodeProperty = mappings.size() > 1 ? mappings[1] : "ProductCode"; QString documentCodeProperty = mappings.size() > 2 ? mappings[2] : "DocumentCode"; QString supplierProperty = mappings.size() > 3 ? mappings[3] : "Supplier"; // Получаем СЫРЫЕ значения свойств БЕЗ вычисления выражений для правильного разделения QString nameOriginalString = getComponentRawValue(component, nameProperty); QString productCodeOriginalString = getComponentRawValue(component, productCodeProperty); QString documentCodeOriginalString = getComponentRawValue(component, documentCodeProperty); QString supplierOriginalString = getComponentRawValue(component, supplierProperty); // Получаем данные с разделением длинных строк QString nameValue = getMappedValue(component, "Name"); // Для одного элемента: единственное число + название QString componentTypeName = getComponentTypeName(component); // Добавляем пустую строку перед m_tableModel->addEmptyRow(); QString productCodeValue = getMappedValue(component, "ProductCode"); QString documentCodeValue = getMappedValue(component, "DocumentCode"); QString supplierValue = getMappedValue(component, "Supplier"); QString whereUsedValue = getWhereUsed(); // Получаем из titleInscriptionsModel или ProjectParamTable QString noteValue = getMappedValue(component, "Note"); qDebug() << "[SPLIT_CALL] ========== ВЫЗОВ splitLongDesignation для Name (один элемент) =========="; qDebug() << "[SPLIT_CALL] nameValue:" << nameValue; qDebug() << "[SPLIT_CALL] nameOriginalString:" << nameOriginalString; qDebug() << "[SPLIT_CALL] component:" << (component ? "не nullptr" : "nullptr"); qDebug() << "[SPLIT_CALL] maxLength: 32"; // Разделяем длинные строки (используем оригинальные строки для обработки выражений) // ВАЖНО: сначала разделяем, потом добавляем название элемента к первой части QList nameParts = splitLongDesignation(nameValue, nameOriginalString, component, 32); qDebug() << "[SPLIT_CALL] Результат nameParts:" << nameParts; // Добавляем название элемента к первой части, если нужно if (!componentTypeName.isEmpty() && !nameParts.isEmpty()) { if (!nameParts.first().isEmpty()) { nameParts.first() = componentTypeName + " " + nameParts.first(); } else { nameParts.first() = componentTypeName; } } QList productCodeParts = splitLongDesignation(productCodeValue, productCodeOriginalString, component, 25); QList documentCodeParts = splitLongDesignation(documentCodeValue, documentCodeOriginalString, component, 35); QList supplierParts = splitLongDesignation(supplierValue, supplierOriginalString, component, 25); QList whereUsedParts = splitLongDesignation(whereUsedValue, whereUsedValue, component, 35); QList noteParts = splitLongDesignation(noteValue, noteValue, component, 16); // Определяем максимальное количество строк int maxRows = qMax(qMax(qMax(nameParts.size(), productCodeParts.size()), qMax(documentCodeParts.size(), supplierParts.size())), qMax(whereUsedParts.size(), noteParts.size())); // Создаем строки для каждой части for (int i = 0; i < maxRows; ++i) { VedomostRowData row; // Заполняем данные с учетом разделения row.name = VedomostCellData(i < nameParts.size() ? nameParts[i] : "", 0, 1, false, true, false, 100, false); row.productCode = VedomostCellData(i < productCodeParts.size() ? productCodeParts[i] : "", 0, 1, false, true, false, 100, false); row.documentCode = VedomostCellData(i < documentCodeParts.size() ? documentCodeParts[i] : "", 0, 1, false, true, false, 100, false); row.supplier = VedomostCellData(i < supplierParts.size() ? supplierParts[i] : "", 0, 1, false, true, false, 100, false); // В whereUsed показываем значение из titleInscriptionsModel или ProjectParamTable if (i < whereUsedParts.size()) { row.whereUsed = VedomostCellData(whereUsedParts[i], 0, 1, false, true, false, 100, false); } else { row.whereUsed = VedomostCellData("", 0, 1, false, true, false, 100, false); } // Количества только в первой строке if (i == 0) { row.quantityPerItem = VedomostCellData("1", 0, 1, false, true, false, 100, false); row.quantityInSet = VedomostCellData("", 0, 1, false, true, false, 100, false); row.quantityForReg = VedomostCellData("", 0, 1, false, true, false, 100, false); // totalQuantity должен быть автоматически сгенерирован для автоматического пересчета row.totalQuantity = VedomostCellData("1", 0, 1, false, true, true, 100, false); } else { row.quantityPerItem = VedomostCellData("", 0, 1, false, true, false, 100, false); row.quantityInSet = VedomostCellData("", 0, 1, false, true, false, 100, false); row.quantityForReg = VedomostCellData("", 0, 1, false, true, false, 100, false); row.totalQuantity = VedomostCellData("", 0, 1, false, true, false, 100, false); } row.note = VedomostCellData(i < noteParts.size() ? noteParts[i] : "", 0, 1, false, true, false, 100, false); m_tableModel->addRow(row); } // Добавляем пустую строку после m_tableModel->addEmptyRow(); } } qDebug() << "VedomostController::generateTableFromComponents: Таблица сгенерирована, строк:" << m_tableModel->rowCount(); // Оптимизируем разрывы страниц после генерации таблицы m_tableModel->optimizePageBreaks(); } catch (const std::exception &e) { qDebug() << "VedomostController::generateTableFromComponents: Ошибка при генерации:" << e.what(); } catch (...) { qDebug() << "VedomostController::generateTableFromComponents: Неизвестная ошибка при генерации"; } } void VedomostController::updateTableFromComponents() { if (!m_tableModel) { qDebug() << "VedomostController::updateTableFromComponents: m_tableModel is null"; return; } qDebug() << "VedomostController::updateTableFromComponents: Обновляем таблицу из" << m_components.size() << "компонентов"; if (m_components.isEmpty()) { qDebug() << "VedomostController::updateTableFromComponents: Нет компонентов для обновления"; return; } try { // Метод updateTableFromComponents больше не используется в новой логике qDebug() << "VedomostController::updateTableFromComponents: Метод устарел"; } catch (const std::exception &e) { qDebug() << "VedomostController::updateTableFromComponents: Ошибка при обновлении:" << e.what(); } catch (...) { qDebug() << "VedomostController::updateTableFromComponents: Неизвестная ошибка при обновлении"; } } QString VedomostController::getComponentValue(ComponentModel *component, const QString &propertyName) { if (!component) { return ""; } qDebug() << "VedomostController::getComponentValue: propertyName =" << propertyName; // Получаем значение свойства компонента QString value; if (propertyName == "Designator") { value = component->designator(); qDebug() << "VedomostController::getComponentValue: Designator =" << value; } else { QMap props = component->properties(); value = props.value(propertyName, ""); qDebug() << "VedomostController::getComponentValue: Свойство" << propertyName << "=" << value; } // Проверяем, является ли значение свойства выражением if (value.startsWith("=")) { qDebug() << "VedomostController::getComponentValue: Значение свойства является выражением:" << value; return parseExpression(component, value); } return value; } QString VedomostController::getComponentRawValue(ComponentModel *component, const QString &propertyName) { if (!component) { return ""; } qDebug() << "[RAW_VALUE] VedomostController::getComponentRawValue: propertyName =" << propertyName; // Получаем сырое значение свойства компонента БЕЗ вычисления выражений QString value; if (propertyName == "Designator") { value = component->designator(); qDebug() << "[RAW_VALUE] Designator =" << value; } else { QMap props = component->properties(); value = props.value(propertyName, ""); qDebug() << "[RAW_VALUE] Свойство" << propertyName << "=" << value; } // Возвращаем сырое значение без вычисления return value; } QString VedomostController::getMappedValue(ComponentModel *component, const QString &columnName) { if (!component) { return ""; } qDebug() << "VedomostController::getMappedValue: columnName =" << columnName; // Определяем индекс колонки в маппинге int columnIndex = -1; if (columnName == "Name") columnIndex = 0; else if (columnName == "ProductCode") columnIndex = 1; else if (columnName == "DocumentCode") columnIndex = 2; else if (columnName == "Supplier") columnIndex = 3; else if (columnName == "WhereUsed") columnIndex = 4; else if (columnName == "Note") columnIndex = 5; if (columnIndex < 0 || columnIndex >= m_columnMappings.size()) { qDebug() << "VedomostController::getMappedValue: Неизвестная колонка или индекс вне диапазона:" << columnName; return ""; } // Получаем маппинг для этой колонки QString mappedProperty = m_columnMappings[columnIndex]; // Если маппинг пустой или "Не выбрано", возвращаем пустую строку if (mappedProperty.isEmpty() || mappedProperty == "-- Не выбрано --") { qDebug() << "VedomostController::getMappedValue: Маппинг пустой для колонки" << columnName; return ""; } qDebug() << "VedomostController::getMappedValue: Маппинг" << columnName << "->" << mappedProperty; // Для колонки "WhereUsed" значение получается из ProjectParamTable, а не из компонента if (columnName == "WhereUsed") { return getWhereUsed(); } // Получаем значение из компонента QString value = getComponentValue(component, mappedProperty); return value; } QString VedomostController::parseExpression(ComponentModel *component, const QString &expression) { if (!component) { return ""; } qDebug() << "VedomostController::parseExpression: Обрабатываем выражение:" << expression; QString result; QString expr; // Определяем, как обрабатывать выражение if (expression.startsWith("=")) { expr = expression.mid(1); // Убираем начальный "=" } else if (expression.startsWith("\"") && expression.endsWith("\"")) { // Если это просто значение в кавычках, возвращаем его без кавычек return expression.mid(1, expression.length() - 2); } else { // Если это не выражение, возвращаем как есть return expression; } // Разбиваем выражение по операторам "+" с учетом кавычек QStringList parts; QString currentPart; bool inDoubleQuotes = false; bool inSingleQuotes = false; for (int i = 0; i < expr.length(); ++i) { QChar ch = expr[i]; if (ch == '"' && !inSingleQuotes) { inDoubleQuotes = !inDoubleQuotes; currentPart += ch; } else if (ch == '\'' && !inDoubleQuotes) { inSingleQuotes = !inSingleQuotes; currentPart += ch; } else if (ch == '+' && !inDoubleQuotes && !inSingleQuotes) { // Это разделитель "+" вне кавычек if (!currentPart.trimmed().isEmpty()) { parts.append(currentPart.trimmed()); } currentPart.clear(); } else { currentPart += ch; } } // Добавляем последнюю часть if (!currentPart.trimmed().isEmpty()) { parts.append(currentPart.trimmed()); } qDebug() << "VedomostController::parseExpression: Части выражения:" << parts; for (const QString &part : parts) { QString trimmedPart = part.trimmed(); qDebug() << "VedomostController::parseExpression: Обрабатываем часть:" << trimmedPart; // Если это не пустая строка, добавляем к результату if (!trimmedPart.isEmpty()) { QString value; // Проверяем, является ли это литералом в одинарных кавычках if (trimmedPart.startsWith("'") && trimmedPart.endsWith("'")) { // Это литерал - убираем кавычки и используем как есть value = trimmedPart.mid(1, trimmedPart.length() - 2); qDebug() << "VedomostController::parseExpression: Найден литерал в одинарных кавычках:" << value; } // Проверяем, является ли это именем свойства компонента в двойных кавычках else if (trimmedPart.startsWith("\"") && trimmedPart.endsWith("\"")) { // Это свойство компонента - убираем кавычки и ищем в свойствах QString propertyName = trimmedPart.mid(1, trimmedPart.length() - 2); qDebug() << "VedomostController::parseExpression: Ищем свойство компонента:" << propertyName; if (propertyName == "Designator") { value = component->designator(); qDebug() << "VedomostController::parseExpression: Найдено свойство Designator =" << value; } else { // Ищем в свойствах компонента QMap props = component->properties(); value = props.value(propertyName, ""); qDebug() << "VedomostController::parseExpression: Ищем свойство" << propertyName << "в компоненте, результат:" << value; // Рекурсивно обрабатываем вложенные выражения if (value.startsWith("=")) { qDebug() << "VedomostController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем"; value = parseExpression(component, value); } } } // Проверяем, является ли это именем свойства компонента без кавычек else if (trimmedPart == "Designator") { value = component->designator(); qDebug() << "VedomostController::parseExpression: Найдено свойство Designator =" << value; } else { // Ищем в свойствах компонента QMap props = component->properties(); value = props.value(trimmedPart, ""); qDebug() << "VedomostController::parseExpression: Ищем свойство" << trimmedPart << "в компоненте, результат:" << value; // Рекурсивно обрабатываем вложенные выражения if (value.startsWith("=")) { qDebug() << "VedomostController::parseExpression: Значение свойства является вложенным выражением, рекурсивно обрабатываем"; value = parseExpression(component, value); } } // Добавляем значение к результату if (!value.isEmpty()) { if (!result.isEmpty()) { result += value; } else { result = value; } qDebug() << "VedomostController::parseExpression: Добавили к результату, текущий результат:" << result; } } } qDebug() << "VedomostController::parseExpression: Финальный результат:" << result; return result; } QList VedomostController::splitByConnectors(const QString &text, int maxLength) { QList result; if (text.length() <= maxLength) { result.append(text); return result; } // Ищем соединения (тире, дефисы, точки) QStringList connectors = {" - ", "–", "—", " — ", " — ", " / ", " \\ ", " + ", " = "}; // Ищем ближайший к середине строки разделитель int targetLength = maxLength; int bestSplitPos = -1; int minDifference = text.length(); for (const QString &connector : connectors) { int pos = text.indexOf(connector); while (pos != -1) { int leftLength = pos; int rightLength = text.length() - pos - connector.length(); // Вычисляем разницу с целевой длиной int leftDiff = abs(leftLength - targetLength); int rightDiff = abs(rightLength - targetLength); int totalDiff = leftDiff + rightDiff; // Если эта позиция лучше предыдущей, запоминаем её if (totalDiff < minDifference) { minDifference = totalDiff; bestSplitPos = pos; } pos = text.indexOf(connector, pos + 1); } } // Если нашли подходящий разделитель, разделяем по нему if (bestSplitPos != -1) { // Определяем длину разделителя int connectorLength = 0; for (const QString &connector : connectors) { if (text.mid(bestSplitPos, connector.length()) == connector) { connectorLength = connector.length(); break; } } QString leftPart = text.left(bestSplitPos).trimmed(); QString rightPart = text.mid(bestSplitPos + connectorLength).trimmed(); // Рекурсивно обрабатываем обе части if (!leftPart.isEmpty()) { QList leftParts = splitByConnectors(leftPart, maxLength); result.append(leftParts); } if (!rightPart.isEmpty()) { QList rightParts = splitByConnectors(rightPart, maxLength); result.append(rightParts); } } // Если разделители не найдены или не подходят, возвращаем исходный текст if (result.isEmpty()) { result.append(text); } return result; } QList VedomostController::splitComplexStringByMiddle(const QString &text, int maxLength) { QList result; if (text.length() <= maxLength) { result.append(text); return result; } qDebug() << "[SPLIT_DEBUG] splitComplexStringByMiddle: Разделяем комплексную строку:" << text; // Ищем запятые в тексте QList commaPositions; for (int i = 0; i < text.length(); ++i) { if (text[i] == ',') { commaPositions.append(i); } } if (commaPositions.isEmpty()) { // Если запятых нет, используем обычное разделение по пробелам qDebug() << "[SPLIT_DEBUG] splitComplexStringByMiddle: Запятых не найдено, используем splitBySpaces"; return splitBySpaces(text, maxLength); } // Ищем запятую, ближайшую к середине строки int targetLength = maxLength; int bestSplitPos = -1; int minDifference = text.length(); for (int pos : commaPositions) { int leftLength = pos + 1; // Включаем запятую в левую часть int rightLength = text.length() - pos - 1; // Вычисляем разницу с целевой длиной int leftDiff = abs(leftLength - targetLength); int rightDiff = abs(rightLength - targetLength); int totalDiff = leftDiff + rightDiff; // Если эта позиция лучше предыдущей, запоминаем её if (totalDiff < minDifference) { minDifference = totalDiff; bestSplitPos = pos; } } // Если нашли подходящую запятую, разделяем по ней if (bestSplitPos != -1) { QString leftPart = text.left(bestSplitPos + 1).trimmed(); // Включаем запятую QString rightPart = text.mid(bestSplitPos + 1).trimmed(); qDebug() << "[SPLIT_DEBUG] splitComplexStringByMiddle: Найдена запятая на позиции" << bestSplitPos; qDebug() << "[SPLIT_DEBUG] splitComplexStringByMiddle: Левая часть:" << leftPart; qDebug() << "[SPLIT_DEBUG] splitComplexStringByMiddle: Правая часть:" << rightPart; // Рекурсивно обрабатываем обе части if (!leftPart.isEmpty()) { QList leftParts = splitComplexStringByMiddle(leftPart, maxLength); result.append(leftParts); } if (!rightPart.isEmpty()) { QList rightParts = splitComplexStringByMiddle(rightPart, maxLength); result.append(rightParts); } } else { // Если не удалось найти подходящую запятую, используем обычное разделение qDebug() << "[SPLIT_DEBUG] splitComplexStringByMiddle: Не удалось найти подходящую запятую, используем splitBySpaces"; result = splitBySpaces(text, maxLength); } qDebug() << "[SPLIT_DEBUG] splitComplexStringByMiddle: Результат:" << result; return result; } QStringList VedomostController::collectPropertyNames() { QStringList propertyNames; // Добавляем основные свойства компонента propertyNames << "Designator" << "Name" << "ProductCode" << "DocumentCode" << "Supplier"; // Собираем дополнительные свойства из компонентов for (ComponentModel *component : m_components) { QMap props = component->properties(); for (auto it = props.begin(); it != props.end(); ++it) { if (!propertyNames.contains(it.key())) { propertyNames.append(it.key()); } } } // Если дополнительные свойства не найдены в компонентах, добавляем стандартные if (propertyNames.size() <= 5) { // Только базовые свойства propertyNames << "Type" << "Value" << "Footprint" << "Description" << "ManufacturerPartNumber"; qDebug() << "VedomostController: Дополнительные свойства не найдены, используем стандартные:" << propertyNames; } // Добавляем специальные свойства для ведомости propertyNames << "WhereUsed" << "QuantityPerItem" << "QuantityInSet" << "QuantityForReg" << "TotalQuantity" << "Note"; return propertyNames; } void VedomostController::setColumnMappings(const QStringList &mappings) { qDebug() << "VedomostController::setColumnMappings: Устанавливаем новые настройки колонок:" << mappings; m_columnMappings = mappings; // Сохраняем настройки в ProjectSettingsModel if (m_projectSettingsModel) { for (int i = 0; i < mappings.size(); ++i) { m_projectSettingsModel->setColumnMapping("Vedomost", i, mappings[i]); } qDebug() << "VedomostController::setColumnMappings: Настройки сохранены в ProjectSettingsModel"; } // Обновляем настройки в модели if (m_tableModel) { m_tableModel->setColumnMappings(mappings); qDebug() << "VedomostController::setColumnMappings: Настройки обновлены в модели"; } qDebug() << "VedomostController::setColumnMappings: Настройки колонок обновлены:" << m_columnMappings; } QStringList VedomostController::getColumnMappings() const { // Если есть ProjectSettingsModel, читаем настройки из него if (m_projectSettingsModel) { QStringList settings = m_projectSettingsModel->getColumnMappings("Vedomost"); if (!settings.isEmpty()) { qDebug() << "VedomostController::getColumnMappings: Получены настройки из ProjectSettingsModel:" << settings; return settings; } } // Fallback: возвращаем локальные настройки qDebug() << "VedomostController::getColumnMappings: Возвращаем локальные настройки:" << m_columnMappings; return m_columnMappings; } QStringList VedomostController::getAvailableProperties() const { QStringList properties = m_propertyNames; // Убираем специальные значения, оставляем только реальные свойства компонентов return properties; } QString VedomostController::getComponentType(ComponentModel *component) { if (!component) return ""; // Получаем тип компонента из маппинга дезигнаторов if (m_designatorMappingModel) { QString componentType = m_designatorMappingModel->getComponentType(component->designator(), true); // true для множественного числа if (!componentType.isEmpty()) { return componentType; } } // Если маппинг не найден, используем тип из свойств компонента QMap props = component->properties(); QString type = props.value("Type", ""); if (!type.isEmpty()) { return type; } // Если и тип не найден, используем designator как fallback return component->designator(); } QString VedomostController::getComponentTypeName(ComponentModel *component) { if (!component) return ""; // Получаем название элемента по десигнатору в единственном числе if (m_designatorMappingModel) { QString componentType = m_designatorMappingModel->getComponentType(component->designator(), false); // false для единственного числа if (!componentType.isEmpty()) { return componentType; } } // Если маппинг не найден, используем тип из свойств компонента QMap props = component->properties(); QString type = props.value("Type", ""); if (!type.isEmpty()) { return type; } // Если и тип не найден, возвращаем пустую строку return QString(); } QPair VedomostController::splitDesignator(const QString &designator) { if (designator.isEmpty()) { return QPair("", ""); } // Используем регулярное выражение для разделения буквенной и цифровой части QRegExp regex("([A-Za-z]+)(\\d*)"); if (regex.indexIn(designator) != -1) { QString letterPart = regex.cap(1); QString numberPart = regex.cap(2); return QPair(letterPart, numberPart); } // Fallback: если регулярное выражение не сработало, // ищем первую цифру и разделяем по ней for (int i = 0; i < designator.length(); ++i) { if (designator[i].isDigit()) { QString letterPart = designator.left(i); QString numberPart = designator.mid(i); return QPair(letterPart, numberPart); } } // Если цифр нет, возвращаем весь десигнатор как буквенную часть return QPair(designator, ""); } // Новые методы для управления строками void VedomostController::addEmptyRow() { if (m_tableModel) { m_tableModel->addEmptyRow(); qDebug() << "VedomostController::addEmptyRow: Добавлена пустая строка"; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void VedomostController::addEmptyRowAt(int position) { if (m_tableModel) { m_tableModel->addEmptyRowAt(position); qDebug() << "VedomostController::addEmptyRowAt: Добавлена пустая строка в позицию" << position; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void VedomostController::insertEmptyRowAt(int position) { if (m_tableModel) { m_tableModel->addEmptyRowAt(position); qDebug() << "VedomostController::insertEmptyRowAt: Вставлена пустая строка в позицию" << position; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void VedomostController::removeRow(int row) { if (m_tableModel) { m_tableModel->removeRow(row); qDebug() << "VedomostController::removeRow: Удалена строка" << row; // Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы" // m_tableModel->optimizePageBreaks(); } } void VedomostController::clearTable() { if (m_tableModel) { m_tableModel->clear(); qDebug() << "VedomostController::clearTable: Таблица очищена"; } } void VedomostController::optimizePageBreaks() { if (m_tableModel) { qDebug() << "VedomostController::optimizePageBreaks: Оптимизируем разрывы страниц"; m_tableModel->optimizePageBreaks(); } } int VedomostController::getFontSize() const { if (m_projectSettingsModel) { return m_projectSettingsModel->getFontSize("Vedomost"); } return 12; // По умолчанию } void VedomostController::setFontSize(int fontSize) { if (m_projectSettingsModel) { m_projectSettingsModel->setFontSize("Vedomost", fontSize); } } int VedomostController::getFontStretch() const { if (m_projectSettingsModel) { return m_projectSettingsModel->getFontStretch("Vedomost"); } return 100; // По умолчанию (нормальный) } void VedomostController::setFontStretch(int stretch) { if (m_projectSettingsModel) { m_projectSettingsModel->setFontStretch("Vedomost", stretch); } } QString VedomostController::getWhereUsed() const { // Сначала пытаемся получить значение из titleInscriptionsModel (поля 101 или 1001) if (m_mainController) { TitleInscriptionsModel *titleModel = m_mainController->titleInscriptionsModel(); ProjectParamTableModel *paramModel = m_mainController->projectParamTableModel(); if (titleModel) { // Пробуем сначала поле 101, затем 1001 QString inscriptionValue; int fieldNumber = -1; if (!titleModel->getInscriptionValue(101).isEmpty()) { inscriptionValue = titleModel->getInscriptionValue(101); fieldNumber = 101; } else if (!titleModel->getInscriptionValue(1001).isEmpty()) { inscriptionValue = titleModel->getInscriptionValue(1001); fieldNumber = 1001; } if (!inscriptionValue.isEmpty() && inscriptionValue != "-- Не выбрано --") { // Если значение - это название поля из ProjectParamTable, получаем реальное значение if (paramModel) { QMap params = paramModel->getDataAsMap(); if (params.contains(inscriptionValue)) { QString projectParamValue = params[inscriptionValue]; if (!projectParamValue.isEmpty()) { return projectParamValue; } } } // Если сопоставления нет или значение параметра пустое, возвращаем значение надписи return inscriptionValue; } } } // Fallback: используем старую логику из columnMappings QStringList mappings = getColumnMappings(); // Поле "WhereUsed" находится в позиции 4 в columnMappings if (mappings.size() > 4) { QString fieldName = mappings[4]; if (!fieldName.isEmpty()) { // Получаем значение из projectparamtable if (m_mainController) { ProjectParamTableModel *paramModel = m_mainController->projectParamTableModel(); if (paramModel) { QMap params = paramModel->getDataAsMap(); if (params.contains(fieldName)) { return params[fieldName]; // Возвращаем значение, а не название поля } } } // Если не удалось получить значение из модели, возвращаем название поля return fieldName; } } return QString(); } QList VedomostController::splitLongDesignation(const QString &designation, const QString originalString, ComponentModel *component, int maxLength) { QList result; qDebug() << "[SPLIT_DEBUG] ========== splitLongDesignation START =========="; qDebug() << "[SPLIT_DEBUG] designation:" << designation; qDebug() << "[SPLIT_DEBUG] designation.length():" << designation.length(); qDebug() << "[SPLIT_DEBUG] originalString:" << originalString; qDebug() << "[SPLIT_DEBUG] originalString.startsWith('='):" << originalString.startsWith("="); qDebug() << "[SPLIT_DEBUG] component:" << (component ? "не nullptr" : "nullptr"); qDebug() << "[SPLIT_DEBUG] maxLength:" << maxLength; if (designation.length() <= maxLength) { qDebug() << "[SPLIT_DEBUG] Строка короткая, возвращаем как есть"; result.append(designation); qDebug() << "[SPLIT_DEBUG] ========== splitLongDesignation END (короткая строка) =========="; return result; } // Проверяем, является ли наименование выражением с частями if (originalString.startsWith("=") && component) { qDebug() << "[SPLIT_DEBUG] >>> ВЕТКА: Это выражение, разделяем по плюсам"; // Это выражение - разделяем по частям QString expr = originalString.mid(1); // Убираем начальный "=" qDebug() << "[SPLIT_DEBUG] Выражение без '=':" << expr; QStringList parts; QString currentPart; bool inDoubleQuotes = false; bool inSingleQuotes = false; for (int i = 0; i < expr.length(); ++i) { QChar ch = expr[i]; if (ch == '"' && !inSingleQuotes) { inDoubleQuotes = !inDoubleQuotes; currentPart += ch; } else if (ch == '\'' && !inDoubleQuotes) { inSingleQuotes = !inSingleQuotes; currentPart += ch; } else if (ch == '+' && !inDoubleQuotes && !inSingleQuotes) { // Это разделитель "+" вне кавычек qDebug() << "[SPLIT_DEBUG] Найден разделитель '+' на позиции" << i << ", текущая часть:" << currentPart; if (!currentPart.trimmed().isEmpty()) { parts.append(currentPart.trimmed()); qDebug() << "[SPLIT_DEBUG] Добавлена часть в список:" << currentPart.trimmed(); } currentPart.clear(); } else { currentPart += ch; } } // Добавляем последнюю часть if (!currentPart.trimmed().isEmpty()) { parts.append(currentPart.trimmed()); qDebug() << "[SPLIT_DEBUG] Добавлена последняя часть:" << currentPart.trimmed(); } qDebug() << "[SPLIT_DEBUG] Всего частей выражения:" << parts.size(); qDebug() << "[SPLIT_DEBUG] Части выражения:" << parts; // Сначала собираем все части в одну строку для комплексных строк QString fullString; for (int i = 0; i < parts.size(); ++i) { QString trimmedPart = parts[i].trimmed(); if (!trimmedPart.isEmpty()) { QString displayPart; // Проверяем, является ли это литералом в одинарных кавычках if (trimmedPart.startsWith("'") && trimmedPart.endsWith("'")) { // Это литерал (запятая, пробел и т.д.) displayPart = trimmedPart.mid(1, trimmedPart.length() - 2); } else { // Это имя свойства - получаем значение из компонента QMap props = component->properties(); qDebug() << "[SPLIT_DEBUG] Обрабатываем свойство:" << trimmedPart; if (trimmedPart == "Designator") { displayPart = component->designator(); qDebug() << "[SPLIT_DEBUG] Свойство Designator, значение:" << displayPart; } else if (trimmedPart.startsWith("\"") && trimmedPart.endsWith("\"")) { QString propertyName = trimmedPart.mid(1, trimmedPart.length() - 2); displayPart = props.value(propertyName, ""); qDebug() << "[SPLIT_DEBUG] Свойство в кавычках:" << propertyName << ", значение:" << displayPart; // Рекурсивно обрабатываем вложенные выражения if (displayPart.startsWith("=")) { qDebug() << "[SPLIT_DEBUG] Значение свойства является вложенным выражением, рекурсивно обрабатываем"; displayPart = parseExpression(component, displayPart); } } else { displayPart = props.value(trimmedPart, ""); qDebug() << "[SPLIT_DEBUG] Свойство без кавычек:" << trimmedPart << ", значение:" << displayPart; // Рекурсивно обрабатываем вложенные выражения if (displayPart.startsWith("=")) { qDebug() << "[SPLIT_DEBUG] Значение свойства является вложенным выражением, рекурсивно обрабатываем"; displayPart = parseExpression(component, displayPart); } } } fullString += displayPart; } } qDebug() << "[SPLIT_DEBUG] Собранная полная строка:" << fullString << ", длина:" << fullString.length(); // Проверяем, содержит ли строка запятые (комплексная строка) bool isComplexString = fullString.contains(','); if (isComplexString && fullString.length() > maxLength) { qDebug() << "[SPLIT_DEBUG] Комплексная строка с запятыми, делим по середине"; // Для комплексной строки делим по середине, а не по началу result = splitComplexStringByMiddle(fullString, maxLength); } else if (fullString.length() > maxLength) { qDebug() << "[SPLIT_DEBUG] Строка длинная, разделяем по пробелам"; // Обычная длинная строка - разделяем по пробелам result = splitBySpaces(fullString, maxLength); } else { qDebug() << "[SPLIT_DEBUG] Строка короткая, возвращаем как есть"; result.append(fullString); } } else { qDebug() << "[SPLIT_DEBUG] >>> ВЕТКА: Обычный текст (не выражение)"; qDebug() << "[SPLIT_DEBUG] Причина: originalString.startsWith('=') =" << originalString.startsWith("=") << ", component =" << (component ? "не nullptr" : "nullptr"); // Обычный текст - сначала пытаемся разделить по соединениям (тире, дефисы) qDebug() << "[SPLIT_DEBUG] Пытаемся разделить по соединениям (тире, дефисы)"; result = splitByConnectors(designation, maxLength); qDebug() << "[SPLIT_DEBUG] Результат разделения по соединениям:" << result << ", размер:" << result.size(); // Если разделение по соединениям не дало результата, разделяем по пробелам if (result.size() <= 1) { qDebug() << "[SPLIT_DEBUG] Разделение по соединениям не дало результата, разделяем по пробелам"; result = splitBySpaces(designation, maxLength); qDebug() << "[SPLIT_DEBUG] Результат разделения по пробелам:" << result; } } qDebug() << "[SPLIT_DEBUG] ФИНАЛЬНЫЙ результат разделения:" << result; qDebug() << "[SPLIT_DEBUG] ========== splitLongDesignation END =========="; return result; } QList VedomostController::splitBySpaces(const QString &text, int maxLength) { QList result; if (text.length() <= maxLength) { result.append(text); return result; } // Ищем пробел, ближайший к середине строки int targetLength = maxLength; int bestSplitPos = -1; int minDifference = text.length(); // Минимальная разница с целевой длиной // Ищем пробелы в тексте for (int i = 0; i < text.length(); ++i) { if (text[i] == ' ') { int leftLength = i; int rightLength = text.length() - i - 1; // Вычисляем разницу с целевой длиной int leftDiff = abs(leftLength - targetLength); int rightDiff = abs(rightLength - targetLength); int totalDiff = leftDiff + rightDiff; // Если эта позиция лучше предыдущей, запоминаем её if (totalDiff < minDifference) { minDifference = totalDiff; bestSplitPos = i; } } } // Если нашли подходящий пробел, разделяем по нему if (bestSplitPos != -1) { QString leftPart = text.left(bestSplitPos).trimmed(); QString rightPart = text.mid(bestSplitPos + 1).trimmed(); // Рекурсивно обрабатываем обе части if (!leftPart.isEmpty()) { QList leftParts = splitBySpaces(leftPart, maxLength); result.append(leftParts); } if (!rightPart.isEmpty()) { QList rightParts = splitBySpaces(rightPart, maxLength); result.append(rightParts); } } else { // Если пробелов нет или не удалось найти подходящий, разбиваем посимвольно // for (int i = 0; i < text.length(); i += maxLength) { // result.append(text.mid(i, maxLength)); // } result.append(text); } return result; } QStringList VedomostController::splitDesignatorGroups(const QStringList &designatorGroups, int maxLength) { QStringList result; QString currentLine; for (const QString &group : designatorGroups) { QString testLine = currentLine.isEmpty() ? group : currentLine + ", " + group; if (testLine.length() <= maxLength) { currentLine = testLine; } else { if (!currentLine.isEmpty()) { result.append(currentLine + ","); } currentLine = group; } } if (!currentLine.isEmpty()) { result.append(currentLine); } // Теперь разбиваем каждую строку, если она длиннее maxLength QStringList finalResult; for (const QString &line : result) { if (line.length() <= maxLength) { finalResult.append(line); } else { // Разбиваем длинную строку посимвольно, но стараемся разбивать по запятым QStringList parts = line.split(", "); QString currentPart = ""; for (const QString &part : parts) { QString testPart = currentPart.isEmpty() ? part : currentPart + ", " + part; if (testPart.length() <= maxLength) { currentPart = testPart; } else { if (!currentPart.isEmpty()) { finalResult.append(currentPart); } currentPart = part; } } if (!currentPart.isEmpty()) { finalResult.append(currentPart); } } } return finalResult; } void VedomostController::onRowDataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles) { Q_UNUSED(roles); if (!m_tableModel) { return; } // Обрабатываем каждую измененную строку for (int row = topLeft.row(); row <= bottomRight.row(); ++row) { // Проверяем, изменились ли колонки с количествами (5, 6, 7) int startCol = topLeft.column(); int endCol = bottomRight.column(); bool quantityChanged = false; for (int col = startCol; col <= endCol; ++col) { if (col == 5 || col == 6 || col == 7) { // quantityPerItem, quantityInSet, quantityForReg quantityChanged = true; break; } } if (quantityChanged) { // Проверяем, что totalQuantity автоматически сгенерировано (не редактировалось вручную) // Используем прямой вызов метода модели для проверки и обновления m_tableModel->updateTotalQuantity(row); } } }