1306 lines
50 KiB
C++
1306 lines
50 KiB
C++
#include "vedomosttablemodel.h"
|
|
#include "componentmodel.h"
|
|
#include <QDebug>
|
|
#include <QDataStream>
|
|
#include <QBuffer>
|
|
#include <QJsonDocument>
|
|
#include <QJsonObject>
|
|
#include <QJsonArray>
|
|
#include <QFont>
|
|
#include <QFontMetrics>
|
|
#include <QColor>
|
|
#include <QBrush>
|
|
#include <QSet>
|
|
#include <QPair>
|
|
|
|
// Регистрируем структуры для работы с QVariant
|
|
Q_DECLARE_METATYPE(VedomostCellData)
|
|
Q_DECLARE_METATYPE(VedomostRowData)
|
|
|
|
// Статические константы
|
|
const QStringList VedomostTableModel::DEFAULT_HEADERS = {
|
|
"Наименование", "Код продукции", "Обозначение документа на поставку",
|
|
"Поставщик", "Куда входит (обозначение)", "Количество на изделие",
|
|
"Количество в комплекте", "Количество на регулир", "Количество всего", "Примечание"
|
|
};
|
|
|
|
VedomostTableModel::VedomostTableModel(QObject *parent)
|
|
: QAbstractTableModel(parent)
|
|
{
|
|
qDebug() << "VedomostTableModel: Конструктор";
|
|
}
|
|
|
|
int VedomostTableModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
if (parent.isValid())
|
|
return 0;
|
|
return m_rows.size();
|
|
}
|
|
|
|
int VedomostTableModel::columnCount(const QModelIndex &parent) const
|
|
{
|
|
if (parent.isValid())
|
|
return 0;
|
|
return COLUMN_COUNT;
|
|
}
|
|
|
|
QVariant VedomostTableModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid() || index.row() >= m_rows.size())
|
|
return QVariant();
|
|
|
|
const VedomostRowData &row = m_rows.at(index.row());
|
|
|
|
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
|
switch (index.column()) {
|
|
case 0: return row.name.value;
|
|
case 1: return row.productCode.value;
|
|
case 2: return row.documentCode.value;
|
|
case 3: return row.supplier.value;
|
|
case 4: return row.whereUsed.value;
|
|
case 5: return row.quantityPerItem.value;
|
|
case 6: return row.quantityInSet.value;
|
|
case 7: return row.quantityForReg.value;
|
|
case 8: return row.totalQuantity.value;
|
|
case 9: return row.note.value;
|
|
default: return QVariant();
|
|
}
|
|
}
|
|
|
|
if (role == Qt::BackgroundRole) {
|
|
if (row.isHeader) {
|
|
return QBrush(QColor(200, 200, 200));
|
|
} else {
|
|
// Проверяем переполнение ячейки (используем приблизительную ширину колонки)
|
|
// Приблизительные ширины колонок для Vedomost (в пикселях)
|
|
static const qreal columnWidths[] = {200, 150, 200, 150, 150, 80, 80, 80, 80, 150}; // 10 колонок
|
|
qreal columnWidth = (index.column() < 10) ? columnWidths[index.column()] : 150;
|
|
|
|
// Проверяем переполнение ячейки (используем информацию из PDF)
|
|
QPair<int, int> cellKey(index.row(), index.column());
|
|
if (m_overflowCells.contains(cellKey)) {
|
|
// Подсвечиваем красным, если ячейка переполнена (помечена из PDF)
|
|
return QBrush(QColor(255, 200, 200)); // Светло-красный
|
|
}
|
|
|
|
// Чередуем оттенки фона для разных страниц
|
|
// Нечетные страницы - светло-голубой, четные - светло-желтый
|
|
if (row.pageNumber % 2 == 1) {
|
|
return QBrush(QColor(240, 248, 255)); // AliceBlue - для нечетных страниц
|
|
} else {
|
|
return QBrush(QColor(255, 255, 240)); // Ivory - для четных страниц
|
|
}
|
|
}
|
|
}
|
|
|
|
if (role == Qt::FontRole) {
|
|
QFont font;
|
|
bool isBold = false;
|
|
bool isUnderline = false;
|
|
|
|
// Проверяем свойства ячейки
|
|
switch (index.column()) {
|
|
case 0:
|
|
isBold = row.name.isHeader;
|
|
isUnderline = row.name.isUnderline;
|
|
break;
|
|
case 1:
|
|
isBold = row.productCode.isHeader;
|
|
isUnderline = row.productCode.isUnderline;
|
|
break;
|
|
case 2:
|
|
isBold = row.documentCode.isHeader;
|
|
isUnderline = row.documentCode.isUnderline;
|
|
break;
|
|
case 3:
|
|
isBold = row.supplier.isHeader;
|
|
isUnderline = row.supplier.isUnderline;
|
|
break;
|
|
case 4:
|
|
isBold = row.whereUsed.isHeader;
|
|
isUnderline = row.whereUsed.isUnderline;
|
|
break;
|
|
case 5:
|
|
isBold = row.quantityPerItem.isHeader;
|
|
isUnderline = row.quantityPerItem.isUnderline;
|
|
break;
|
|
case 6:
|
|
isBold = row.quantityInSet.isHeader;
|
|
isUnderline = row.quantityInSet.isUnderline;
|
|
break;
|
|
case 7:
|
|
isBold = row.quantityForReg.isHeader;
|
|
isUnderline = row.quantityForReg.isUnderline;
|
|
break;
|
|
case 8:
|
|
isBold = row.totalQuantity.isHeader;
|
|
isUnderline = row.totalQuantity.isUnderline;
|
|
break;
|
|
case 9:
|
|
isBold = row.note.isHeader;
|
|
isUnderline = row.note.isUnderline;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (isBold) {
|
|
font.setBold(true);
|
|
}
|
|
if (isUnderline) {
|
|
font.setUnderline(true);
|
|
}
|
|
|
|
if (isBold || isUnderline) {
|
|
return font;
|
|
}
|
|
}
|
|
|
|
if (role == Qt::UserRole) {
|
|
// Возвращаем структуру ячейки для дополнительной информации
|
|
switch (index.column()) {
|
|
case 0: return QVariant::fromValue(row.name);
|
|
case 1: return QVariant::fromValue(row.productCode);
|
|
case 2: return QVariant::fromValue(row.documentCode);
|
|
case 3: return QVariant::fromValue(row.supplier);
|
|
case 4: return QVariant::fromValue(row.whereUsed);
|
|
case 5: return QVariant::fromValue(row.quantityPerItem);
|
|
case 6: return QVariant::fromValue(row.quantityInSet);
|
|
case 7: return QVariant::fromValue(row.quantityForReg);
|
|
case 8: return QVariant::fromValue(row.totalQuantity);
|
|
case 9: return QVariant::fromValue(row.note);
|
|
default: return QVariant();
|
|
}
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
QVariant VedomostTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role != Qt::DisplayRole)
|
|
return QVariant();
|
|
|
|
if (orientation == Qt::Horizontal) {
|
|
if (section >= 0 && section < DEFAULT_HEADERS.size()) {
|
|
return DEFAULT_HEADERS.at(section);
|
|
}
|
|
} else {
|
|
// Вертикальные заголовки - номера строк с указанием страницы
|
|
if (section < m_rows.size()) {
|
|
const VedomostRowData &row = m_rows[section];
|
|
return QString("%1 (стр. %2)").arg(section + 1).arg(row.pageNumber);
|
|
} else {
|
|
return section + 1;
|
|
}
|
|
}
|
|
|
|
return QVariant();
|
|
}
|
|
|
|
bool VedomostTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
|
{
|
|
if (!index.isValid() || index.row() >= m_rows.size() || role != Qt::EditRole)
|
|
return false;
|
|
|
|
// Проверяем, можно ли редактировать эту строку
|
|
if (!canEditRow(index.row())) {
|
|
qDebug() << "VedomostTableModel::setData: Строка" << index.row() << "не может быть отредактирована";
|
|
return false;
|
|
}
|
|
|
|
VedomostRowData &row = m_rows[index.row()];
|
|
QString stringValue = value.toString();
|
|
|
|
bool changed = false;
|
|
switch (index.column()) {
|
|
case 0:
|
|
if (row.name.value != stringValue) {
|
|
row.name.value = stringValue;
|
|
row.name.isAutoGenerated = false;
|
|
row.name.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (row.productCode.value != stringValue) {
|
|
row.productCode.value = stringValue;
|
|
row.productCode.isAutoGenerated = false;
|
|
row.productCode.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (row.documentCode.value != stringValue) {
|
|
row.documentCode.value = stringValue;
|
|
row.documentCode.isAutoGenerated = false;
|
|
row.documentCode.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (row.supplier.value != stringValue) {
|
|
row.supplier.value = stringValue;
|
|
row.supplier.isAutoGenerated = false;
|
|
row.supplier.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (row.whereUsed.value != stringValue) {
|
|
row.whereUsed.value = stringValue;
|
|
row.whereUsed.isAutoGenerated = false;
|
|
row.whereUsed.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (row.quantityPerItem.value != stringValue) {
|
|
row.quantityPerItem.value = stringValue;
|
|
row.quantityPerItem.isAutoGenerated = false;
|
|
row.quantityPerItem.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (row.quantityInSet.value != stringValue) {
|
|
row.quantityInSet.value = stringValue;
|
|
row.quantityInSet.isAutoGenerated = false;
|
|
row.quantityInSet.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (row.quantityForReg.value != stringValue) {
|
|
row.quantityForReg.value = stringValue;
|
|
row.quantityForReg.isAutoGenerated = false;
|
|
row.quantityForReg.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (row.totalQuantity.value != stringValue) {
|
|
row.totalQuantity.value = stringValue;
|
|
row.totalQuantity.isAutoGenerated = false;
|
|
row.totalQuantity.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 9:
|
|
if (row.note.value != stringValue) {
|
|
row.note.value = stringValue;
|
|
row.note.isAutoGenerated = false;
|
|
row.note.isEmpty = stringValue.isEmpty();
|
|
changed = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (changed) {
|
|
updateRowEmptyStatus(index.row());
|
|
emit dataChanged(index, index);
|
|
emit modelDataChanged();
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
bool VedomostTableModel::setCellStretch(int row, int column, int stretch)
|
|
{
|
|
if (row < 0 || row >= m_rows.size() || column < 0 || column >= COLUMN_COUNT) {
|
|
return false;
|
|
}
|
|
|
|
VedomostRowData &rowData = m_rows[row];
|
|
bool changed = false;
|
|
|
|
switch (column) {
|
|
case 0:
|
|
if (rowData.name.stretch != stretch) {
|
|
rowData.name.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (rowData.productCode.stretch != stretch) {
|
|
rowData.productCode.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (rowData.documentCode.stretch != stretch) {
|
|
rowData.documentCode.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (rowData.supplier.stretch != stretch) {
|
|
rowData.supplier.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (rowData.whereUsed.stretch != stretch) {
|
|
rowData.whereUsed.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (rowData.quantityPerItem.stretch != stretch) {
|
|
rowData.quantityPerItem.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (rowData.quantityInSet.stretch != stretch) {
|
|
rowData.quantityInSet.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (rowData.quantityForReg.stretch != stretch) {
|
|
rowData.quantityForReg.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (rowData.totalQuantity.stretch != stretch) {
|
|
rowData.totalQuantity.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 9:
|
|
if (rowData.note.stretch != stretch) {
|
|
rowData.note.stretch = stretch;
|
|
changed = true;
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (changed) {
|
|
QModelIndex index = createIndex(row, column);
|
|
emit dataChanged(index, index, QVector<int>() << Qt::UserRole);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool VedomostTableModel::setCellHeader(int row, int column, bool isHeader)
|
|
{
|
|
if (row < 0 || row >= m_rows.size() || column < 0 || column >= COLUMN_COUNT) {
|
|
return false;
|
|
}
|
|
|
|
VedomostRowData &rowData = m_rows[row];
|
|
bool changed = false;
|
|
|
|
switch (column) {
|
|
case 0:
|
|
if (rowData.name.isHeader != isHeader) {
|
|
rowData.name.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (rowData.productCode.isHeader != isHeader) {
|
|
rowData.productCode.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (rowData.documentCode.isHeader != isHeader) {
|
|
rowData.documentCode.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (rowData.supplier.isHeader != isHeader) {
|
|
rowData.supplier.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (rowData.whereUsed.isHeader != isHeader) {
|
|
rowData.whereUsed.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (rowData.quantityPerItem.isHeader != isHeader) {
|
|
rowData.quantityPerItem.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (rowData.quantityInSet.isHeader != isHeader) {
|
|
rowData.quantityInSet.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (rowData.quantityForReg.isHeader != isHeader) {
|
|
rowData.quantityForReg.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (rowData.totalQuantity.isHeader != isHeader) {
|
|
rowData.totalQuantity.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 9:
|
|
if (rowData.note.isHeader != isHeader) {
|
|
rowData.note.isHeader = isHeader;
|
|
changed = true;
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (changed) {
|
|
QModelIndex index = createIndex(row, column);
|
|
emit dataChanged(index, index, QVector<int>() << Qt::UserRole);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool VedomostTableModel::setCellUnderline(int row, int column, bool isUnderline)
|
|
{
|
|
if (row < 0 || row >= m_rows.size() || column < 0 || column >= COLUMN_COUNT) {
|
|
return false;
|
|
}
|
|
|
|
VedomostRowData &rowData = m_rows[row];
|
|
bool changed = false;
|
|
|
|
switch (column) {
|
|
case 0:
|
|
if (rowData.name.isUnderline != isUnderline) {
|
|
rowData.name.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (rowData.productCode.isUnderline != isUnderline) {
|
|
rowData.productCode.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (rowData.documentCode.isUnderline != isUnderline) {
|
|
rowData.documentCode.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (rowData.supplier.isUnderline != isUnderline) {
|
|
rowData.supplier.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 4:
|
|
if (rowData.whereUsed.isUnderline != isUnderline) {
|
|
rowData.whereUsed.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (rowData.quantityPerItem.isUnderline != isUnderline) {
|
|
rowData.quantityPerItem.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (rowData.quantityInSet.isUnderline != isUnderline) {
|
|
rowData.quantityInSet.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 7:
|
|
if (rowData.quantityForReg.isUnderline != isUnderline) {
|
|
rowData.quantityForReg.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 8:
|
|
if (rowData.totalQuantity.isUnderline != isUnderline) {
|
|
rowData.totalQuantity.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
case 9:
|
|
if (rowData.note.isUnderline != isUnderline) {
|
|
rowData.note.isUnderline = isUnderline;
|
|
changed = true;
|
|
}
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
if (changed) {
|
|
QModelIndex index = createIndex(row, column);
|
|
emit dataChanged(index, index, QVector<int>() << Qt::UserRole);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool VedomostTableModel::isCellOverflow(int row, int column, qreal columnWidth, int fontSize) const
|
|
{
|
|
if (row < 0 || row >= m_rows.size() || column < 0 || column >= COLUMN_COUNT) {
|
|
return false;
|
|
}
|
|
|
|
const VedomostRowData &rowData = m_rows[row];
|
|
QString cellText;
|
|
int cellStretch = 100;
|
|
|
|
// Получаем текст и поджим ячейки
|
|
switch (column) {
|
|
case 0: cellText = rowData.name.value; cellStretch = rowData.name.stretch; break;
|
|
case 1: cellText = rowData.productCode.value; cellStretch = rowData.productCode.stretch; break;
|
|
case 2: cellText = rowData.documentCode.value; cellStretch = rowData.documentCode.stretch; break;
|
|
case 3: cellText = rowData.supplier.value; cellStretch = rowData.supplier.stretch; break;
|
|
case 4: cellText = rowData.whereUsed.value; cellStretch = rowData.whereUsed.stretch; break;
|
|
case 5: cellText = rowData.quantityPerItem.value; cellStretch = rowData.quantityPerItem.stretch; break;
|
|
case 6: cellText = rowData.quantityInSet.value; cellStretch = rowData.quantityInSet.stretch; break;
|
|
case 7: cellText = rowData.quantityForReg.value; cellStretch = rowData.quantityForReg.stretch; break;
|
|
case 8: cellText = rowData.totalQuantity.value; cellStretch = rowData.totalQuantity.stretch; break;
|
|
case 9: cellText = rowData.note.value; cellStretch = rowData.note.stretch; break;
|
|
default: return false;
|
|
}
|
|
|
|
if (cellText.isEmpty()) {
|
|
return false;
|
|
}
|
|
|
|
// Используем ту же логику, что и в PDF: масштабирование вместо setStretch
|
|
// Создаем шрифт без поджима для измерения ширины
|
|
QFont testFont("Arial", fontSize);
|
|
QFontMetrics metrics(testFont);
|
|
|
|
// Вычисляем коэффициент масштабирования (как в drawCellWithStretch)
|
|
qreal scaleX = cellStretch / 100.0;
|
|
|
|
// Вычисляем ширину текста после масштабирования
|
|
qreal textWidth = metrics.horizontalAdvance(cellText) * scaleX;
|
|
|
|
// Используем ту же логику, что и в PDF: если текст обрезается с многоточием, это переполнение
|
|
// Вычисляем максимальную ширину текста до масштабирования (как в drawCellWithStretch)
|
|
qreal maxTextWidth = columnWidth / scaleX;
|
|
|
|
// Проверяем, будет ли текст обрезан с многоточием (как в PDF)
|
|
// Если исходный текст шире, чем максимальная ширина, то он будет обрезан
|
|
qreal originalTextWidth = metrics.horizontalAdvance(cellText);
|
|
|
|
// Добавляем небольшой запас для учета возможных расхождений между Arial и GOST шрифтами
|
|
qreal tolerance = 3.0;
|
|
|
|
// Переполнение, если текст будет обрезан с многоточием
|
|
return originalTextWidth > (maxTextWidth + tolerance);
|
|
}
|
|
|
|
void VedomostTableModel::setCellOverflow(int row, int column, bool overflow)
|
|
{
|
|
if (row < 0 || row >= m_rows.size() || column < 0 || column >= COLUMN_COUNT) {
|
|
return;
|
|
}
|
|
|
|
QPair<int, int> cellKey(row, column);
|
|
|
|
if (overflow) {
|
|
if (!m_overflowCells.contains(cellKey)) {
|
|
m_overflowCells.insert(cellKey);
|
|
QModelIndex index = createIndex(row, column);
|
|
emit dataChanged(index, index, QVector<int>() << Qt::BackgroundRole);
|
|
}
|
|
} else {
|
|
if (m_overflowCells.contains(cellKey)) {
|
|
m_overflowCells.remove(cellKey);
|
|
QModelIndex index = createIndex(row, column);
|
|
emit dataChanged(index, index, QVector<int>() << Qt::BackgroundRole);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VedomostTableModel::clearOverflowMarks()
|
|
{
|
|
if (m_overflowCells.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// Сохраняем индексы для обновления
|
|
QList<QModelIndex> indicesToUpdate;
|
|
QSet<QPair<int, int>>::const_iterator it;
|
|
for (it = m_overflowCells.constBegin(); it != m_overflowCells.constEnd(); ++it) {
|
|
const QPair<int, int> &cellKey = *it;
|
|
indicesToUpdate.append(createIndex(cellKey.first, cellKey.second));
|
|
}
|
|
|
|
m_overflowCells.clear();
|
|
|
|
// Обновляем все затронутые ячейки
|
|
for (const QModelIndex &index : indicesToUpdate) {
|
|
emit dataChanged(index, index, QVector<int>() << Qt::BackgroundRole);
|
|
}
|
|
}
|
|
|
|
Qt::ItemFlags VedomostTableModel::flags(const QModelIndex &index) const
|
|
{
|
|
if (!index.isValid())
|
|
return Qt::NoItemFlags;
|
|
|
|
Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
|
|
|
// Проверяем, можно ли редактировать эту строку
|
|
if (canEditRow(index.row())) {
|
|
flags |= Qt::ItemIsEditable;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
void VedomostTableModel::setColumnMappings(const QStringList &mappings)
|
|
{
|
|
if (m_columnMappings != mappings) {
|
|
m_columnMappings = mappings;
|
|
emit columnMappingsChanged();
|
|
}
|
|
}
|
|
|
|
QStringList VedomostTableModel::columnMappings() const
|
|
{
|
|
return m_columnMappings;
|
|
}
|
|
|
|
void VedomostTableModel::addRow(const VedomostRowData &rowData)
|
|
{
|
|
beginInsertRows(QModelIndex(), m_rows.size(), m_rows.size());
|
|
m_rows.append(rowData);
|
|
endInsertRows();
|
|
|
|
// Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы"
|
|
// updateRowNumbers();
|
|
emit modelDataChanged();
|
|
}
|
|
|
|
void VedomostTableModel::insertRow(int position, const VedomostRowData &rowData)
|
|
{
|
|
if (position < 0 || position > m_rows.size())
|
|
return;
|
|
|
|
beginInsertRows(QModelIndex(), position, position);
|
|
m_rows.insert(position, rowData);
|
|
endInsertRows();
|
|
|
|
// Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы"
|
|
// updateRowNumbers();
|
|
emit modelDataChanged();
|
|
}
|
|
|
|
void VedomostTableModel::removeRow(int row)
|
|
{
|
|
if (row < 0 || row >= m_rows.size())
|
|
return;
|
|
|
|
beginRemoveRows(QModelIndex(), row, row);
|
|
m_rows.removeAt(row);
|
|
endRemoveRows();
|
|
|
|
// Автоматический пересчет отключен - пересчет выполняется по кнопке "Пересчет таблицы"
|
|
// updateRowNumbers();
|
|
emit modelDataChanged();
|
|
}
|
|
|
|
void VedomostTableModel::clear()
|
|
{
|
|
if (m_rows.isEmpty())
|
|
return;
|
|
|
|
beginRemoveRows(QModelIndex(), 0, m_rows.size() - 1);
|
|
m_rows.clear();
|
|
endRemoveRows();
|
|
|
|
emit modelDataChanged();
|
|
}
|
|
|
|
void VedomostTableModel::addEmptyRow()
|
|
{
|
|
addRow(createEmptyRow());
|
|
}
|
|
|
|
void VedomostTableModel::addEmptyRowAt(int position)
|
|
{
|
|
insertRow(position, createEmptyRow());
|
|
}
|
|
|
|
VedomostRowData VedomostTableModel::createEmptyRow() const
|
|
{
|
|
VedomostRowData row;
|
|
row.name = createCellData("", false, false, true);
|
|
row.productCode = createCellData("", false, false, true);
|
|
row.documentCode = createCellData("", false, false, true);
|
|
row.supplier = createCellData("", false, false, true);
|
|
row.whereUsed = createCellData("", false, false, true);
|
|
row.quantityPerItem = createCellData("", false, false, true);
|
|
row.quantityInSet = createCellData("", false, false, true);
|
|
row.quantityForReg = createCellData("", false, false, true);
|
|
row.totalQuantity = createCellData("", false, false, true);
|
|
row.note = createCellData("", false, false, true);
|
|
row.isEmpty = true;
|
|
return row;
|
|
}
|
|
|
|
bool VedomostTableModel::canEditRow(int row) const
|
|
{
|
|
if (row < 0 || row >= m_rows.size())
|
|
return false;
|
|
|
|
// Разрешаем редактирование и удаление всех строк, включая заголовки и пустые
|
|
return true;
|
|
}
|
|
|
|
void VedomostTableModel::setComponents(const QList<ComponentModel*> &components)
|
|
{
|
|
m_components = components;
|
|
}
|
|
|
|
void VedomostTableModel::setPropertyNames(const QStringList &propertyNames)
|
|
{
|
|
m_propertyNames = propertyNames;
|
|
}
|
|
|
|
QStringList VedomostTableModel::availableProperties() const
|
|
{
|
|
return m_propertyNames;
|
|
}
|
|
|
|
QByteArray VedomostTableModel::saveToDatabase() const
|
|
{
|
|
qDebug() << "VedomostTableModel::saveToDatabase: Начинаем сохранение в БД";
|
|
qDebug() << "VedomostTableModel::saveToDatabase: Количество строк:" << m_rows.size();
|
|
qDebug() << "VedomostTableModel::saveToDatabase: Настройки колонок:" << m_columnMappings;
|
|
|
|
QJsonObject root;
|
|
QJsonArray rowsArray;
|
|
|
|
for (const VedomostRowData &row : m_rows) {
|
|
QJsonObject rowObj;
|
|
|
|
// Отладочная информация для первых нескольких строк
|
|
static int debugCounter = 0;
|
|
if (debugCounter < 5) {
|
|
qDebug() << "VedomostTableModel::saveToDatabase: Строка" << debugCounter << ":";
|
|
qDebug() << " name.value:" << row.name.value;
|
|
qDebug() << " productCode.value:" << row.productCode.value;
|
|
qDebug() << " isHeader:" << row.isHeader;
|
|
qDebug() << " isEmpty:" << row.isEmpty;
|
|
debugCounter++;
|
|
}
|
|
|
|
// Сохраняем данные ячеек
|
|
QJsonObject nameObj;
|
|
nameObj["value"] = row.name.value;
|
|
nameObj["rowNumber"] = row.name.rowNumber;
|
|
nameObj["pageNumber"] = row.name.pageNumber;
|
|
nameObj["isHeader"] = row.name.isHeader;
|
|
nameObj["isAutoGenerated"] = row.name.isAutoGenerated;
|
|
nameObj["isEmpty"] = row.name.isEmpty;
|
|
nameObj["stretch"] = row.name.stretch;
|
|
nameObj["isUnderline"] = row.name.isUnderline;
|
|
rowObj["name"] = nameObj;
|
|
|
|
QJsonObject productCodeObj;
|
|
productCodeObj["value"] = row.productCode.value;
|
|
productCodeObj["rowNumber"] = row.productCode.rowNumber;
|
|
productCodeObj["pageNumber"] = row.productCode.pageNumber;
|
|
productCodeObj["isHeader"] = row.productCode.isHeader;
|
|
productCodeObj["isAutoGenerated"] = row.productCode.isAutoGenerated;
|
|
productCodeObj["isEmpty"] = row.productCode.isEmpty;
|
|
productCodeObj["stretch"] = row.productCode.stretch;
|
|
productCodeObj["isUnderline"] = row.productCode.isUnderline;
|
|
rowObj["productCode"] = productCodeObj;
|
|
|
|
QJsonObject documentCodeObj;
|
|
documentCodeObj["value"] = row.documentCode.value;
|
|
documentCodeObj["rowNumber"] = row.documentCode.rowNumber;
|
|
documentCodeObj["pageNumber"] = row.documentCode.pageNumber;
|
|
documentCodeObj["isHeader"] = row.documentCode.isHeader;
|
|
documentCodeObj["isAutoGenerated"] = row.documentCode.isAutoGenerated;
|
|
documentCodeObj["isEmpty"] = row.documentCode.isEmpty;
|
|
documentCodeObj["stretch"] = row.documentCode.stretch;
|
|
documentCodeObj["isUnderline"] = row.documentCode.isUnderline;
|
|
rowObj["documentCode"] = documentCodeObj;
|
|
|
|
QJsonObject supplierObj;
|
|
supplierObj["value"] = row.supplier.value;
|
|
supplierObj["rowNumber"] = row.supplier.rowNumber;
|
|
supplierObj["pageNumber"] = row.supplier.pageNumber;
|
|
supplierObj["isHeader"] = row.supplier.isHeader;
|
|
supplierObj["isAutoGenerated"] = row.supplier.isAutoGenerated;
|
|
supplierObj["isEmpty"] = row.supplier.isEmpty;
|
|
supplierObj["stretch"] = row.supplier.stretch;
|
|
supplierObj["isUnderline"] = row.supplier.isUnderline;
|
|
rowObj["supplier"] = supplierObj;
|
|
|
|
QJsonObject whereUsedObj;
|
|
whereUsedObj["value"] = row.whereUsed.value;
|
|
whereUsedObj["rowNumber"] = row.whereUsed.rowNumber;
|
|
whereUsedObj["pageNumber"] = row.whereUsed.pageNumber;
|
|
whereUsedObj["isHeader"] = row.whereUsed.isHeader;
|
|
whereUsedObj["isAutoGenerated"] = row.whereUsed.isAutoGenerated;
|
|
whereUsedObj["isEmpty"] = row.whereUsed.isEmpty;
|
|
whereUsedObj["stretch"] = row.whereUsed.stretch;
|
|
whereUsedObj["isUnderline"] = row.whereUsed.isUnderline;
|
|
rowObj["whereUsed"] = whereUsedObj;
|
|
|
|
QJsonObject quantityPerItemObj;
|
|
quantityPerItemObj["value"] = row.quantityPerItem.value;
|
|
quantityPerItemObj["rowNumber"] = row.quantityPerItem.rowNumber;
|
|
quantityPerItemObj["pageNumber"] = row.quantityPerItem.pageNumber;
|
|
quantityPerItemObj["isHeader"] = row.quantityPerItem.isHeader;
|
|
quantityPerItemObj["isAutoGenerated"] = row.quantityPerItem.isAutoGenerated;
|
|
quantityPerItemObj["isEmpty"] = row.quantityPerItem.isEmpty;
|
|
quantityPerItemObj["stretch"] = row.quantityPerItem.stretch;
|
|
quantityPerItemObj["isUnderline"] = row.quantityPerItem.isUnderline;
|
|
rowObj["quantityPerItem"] = quantityPerItemObj;
|
|
|
|
QJsonObject quantityInSetObj;
|
|
quantityInSetObj["value"] = row.quantityInSet.value;
|
|
quantityInSetObj["rowNumber"] = row.quantityInSet.rowNumber;
|
|
quantityInSetObj["pageNumber"] = row.quantityInSet.pageNumber;
|
|
quantityInSetObj["isHeader"] = row.quantityInSet.isHeader;
|
|
quantityInSetObj["isAutoGenerated"] = row.quantityInSet.isAutoGenerated;
|
|
quantityInSetObj["isEmpty"] = row.quantityInSet.isEmpty;
|
|
quantityInSetObj["stretch"] = row.quantityInSet.stretch;
|
|
quantityInSetObj["isUnderline"] = row.quantityInSet.isUnderline;
|
|
rowObj["quantityInSet"] = quantityInSetObj;
|
|
|
|
QJsonObject quantityForRegObj;
|
|
quantityForRegObj["value"] = row.quantityForReg.value;
|
|
quantityForRegObj["rowNumber"] = row.quantityForReg.rowNumber;
|
|
quantityForRegObj["pageNumber"] = row.quantityForReg.pageNumber;
|
|
quantityForRegObj["isHeader"] = row.quantityForReg.isHeader;
|
|
quantityForRegObj["isAutoGenerated"] = row.quantityForReg.isAutoGenerated;
|
|
quantityForRegObj["isEmpty"] = row.quantityForReg.isEmpty;
|
|
quantityForRegObj["stretch"] = row.quantityForReg.stretch;
|
|
quantityForRegObj["isUnderline"] = row.quantityForReg.isUnderline;
|
|
rowObj["quantityForReg"] = quantityForRegObj;
|
|
|
|
QJsonObject totalQuantityObj;
|
|
totalQuantityObj["value"] = row.totalQuantity.value;
|
|
totalQuantityObj["rowNumber"] = row.totalQuantity.rowNumber;
|
|
totalQuantityObj["pageNumber"] = row.totalQuantity.pageNumber;
|
|
totalQuantityObj["isHeader"] = row.totalQuantity.isHeader;
|
|
totalQuantityObj["isAutoGenerated"] = row.totalQuantity.isAutoGenerated;
|
|
totalQuantityObj["isEmpty"] = row.totalQuantity.isEmpty;
|
|
totalQuantityObj["stretch"] = row.totalQuantity.stretch;
|
|
totalQuantityObj["isUnderline"] = row.totalQuantity.isUnderline;
|
|
rowObj["totalQuantity"] = totalQuantityObj;
|
|
|
|
QJsonObject noteObj;
|
|
noteObj["value"] = row.note.value;
|
|
noteObj["rowNumber"] = row.note.rowNumber;
|
|
noteObj["pageNumber"] = row.note.pageNumber;
|
|
noteObj["isHeader"] = row.note.isHeader;
|
|
noteObj["isAutoGenerated"] = row.note.isAutoGenerated;
|
|
noteObj["isEmpty"] = row.note.isEmpty;
|
|
noteObj["stretch"] = row.note.stretch;
|
|
noteObj["isUnderline"] = row.note.isUnderline;
|
|
rowObj["note"] = noteObj;
|
|
|
|
rowObj["isHeader"] = row.isHeader;
|
|
rowObj["isEmpty"] = row.isEmpty;
|
|
rowObj["rowNumber"] = row.rowNumber;
|
|
rowObj["pageNumber"] = row.pageNumber;
|
|
|
|
rowsArray.append(rowObj);
|
|
}
|
|
|
|
root["rows"] = rowsArray;
|
|
root["columnMappings"] = QJsonArray::fromStringList(m_columnMappings);
|
|
root["propertyNames"] = QJsonArray::fromStringList(m_propertyNames);
|
|
|
|
QJsonDocument doc(root);
|
|
QByteArray result = doc.toJson();
|
|
|
|
qDebug() << "VedomostTableModel::saveToDatabase: Сохранение завершено, размер данных:" << result.size() << "байт";
|
|
return result;
|
|
}
|
|
|
|
void VedomostTableModel::loadFromDatabase(const QByteArray &data)
|
|
{
|
|
qDebug() << "VedomostTableModel::loadFromDatabase: Начинаем загрузку из БД, размер данных:" << data.size();
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(data);
|
|
if (!doc.isObject()) {
|
|
qDebug() << "VedomostTableModel::loadFromDatabase: Ошибка парсинга JSON";
|
|
return;
|
|
}
|
|
|
|
qDebug() << "VedomostTableModel::loadFromDatabase: JSON успешно распарсен";
|
|
|
|
QJsonObject root = doc.object();
|
|
|
|
// Загружаем маппинг колонок
|
|
if (root.contains("columnMappings")) {
|
|
QJsonArray mappingsArray = root["columnMappings"].toArray();
|
|
QStringList mappings;
|
|
for (const QJsonValue &value : mappingsArray) {
|
|
mappings.append(value.toString());
|
|
}
|
|
m_columnMappings = mappings;
|
|
}
|
|
|
|
// Загружаем свойства
|
|
if (root.contains("propertyNames")) {
|
|
QJsonArray propertiesArray = root["propertyNames"].toArray();
|
|
QStringList properties;
|
|
for (const QJsonValue &value : propertiesArray) {
|
|
properties.append(value.toString());
|
|
}
|
|
m_propertyNames = properties;
|
|
}
|
|
|
|
// Загружаем строки
|
|
beginResetModel();
|
|
m_rows.clear();
|
|
|
|
if (root.contains("rows")) {
|
|
QJsonArray rowsArray = root["rows"].toArray();
|
|
for (const QJsonValue &value : rowsArray) {
|
|
QJsonObject rowObj = value.toObject();
|
|
VedomostRowData row;
|
|
|
|
// Загружаем данные ячеек
|
|
if (rowObj.contains("name")) {
|
|
QJsonObject nameObj = rowObj["name"].toObject();
|
|
row.name.value = nameObj["value"].toString();
|
|
row.name.rowNumber = nameObj["rowNumber"].toInt();
|
|
row.name.pageNumber = nameObj["pageNumber"].toInt();
|
|
row.name.isHeader = nameObj["isHeader"].toBool();
|
|
row.name.isAutoGenerated = nameObj["isAutoGenerated"].toBool();
|
|
row.name.isEmpty = nameObj["isEmpty"].toBool();
|
|
row.name.stretch = nameObj["stretch"].toInt(100);
|
|
row.name.isUnderline = nameObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
if (rowObj.contains("productCode")) {
|
|
QJsonObject productCodeObj = rowObj["productCode"].toObject();
|
|
row.productCode.value = productCodeObj["value"].toString();
|
|
row.productCode.rowNumber = productCodeObj["rowNumber"].toInt();
|
|
row.productCode.pageNumber = productCodeObj["pageNumber"].toInt();
|
|
row.productCode.isHeader = productCodeObj["isHeader"].toBool();
|
|
row.productCode.isAutoGenerated = productCodeObj["isAutoGenerated"].toBool();
|
|
row.productCode.isEmpty = productCodeObj["isEmpty"].toBool();
|
|
row.productCode.stretch = productCodeObj["stretch"].toInt(100);
|
|
row.productCode.isUnderline = productCodeObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
if (rowObj.contains("documentCode")) {
|
|
QJsonObject documentCodeObj = rowObj["documentCode"].toObject();
|
|
row.documentCode.value = documentCodeObj["value"].toString();
|
|
row.documentCode.rowNumber = documentCodeObj["rowNumber"].toInt();
|
|
row.documentCode.pageNumber = documentCodeObj["pageNumber"].toInt();
|
|
row.documentCode.isHeader = documentCodeObj["isHeader"].toBool();
|
|
row.documentCode.isAutoGenerated = documentCodeObj["isAutoGenerated"].toBool();
|
|
row.documentCode.isEmpty = documentCodeObj["isEmpty"].toBool();
|
|
row.documentCode.stretch = documentCodeObj["stretch"].toInt(100);
|
|
row.documentCode.isUnderline = documentCodeObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
if (rowObj.contains("supplier")) {
|
|
QJsonObject supplierObj = rowObj["supplier"].toObject();
|
|
row.supplier.value = supplierObj["value"].toString();
|
|
row.supplier.rowNumber = supplierObj["rowNumber"].toInt();
|
|
row.supplier.pageNumber = supplierObj["pageNumber"].toInt();
|
|
row.supplier.isHeader = supplierObj["isHeader"].toBool();
|
|
row.supplier.isAutoGenerated = supplierObj["isAutoGenerated"].toBool();
|
|
row.supplier.isEmpty = supplierObj["isEmpty"].toBool();
|
|
row.supplier.stretch = supplierObj["stretch"].toInt(100);
|
|
row.supplier.isUnderline = supplierObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
if (rowObj.contains("whereUsed")) {
|
|
QJsonObject whereUsedObj = rowObj["whereUsed"].toObject();
|
|
row.whereUsed.value = whereUsedObj["value"].toString();
|
|
row.whereUsed.rowNumber = whereUsedObj["rowNumber"].toInt();
|
|
row.whereUsed.pageNumber = whereUsedObj["pageNumber"].toInt();
|
|
row.whereUsed.isHeader = whereUsedObj["isHeader"].toBool();
|
|
row.whereUsed.isAutoGenerated = whereUsedObj["isAutoGenerated"].toBool();
|
|
row.whereUsed.isEmpty = whereUsedObj["isEmpty"].toBool();
|
|
row.whereUsed.stretch = whereUsedObj["stretch"].toInt(100);
|
|
row.whereUsed.isUnderline = whereUsedObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
if (rowObj.contains("quantityPerItem")) {
|
|
QJsonObject quantityPerItemObj = rowObj["quantityPerItem"].toObject();
|
|
row.quantityPerItem.value = quantityPerItemObj["value"].toString();
|
|
row.quantityPerItem.rowNumber = quantityPerItemObj["rowNumber"].toInt();
|
|
row.quantityPerItem.pageNumber = quantityPerItemObj["pageNumber"].toInt();
|
|
row.quantityPerItem.isHeader = quantityPerItemObj["isHeader"].toBool();
|
|
row.quantityPerItem.isAutoGenerated = quantityPerItemObj["isAutoGenerated"].toBool();
|
|
row.quantityPerItem.isEmpty = quantityPerItemObj["isEmpty"].toBool();
|
|
row.quantityPerItem.stretch = quantityPerItemObj["stretch"].toInt(100);
|
|
row.quantityPerItem.isUnderline = quantityPerItemObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
if (rowObj.contains("quantityInSet")) {
|
|
QJsonObject quantityInSetObj = rowObj["quantityInSet"].toObject();
|
|
row.quantityInSet.value = quantityInSetObj["value"].toString();
|
|
row.quantityInSet.rowNumber = quantityInSetObj["rowNumber"].toInt();
|
|
row.quantityInSet.pageNumber = quantityInSetObj["pageNumber"].toInt();
|
|
row.quantityInSet.isHeader = quantityInSetObj["isHeader"].toBool();
|
|
row.quantityInSet.isAutoGenerated = quantityInSetObj["isAutoGenerated"].toBool();
|
|
row.quantityInSet.isEmpty = quantityInSetObj["isEmpty"].toBool();
|
|
row.quantityInSet.stretch = quantityInSetObj["stretch"].toInt(100);
|
|
row.quantityInSet.isUnderline = quantityInSetObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
if (rowObj.contains("quantityForReg")) {
|
|
QJsonObject quantityForRegObj = rowObj["quantityForReg"].toObject();
|
|
row.quantityForReg.value = quantityForRegObj["value"].toString();
|
|
row.quantityForReg.rowNumber = quantityForRegObj["rowNumber"].toInt();
|
|
row.quantityForReg.pageNumber = quantityForRegObj["pageNumber"].toInt();
|
|
row.quantityForReg.isHeader = quantityForRegObj["isHeader"].toBool();
|
|
row.quantityForReg.isAutoGenerated = quantityForRegObj["isAutoGenerated"].toBool();
|
|
row.quantityForReg.isEmpty = quantityForRegObj["isEmpty"].toBool();
|
|
row.quantityForReg.stretch = quantityForRegObj["stretch"].toInt(100);
|
|
row.quantityForReg.isUnderline = quantityForRegObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
if (rowObj.contains("totalQuantity")) {
|
|
QJsonObject totalQuantityObj = rowObj["totalQuantity"].toObject();
|
|
row.totalQuantity.value = totalQuantityObj["value"].toString();
|
|
row.totalQuantity.rowNumber = totalQuantityObj["rowNumber"].toInt();
|
|
row.totalQuantity.pageNumber = totalQuantityObj["pageNumber"].toInt();
|
|
row.totalQuantity.isHeader = totalQuantityObj["isHeader"].toBool();
|
|
row.totalQuantity.isAutoGenerated = totalQuantityObj["isAutoGenerated"].toBool();
|
|
row.totalQuantity.isEmpty = totalQuantityObj["isEmpty"].toBool();
|
|
row.totalQuantity.stretch = totalQuantityObj["stretch"].toInt(100);
|
|
row.totalQuantity.isUnderline = totalQuantityObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
if (rowObj.contains("note")) {
|
|
QJsonObject noteObj = rowObj["note"].toObject();
|
|
row.note.value = noteObj["value"].toString();
|
|
row.note.rowNumber = noteObj["rowNumber"].toInt();
|
|
row.note.pageNumber = noteObj["pageNumber"].toInt();
|
|
row.note.isHeader = noteObj["isHeader"].toBool();
|
|
row.note.isAutoGenerated = noteObj["isAutoGenerated"].toBool();
|
|
row.note.isEmpty = noteObj["isEmpty"].toBool();
|
|
row.note.stretch = noteObj["stretch"].toInt(100);
|
|
row.note.isUnderline = noteObj["isUnderline"].toBool(false);
|
|
}
|
|
|
|
row.isHeader = rowObj["isHeader"].toBool();
|
|
row.isEmpty = rowObj["isEmpty"].toBool();
|
|
row.rowNumber = rowObj["rowNumber"].toInt();
|
|
row.pageNumber = rowObj["pageNumber"].toInt();
|
|
|
|
m_rows.append(row);
|
|
}
|
|
}
|
|
|
|
endResetModel();
|
|
emit modelDataChanged();
|
|
emit columnMappingsChanged();
|
|
|
|
qDebug() << "VedomostTableModel::loadFromDatabase: Загружено строк:" << m_rows.size();
|
|
qDebug() << "VedomostTableModel::loadFromDatabase: Маппинг колонок:" << m_columnMappings;
|
|
qDebug() << "VedomostTableModel::loadFromDatabase: Свойства:" << m_propertyNames;
|
|
}
|
|
|
|
QString VedomostTableModel::getDocumentTitle() const
|
|
{
|
|
return "Ведомость покупных изделий";
|
|
}
|
|
|
|
QString VedomostTableModel::getDocumentType() const
|
|
{
|
|
return "Vedomost";
|
|
}
|
|
|
|
bool VedomostTableModel::isRowHeader(int row) const
|
|
{
|
|
if (row < 0 || row >= m_rows.size())
|
|
return false;
|
|
return m_rows.at(row).isHeader;
|
|
}
|
|
|
|
void VedomostTableModel::updatePageNumbers()
|
|
{
|
|
qDebug() << "VedomostTableModel::updatePageNumbers: Обновляем номера страниц";
|
|
|
|
int currentRow = 0;
|
|
int currentPage = 1;
|
|
int rowsOnCurrentPage = 0;
|
|
|
|
for (int i = 0; i < m_rows.size(); ++i) {
|
|
VedomostRowData &row = m_rows[i];
|
|
|
|
// Определяем, сколько строк помещается на текущей странице
|
|
int maxRowsOnPage = (currentPage == 1) ? ROWS_PER_FIRST_PAGE : ROWS_PER_OTHER_PAGE;
|
|
|
|
// Если текущая страница заполнена, переходим на следующую
|
|
if (rowsOnCurrentPage >= maxRowsOnPage) {
|
|
currentPage++;
|
|
rowsOnCurrentPage = 0;
|
|
maxRowsOnPage = ROWS_PER_OTHER_PAGE; // Начиная со второй страницы
|
|
}
|
|
|
|
// Устанавливаем номер страницы для строки
|
|
row.pageNumber = currentPage;
|
|
row.name.pageNumber = currentPage;
|
|
row.productCode.pageNumber = currentPage;
|
|
row.documentCode.pageNumber = currentPage;
|
|
row.supplier.pageNumber = currentPage;
|
|
row.whereUsed.pageNumber = currentPage;
|
|
row.quantityPerItem.pageNumber = currentPage;
|
|
row.quantityInSet.pageNumber = currentPage;
|
|
row.quantityForReg.pageNumber = currentPage;
|
|
row.totalQuantity.pageNumber = currentPage;
|
|
row.note.pageNumber = currentPage;
|
|
|
|
// Увеличиваем счетчик строк на текущей странице
|
|
rowsOnCurrentPage++;
|
|
currentRow++;
|
|
|
|
qDebug() << "VedomostTableModel::updatePageNumbers: Строка" << i << "на странице" << currentPage
|
|
<< "(строк на странице:" << rowsOnCurrentPage << "/" << maxRowsOnPage << ")";
|
|
}
|
|
|
|
qDebug() << "VedomostTableModel::updatePageNumbers: Обновление завершено, всего страниц:" << currentPage;
|
|
|
|
// Уведомляем UI об изменении данных для обновления отображения страниц
|
|
if (!m_rows.isEmpty()) {
|
|
emit dataChanged(index(0, 0), index(m_rows.size() - 1, COLUMN_COUNT - 1),
|
|
QVector<int>() << Qt::BackgroundRole);
|
|
emit headerDataChanged(Qt::Vertical, 0, m_rows.size() - 1);
|
|
}
|
|
}
|
|
|
|
int VedomostTableModel::getPageForRow(int rowNumber) const
|
|
{
|
|
if (rowNumber < 0 || rowNumber >= m_rows.size())
|
|
return 1;
|
|
return m_rows.at(rowNumber).pageNumber;
|
|
}
|
|
|
|
bool VedomostTableModel::needsPageBreakProtection(int rowIndex) const
|
|
{
|
|
if (rowIndex < 0 || rowIndex >= m_rows.size())
|
|
return false;
|
|
|
|
// Проверяем, не является ли строка заголовком
|
|
if (m_rows.at(rowIndex).isHeader) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void VedomostTableModel::addPageBreakProtection()
|
|
{
|
|
// Реализация защиты от разрыва страниц
|
|
updatePageNumbers();
|
|
}
|
|
|
|
void VedomostTableModel::optimizePageBreaks()
|
|
{
|
|
updatePageNumbers();
|
|
addPageBreakProtection();
|
|
}
|
|
|
|
void VedomostTableModel::updatePageDisplay()
|
|
{
|
|
updatePageNumbers();
|
|
emit modelDataChanged();
|
|
}
|
|
|
|
void VedomostTableModel::updateRowNumbers()
|
|
{
|
|
for (int i = 0; i < m_rows.size(); ++i) {
|
|
m_rows[i].rowNumber = i + 1;
|
|
}
|
|
}
|
|
|
|
void VedomostTableModel::updateRowEmptyStatus(int row)
|
|
{
|
|
if (row < 0 || row >= m_rows.size())
|
|
return;
|
|
|
|
VedomostRowData &rowData = m_rows[row];
|
|
bool isEmpty = true;
|
|
|
|
// Проверяем, есть ли хотя бы одно непустое значение в строке
|
|
if (!rowData.name.value.isEmpty() || !rowData.productCode.value.isEmpty() ||
|
|
!rowData.documentCode.value.isEmpty() || !rowData.supplier.value.isEmpty() ||
|
|
!rowData.whereUsed.value.isEmpty() || !rowData.quantityPerItem.value.isEmpty() ||
|
|
!rowData.quantityInSet.value.isEmpty() || !rowData.quantityForReg.value.isEmpty() ||
|
|
!rowData.totalQuantity.value.isEmpty() || !rowData.note.value.isEmpty()) {
|
|
isEmpty = false;
|
|
}
|
|
|
|
rowData.isEmpty = isEmpty;
|
|
}
|
|
|
|
void VedomostTableModel::updateTotalQuantity(int row)
|
|
{
|
|
if (row < 0 || row >= m_rows.size())
|
|
return;
|
|
|
|
VedomostRowData &rowData = m_rows[row];
|
|
|
|
// Пересчитываем totalQuantity только если оно автоматически сгенерировано
|
|
if (!rowData.totalQuantity.isAutoGenerated) {
|
|
return; // Если редактировалось вручную, не пересчитываем
|
|
}
|
|
|
|
// Получаем значения количеств как числа
|
|
bool ok1, ok2, ok3;
|
|
int qtyPerItem = rowData.quantityPerItem.value.toInt(&ok1);
|
|
int qtyInSet = rowData.quantityInSet.value.toInt(&ok2);
|
|
int qtyForReg = rowData.quantityForReg.value.toInt(&ok3);
|
|
|
|
// Вычисляем сумму
|
|
QString newTotal;
|
|
if (ok1 || ok2 || ok3) {
|
|
// Если хотя бы одно значение можно преобразовать в число, вычисляем сумму
|
|
int total = (ok1 ? qtyPerItem : 0) + (ok2 ? qtyInSet : 0) + (ok3 ? qtyForReg : 0);
|
|
newTotal = QString::number(total);
|
|
} else {
|
|
// Если ни одно значение не является числом, просто склеиваем строки
|
|
newTotal = rowData.quantityPerItem.value + rowData.quantityInSet.value + rowData.quantityForReg.value;
|
|
}
|
|
|
|
// Обновляем значение только если оно изменилось
|
|
if (rowData.totalQuantity.value != newTotal) {
|
|
rowData.totalQuantity.value = newTotal;
|
|
rowData.totalQuantity.isEmpty = newTotal.isEmpty();
|
|
|
|
// Уведомляем об изменении ячейки totalQuantity (колонка 8)
|
|
QModelIndex totalIndex = index(row, 8);
|
|
emit dataChanged(totalIndex, totalIndex);
|
|
}
|
|
}
|
|
|
|
VedomostCellData VedomostTableModel::createCellData(const QString &value, bool isHeader,
|
|
bool isAuto, bool isEmpty) const
|
|
{
|
|
VedomostCellData cellData;
|
|
cellData.value = value;
|
|
cellData.isHeader = isHeader;
|
|
cellData.isAutoGenerated = isAuto;
|
|
cellData.isEmpty = isEmpty;
|
|
return cellData;
|
|
}
|