From 86a11faf704bf0ad7a1f67ad3a97c402c62d3ff1 Mon Sep 17 00:00:00 2001 From: leca Date: Thu, 8 May 2025 21:02:56 +0300 Subject: [PATCH] huge wip --- CMakeLists.txt | 30 ++-- check/check.cpp | 4 + email_parser/emailparser.cpp | 124 ++++++++++++++++- email_parser/emailparser.h | 4 + main.cpp | 14 +- mainwindow.cpp | 88 +++++++++--- mainwindow.h | 10 ++ output/output_options.cpp | 5 - output/output_options.h | 8 +- outputdialog.cpp | 90 ++++++------ outputdialog.h | 4 +- scenes/mainwindow.ui | 221 ++++++++++++++++++------------ scenes/outputdialog.ui | 87 +++++++++--- scenes/settingsdialog.ui | 236 +++++++++++++++++--------------- settings/settings.cpp | 18 +-- settingsdialog.cpp | 1 + translations/en_US.ts | 190 ++++++++++++++++--------- translations/ru_RU.ts | 180 +++++++++++++++--------- utils/utils.cpp | 12 ++ utils/utils.h | 4 +- widgets/checklistviewwidget.cpp | 29 ++++ widgets/checklistviewwidget.h | 20 +++ widgets/tablewidgetmovable.cpp | 86 ++++++++++++ widgets/tablewidgetmovable.hpp | 17 +++ 24 files changed, 1022 insertions(+), 460 deletions(-) create mode 100644 widgets/checklistviewwidget.cpp create mode 100644 widgets/checklistviewwidget.h create mode 100644 widgets/tablewidgetmovable.cpp create mode 100644 widgets/tablewidgetmovable.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b20c4f..820f89d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,12 +62,12 @@ set(CMAKE_AUTOUIC_SEARCH_PATHS scenes) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(Qt5Core REQUIRED) +find_package(Qt6Core REQUIRED) if (BUILD_TRANSLATIONS) - find_package(Qt5 REQUIRED COMPONENTS LinguistTools) + find_package(Qt6 REQUIRED COMPONENTS LinguistTools) endif() -find_package(Qt5Gui REQUIRED) -find_package(Qt5Widgets REQUIRED) +find_package(Qt6Gui REQUIRED) +find_package(Qt6Widgets REQUIRED) set(TRANSLATION_SOURCES main.cpp @@ -95,6 +95,9 @@ set(PROJECT_SOURCES net/net.h net/net.cpp settings/settings.h settings/settings.cpp + widgets/tablewidgetmovable.hpp widgets/tablewidgetmovable.cpp + widgets/checklistviewwidget.h widgets/checklistviewwidget.cpp + ${TRANSLATION_SOURCES} ) @@ -111,7 +114,7 @@ if (BUILD_OFD_BINARYEYE_SCAN) endif() if (BUILD_EMAIL_MODE) - list(APPEND PROJECT_SOURCES email_parser/emailparser.h email_parser/emailparser.cpp) + # list(APPEND PROJECT_SOURCES email_parser/emailparser.h email_parser/emailparser.cpp) list(APPEND PROJECT_SOURCES utils/base64.h utils/base64.cpp) endif() @@ -122,9 +125,9 @@ if (BUILD_TRANSLATIONS) translations/ru_RU.ts ) - qt5_create_translation(QM_FILES "${TRANSLATION_SOURCES}" ${TS_FILES}) + qt_create_translation(QM_FILES "${TRANSLATION_SOURCES}" ${TS_FILES}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/translations.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc COPYONLY) - qt5_add_resources(TRANSLATIONQRC ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) + qt_add_resources(TRANSLATIONQRC ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) add_custom_target(translations ALL DEPENDS ${QM_FILES}) add_custom_target(resources ALL DEPENDS ${TRANSLATIONQRC}) add_dependencies(resources translations) @@ -133,13 +136,13 @@ endif() # Media QRC configure_file(${CMAKE_CURRENT_SOURCE_DIR}/media.qrc ${CMAKE_CURRENT_BINARY_DIR}/media.qrc COPYONLY) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -qt5_add_resources(MEDIAQRC ${CMAKE_CURRENT_BINARY_DIR}/media.qrc) +qt_add_resources(MEDIAQRC ${CMAKE_CURRENT_BINARY_DIR}/media.qrc) add_custom_target(mediaresource ALL DEPENDS ${MEDIAQRC}) #Scenes QRC configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scenes.qrc ${CMAKE_CURRENT_BINARY_DIR}/scenes.qrc COPYONLY) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/scenes DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -qt5_add_resources(SCENESQRC ${CMAKE_CURRENT_BINARY_DIR}/scenes.qrc) +qt_add_resources(SCENESQRC ${CMAKE_CURRENT_BINARY_DIR}/scenes.qrc) add_custom_target(scenessource ALL DEPENDS ${SCENESQRC}) set(SOURCES "") @@ -163,7 +166,9 @@ else() ) endif() -target_link_libraries(checks-parser PRIVATE Qt5::Widgets) +target_link_libraries(checks-parser PRIVATE Qt6::Widgets) + +target_include_directories(checks-parser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/widgets) if (BUILD_OFD_LOCAL_QR_SCAN) target_include_directories(checks-parser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/image_redactor) @@ -219,6 +224,11 @@ if (BUILD_OFD_LOCAL_QR_SCAN OR BUILD_OFD_BINARYEYE_SCAN) include_directories( ${OpenCV_INCLUDE_DIRS} ) endif() +# if (BUILD_EMAIL_MODE) +# find_package(vmime REQUIRED) +# target_link_libraries(checks-parser PRIVATE vmime) +# endif() + if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9) target_link_libraries(checks-parser PRIVATE -lstdc++fs) endif() diff --git a/check/check.cpp b/check/check.cpp index 587d0eb..a4c41a0 100644 --- a/check/check.cpp +++ b/check/check.cpp @@ -24,3 +24,7 @@ double Check::calculae_total_price() { std::vector& Check::get_goods() { return goods; } + +std::string Check::get_date() { + return date; +} diff --git a/email_parser/emailparser.cpp b/email_parser/emailparser.cpp index 020b612..b8e7941 100644 --- a/email_parser/emailparser.cpp +++ b/email_parser/emailparser.cpp @@ -1,9 +1,12 @@ +#include "utils/utils.h" #include -#include +#include #include #include +#include #include +#include #include #if __GNUC__ < 8 && __clang_major__ < 17 # include @@ -14,7 +17,7 @@ #endif std::string EmailParser::get_payload_in_email(std::string &email_content) { - boost::regex content_type_and_transfer_encoding_regex("Content-Type") + boost::regex content_type_and_transfer_encoding_regex("Content-Type"); // boost::regex body_start_regex("\r\n\r\n"); //boost::regex_constants::egrep // boost::smatch smatch; // if (boost::regex_search(email_content, smatch, body_start_regex)) { @@ -23,6 +26,40 @@ std::string EmailParser::get_payload_in_email(std::string &email_content) { // return ""; } +std::multimap EmailParser::parse_email_content_types(std::string path) { + std::ifstream input_file(path, std::ios::in); + + std::string line = ""; + std::multimap mail_options; + std::string latest_key; + while(std::getline(input_file, line)) { + // ; + + char first_char = line.substr(0, 1)[0]; + // if (line == "\0") { + // break; + // } + + std::vector split_by_colon = split(line, ":"); + std::string key = split_by_colon[0]; + std::string value = ""; + + for (int i = 1; i < split_by_colon.size(); i ++) value += split_by_colon[i]; + if (key != "Content-Type" ) { + continue; + } + + if (first_char == '\t') { + mail_options.emplace(std::make_pair(latest_key, line)); + } else { + // std::cout << "key: " << key << "\nvalue: " << value << std::endl; + mail_options.emplace(std::make_pair(key, value)); + latest_key = key; + } + } + return mail_options; +} + // std::vector EmailParser::find_base64_blocks_in_email(std::string &email_content) { // std::string glued_together; // for (auto c : email_content) { @@ -38,6 +75,18 @@ EmailParser::EmailParser() { } +std::vector search_content_types_in_email_content(std::string& email_content) { + std::vector content_type_positions = {}; + boost::regex image_content_type_regex("Content-Type: image/.*"); + for (boost::sregex_iterator it{email_content.begin(), email_content.end(), image_content_type_regex}, end{}; + it != end; it++) { + content_type_positions.push_back(it->position()); + } + return content_type_positions; +} + +// void find_and_decode_email_content() + Check EmailParser::parse(std::string &email_content) { //1. Search "Content-Type: image/.*" in the .eml file. // 1.1 If found 0, go to [2] @@ -46,7 +95,15 @@ Check EmailParser::parse(std::string &email_content) { //2. Try decoding content of the e-mail //3. Search "t=\d{8}T\d{4,6}&s=\d{1,6}\.\d{1,2}&fn=\d{10,16}&i=\d{6}&fp=\d{10}&n=\d". Note that in some emails = and & signs could be replaced with its code in HTTP requests: %3D, %26 // 3.1 If not found, notify the user that we could not parse the .eml file + std::vector content_type_positions = search_content_types_in_email_content(email_content); + if (content_type_positions.size() < 0) { + + } else if (content_type_positions.size() == 1) { + + } else { + + } // std::string payload = get_payload_in_email(email_content); // Check c; @@ -62,9 +119,62 @@ Check EmailParser::parse(std::string &email_content) { } Check EmailParser::parse_file(std::string path) { - std::ifstream ifile(path, std::ios::in | std::ios::binary); - const unsigned int size = std::filesystem::file_size(path); - std::string content(size, '\0'); - ifile.read(content.data(), size); - return parse(content); + + // std::vector contents = read_file(path); + // unsigned int body_start = -1; + // for (unsigned int i = 0; i < contents.size(); i ++) { + // std::string &line = contents[i]; + // if (line == "\r") { + // body_start = i; + // break; + // } + // } + // if (body_start == (unsigned int) -1) throw "Not an E-Mail"; + + // for (unsigned int i = 0; i < contents.size(); i ++) { + // std::string &line = contents[i]; + // if (line[0] == '\t') { + // contents[i - 1] += " " + contents[i]; + // contents.erase(remove(contents.begin(), contents.end(), line), contents.end()); + // i -= 2; + // } + // } + + // for (auto &line : contents) { + // std::cout << line << std::endl; + // } + + // std::cout << contents[body_start + 1] << std::endl; + // unsigned int body_start = contents.find("\r\n\r\n"); + // if (body_start == (unsigned int)-1) + // throw "Not a E-Mail file"; + // std::cout << contents.erase(0, body_start + 4); + // std::cout << contents << std::endl; + + // std::vector> message_parts_positions; + + // while (contents.find("--") > 0) { + + // } + + return Check(); + + std::multimap content_types = parse_email_content_types(path); + bool found_qr_image = false; + for (auto &content_type : content_types) { + boost::regex image_content_type_regex("image\\/(png|gif|jpg|jpeg)"); + boost::cmatch cmatch; + if (boost::regex_match(content_type.second.c_str(), cmatch, image_content_type_regex)) { + std::cout << cmatch << std::endl; + + } + std::cout << content_type.first << ": " << content_type.second << std::endl; + } + + // std::ifstream ifile(path, std::ios::in | std::ios::binary); + // const unsigned int size = std::filesystem::file_size(path); + // std::string content(size, '\0'); + // ifile.read(content.data(), size); + // return parse(content); + return Check(); } diff --git a/email_parser/emailparser.h b/email_parser/emailparser.h index b52f383..c9de132 100644 --- a/email_parser/emailparser.h +++ b/email_parser/emailparser.h @@ -2,14 +2,18 @@ #define CHECKS_PARSER_EMAIL_PARSER #include +#include class EmailParser { std::string get_payload_in_email(std::string &email_content); + std::multimap parse_email_content_types(std::string path); + // std::vector find_base64_blocks_in_email(std::string &email_content); public: EmailParser(); Check parse(std::string &email_content); Check parse_file(std::string path); + }; #endif // CHECKS_PARSER_EMAIL_PARSER diff --git a/main.cpp b/main.cpp index 59f8d0a..35c683f 100644 --- a/main.cpp +++ b/main.cpp @@ -24,16 +24,13 @@ #endif #include #ifdef BUILD_EMAIL_MODE - //placeholder + // #include #endif #include #include int main(int argc, char *argv[]) { -// EmailParser ep; -// ep.parse_file("/home/leca/example_email_receipts/rzd.eml"); -// return 0; curl_global_init(CURL_GLOBAL_ALL); std::string program_data_path = get_path_relative_to_home(".local/share/checks_parser"); @@ -72,14 +69,5 @@ int main(int argc, char *argv[]) { w.show(); - // TODO: move to the window - //Settings button setup - // QPushButton *settingsButton = w.findChild("settings_button"); - // QObject::connect(settingsButton, &QPushButton::clicked, [&]() { - // SettingsDialog d; - // d.show(); - // d.exec(); - // }); - return app.exec(); } diff --git a/mainwindow.cpp b/mainwindow.cpp index 9974eff..9395261 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef BUILD_OFD_BINARYEYE_SCAN # include @@ -31,6 +32,13 @@ MainWindow::MainWindow(QWidget *parent) ui->setupUi(this); ui->stop_server_button->hide(); + QVBoxLayout *layout = new QVBoxLayout; + ui->scrollAreaWidgetContents->setLayout(layout); + QLabel a = QLabel("123"); + layout->addWidget(&a); + // ui->checks_to_parse_label->hide(); + // ui->checks_scroll_area->hide(); + #ifdef BUILD_OFD_BINARYEYE_SCAN QObject::connect(this, &MainWindow::httpErrorOccured, this, &MainWindow::notifyHttpServerFailure); connect(this, SIGNAL(httpNewMessage(QString)), this, SLOT(httpNewMessageHandler(QString))); @@ -170,19 +178,65 @@ void MainWindow::onDataDecode(std::map data) { ui->total_spin_box->setValue(std::stod(total)); } +#ifdef BUILD_EMAIL_MODE + +void MainWindow::on_parse_email_button_clicked() { + QMessageBox infoDialog; + infoDialog.setText(tr("This feature is under development. Wait it to appear in next updates.")); + infoDialog.setIcon(QMessageBox::Warning); + infoDialog.setWindowTitle(tr("Under development")); + infoDialog.exec(); + return; +} + + +#endif // ifdef BUILD_EMAIL_MODE + void MainWindow::on_parse_button_clicked() { + // if (checks.empty()) { + // QMessageBox infoDialog; + // infoDialog.setText(tr("Please, add check(s) to parse")); + // infoDialog.setIcon(QMessageBox::Warning); + // infoDialog.setWindowTitle(tr("No checks to parse")); + // infoDialog.exec(); + // return; + // } + + OutputDialog d = OutputDialog(this, &checks); + d.exec(); +} + +void MainWindow::on_add_new_check_button_clicked() { + Check *new_check = parse_new_check(); + if (new_check == nullptr) { + return; + } + + checks.push_back(*new_check); + + CheckListViewWidget check_list_widget(this, *new_check); + + auto a = QLabel("123"); + ui->scrollAreaWidgetContents->layout()->addWidget(&a); + + delete new_check; + + if (checks.size() == 1) { + ui->checks_scroll_area->show(); + ui->checks_to_parse_label->show(); + } +} + +Check *MainWindow::parse_new_check() { Net net; - net.get_captcha_from_ofdru(); - std::string solved_captcha = ""; - bool success = true; - bool is_captcha_solved = true; - Check check; - do { + for (unsigned short i = 0; i < 5; i ++) { + // Will be repaced with neural network to solve captchas as soon as I train it. + net.get_captcha_from_ofdru(); SolveCaptchaDialog dialog = SolveCaptchaDialog(this, &solved_captcha); dialog.exec(); - is_captcha_solved = true; + Check* check = new Check(); try { std::string check_content = net.fetch_check_data_from_ofdru( @@ -195,44 +249,36 @@ void MainWindow::on_parse_button_clicked() { ui->total_spin_box->text().toDouble() * 100, solved_captcha); - check = parseOfdRuAnswer(check_content); + (*check) = parseOfdRuAnswer(check_content); + return check; } catch(OfdRequestException e) { - success = false; if (!strcmp(e.what(), "Incorrect captcha")) { - is_captcha_solved = false; QMessageBox infoDialog; infoDialog.setText(tr("Captcha was not solved correctly!")); infoDialog.setIcon(QMessageBox::Critical); infoDialog.setWindowTitle(tr("Captcha is incorrect")); infoDialog.exec(); - break; + continue; } else if (!strcmp(e.what(), "Internal server error")) { QMessageBox infoDialog; infoDialog.setText(tr("Internal server error. Please, try again later.")); infoDialog.setIcon(QMessageBox::Critical); infoDialog.setWindowTitle(tr("Internal server error")); infoDialog.exec(); - return; + return nullptr; } else if (!strcmp(e.what(), "Does not exist")) { QMessageBox infoDialog; infoDialog.setText(tr("Check not found. Please, ensure correctness of entered data.")); infoDialog.setIcon(QMessageBox::Critical); infoDialog.setWindowTitle(tr("Check was not found")); infoDialog.exec(); - return; + return nullptr; } } - } while (!is_captcha_solved); - - if (success) { - OutputDialog *d = new OutputDialog(this, check); - d->exec(); - - delete d; } + return nullptr; } - MainWindow::~MainWindow() { delete ui; } diff --git a/mainwindow.h b/mainwindow.h index ffe5b34..055f1d4 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -7,6 +7,8 @@ #include +#include + namespace Ui { class MainWindow; } @@ -18,6 +20,8 @@ class MainWindow : public QWidget public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); + + Check *parse_new_check(); #ifdef BUILD_OFD_BINARYEYE_SCAN void startHttpServer(); #endif @@ -42,8 +46,14 @@ private slots: void httpNewMessageHandler(QString message); #endif +#ifdef BUILD_EMAIL_MODE + void on_parse_email_button_clicked(); +#endif + void on_add_new_check_button_clicked(); + private: Ui::MainWindow *ui; + std::vector checks; #ifdef BUILD_OFD_BINARYEYE_SCAN std::thread *httpServerThread; HttpServer *server = NULL; diff --git a/output/output_options.cpp b/output/output_options.cpp index b362305..5a98394 100644 --- a/output/output_options.cpp +++ b/output/output_options.cpp @@ -50,10 +50,5 @@ bool OutputOptions::get_print_header() { return this->print_header; } void OutputOptions::set_print_total(bool value) { this->print_total = value; } bool OutputOptions::get_print_total() { return this->print_total; } -OutputFormat OutputOptions::get_output_format() { return this->format; } -void OutputOptions::set_output_format(OutputFormat format) { - this->format = format; -} - void OutputOptions::set_path(std::string path) { this->path = path; } std::string &OutputOptions::get_path() { return this->path; } diff --git a/output/output_options.h b/output/output_options.h index eeaa86b..dd169b0 100644 --- a/output/output_options.h +++ b/output/output_options.h @@ -3,7 +3,7 @@ #include #include -#if __GNUC__ < 8 +#if __GNUC__ <= 8 && __clang_major__ < 17 # include #else # include @@ -15,6 +15,7 @@ #include "../settings/settings.h" enum class ColumnType { + date, goods_name, goods_price_per_unit, goods_quantity, @@ -28,14 +29,12 @@ struct Column { // Example: unsigned int position; // "0" <-- 0 = "A", 1 = "B", etc.. column letter in // table processor (i.e. excel or libreoffice) } typedef Column; -enum class OutputFormat { csv, ods, xlsx, plaintext } typedef OutputFormat; class OutputOptions { std::vector order; bool print_header; bool print_total; - OutputFormat format; std::string path; public: @@ -56,9 +55,6 @@ public: void set_print_total(bool); bool get_print_total(); - void set_output_format(OutputFormat); - OutputFormat get_output_format(); - void set_path(std::string); std::string &get_path(); }; diff --git a/outputdialog.cpp b/outputdialog.cpp index 23bc4a9..b32d9c7 100644 --- a/outputdialog.cpp +++ b/outputdialog.cpp @@ -8,28 +8,30 @@ #include "settings/settings.h" #include "utils/utils.h" -OutputDialog::OutputDialog(QWidget *parent, Check &check) - : QDialog(parent), ui(new Ui::OutputDialog), check(check), +OutputDialog::OutputDialog(QWidget *parent, std::vector *checks) + : QDialog(parent), ui(new Ui::OutputDialog), checks(checks), options(OutputOptions()) { - Settings settings(get_path_relative_to_home(".local/share/checks_parser/settings.json")); ui->setupUi(this); - ui->tableWidget->item(0, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_name"]["name"])); - ui->tableWidget->item(0, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_name"]["position"])); + ui->tableWidget->item(0, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["date"]["name"])); + ui->tableWidget->item(0, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["date"]["position"])); - ui->tableWidget->item(1, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_price_per_unit"]["name"])); - ui->tableWidget->item(1, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_price_per_unit"]["position"])); + ui->tableWidget->item(1, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_name"]["name"])); + ui->tableWidget->item(1, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_name"]["position"])); - ui->tableWidget->item(2, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_quantity"]["name"])); - ui->tableWidget->item(2, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_quantity"]["position"])); + ui->tableWidget->item(2, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_price_per_unit"]["name"])); + ui->tableWidget->item(2, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_price_per_unit"]["position"])); - ui->tableWidget->item(3, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_net_weight"]["name"])); - ui->tableWidget->item(3, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_net_weight"]["position"])); + ui->tableWidget->item(3, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_quantity"]["name"])); + ui->tableWidget->item(3, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_quantity"]["position"])); - ui->tableWidget->item(4, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_total"]["name"])); - ui->tableWidget->item(4, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_total"]["position"])); + ui->tableWidget->item(4, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_net_weight"]["name"])); + ui->tableWidget->item(4, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_net_weight"]["position"])); + + ui->tableWidget->item(5, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_total"]["name"])); + ui->tableWidget->item(5, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_total"]["position"])); ui->printHeaderCheckBox->setChecked(settings.get_all_settings()["print_header"]); @@ -61,6 +63,7 @@ void OutputDialog::on_buttonBox_accepted() { this->options.get_columns().end(), compare_position); if (options.get_print_header()) { + // output_file << "date," for (auto &column : this->options.get_columns()) { output_file << column.name << (column.position == this->options.get_columns().size() @@ -69,39 +72,44 @@ void OutputDialog::on_buttonBox_accepted() { } output_file << std::endl; } + for (Check &check : *checks) { + int i = 0; + // auto &goods : check.get_goods() + for (auto it = check.get_goods().begin(); it != check.get_goods().end(); i++, it++) { + for (auto &column : this->options.get_columns()) { + switch (column.type) { + case ColumnType::date: + if (i == 0) output_file << "date goes here"; + break; + case ColumnType::goods_name: + output_file << it->get_name(); + break; + case ColumnType::goods_price_per_unit: + output_file << std::fixed << std::setprecision(2) << it->get_price_per_unit(); + break; + case ColumnType::goods_quantity: + output_file << std::fixed << std::setprecision(2) << it->get_quantity(); + break; + case ColumnType::goods_net_weight: + output_file << it->get_net_weight(); + break; + case ColumnType::goods_total: + output_file << std::fixed << std::setprecision(2) << it->calculate_total_price(); + break; + } - for (auto goods : this->check.get_goods()) { - for (auto &column : this->options.get_columns()) { - switch (column.type) { - case ColumnType::goods_name: - output_file << goods.get_name(); - break; - case ColumnType::goods_price_per_unit: - output_file << std::fixed << std::setprecision(2) << goods.get_price_per_unit(); - break; - case ColumnType::goods_quantity: - output_file << std::fixed << std::setprecision(2) << goods.get_quantity(); - break; - case ColumnType::goods_net_weight: - output_file << goods.get_net_weight(); - break; - case ColumnType::goods_total: - output_file << std::fixed << std::setprecision(2) << goods.calculate_total_price(); - break; - } - - if (column.position != this->options.get_columns().size()) { - output_file << ","; - } else { - output_file << "\n"; + if (column.position != this->options.get_columns().size()) { + output_file << ","; + } else { + output_file << "\n"; + } } } - } - if (this->options.get_print_total()) { - output_file << "Total: " << std::fixed << std::setprecision(2) << check.calculae_total_price() << std::endl; + if (this->options.get_print_total()) { + output_file << "Total: " << std::fixed << std::setprecision(2) << check.calculae_total_price() << std::endl; + } } - output_file.close(); } diff --git a/outputdialog.h b/outputdialog.h index 2f9b3a7..757b1d4 100644 --- a/outputdialog.h +++ b/outputdialog.h @@ -14,10 +14,10 @@ class OutputDialog : public QDialog { Q_OBJECT OutputOptions options; - Check ✓ + std::vector *checks; public: - explicit OutputDialog(QWidget *parent = nullptr, Check & = *(new Check())); + explicit OutputDialog(QWidget *parent = nullptr, std::vector *checks = nullptr); ~OutputDialog(); private slots: diff --git a/scenes/mainwindow.ui b/scenes/mainwindow.ui index 712750c..abb62c7 100644 --- a/scenes/mainwindow.ui +++ b/scenes/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 1039 - 426 + 1269 + 490 @@ -39,29 +39,10 @@ - - - - - 0 - 0 - - + + - Parse - - - - - - - - 0 - 0 - - - - FD (Fiscal Document) + Add new check @@ -78,15 +59,24 @@ - - + + + + + 0 + 0 + + - Operation type + FD (Fiscal Document) - - + + + + + 0 @@ -94,27 +84,7 @@ - Use your phone as a QR code scanner - - - - - - - - 0 - 0 - - - - - - - - - - - Date and time of purchase + Choose image on your PC @@ -131,16 +101,23 @@ - - + + + + FI (Fiscal Identifier) + + + + + - + 0 0 - Choose image on your PC + Settings @@ -151,23 +128,36 @@ - - - - FI (Fiscal Identifier) + + + + + 0 + 0 + - - + + + + + - + 0 0 - Parse an E-Mail + Clear + + + + + + + Date and time of purchase @@ -184,24 +174,74 @@ - - - - - 0 - 0 - + + + + Operation type - - + + + + 4294967296.000000000000000 + + - - + + + + + 0 + 0 + + + + Use your phone as a QR code scanner + + + + + + + + 0 + 0 + + + + Parse + + + + + + + + 0 + 0 + + + + Parse an E-Mail + + + + + + + + 0 + 0 + + + + + + @@ -227,23 +267,36 @@ - - - - 4294967296.000000000000000 - - - - - + + - + 0 0 + + true + + + + + 0 + 0 + 273 + 404 + + + + + + + - Settings + Checks to parse + + + Qt::AlignmentFlag::AlignCenter diff --git a/scenes/outputdialog.ui b/scenes/outputdialog.ui index f458fdf..277e601 100644 --- a/scenes/outputdialog.ui +++ b/scenes/outputdialog.ui @@ -14,10 +14,13 @@ Dialog - - - - Path to export: + + + + Qt::Orientation::Horizontal + + + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok @@ -42,6 +45,13 @@ + + + + Path to export: + + + @@ -50,6 +60,47 @@ 0 + + true + + + QAbstractScrollArea::SizeAdjustPolicy::AdjustToContentsOnFirstShow + + + true + + + false + + + QAbstractItemView::DragDropMode::InternalMove + + + Qt::DropAction::MoveAction + + + true + + + QAbstractItemView::SelectionMode::SingleSelection + + + QAbstractItemView::SelectionBehavior::SelectRows + + + Qt::PenStyle::SolidLine + + + false + + + true + + + + Date + + Goods name @@ -92,7 +143,7 @@ - Name + Date @@ -102,7 +153,7 @@ - Price + Name @@ -112,7 +163,7 @@ - Quantity + Price @@ -122,7 +173,7 @@ - Net weight + Quantity @@ -131,22 +182,22 @@ + + Net weight + + + + + 6 + + + Total price - - - - Qt::Orientation::Horizontal - - - QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok - - - diff --git a/scenes/settingsdialog.ui b/scenes/settingsdialog.ui index 9550339..f53ee43 100644 --- a/scenes/settingsdialog.ui +++ b/scenes/settingsdialog.ui @@ -24,108 +24,14 @@ 0 0 - 673 - 554 + 659 + 634 - - - - Goods quantity alias - - - - - - - - - - - - - Language - - - - - - - Goods net weight alias - - - - - - - Goods price per unit alias - - - - - - - - - - Print total - - - - - - - Goods name alias - - - - - - - - - - - - - - - - Goods total alias - - - - - - - Print header - - - - - - - - - - Goods total position - - - - - - - - - - Goods net weight position - - - - + @@ -139,46 +45,160 @@ - + + + + Goods quantity alias + + + + + + + + + + + + + + + + + + + Goods price per unit alias + + + + + + + + + + Print header + + + + Goods quantity position - - + + - + + + + Goods net weight position + + + + + + + + + + Goods name position - - + + - + Print total - + + + + Date name position + + + + Goods price per unit position - + - - + + + + Goods total position + + + + + + + + + + Goods name alias + + + + + + + + + + Goods total alias + + + + + + + + + + + + + + + + + Language + + + + + + + Goods net weight alias + + + + + + + Date name alias + + + + + diff --git a/settings/settings.cpp b/settings/settings.cpp index de10bbf..15ce602 100644 --- a/settings/settings.cpp +++ b/settings/settings.cpp @@ -2,7 +2,6 @@ #include #include #include -#include "../utils/utils.h" #if __GNUC__ <= 8 && __clang_major__ < 17 # include @@ -23,24 +22,28 @@ Settings::Settings(std::string path) { "print_header": true, "print_total": true, "output_order": { - "goods_name": { + "date" : { "position":1, + "name": "Date" + }, + "goods_name": { + "position":2, "name":"Goods name" }, "goods_price_per_unit": { - "position":2, + "position":3, "name":"Goods price per unit" }, "goods_quantity": { - "position":3, + "position":4, "name":"Goods quantity" }, "goods_net_weight": { - "position":4, + "position":5, "name":"Goods net weight" }, "goods_total": { - "position":5, + "position":6, "name":"Goods total" } } @@ -56,9 +59,6 @@ Settings::Settings(std::string path) { nlohmann::json settings = nlohmann::json::parse(input); this->settings = settings; } - - create_directories(get_path_relative_to_home(this->settings["ofds_modules_dir"])); - create_directories(get_path_relative_to_home(this->settings["stores_modules_dir"])); } void Settings::write_setting(std::string setting, std::string value) { diff --git a/settingsdialog.cpp b/settingsdialog.cpp index dff070d..b147112 100644 --- a/settingsdialog.cpp +++ b/settingsdialog.cpp @@ -138,3 +138,4 @@ void SettingsDialog::on_languageComboBox_currentTextChanged(const QString &chang SettingsDialog::~SettingsDialog() { delete ui; } + diff --git a/translations/en_US.ts b/translations/en_US.ts index ed4649e..c2c5e77 100644 --- a/translations/en_US.ts +++ b/translations/en_US.ts @@ -70,7 +70,7 @@ Store type - + Parse Parse @@ -113,13 +113,13 @@ 0000000000000000 - + FN (Fiscal Number) FN = Фискальный Номер FN (Fiscal Number) - + FD (Fiscal Document) FD = Фискальный Документ FD (Fiscal Document) @@ -133,78 +133,93 @@ Back - + Stop server Stop server - + Choose image on your PC Choose image on your PC - + or or - + Use your phone as a QR code scanner Use your phone as a QR code scanner - + FI (Fiscal Identifier) FI = Фискальный Признак FI (Fiscal Identifier) - + + Add new check + Add new check + + + + Clear + Clear + + + Date and time of purchase Date and time of purchase - + Operation type Operation type - + Parse an E-Mail Parse an E-Mail - + Funds income Приход средств Funds income - + Funds return Возврат средств Funds return - + Funds spend Расход средств Funds spend - + Spends return Возврат расхода Spends return - + + Checks to parse + Checks to parse + + + Settings Settings - + Total Total @@ -213,57 +228,75 @@ checks parser - + QR code for binaryeye to connect QR code for binaryeye to connect - + I've scanned I've scanned - + Could not start http server. 10 times in a row random port was occupied. Either you should run for a lottery ticket, or the problem is in the program. If the lottery ticket wasn't lucky, please, contact the developer. Could not start http server. 10 times in a row random port was occupied. Either you should run for a lottery ticket, or the problem is in the program. If the lottery ticket wasn't lucky, please, contact the developer. - + Could not start http server. Could not start http server. - + Selected image: Selected image: - + + This feature is under development. Wait it to appear in next updates. + This feature is under development. Wait for it to appear in next updates. + + + + Under development + Under development + + + Please, add check(s) to parse + Please, add check(s) to parse + + + No checks to parse + No checks to parse + + + Captcha was not solved correctly! Captcha was not solved correctly! - + Captcha is incorrect Captcha is incorrect - + Internal server error. Please, try again later. Internal server error. Please, try again later. - + Internal server error Internal server error - + Check not found. Please, ensure correctness of entered data. Check not found. Please, ensure correctness of entered data. - + Check was not found Check was not found @@ -276,12 +309,12 @@ Error in parsing - + Please, select a picture where QR code that contains info about check is present Please, select a picture where QR code that contains info about check is present - + Picture was not selected Picture was not selected @@ -489,107 +522,118 @@ Dialog - + Path to export: Path to export: - + Choose Choose - + Print header Print header - + + + Date + Date + + + Goods name Goods name - + Goods price Goods price - + Goods quantity - Goods quality + Goods quantity - + Goods net weight Goods net weight - + Goods total Goods total - + position position - + name name - + 1 1 - + Name Name - + 2 2 - + Price Price - + 3 3 - + Quantity Quantity - + 4 4 - + Net weight Net Weight - + 5 5 - + + 6 + 6 + + + Total price Total price - + Print total Print total @@ -597,7 +641,7 @@ QObject - + Using locale: Using locale: @@ -641,31 +685,41 @@ Dialog - + Goods name position Goods name position - + Goods price per unit alias Goods price per unit alias - + + Date name position + Date name position + + + Language Language + + + Date name alias + Date name alias + TextLabel Language - + en_US en_US - + ru_RU ru_RU @@ -674,12 +728,12 @@ Choose - + Print header Print header - + Goods net weight alias Goods net weight alias @@ -688,17 +742,17 @@ Stores modules url - + Goods total alias Goods total alias - + Goods name alias Goods name alias - + Goods quantity alias Goods quantity alias @@ -711,12 +765,12 @@ OFD modules directory - + Goods price per unit position Goods price per unit position - + Goods net weight position Goods net weight position @@ -725,17 +779,17 @@ OFD modules url - + Goods total position Goods total position - + Goods quantity position Goods quantity position - + Print total Print total diff --git a/translations/ru_RU.ts b/translations/ru_RU.ts index 21c8dab..738f8df 100644 --- a/translations/ru_RU.ts +++ b/translations/ru_RU.ts @@ -70,7 +70,7 @@ Магазин - + Parse Парсить @@ -113,13 +113,13 @@ 0000000000000000 - + FN (Fiscal Number) Фискальный Норма ФН - + FD (Fiscal Document) Фискальный Документ ФД @@ -133,74 +133,89 @@ Назад - + Stop server Остановить сервер - + Choose image on your PC Выбрать изображение на компьютере - + or или - + Use your phone as a QR code scanner Использовать телефон как сканнер QR - + FI (Fiscal Identifier) Фискальный Признак ФП - + + Add new check + + + + + Clear + + + + Date and time of purchase Дата и время покупки - + Operation type Тип операции - + Parse an E-Mail Парсить E-Mail - + Funds income Приход средств - + Funds return Возврат средств - + Funds spend Расход средств - + Spends return Возврат расхода - + + Checks to parse + + + + Settings Настройки - + Total Итого @@ -209,57 +224,67 @@ Парсер чеков - + QR code for binaryeye to connect QR код для подключения BinaryEye - + I've scanned Просканировал - + Could not start http server. 10 times in a row random port was occupied. Either you should run for a lottery ticket, or the problem is in the program. If the lottery ticket wasn't lucky, please, contact the developer. Не смог поднять HTTP сервер. 10 раз подряд случайно выбранный порт был занят. Либо Вам следует бежать за лоттерейным билетом, или в программе баг. Если лотерейный билет не был выигрышным, пожалуйста, сообщите разработчику. - + Could not start http server. Не получилось запустить HTTP сервер. - + Selected image: Выбранное изображение: - + + This feature is under development. Wait it to appear in next updates. + + + + + Under development + + + + Captcha was not solved correctly! Капча была решена неверно! - + Captcha is incorrect Капча введена неверно - + Internal server error. Please, try again later. Внутренняя ошибка сервера. Пожалуйста, попробуйте снова позже. - + Internal server error Внутренняя ошибка сервера - + Check not found. Please, ensure correctness of entered data. Чек не найден. Пожалуйста, убедитесь в правильности введённых данных. - + Check was not found Чек не найден @@ -272,12 +297,12 @@ Ошибка в парсинге - + Please, select a picture where QR code that contains info about check is present Пожалуйста, выберете изображение, содержащее QR код с информацией о чеке - + Picture was not selected Изображение не было выбрано @@ -485,107 +510,118 @@ Диалог - + Path to export: Путь для экспорта: - + Choose Выбрать - + Print header Печатать заголовок - + + + Date + + + + Goods name Имя товара - + Goods price Цена товара - + Goods quantity Количество товара - + Goods net weight Масса нетто товара - + Goods total Всего за товар - + position позиция - + name алиас - + 1 1 - + Name Имя - + 2 2 - + Price Цена - + 3 3 - + Quantity Количество - + 4 4 - + Net weight Масса нетто - + 5 5 - + + 6 + 6 + + + Total price Всего - + Print total Печатать Итого @@ -593,7 +629,7 @@ QObject - + Using locale: Использую локаль: @@ -637,31 +673,41 @@ Диалог - + Goods name position Позиция имени товара - + Goods price per unit alias Алиас цены товара - + + Date name position + + + + Language Язык + + + Date name alias + + TextLabel Язык - + en_US en_US - + ru_RU ru_RU @@ -670,12 +716,12 @@ Выбрать - + Print header Печатать заголовок - + Goods net weight alias Алиас массы нетто товара @@ -684,17 +730,17 @@ URL модулей магазина - + Goods total alias Алиас всего за продукт - + Goods name alias Алиас имени товара - + Goods quantity alias Алиас количества товара @@ -707,12 +753,12 @@ Директория модулей ОФД - + Goods price per unit position Позиция центы товара - + Goods net weight position Позиция массы нетто товара @@ -721,17 +767,17 @@ URL модулей ОФД - + Goods total position Позиция всего за товар - + Goods quantity position Позиция количества товара - + Print total Печатать Итого diff --git a/utils/utils.cpp b/utils/utils.cpp index 496a280..e64343f 100644 --- a/utils/utils.cpp +++ b/utils/utils.cpp @@ -309,3 +309,15 @@ void generate_qr_code(std::string data) { } #endif // ifdef BUILD_OFD_BINARYEYE_SCAN +#ifdef BUILD_EMAIL_MODE +std::vector read_file(std::string path) { + std::ifstream stream(path); + std::vector lines; + std::string buffer; + while(getline(stream, buffer)) { + lines.push_back(buffer); + } + stream.close(); + return lines; +} +#endif // ifdef BUILD_EMAIL_MODE diff --git a/utils/utils.h b/utils/utils.h index acc6acd..6638e20 100644 --- a/utils/utils.h +++ b/utils/utils.h @@ -31,5 +31,7 @@ std::string get_local_ip_address(); #endif void fetch_and_download_modules(); - +#ifdef BUILD_EMAIL_MODE +std::vector read_file(std::string path); +#endif #endif // UTILS_H diff --git a/widgets/checklistviewwidget.cpp b/widgets/checklistviewwidget.cpp new file mode 100644 index 0000000..ce28851 --- /dev/null +++ b/widgets/checklistviewwidget.cpp @@ -0,0 +1,29 @@ +#include "checklistviewwidget.h" + +#include +#include +#include +#include +#include + +CheckListViewWidget::CheckListViewWidget(QWidget *parent, Check check) : QWidget(parent), check(check) { + + std::cout << "I was created with check with date " << check.get_date() << std::endl; + QHBoxLayout *layout = new QHBoxLayout; + + QLabel *label1 = new QLabel(QString::fromStdString(check.get_date())); + QLabel *label2 = new QLabel("Text 2"); + QLabel *label3 = new QLabel("Text 3"); + + QPushButton *deleteButton = new QPushButton(tr("Delete")); + + deleteButton->connect(deleteButton, &QPushButton::clicked, this, &CheckListViewWidget::deleteButtonPressed); + + layout->addWidget(label1); + layout->addWidget(label2); + layout->addWidget(label3); + layout->addSpacing(10); + layout->addWidget(deleteButton); + + setLayout(layout); +} diff --git a/widgets/checklistviewwidget.h b/widgets/checklistviewwidget.h new file mode 100644 index 0000000..05609d5 --- /dev/null +++ b/widgets/checklistviewwidget.h @@ -0,0 +1,20 @@ +#ifndef CHECKLISTVIEWWIDGET_H +#define CHECKLISTVIEWWIDGET_H + +#include +#include + +#include + +class CheckListViewWidget : public QWidget +{ + Q_OBJECT + Check check; +public: + explicit CheckListViewWidget(QWidget *parent = nullptr, Check check = Check()); + +signals: + Check deleteButtonPressed(); +}; + +#endif // CHECKLISTVIEWWIDGET_H diff --git a/widgets/tablewidgetmovable.cpp b/widgets/tablewidgetmovable.cpp new file mode 100644 index 0000000..df3a15d --- /dev/null +++ b/widgets/tablewidgetmovable.cpp @@ -0,0 +1,86 @@ +#include "tablewidgetmovable.hpp" +#include +#include +#include +#include +#include + +TableWidgetMovable::TableWidgetMovable(QWidget *parent) : QTableWidget(parent) { } + +// TOOD: fix None of these works. WIP + +// void TableWidgetMovable::dropEvent(QDropEvent *event) { +// std::cout << event->type() << std::endl; +// if(event->source() == this && event->type() == QEvent::Type::Drop) { +// int oldRow = this->selectedItems()[0]->row(); +// int oldColumn = this->selectedItems()[0]->column(); + +// int newRow = this->indexAt(event->pos()).row(); +// int newColumn = this->indexAt(event->pos()).column(); + +// auto oldCell = this->cellWidget(oldRow, oldColumn); +// auto newCell = this->cellWidget(newRow, newColumn); + +// this->removeCellWidget(oldRow, oldColumn); +// this->removeCellWidget(newRow, newColumn); + +// this->setCellWidget(newRow, newColumn, oldCell); +// this->setCellWidget(oldRow, oldColumn, newCell); +// event->accept(); +// } +// } + +// void TableWidgetMovable::dropEvent(QDropEvent *event) { +// std::cout << event->type() << std::endl; +// if(event->source() == this && event->type() == QEvent::Type::Drop) { +// int oldRow = this->selectedItems()[0]->row(); +// int oldColumn = this->selectedItems()[0]->column(); + +// int newRow = this->indexAt(event->pos()).row(); +// int newColumn = this->indexAt(event->pos()).column(); +// std::cout << oldRow << " " << oldColumn<< " " << newRow << " " << newColumn << std::endl; +// QTableWidgetItem *from = this->itemAt(oldColumn, oldRow), *to = this->itemAt(newColumn, newRow); + +// // QList selectedItems = this->selectedItems(); +// if(newRow == -1) { +// std::cout << newRow << std::endl; +// // newRow = this->rowCount(); +// return; +// } +// // QTableWidgetItem bufferTo, bufferFrom; +// QTableWidgetItem *bufferTo = new QTableWidgetItem("123"), *bufferFrom = new QTableWidgetItem("321"); +// std::cout << to->text().toStdString() << " " << from->text().toStdString() << std::endl; +// *bufferTo = *to; +// *bufferFrom = *from; +// // this->setItem(newRow, newColumn, nullptr); +// // this->setItem(oldRow, oldColumn, nullptr); +// this->takeItem(newRow, newColumn); +// this->takeItem(oldRow, oldColumn); +// this->setItem(newRow, newColumn, from); +// // this->setItem(oldRow, oldColumn, to); +// // *to = *from; +// // *from = buffer; +// return; + +// // int i; +// // for(i = 0; i < selectedItems.length()/this->columnCount(); i++) +// // this->insertRow(newRow); + +// // int currentOldRow = -1; +// // int currentNewRow = newRow-1; +// // QList deleteRows; +// // foreach(selectedItem, selectedItems) { +// // int column = selectedItem->column(); +// // if(selectedItem->row() != currentOldRow) { +// // currentOldRow = selectedItem->row(); +// // deleteRows.append(currentOldRow); +// // currentNewRow++; +// // } +// // this->takeItem(currentOldRow, column); +// // this->setItem(currentNewRow, column, selectedItem); +// // } + +// // for(i = deleteRows.count()-1; i>=0; i--) +// // this->removeRow(deleteRows.at(i)); +// } +// } diff --git a/widgets/tablewidgetmovable.hpp b/widgets/tablewidgetmovable.hpp new file mode 100644 index 0000000..21935ee --- /dev/null +++ b/widgets/tablewidgetmovable.hpp @@ -0,0 +1,17 @@ +#ifndef TABLEWIDGETMOVABLE_HPP +#define TABLEWIDGETMOVABLE_HPP + +#include +#include +#include + +class TableWidgetMovable : public QTableWidget +{ + Q_OBJECT +public: + TableWidgetMovable(QWidget *parent = nullptr); + // void dropEvent(QDropEvent *event); + // void swapRows(int row1, int row2); +}; + +#endif // TABLEWIDGETMOVABLE_HPP