Skip to content
Merged
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@

В апреле 2017 года автор проекта [Валерий Агеев](https://bitbucket.org/awa15/) поделился с сообществом [исходными кодами проекта](https://bitbucket.org/awa15/tool_1cd-sources/src). Изначально проект был написан на Borland C++ Builder, здесь мы занимаемся переносом на "ванильный C++" с поддержкой компиляторов gcc, mingw и msvc и постепенным переходом на более свежие стандарты языка C++.

## Поддерживаемые версии

Программа работает с файлами `1CD` по версии формата, записанной в заголовке базы:

| Версия формата | Режим |
|---|---|
| 8.0.3, 8.0.5 | только чтение |
| 8.1.0, 8.2.0, 8.2.14, 8.3.8 | чтение и запись |

Формат `8.3.8` используют все платформы 1С:Предприятие 8.3.8 и новее (в том числе 8.3.x и 8.5.x) — их базы открываются как `8.3.8`. Версия формата в заголовке не обязательно совпадает с версией платформы, создавшей базу. Базы с другой версией формата не поддерживаются.

## Установка

Проект в процессе разработки.
Expand Down Expand Up @@ -98,5 +109,17 @@ sudo apt-get install ctool1cd

Найти потерянные объекты и сохранить.

`-sts, -SaveTablesSize <путь>`

Сохранить размеры таблиц базы в указанный файл.

`-ct, -ClearTable <таблица>`

Пометить удалёнными все записи указанной таблицы. Команда изменяет базу.

`-dr, -DeleteRecord <таблица> <номер>`

Пометить удалённой одну запись таблицы по её физическому номеру. Команда изменяет базу.

Если в пути содержатся пробелы, его необходимо заключать в кавычки. Пути следует указывать без завершающего слеша `/` и бэкслеша `\`
Для команд `-dc`, `-ddc`, `-drc` вместо пути можно указывать имя файла конфигурации (имя файла должно заканчиваться на `.cf`).
60 changes: 60 additions & 0 deletions src/ctool1cd/App.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include "ParseCommandLine.h"
#include "ErrorCode.h"
#include "Messenger.h"
#include "Table.h"
#include "TableRecord.h"

extern Registrator msreg_g;

Expand Down Expand Up @@ -288,6 +290,56 @@ void App::import_from_binary(const ParsedCommand &pc)

} // import_from_binary

void App::clear_table(const ParsedCommand &pc)
{
const string tname = LowerCase(pc.param1);
for (int j = 0; j < base1CD->get_numtables(); j++) {
Table *tbl = base1CD->get_table(j);
if (LowerCase(tbl->get_name()) != tname) {
continue;
}
tbl->begin_edit();
uint32_t total = tbl->get_phys_numrecords();
uint32_t deleted = 0;
for (uint32_t i = 1; i < total; i++) {
TableRecord *rec = tbl->get_record(i);
bool removed = rec->is_removed();
delete rec;
if (!removed) {
tbl->mark_record_removed(i);
deleted++;
}
}
base1CD->flush();
msreg_g.AddMessage("Очистка таблицы выполнена.", MessageState::Succesfull)
.with("Таблица", tbl->get_name())
.with("Удалено записей", (int)deleted)
.with("Физических записей всего", (int)total);
return;
}
msreg_g.AddError("Таблица для очистки не найдена.").with("Таблица", pc.param1);
}

void App::delete_one_record(const ParsedCommand &pc)
{
const string tname = LowerCase(pc.param1);
uint32_t idx = (uint32_t)strtoul(pc.param2.c_str(), nullptr, 10);
for (int j = 0; j < base1CD->get_numtables(); j++) {
Table *tbl = base1CD->get_table(j);
if (LowerCase(tbl->get_name()) != tname) {
continue;
}
tbl->begin_edit();
tbl->mark_record_removed(idx);
base1CD->flush();
msreg_g.AddMessage("Запись помечена удалённой.", MessageState::Succesfull)
.with("Таблица", tbl->get_name())
.with("Физический номер записи", (int)idx);
return;
}
msreg_g.AddError("Таблица не найдена.").with("Таблица", pc.param1);
}

void App::save_config(const boost::filesystem::path& param_path)
{
if(!is_infobase()) {
Expand Down Expand Up @@ -660,6 +712,14 @@ int App::Run()
save_tables_sizes(pc);
break;
}
case Command::clear_table: {
clear_table(pc);
break;
}
case Command::delete_one_record: {
delete_one_record(pc);
break;
}
}
}
catch (string &s) {
Expand Down
4 changes: 4 additions & 0 deletions src/ctool1cd/App.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ class App {

void save_tables_sizes(const ParsedCommand& pc);

void clear_table(const ParsedCommand& pc);

void delete_one_record(const ParsedCommand& pc);

inline bool is_infobase() const;

};
Expand Down
4 changes: 4 additions & 0 deletions src/ctool1cd/ParseCommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ CommandDefinition CommandParse::definitions[] =
{"savelostobjects", Command::find_and_save_lost_objects, 1, ""}, // 37
{"sts", Command::save_tables_size, 1, ""}, // 38
{"savetablessize", Command::save_tables_size, 1, ""}, // 39
{"ct", Command::clear_table, 1, ""}, // 40
{"cleartable", Command::clear_table, 1, ""}, // 41
{"dr", Command::delete_one_record, 2, ""}, // 42
{"deleterecord", Command::delete_one_record, 2, ""}, // 43
};


Expand Down
2 changes: 2 additions & 0 deletions src/ctool1cd/ParseCommandLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ enum class Command
import_from_binary, // загрузить таблицы из двоичных файлов, выгруженных экспортом
find_and_save_lost_objects, // найти и сохранить потерянные объекты
save_tables_size, // сохранить в файл размеры таблиц
clear_table, // удалить все записи указанной таблицы
delete_one_record, // пометить удалённой одну запись таблицы по физическому индексу
};

struct CommandDefinition
Expand Down
62 changes: 61 additions & 1 deletion src/gtool1cd/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
#include "configurations_window.h"
#include "about_dialog.h"
#include <QSortFilterProxyModel>
#include <QMenu>
#include <QMessageBox>

void MainWindow::AddDetailedMessage(
const std::string &description,
Expand Down Expand Up @@ -70,7 +72,13 @@ void MainWindow::show_table_context_menu(const QPoint &pos)
QAction action_import_blob(tr("Импорт BLOB"), this);
connect(&action_import_blob, SIGNAL(triggered()), this, SLOT(import_blob_file()));
contextMenu.addAction(&action_import_blob);


contextMenu.addSeparator();

QAction action_clear_table(tr("Очистить таблицу"), this);
connect(&action_clear_table, SIGNAL(triggered()), this, SLOT(clear_table_action()));
contextMenu.addAction(&action_clear_table);

contextMenu.exec(mapToGlobal(pos));
}

Expand Down Expand Up @@ -109,6 +117,58 @@ void MainWindow::import_blob_file()
}
}

void MainWindow::clear_table_action()
{
auto indexes = ui->tableListView->selectionModel()->selectedIndexes();
if (indexes.empty()) {
return;
}

auto *proxy = qobject_cast<QSortFilterProxyModel*>(ui->tableListView->model());
QModelIndex src = proxy ? proxy->mapToSource(indexes.first()) : indexes.first();
Table *t = db->get_table(src.row());

if (db->get_readonly()) {
QMessageBox::warning(this, tr("Очистка таблицы"),
tr("База открыта только для чтения — очистка невозможна."));
return;
}

if (QMessageBox::warning(this, tr("Очистка таблицы"),
tr("Пометить удалёнными ВСЕ записи таблицы «%1»?\nДействие изменяет базу и необратимо.")
.arg(QString::fromStdString(t->get_name())),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
return;
}

uint32_t deleted = 0;
try {
t->begin_edit();
uint32_t total = t->get_phys_numrecords();
for (uint32_t i = 1; i < total; i++) {
auto *rec = t->get_record(i);
bool removed = rec->is_removed();
delete rec;
if (!removed) {
t->mark_record_removed(i);
deleted++;
}
}
db->flush();
} catch (DetailedException &ex) {
QMessageBox::critical(this, tr("Очистка таблицы"), QString(ex.what()));
return;
}

auto it = table_windows.find(t);
if (it != table_windows.end()) {
static_cast<TableDataWindow*>(it.value())->reload();
}

QMessageBox::information(this, tr("Очистка таблицы"),
tr("Готово. Помечено удалёнными записей: %1.").arg(deleted));
}

MainWindow::~MainWindow()
{
delete ui;
Expand Down
4 changes: 3 additions & 1 deletion src/gtool1cd/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ private slots:
void show_table_context_menu(const QPoint &);

void export_blob_file();

void import_blob_file();

void clear_table_action();

private:
Ui::MainWindow *ui;
T_1CD *db;
Expand Down
13 changes: 13 additions & 0 deletions src/gtool1cd/models/table_data_model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,19 @@ const TableRecord *TableDataModel::getRecord(const QModelIndex &index) const

}

uint32_t TableDataModel::physicalRecordNo(const QModelIndex &index) const
{
return _index == nullptr
? index.row()
: _index->get_numrec(index.row());
}

void TableDataModel::notifyRowChanged(int row)
{
emit dataChanged(index(row, 0, QModelIndex()),
index(row, columnCount(QModelIndex()) - 1, QModelIndex()));
}

TStream *TableDataModel::getBlobStream(const QModelIndex &index) const
{
Field *f = table->get_field(index.column());
Expand Down
6 changes: 6 additions & 0 deletions src/gtool1cd/models/table_data_model.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ class TableDataModel : public QAbstractItemModel

const TableRecord *getRecord(const QModelIndex &index) const;

// Физический номер записи в таблице для строки модели
uint32_t physicalRecordNo(const QModelIndex &index) const;

// Сообщить представлению, что строка изменилась (например, помечена удалённой)
void notifyRowChanged(int row);

TStream *getBlobStream(const QModelIndex &index) const;

private:
Expand Down
57 changes: 57 additions & 0 deletions src/gtool1cd/table_data_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
#include <QItemSelection>
#include "models/skobka_tree_model.h"
#include <QFileDialog>
#include <QMenu>
#include <QMessageBox>
#include "models/v8catalog_tree_model.h"

QString index_presentation(Index *index)
Expand Down Expand Up @@ -81,6 +83,10 @@ TableDataWindow::TableDataWindow(QWidget *parent, Table *table)
emit ui->indexChooseBox->activated(1);
}

ui->dataView->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->dataView, SIGNAL(customContextMenuRequested(QPoint)),
this, SLOT(show_data_context_menu(QPoint)));

ui->dataView->setFocus();
}

Expand Down Expand Up @@ -199,3 +205,54 @@ void TableDataWindow::on_saveBlobButton_clicked()
auto model = static_cast<TableDataModel*>(ui->dataView->model());
model->dumpBlob(index, filename);
}

void TableDataWindow::reload()
{
on_indexChooseBox_activated(ui->indexChooseBox->currentIndex());
}

void TableDataWindow::show_data_context_menu(const QPoint &pos)
{
if (!ui->dataView->currentIndex().isValid()) {
return;
}
QMenu menu(this);
QAction action_delete(tr("Удалить запись"), this);
connect(&action_delete, SIGNAL(triggered()), this, SLOT(delete_record_action()));
menu.addAction(&action_delete);
menu.exec(ui->dataView->mapToGlobal(pos));
}

void TableDataWindow::delete_record_action()
{
auto index = ui->dataView->currentIndex();
if (!index.isValid()) {
return;
}
auto *model = static_cast<TableDataModel*>(ui->dataView->model());
uint32_t phys_numrecord = model->physicalRecordNo(index);

if (table->get_base()->get_readonly()) {
QMessageBox::warning(this, tr("Удаление записи"),
tr("База открыта только для чтения — удаление невозможно."));
return;
}

if (QMessageBox::warning(this, tr("Удаление записи"),
tr("Пометить запись №%1 таблицы «%2» удалённой?\nДействие изменяет базу.")
.arg(phys_numrecord).arg(QString::fromStdString(table->get_name())),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes) {
return;
}

try {
table->begin_edit();
table->mark_record_removed(phys_numrecord);
table->get_base()->flush();
} catch (DetailedException &ex) {
QMessageBox::critical(this, tr("Удаление записи"), QString(ex.what()));
return;
}

model->notifyRowChanged(index.row());
}
6 changes: 6 additions & 0 deletions src/gtool1cd/table_data_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,14 @@ class TableDataWindow : public QMainWindow
explicit TableDataWindow(QWidget *parent, Table *table);
~TableDataWindow();

void reload(); // перечитать данные таблицы в текущем представлении

private slots:

void show_data_context_menu(const QPoint &pos);

void delete_record_action();

void on_descriptionButton_clicked();

void on_fieldsButton_clicked();
Expand Down
Loading