1486 lines
77 KiB
C++
1486 lines
77 KiB
C++
#include "vedomostcontroller.h"
|
||
#include "../model/componentmodel.h"
|
||
#include "../model/projectparamtablemodel.h"
|
||
#include "../model/titleinscriptionsmodel.h"
|
||
#include "../controller/maincontroller.h"
|
||
#include <QDebug>
|
||
#include <QCollator>
|
||
|
||
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<ComponentModel*> &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<QString, QList<ComponentModel*>> groupedByLetter;
|
||
for (ComponentModel *component : m_components) {
|
||
QPair<QString, QString> 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<ComponentModel*> componentsInGroup = it.value();
|
||
|
||
if (componentsInGroup.size() > 1) {
|
||
// Если компонентов больше одного, группируем компоненты по наименованию
|
||
QList<QPair<QString, QList<ComponentModel*>>> 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<ComponentModel*> 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<ComponentModel*> 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<QString> 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<QString> productCodeParts = splitLongDesignation(productCodeValue, productCodeOriginalString, firstComponent, 25);
|
||
QList<QString> documentCodeParts = splitLongDesignation(documentCodeValue, documentCodeOriginalString, firstComponent, 35);
|
||
QList<QString> supplierParts = splitLongDesignation(supplierValue, supplierOriginalString, firstComponent, 25);
|
||
QList<QString> whereUsedParts = splitLongDesignation(whereUsedValue, whereUsedValue, firstComponent, 35);
|
||
QList<QString> 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<QString, QString> partsA = splitDesignator(designatorA);
|
||
QPair<QString, QString> 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<QString> designatorGroupsForThisName;
|
||
QString currentGroup = componentsWithSameName.first()->designator();
|
||
QString lastDesignator = currentGroup;
|
||
|
||
for (int i = 1; i < componentsWithSameName.size(); ++i) {
|
||
QString currentDesignator = componentsWithSameName[i]->designator();
|
||
QPair<QString, QString> currentParts = splitDesignator(currentDesignator);
|
||
QPair<QString, QString> 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<QString> 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<QString> productCodeParts = splitLongDesignation(productCodeValue, productCodeOriginalString, component, 25);
|
||
QList<QString> documentCodeParts = splitLongDesignation(documentCodeValue, documentCodeOriginalString, component, 35);
|
||
QList<QString> supplierParts = splitLongDesignation(supplierValue, supplierOriginalString, component, 25);
|
||
QList<QString> whereUsedParts = splitLongDesignation(whereUsedValue, whereUsedValue, component, 35);
|
||
QList<QString> 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<QString, QString> 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<QString, QString> 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<QString, QString> 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<QString, QString> 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<QString> VedomostController::splitByConnectors(const QString &text, int maxLength)
|
||
{
|
||
QList<QString> 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<QString> leftParts = splitByConnectors(leftPart, maxLength);
|
||
result.append(leftParts);
|
||
}
|
||
|
||
if (!rightPart.isEmpty()) {
|
||
QList<QString> rightParts = splitByConnectors(rightPart, maxLength);
|
||
result.append(rightParts);
|
||
}
|
||
}
|
||
|
||
// Если разделители не найдены или не подходят, возвращаем исходный текст
|
||
if (result.isEmpty()) {
|
||
result.append(text);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
QList<QString> VedomostController::splitComplexStringByMiddle(const QString &text, int maxLength)
|
||
{
|
||
QList<QString> result;
|
||
|
||
if (text.length() <= maxLength) {
|
||
result.append(text);
|
||
return result;
|
||
}
|
||
|
||
qDebug() << "[SPLIT_DEBUG] splitComplexStringByMiddle: Разделяем комплексную строку:" << text;
|
||
|
||
// Ищем запятые в тексте
|
||
QList<int> 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<QString> leftParts = splitComplexStringByMiddle(leftPart, maxLength);
|
||
result.append(leftParts);
|
||
}
|
||
|
||
if (!rightPart.isEmpty()) {
|
||
QList<QString> 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<QString, QString> 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<QString, QString> 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<QString, QString> props = component->properties();
|
||
QString type = props.value("Type", "");
|
||
if (!type.isEmpty()) {
|
||
return type;
|
||
}
|
||
|
||
// Если и тип не найден, возвращаем пустую строку
|
||
return QString();
|
||
}
|
||
|
||
QPair<QString, QString> VedomostController::splitDesignator(const QString &designator)
|
||
{
|
||
if (designator.isEmpty()) {
|
||
return QPair<QString, QString>("", "");
|
||
}
|
||
|
||
// Используем регулярное выражение для разделения буквенной и цифровой части
|
||
QRegExp regex("([A-Za-z]+)(\\d*)");
|
||
if (regex.indexIn(designator) != -1) {
|
||
QString letterPart = regex.cap(1);
|
||
QString numberPart = regex.cap(2);
|
||
return QPair<QString, QString>(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<QString, QString>(letterPart, numberPart);
|
||
}
|
||
}
|
||
|
||
// Если цифр нет, возвращаем весь десигнатор как буквенную часть
|
||
return QPair<QString, QString>(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<QString, QString> 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<QString, QString> params = paramModel->getDataAsMap();
|
||
if (params.contains(fieldName)) {
|
||
return params[fieldName]; // Возвращаем значение, а не название поля
|
||
}
|
||
}
|
||
}
|
||
// Если не удалось получить значение из модели, возвращаем название поля
|
||
return fieldName;
|
||
}
|
||
}
|
||
return QString();
|
||
}
|
||
|
||
QList<QString> VedomostController::splitLongDesignation(const QString &designation, const QString originalString, ComponentModel *component, int maxLength)
|
||
{
|
||
QList<QString> 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<QString, QString> 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<QString> VedomostController::splitBySpaces(const QString &text, int maxLength)
|
||
{
|
||
QList<QString> 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<QString> leftParts = splitBySpaces(leftPart, maxLength);
|
||
result.append(leftParts);
|
||
}
|
||
|
||
if (!rightPart.isEmpty()) {
|
||
QList<QString> 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<int> &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);
|
||
}
|
||
}
|
||
}
|