Compare commits

..

No commits in common. "master" and "ui_rework" have entirely different histories.

92 changed files with 3931 additions and 3935 deletions

3
.env
View File

@ -1,3 +0,0 @@
name=checks-parser
version=alpha_0.0.4
revision=1

2
.gitignore vendored
View File

@ -124,5 +124,3 @@ checks-parser
deploy/appimage/AppDir/usr/share/doc/ deploy/appimage/AppDir/usr/share/doc/
deploy/appimage/AppDir/usr/share/ deploy/appimage/AppDir/usr/share/
*.deb *.deb
binaries

View File

View File

View File

@ -1,57 +1,12 @@
cmake_minimum_required(VERSION 3.10) cmake_minimum_required(VERSION 3.10)
project(checks-parser VERSION 0.0.4 LANGUAGES CXX) project(checks-parser VERSION 0.1 LANGUAGES CXX)
option(BUILD_TRANSLATIONS "Build translations?" ON) option(BUILD_TRANSLATIONS "Build translations?" ON)
option(BUILD_EMAIL_MODE "Build email mode?" ON)
option(BUILD_OFD_LOCAL_QR_SCAN "Build OFDs' local qr scanner?" ON)
option(BUILD_OFD_BINARYEYE_SCAN "Build OFDs' binaryeye scanner?" ON)
if (NOT (BUILD_EMAIL_MODE OR BUILD_OFD_LOCAL_QR_SCAN OR BUILD_OFD_BINARYEYE_SCAN)) include(FetchContent)
message(FATAL_ERROR "You must specify at least one of the modes of data input!")
return()
endif()
if (BUILD_TRANSLATIONS)
if(CMAKE_VERSION VERSION_LESS 3.12)
add_definitions(-DBUILD_TRANSLATIONS)
else()
add_compile_definitions(BUILD_TRANSLATIONS)
endif()
endif()
if (BUILD_OFD_LOCAL_QR_SCAN OR BUILD_OFD_BINARYEYE_SCAN)
if(CMAKE_VERSION VERSION_LESS 3.12)
add_definitions(-DBUILD_OFD_MODE)
else()
add_compile_definitions(BUILD_OFD_MODE)
endif()
endif()
if (BUILD_EMAIL_MODE)
if(CMAKE_VERSION VERSION_LESS 3.12)
add_definitions(-DBUILD_EMAIL_MODE)
else()
add_compile_definitions(BUILD_EMAIL_MODE)
endif()
endif()
if (BUILD_OFD_LOCAL_QR_SCAN)
if(CMAKE_VERSION VERSION_LESS 3.12)
add_definitions(-DBUILD_OFD_LOCAL_QR_SCAN)
else()
add_compile_definitions(BUILD_OFD_LOCAL_QR_SCAN)
endif()
endif()
if (BUILD_OFD_BINARYEYE_SCAN)
if(CMAKE_VERSION VERSION_LESS 3.12)
add_definitions(-DBUILD_OFD_BINARYEYE_SCAN)
else()
add_compile_definitions(BUILD_OFD_BINARYEYE_SCAN)
endif()
endif()
SET(CMAKE_BUILD_TYPE Debug) SET(CMAKE_BUILD_TYPE Debug)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
@ -62,74 +17,58 @@ set(CMAKE_AUTOUIC_SEARCH_PATHS scenes)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Qt6Core REQUIRED) find_package(Qt5Core REQUIRED)
if (BUILD_TRANSLATIONS) find_package(Qt5 REQUIRED COMPONENTS LinguistTools)
find_package(Qt6 REQUIRED COMPONENTS LinguistTools) find_package(Qt5Gui REQUIRED)
endif() find_package(Qt5Widgets REQUIRED)
find_package(Qt6Gui REQUIRED) find_package(Qt5UiTools REQUIRED)
find_package(Qt6Widgets REQUIRED)
set(TRANSLATION_SOURCES
main.cpp
mainwindow.cpp mainwindow.h scenes/mainwindow.ui
outputdialog.h outputdialog.cpp scenes/outputdialog.ui
settingsdialog.h settingsdialog.cpp scenes/settingsdialog.ui
)
if (BUILD_OFD_LOCAL_QR_SCAN)
list(APPEND TRANSLATION_SOURCES adjustpicturedialog.h adjustpicturedialog.cpp scenes/adjustpicturedialog.ui)
endif()
if (BUILD_OFD_LOCAL_QR_SCAN OR BUILD_OFD_BINARYEYE_SCAN)
list(APPEND TRANSLATION_SOURCES solvecaptchadialog.h solvecaptchadialog.cpp scenes/solvecaptchadialog.ui)
endif()
set(PROJECT_SOURCES set(PROJECT_SOURCES
main.cpp
mainwindow.h mainwindow.cpp scenes/mainwindow.ui
goods/goods.h goods/goods.cpp goods/goods.h goods/goods.cpp
check/check.h check/check.cpp check/check.h check/check.cpp
parser/parser.h parser/parser.cpp
parser/module.h parser/module.cpp
output/output_options.h output/output_options.cpp output/output_options.h output/output_options.cpp
utils/utils.h utils/utils.cpp utils/utils.h utils/utils.cpp
image/checkimage.h image/checkimage.cpp
net/net.h net/net.cpp net/net.h net/net.cpp
settings/settings.h settings/settings.cpp settings/settings.h settings/settings.cpp
exceptions/ofdrequestexception.h exceptions/ofdrequestexception.cpp
emailtextscene.h emailtextscene.cpp scenes/emailtextscene.ui
ocrscene.h ocrscene.cpp scenes/ocrscene.ui
widgets/outputcolumn.h widgets/outputcolumn.cpp ofdscene.h ofdscene.cpp scenes/ofdscene.ui
widgets/outputcolumnmodel.h widgets/outputcolumnmodel.cpp outputdialog.h outputdialog.cpp scenes/outputdialog.ui
adjustpicturedialog.h adjustpicturedialog.cpp scenes/adjustpicturedialog.ui
${TRANSLATION_SOURCES} image_redactor/imageredactor.h image_redactor/imageredactor.cpp
solvecaptchadialog.h solvecaptchadialog.cpp scenes/solvecaptchadialog.ui
) )
if (BUILD_OFD_LOCAL_QR_SCAN) set(TRANSLATION_SOURCES
list(APPEND PROJECT_SOURCES image_redactor/imageredactor.h image_redactor/imageredactor.cpp) main.cpp
endif() mainwindow.cpp mainwindow.h scenes/mainwindow.ui
emailtextscene.cpp emailtextscene.h scenes/emailtextscene.ui
ocrscene.cpp ocrscene.h scenes/ocrscene.ui
ofdscene.cpp ofdscene.h scenes/ofdscene.ui
outputdialog.h outputdialog.cpp scenes/outputdialog.ui
adjustpicturedialog.h adjustpicturedialog.cpp scenes/adjustpicturedialog.ui
solvecaptchadialog.h solvecaptchadialog.cpp scenes/solvecaptchadialog.ui
)
if (BUILD_OFD_BINARYEYE_SCAN OR BUILD_OFD_LOCAL_QR_SCAN) set(TS_FILES
list(APPEND PROJECT_SOURCES exceptions/ofdrequestexception.h exceptions/ofdrequestexception.cpp)
endif()
if (BUILD_OFD_BINARYEYE_SCAN)
list(APPEND PROJECT_SOURCES http_server/http_server.h http_server/http_server.cpp)
endif()
if (BUILD_EMAIL_MODE)
# list(APPEND PROJECT_SOURCES email_parser/emailparser.h email_parser/emailparser.cpp)
list(APPEND PROJECT_SOURCES utils/base64.h utils/base64.cpp)
endif()
if (BUILD_TRANSLATIONS)
set(TS_FILES
translations/en_US.ts translations/en_US.ts
translations/ru_RU.ts translations/ru_RU.ts
) )
qt_create_translation(QM_FILES "${TRANSLATION_SOURCES}" ${TS_FILES}) if (BUILD_TRANSLATIONS)
qt5_create_translation(QM_FILES "${TRANSLATION_SOURCES}" ${TS_FILES})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/translations.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc COPYONLY) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/translations.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc COPYONLY)
qt_add_resources(TRANSLATIONQRC ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc) qt5_add_resources(TRANSLATIONQRC ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc)
add_custom_target(translations ALL DEPENDS ${QM_FILES}) add_custom_target(translations ALL DEPENDS ${QM_FILES})
add_custom_target(resources ALL DEPENDS ${TRANSLATIONQRC}) add_custom_target(resources ALL DEPENDS ${TRANSLATIONQRC})
add_dependencies(resources translations) add_dependencies(resources translations)
@ -138,13 +77,13 @@ endif()
# Media QRC # Media QRC
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/media.qrc ${CMAKE_CURRENT_BINARY_DIR}/media.qrc COPYONLY) 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}) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
qt_add_resources(MEDIAQRC ${CMAKE_CURRENT_BINARY_DIR}/media.qrc) qt5_add_resources(MEDIAQRC ${CMAKE_CURRENT_BINARY_DIR}/media.qrc)
add_custom_target(mediaresource ALL DEPENDS ${MEDIAQRC}) add_custom_target(mediaresource ALL DEPENDS ${MEDIAQRC})
#Scenes QRC #Scenes QRC
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/scenes.qrc ${CMAKE_CURRENT_BINARY_DIR}/scenes.qrc COPYONLY) 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}) file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/scenes DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
qt_add_resources(SCENESQRC ${CMAKE_CURRENT_BINARY_DIR}/scenes.qrc) qt5_add_resources(SCENESQRC ${CMAKE_CURRENT_BINARY_DIR}/scenes.qrc)
add_custom_target(scenessource ALL DEPENDS ${SCENESQRC}) add_custom_target(scenessource ALL DEPENDS ${SCENESQRC})
set(SOURCES "") set(SOURCES "")
@ -165,18 +104,12 @@ else()
add_executable(checks-parser add_executable(checks-parser
${PROJECT_SOURCES} ${PROJECT_SOURCES}
${SOURCES} ${SOURCES}
widgets/checkqueuetablemodel.h widgets/checkqueuetablemodel.cpp
# widgets/checkqueuetableview.h widgets/checkqueuetableview.cpp
) )
endif() endif()
target_link_libraries(checks-parser PRIVATE Qt6::Widgets) target_link_libraries(checks-parser PRIVATE Qt5::Widgets Qt5::UiTools)
target_include_directories(checks-parser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/widgets) target_include_directories(checks-parser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/image_redactor)
if (BUILD_OFD_LOCAL_QR_SCAN)
target_include_directories(checks-parser PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/image_redactor)
endif()
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER org.foxarmy.checks-parser) set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER org.foxarmy.checks-parser)
@ -200,39 +133,29 @@ if(WIN32)
set(OpenCV_DIR /usr/local/lib/cmake/opencv4) set(OpenCV_DIR /usr/local/lib/cmake/opencv4)
endif() endif()
if (BUILD_OFD_BINARYEYE_SCAN) FetchContent_Declare(httplib SYSTEM
find_package(PkgConfig) GIT_REPOSITORY https://github.com/yhirose/cpp-httplib
pkg_check_modules(QRENCODE REQUIRED libqrencode) GIT_TAG c765584e6b1055fe0dfe3e9e6d1b4b09aa305070
include_directories(${QRENCODE_INCLUDE_DIRS}) GIT_SHALLOW TRUE)
link_directories(${QRENCODE_LIBRARY_DIRS}) FetchContent_MakeAvailable(httplib)
target_link_libraries(checks-parser PRIVATE ${QRENCODE_LIBRARIES})
endif()
# if (BUILD_OFD_LOCAL_QR_SCAN)
# target_link_libraries(checks-parser PRIVATE -lzbar)
# endif()
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs)
find_package(PkgConfig)
pkg_check_modules(QRENCODE REQUIRED libqrencode)
include_directories(${QRENCODE_INCLUDE_DIRS})
link_directories(${QRENCODE_LIBRARY_DIRS})
target_link_libraries(checks-parser PRIVATE ${QRENCODE_LIBRARIES})
include_directories( ${OpenCV_INCLUDE_DIRS} )
target_include_directories(checks-parser PUBLIC ${OpenCV_INCLUDE_DIRS})
target_link_libraries(checks-parser PRIVATE -lzbar)
target_link_libraries(checks-parser PRIVATE -ltesseract)
target_link_libraries(checks-parser PRIVATE -lcurl) target_link_libraries(checks-parser PRIVATE -lcurl)
target_link_libraries(checks-parser PRIVATE ${OpenCV_LIBS})
target_link_libraries(checks-parser PRIVATE httplib)
if (CMAKE_VERSION VERSION_LESS 3.30) if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8)
find_package(Boost 1.45.0 REQUIRED COMPONENTS regex)
else()
find_package(Boost 1.45.0 CONFIG REQUIRED COMPONENTS regex)
endif()
include_directories(${Boost_INCLUDE_DIRS})
target_link_libraries(checks-parser PUBLIC ${Boost_LIBRARIES})
if (BUILD_OFD_LOCAL_QR_SCAN OR BUILD_OFD_BINARYEYE_SCAN)
find_package(OpenCV REQUIRED COMPONENTS core imgproc imgcodecs opencv_objdetect)
target_link_libraries(checks-parser PRIVATE ${OpenCV_LIBS})
target_include_directories(checks-parser PUBLIC ${OpenCV_INCLUDE_DIRS})
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) target_link_libraries(checks-parser PRIVATE -lstdc++fs)
endif() endif()

View File

@ -1,37 +1,28 @@
# Checks parser
-->[Русская версия](https://git.foxarmy.org/leca/checks-parser/src/branch/master/README.ru.md)<-- -->[Русская версия](https://git.foxarmy.org/leca/checks-parser/src/branch/master/README.ru.md)<--
# Checks parser
checks parser is a program that help parsing different checks to csv. checks parser is a program that help parsing different checks to csv.
!!!CURRENTLY SUPPORTED ONLY RUSSIAN CHECKS!!! !!!CURRENTLY SUPPORTED ONLY RUSSIAN CHECKS!!!
To know why, see [this section](https://git.foxarmy.org/leca/checks-parser#checks-from-different-countries) To know why, see [this section](https://git.foxarmy.org/leca/checks-parser#checks-from-different-countries)
## Usage # Usage
For more detailed description, please, refer to [the wiki](https://git.foxarmy.org/leca/checks-parser/wiki/Description-%5BEN%5D) For more detailed description, please, refer to [the wiki](https://git.foxarmy.org/leca/checks-parser/wiki/Description-%5BEN%5D)
## Input ### Input
Ways you can input a check to this programm: Ways you can input a check to this programm:
* Via image (it uses OCR(Optical Character Recognition) to parse check content. The picture of a check must be contrast and well-lined (text must be perpendicular to right and left borders of an image) enough in order to be parsed well.) OCR is not a magic wand :( * Via image (it uses OCR(Optical Character Recognition) to parse check content. The picture of a check must be contrast and well-lined (text must be perpendicular to right and left borders of an image) enough in order to be parsed well.) OCR is not a magic wand :(
* Via plaintext, copied from an E-Mail. Just copy&paste text from your email, pick a correct store type (autodetect is in my plans!) and parse. * Via plaintext, copied from an E-Mail. Just copy&paste text from your email, pick a correct store type (autodetect is in my plans!) and parse.
* Via QRCode on check (this method queries check content from OFD (ОФД, Оператор Фискальных Данных in Russian) (My program makes requests to ofd.ru)). * Via QRCode on check (this method queries check content from OFD (ОФД, Оператор Фискальных Данных in Russian) (My program makes requests to ofd.ru)).
### Output ### Output
At the start of writing this program, I considered 3 or more output formats: csv, xlsx and ods. But throught the development I understood that most of modern table processor (i.e. electronic tables) can import csv much better than I'd be writing a shitty export module, adding more dependencies and shitty code to the codebase. So I decided that there's no need to use anything other than csv format. At the start of writing this program, I considered 3 or more output formats: csv, xlsx and ods. But throught the development I understood that most of modern table processor (i.e. electronic tables) can import csv much better than I'd be writing a shitty export module, adding more dependencies and shitty code to the codebase. So I decided that there's no need to use anything other than csv format.
To export, you need to specify an output file path and, if you wish, you can change order and/or rename columns, choose to print or not to print header (column names) and total. To export, you need to specify an output file path and, if you wish, you can change order and/or rename columns, choose to print or not to print header (column names) and total.
## Installing # Installing
## Building
### Building
In general, you need to install following dependencies in order to build that app(I suppose you have installed all the build necessaries such as cmake, make, gcc, git, etc...): In general, you need to install following dependencies in order to build that app(I suppose you have installed all the build necessaries such as cmake, make, gcc, git, etc...):
* boost
* tesseract (you also have to install appropriate for your needs language data) * tesseract (you also have to install appropriate for your needs language data)
* opencv * opencv
* zbar * zbar
@ -41,15 +32,12 @@ In general, you need to install following dependencies in order to build that ap
* qrencode * qrencode
Please, do not hesitate to open an issue if you cannot build that. I will help and if you are building on a distro that is not listed there, we can append that list as soon as we will solve your problem! Please, do not hesitate to open an issue if you cannot build that. I will help and if you are building on a distro that is not listed there, we can append that list as soon as we will solve your problem!
### Linux
#### Linux
##### Arch Linux-based ##### Arch Linux-based
I recommend using aur helper (I use yay) to install dependencies. Or, if you're masochist, you can build all by yourself ¯\\\_(ツ)\_/¯ I recommend using aur helper (I use yay) to install dependencies. Or, if you're masochist, you can build all by yourself ¯\\\_(ツ)\_/¯
``` ```
#Install dependencies #Install dependencies
yay -S base-devel qt5-base opencv zbar nlohmann-json tesseract qrencode boost yay -S base-devel qt5-base opencv zbar nlohmann-json tesseract qrencode
#Install a language package for OCR. Replace ``LANG` to your language. For example, ``tesseract-data-rus`` for russian language #Install a language package for OCR. Replace ``LANG` to your language. For example, ``tesseract-data-rus`` for russian language
yay -S tesseract-data-LANG yay -S tesseract-data-LANG
#Clone and compile an app #Clone and compile an app
@ -60,24 +48,17 @@ make -j{nproc}
#If you wish to install that program system-wide, run #If you wish to install that program system-wide, run
sudo make install sudo make install
``` ```
##### Debian-based ##### Debian-based
In debian-based distributions most, but not every, package names are the same. In debian-based distributions most, but not every, package names are the same.
Installation of dependencies for different debian-based distros: Installation of dependencies for different debian-based distros:
###### Ubuntu 18.04 ###### Ubuntu 18.04
```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json-dev libcurl4-openssl-dev libtesseract-dev libqrencode-dev```
```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json-dev libcurl4-openssl-dev libtesseract-dev libqrencode-dev libboost-regex-dev```
###### Ubuntu 20.04, LMDE (tested only 6), Debian (tested only 12) ###### Ubuntu 20.04, LMDE (tested only 6), Debian (tested only 12)
```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json3-dev libcurl4-openssl-dev libtesseract-dev libqrencode-dev```
```apt install -y qt6-base-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev qt6-tools-dev nlohmann-json3-dev libcurl4-openssl-dev libqrencode-dev libboost-regex-dev```
Next steps are identical for every debian-based distro Next steps are identical for every debian-based distro
```
```sh
#Clone and compile an app #Clone and compile an app
git clone https://git.foxarmy.org/leca/checks-parser git clone https://git.foxarmy.org/leca/checks-parser
cd checks-parser cd checks-parser
@ -87,42 +68,36 @@ make -j{nproc}
sudo make install sudo make install
``` ```
#### Windows ### Windows
See [Precompiled binaries](https://git.foxarmy.org/leca/checks-parser#precompiled-binaries) See [Precompiled binaries](https://git.foxarmy.org/leca/checks-parser#precompiled-binaries)
### Mac OS
#### Mac OS
Probably not, I do not have nor desire or time. But if you can maintain that program on Mac, I'd be grateful! Please, contact me, if you can! Probably not, I do not have nor desire or time. But if you can maintain that program on Mac, I'd be grateful! Please, contact me, if you can!
### Precompiled binaries ## Precompiled binaries
Currently I have published the program to the [AUR](https://aur.archlinux.org/packages/checks-parser-git). Currently I have published the program to the [AUR](https://aur.archlinux.org/packages/checks-parser-git).
Every new release will certainly contain AppImage and tarball. I am working towards binaries for Windows and deb packets. Expect them to appear in next releases! Every new release will certainly contain AppImage and tarball. I am working towards binaries for Windows and deb packets. Expect them to appear in next releases!
## Special thanks # Special thanks
HyperFlint (@hyperflint:foxarmy.org) - for the great idea to use OFD and a huge help in release preparations! HyperFlint (@hyperflint:foxarmy.org) - for the great idea to use OFD and a huge help in release preparations!
[ofd.ru](https://check.ofd.ru) - for providing a way to request data from FNS. https://check.ofd.ru - for providing a way to request data from FNS.
## Contribution # Contribution
If you want to contribute to the project, you can do it by some of the following: If you want to contribute to the project, you can do it by some of the following:
## Checks from different countries
### Checks from different countries
I live in Russia and only know how Russian state checks system works. If you live in another country and want to help me with adding support to checks from your country - feel free to contact me! I live in Russia and only know how Russian state checks system works. If you live in another country and want to help me with adding support to checks from your country - feel free to contact me!
## Issues and PRs
### Issues and PRs
If you have found a bug, or want to suggest a feature - don't hesitate to open an issue / a PR! If you have found a bug, or want to suggest a feature - don't hesitate to open an issue / a PR!
### Tell friends ## Tell friends
You can help me by distributing that program. If you know people that are in search of such program, please let them know about its existance! You can help me by distributing that program. If you know people that are in search of such program, please let them know about its existance!
### Donate ## Donate
```XMR 45ZjyH5YWdRfKxLoKEBYaiHUTcP5Z8Gv64QQxmabbooPAa7KPBxZLmqft5ohKXn5VpHiVj1x9JKCcAcAjdu9jA8b5N8XqR7``` ```XMR 45ZjyH5YWdRfKxLoKEBYaiHUTcP5Z8Gv64QQxmabbooPAa7KPBxZLmqft5ohKXn5VpHiVj1x9JKCcAcAjdu9jA8b5N8XqR7```

View File

@ -23,14 +23,12 @@
# Установка # Установка
## Сборка из исходников ## Сборка из исходников
В целом, вам нужно установить следующие зависимости, чтобы собрать приложение (я предполагаю, что вы уже имеете на системе базовые пакеты вроде cmake, make, gcc, git и так далее): В целом, вам нужно установить следующие зависимости, чтобы собрать приложение (я предполагаю, что вы уже имеете на системе базовые пакеты вроде cmake, make, gcc, git и так далее):
* boost
* tesseract (также вам нужно будет установить языковой пакет для него, например tesseract-data-rus на Arch Linux или tesseract-ocr-rus на Debian Linux.) * tesseract (также вам нужно будет установить языковой пакет для него, например tesseract-data-rus на Arch Linux или tesseract-ocr-rus на Debian Linux.)
* opencv * opencv
* zbar * zbar
* curl * curl
* nlohmann-json * nlohmann-json
* qt5 * qt5
* qrencode
Пожалуйста, не стесняйтесь и открывайте issue, если вы не можете собрать приложение. Я помогу вам, и если вы собираете приложение на дистрибутиве, который здесь не перечислен, как только мы решим вашу проблему, я добавлю новый дистрибутив в этот список! Пожалуйста, не стесняйтесь и открывайте issue, если вы не можете собрать приложение. Я помогу вам, и если вы собираете приложение на дистрибутиве, который здесь не перечислен, как только мы решим вашу проблему, я добавлю новый дистрибутив в этот список!
@ -39,7 +37,7 @@
Я рекомендую использовать помощник для АУРа (я использую yay) чтобы установить зависимости. Или, если вы мазохист, можете собрать все зависимости ручками ¯\\\_(ツ)\_/¯ Я рекомендую использовать помощник для АУРа (я использую yay) чтобы установить зависимости. Или, если вы мазохист, можете собрать все зависимости ручками ¯\\\_(ツ)\_/¯
``` ```
#Установка зависимостей #Установка зависимостей
yay -S base-devel qt5-base opencv zbar nlohmann-json tesseract qrencode boost yay -S base-devel qt5-base opencv zbar nlohmann-json tesseract qrencode
#Установка языкового пакета для OCR. Замените ``LANG` на желаемый язык. Например, ``tesseract-data-rus`` для русского языка #Установка языкового пакета для OCR. Замените ``LANG` на желаемый язык. Например, ``tesseract-data-rus`` для русского языка
yay -S tesseract-data-LANG yay -S tesseract-data-LANG
#Загрузка исходгого кода и сборка приложения #Загрузка исходгого кода и сборка приложения
@ -55,9 +53,9 @@ sudo make install
Установка зависимостей для различных debian дистрибутивов: Установка зависимостей для различных debian дистрибутивов:
###### Ubuntu 18.04 ###### Ubuntu 18.04
```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json-dev libcurl4-openssl-dev libtesseract-dev libqrencode-dev libboost-regex-dev``` ```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json-dev libcurl4-openssl-dev libtesseract-dev```
###### Ubuntu 20.04, LMDE (проверил только 6), Debian (проверил только 12) ###### Ubuntu 20.04, LMDE (проверил только 6), Debian (проверил только 12)
```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json3-dev libcurl4-openssl-dev libtesseract-dev libqrencode-dev libboost-regex-dev``` ```apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json3-dev libcurl4-openssl-dev libtesseract-dev```
Следующие шаги идеинтичны для всех дистрибутивов, основанных на debian: Следующие шаги идеинтичны для всех дистрибутивов, основанных на debian:
``` ```

30
TODO Normal file
View File

@ -0,0 +1,30 @@
Complete module "export": [done]
make UI; [done]
make export to .csv [done]
Complete module "image-to-text": [done]
make UI; [done]
make use of tesseract/opencv (https://learnopencv.com/deep-learning-based-text-recognition-ocr-using-tesseract-and-opencv/); [done]
Add features:
autodetect store type
auto download of stores modules [done]
auto download of ofd modules [done]
settings, a window for editing settings. [done]
add ability to control contrast and rotation of a check image before passing it to OCR [done]
add ability to scan a qr code and request data from ofd.ru [done]
add ability to change language from preferences
Refactor:
Get rid of CPR, use libcurl instead [done]
Build:
Write script for AppImage deployment [done]
Find out dependencies packet names on different distros [done for arch, ubuntu]
Ensure success of building on most popular distros [done for arch, ubuntu]
Try to compile it on Windows
Issues:
Captcha is not showing when running in appimage [solved]
Stores modules are not being downloaded
I need to pack tesseract data for ru, en [solved]

View File

@ -1,16 +1,13 @@
#include "adjustpicturedialog.h" #include "adjustpicturedialog.h"
#include "ui_adjustpicturedialog.h" #include "ui_adjustpicturedialog.h"
#include <utils/utils.h> #include "utils/utils.h"
#include <opencv2/objdetect.hpp>
#include <opencv2/imgcodecs.hpp> #include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <string> #include <string>
#include <opencv2/core/mat.hpp>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
#include <zbar.h>
using cv::QRCodeDetector, cv::Mat;
AdjustPictureDialog::AdjustPictureDialog(QWidget *parent, std::string imagePath) AdjustPictureDialog::AdjustPictureDialog(QWidget *parent, std::string imagePath)
: QDialog(parent) : QDialog(parent)
@ -57,26 +54,24 @@ void AdjustPictureDialog::accept() {
} }
std::string AdjustPictureDialog::decode() { std::string AdjustPictureDialog::decode() {
Mat im = cv::imread(get_path_relative_to_home(".local/share/checks_parser/temp.png")); cv::Mat im = cv::imread(get_path_relative_to_home(".local/share/checks_parser/temp.png"));
QRCodeDetector qrDecoder = QRCodeDetector();
return qrDecoder.detectAndDecode(im);
// zbar::ImageScanner scanner; zbar::ImageScanner scanner;
// scanner.set_config(zbar::ZBAR_QRCODE, zbar::ZBAR_CFG_ENABLE, 1); scanner.set_config(zbar::ZBAR_QRCODE, zbar::ZBAR_CFG_ENABLE, 1);
// cv::Mat imGray; cv::Mat imGray;
// cv::cvtColor(im, imGray, cv::COLOR_BGR2GRAY); cv::cvtColor(im, imGray, cv::COLOR_BGR2GRAY);
// zbar::Image image(im.cols, im.rows, "Y800", (uchar *) imGray.data, im.cols * im.rows); zbar::Image image(im.cols, im.rows, "Y800", (uchar *) imGray.data, im.cols * im.rows);
// scanner.scan(image); scanner.scan(image);
// std::string result = ""; std::string result = "";
// for (zbar::Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end(); ++symbol) { for (zbar::Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end(); ++symbol) {
// result = symbol->get_data(); result = symbol->get_data();
// } }
// return result; return result;
} }
void AdjustPictureDialog::computeContrastLookupTable() { void AdjustPictureDialog::computeContrastLookupTable() {

View File

@ -1,78 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="115.83108mm"
height="115.8316mm"
viewBox="0 0 115.83108 115.8316"
version="1.1"
id="svg1"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
sodipodi:docname="settings.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:zoom="0.67565763"
inkscape:cx="-76.962055"
inkscape:cy="230.88617"
inkscape:window-width="1920"
inkscape:window-height="1029"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="layer1">
<inkscape:grid
id="grid4"
units="mm"
originx="-47.084513"
originy="-90.584188"
spacingx="0.99999998"
spacingy="1"
empcolor="#0099e5"
empopacity="0.30196078"
color="#0099e5"
opacity="0.14901961"
empspacing="5"
enabled="true"
visible="true" />
</sodipodi:namedview>
<defs
id="defs1" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-47.084512,-90.584183)">
<rect
style="display:none;fill:#37f500;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-opacity:0.075573"
id="rect11"
width="169.95171"
height="169.95171"
x="20.362879"
y="61.871819"
ry="8.590621" />
<path
id="path10"
style="display:inline;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-opacity:0.075573"
inkscape:label="gear"
d="m 75.96901,0.80545601 c -11.155773,5e-7 -3.381873,12.02630099 -10.5755,17.65980599 2.26264,2.865218 2.781879,6.635337 1.536071,9.912568 l 0.842199,0.8422 a 23.648779,23.648779 0 0 1 8.720476,-1.56428 23.648779,23.648779 0 0 1 21.188309,14.268711 23.648779,23.648779 0 0 1 0.433613,17.637966 l 8.483422,8.483422 c 0.001,-1.137526 0.23505,-2.370832 0.79807,-3.730066 4.67012,-11.27469 19.06665,-0.813378 19.06665,-13.017015 0,-12.203637 -14.39653,-1.742323 -19.06665,-13.017014 -4.67013,-11.27469 12.9067,-14.057796 4.27742,-22.68707 -8.62927,-8.6292737 -11.41192,8.948014 -22.686611,4.277884 C 77.711789,15.202439 88.172646,0.80545546 75.96901,0.80545601 Z M 106.5976,68.045849 c -0.009,8.298119 12.66446,11.368041 5.07549,18.957003 -7.59196,7.591964 -10.66109,-5.094535 -18.966554,-5.075493 l 16.355784,16.355783 c 2.70707,2.707048 7.06538,2.707048 9.77243,0 l 4.11408,-4.114084 c 2.70705,-2.707068 2.70705,-7.065327 0,-9.77243 z M 92.706536,81.927359 84.164419,73.385241 A 23.648779,23.648779 0 0 1 54.292034,60.751335 l -0.0678,-0.156519 A 23.648779,23.648779 0 0 1 53.850687,43.071964 l -0.808075,-0.808075 c -3.276856,1.245655 -7.046115,0.726779 -9.911204,-1.53516 -5.635193,7.18452 -17.655256,-0.582996 -17.655256,10.570039 1e-6,12.203638 14.396527,1.742324 19.066657,13.017015 4.67013,11.274689 -12.907157,14.057795 -4.277883,22.687069 8.629275,8.629275 11.412378,-8.948013 22.687069,-4.277883 11.27469,4.670129 0.813378,19.067111 13.017015,19.067111 12.203637,0 1.742778,-14.396981 13.017469,-19.067111 1.355311,-0.561389 2.58526,-0.795009 3.720057,-0.79761 z M 43.131408,40.728729 c 0.530828,-0.676772 1.009354,-1.476346 1.411401,-2.446975 1.382806,-3.338388 0.796238,-5.922366 -0.464097,-8.143995 -5.610754,-0.603322 -9.948354,-8.65044 -17.091514,-10.361651 -0.190642,2.831294 0.790952,5.725383 2.964307,7.898752 l 12.263539,12.263539 c 0.290197,0.290195 0.602358,0.542427 0.916364,0.79033 z m 0.947304,-10.59097 c 2.166221,0.232933 4.522048,-0.640922 7.302705,-3.421578 2.784135,-2.78414 3.656675,-5.142371 3.420669,-7.310895 -5.277946,-2.997957 -8.46522,-9.8825424 -14.53716,-3.810602 -6.074184,6.074185 0.81761,9.261629 3.813786,14.543075 z M 54.802086,19.405286 c 2.222924,1.262656 4.808593,1.851301 8.149909,0.467282 0.968102,-0.401001 1.765816,-0.878149 2.441515,-1.407306 -0.247687,-0.313651 -0.499528,-0.625564 -0.78942,-0.915455 L 52.340551,5.2862683 C 50.34246,3.2881697 47.734568,2.2888648 45.127026,2.2887468 v 4.55e-4 c -0.228937,-1.05e-5 -0.457614,0.017002 -0.686136,0.032305 1.710334,7.1397402 9.75097,11.4767772 10.361196,17.0837792 z M 53.850687,43.071964 84.164419,73.385241 A 23.648779,23.648779 0 0 0 85.382901,72.993034 23.648779,23.648779 0 0 0 98.114178,59.562427 L 67.77178,29.22003 a 23.648779,23.648779 0 0 0 -1.137947,0.350348 23.648779,23.648779 0 0 0 -12.783146,13.501586 z"
transform="matrix(1.1357528,0,0,1.1357528,18.717777,90.23726)" />
<path
id="path11"
style="display:none;fill:#f50000;fill-opacity:1;stroke:#000000;stroke-width:1.51285;stroke-opacity:0.075573"
d="m 63.089959,81.902523 c -0.311091,-1.7e-5 -0.621755,0.02326 -0.932283,0.04412 3.222959,13.454148 22.996087,19.583657 9.430752,33.149007 -13.565319,13.56532 -19.694039,-6.20771 -33.148136,-9.43075 -0.259055,3.84733 1.074576,7.78001 4.02786,10.73331 l 16.664641,16.66466 c 3.992393,3.99236 9.772658,5.03826 14.71346,3.16006 l 76.122907,76.12203 c 3.67851,3.67851 9.60071,3.67851 13.27922,0 l 5.59023,-5.59023 c 3.6785,-3.67853 3.6785,-9.60066 0,-13.27923 L 92.71658,117.35349 c 1.878195,-4.9408 0.832348,-10.72105 -3.16008,-14.71346 L 72.891859,85.975392 c -2.715126,-2.715126 -6.258615,-4.072783 -9.8019,-4.072937 z"
inkscape:label="wrench" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.7 KiB

View File

@ -1,29 +1,14 @@
#include "check.h" #include "check.h"
#include "../goods/goods.h" #include "../goods/goods.h"
#include <QObject>
Check::Check() {} Check::Check() {}
Check::Check(std::string date, double total, OperationType type, std::string fn, std::string fd, std::string fi, std::vector<Goods> goods) { void Check::add_goods(Goods goods) { this->goods.push_back(goods); }
set_date(date);
set_total(total);
set_operation_type(type);
set_fn(fn);
set_fd(fd);
set_fi(fi);
set_goods(goods);
}
void Check::add_goods(Goods goods) {
this->goods.push_back(goods);
this->total = this->calculae_total_price();
}
void Check::add_goods(std::vector<Goods> &goods) { void Check::add_goods(std::vector<Goods> &goods) {
for (auto &g : goods) { for (auto g : goods) {
this->goods.push_back(g); this->goods.push_back(g);
} }
this->total = this->calculae_total_price();
} }
double Check::calculae_total_price() { double Check::calculae_total_price() {
@ -32,44 +17,10 @@ double Check::calculae_total_price() {
for (Goods &g : goods) { for (Goods &g : goods) {
total += g.calculate_total_price(); total += g.calculate_total_price();
} }
return total; return total;
} }
std::vector<Goods>& Check::get_goods() { return goods; } std::vector<Goods>& Check::get_goods() {
return goods;
void Check::set_fn(std::string fn) { this->fn = fn; }
void Check::set_fd(std::string fd) { this->fd = fd; }
void Check::set_fi(std::string fi) { this->fi = fi; }
std::string Check::get_date() { return date; }
OperationType Check::get_operationType() { return operation_type; }
void Check::set_date(std::string date) { this->date = date; }
void Check::set_operation_type(OperationType t) { this->operation_type = t; }
void Check::set_total(double total) { this->total = total; }
void Check::set_goods(std::vector<Goods> goods) { this->goods = goods; }
std::string Check::get_fn() { return fn; }
std::string Check::get_fd() { return fd; }
std::string Check::get_fi() { return fi; }
double Check::get_total() { return total; }
bool Check::operator==(Check &c) {
return
this->date == c.date &&
this->fd == c.fd &&
this->fi == c.fi &&
this->fn == c.fn &&
this->operation_type == c.operation_type &&
this->total == c.total;
} }
bool Check::operator==(const Check &c) {
return
this->date == c.date &&
this->fd == c.fd &&
this->fi == c.fi &&
this->fn == c.fn &&
this->operation_type == c.operation_type &&
this->total == c.total;
}
Q_DECLARE_METATYPE(Check)

View File

@ -1,56 +1,19 @@
#ifndef CHECK_H #ifndef CHECK_H
#define CHECK_H #define CHECK_H
#include "../goods/goods.h" #include "../goods/goods.h"
#include <vector> #include <vector>
typedef enum OperationTypes {
funds_income, // Приход средств
funds_return, // Возврат прихода
funds_spend, // Расход средств
spends_return // Возврат расхода
} OperationType;
class Check { class Check {
std::string fn; // Fiscal Number = Фискальный номер
std::string fd; // Fiscal Document = Фискальный документ
std::string fi; // Fiscal Identifier = Фискальный признак
std::string date;
OperationType operation_type;
double total;
std::vector<Goods> goods; std::vector<Goods> goods;
public: public:
Check(); Check();
Check(std::string date, double total, OperationType type, std::string fn, std::string fd, std::string fi, std::vector<Goods> goods);
void add_goods(Goods); void add_goods(Goods);
void add_goods(std::vector<Goods> &goods); void add_goods(std::vector<Goods> &goods);
double calculae_total_price(); double calculae_total_price();
std::vector<Goods> &get_goods(); std::vector<Goods> &get_goods();
void set_fn(std::string);
void set_fd(std::string);
void set_fi(std::string);
void set_date(std::string);
void set_operation_type(OperationType);
void set_total(double);
void set_goods(std::vector<Goods>);
std::string get_fn();
std::string get_fd();
std::string get_fi();
std::string get_date();
OperationType get_operationType();
double get_total();
bool operator==(Check &);
bool operator==(const Check &);
}; };
#endif // CHECK_H #endif // CHECK_H

View File

@ -0,0 +1,34 @@
FROM archlinux
#Update
RUN pacman --noconfirm -Sy
#Build dependencies
RUN pacman --noconfirm -S sudo cmake git coreutils base-devel qt5-base
RUN echo "MAKEFLAGS=\"-j${nproc}\"" >> /etc/makepkg.conf
RUN useradd -ms /bin/bash checks-parser
RUN usermod -a -G wheel checks-parser
RUN echo "checks-parser ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
USER checks-parser
WORKDIR /home/checks-parser
#Dependencies from sources
RUN git clone https://aur.archlinux.org/yay.git && \
cd yay && \
makepkg -si --noconfirm && \
cd .. && sudo rm -rf yay
RUN yay -S opencv zbar nlohmann-json tesseract tesseract-data-rus
#building
WORKDIR /home/checks-parser
RUN git clone https://git.foxarmy.org/leca/checks-parser && \
cd checks-parser && \
cmake . && \
make -j ${nproc} && \
sudo make install
ENTRYPOINT ["checks-parser"]

View File

@ -0,0 +1,34 @@
FROM debian:bullseye
#Update
RUN apt update -y && apt upgrade -y
#Build dependencies
RUN apt install -y git build-essential cmake
#Dependencies from repos (qt5, openssl, tesseract and lang package)
RUN apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev
#Dependencies from sources
#cpr
WORKDIR /root
RUN git clone https://github.com/whoshuu/cpr.git && \
cd cpr && \
mkdir build && cd build && \
cmake .. && \
make -j ${nproc} && \
make install && \
cd /root && rm -rf cpr
#building
WORKDIR /root
RUN git clone https://git.foxarmy.org/leca/checks-parser && \
cd checks-parser && \
mkdir build && cd build &&\
cmake .. && \
make -j ${nproc} && \
make install && \
ENTRYPOINT ["checks-parser"]

View File

@ -1,5 +1,4 @@
#!/bin/bash #!/bin/bash
export TESSDATA_PREFIX=$APPDIR/usr/share/tesseract-ocr/4.00/tessdata export TESSDATA_PREFIX=\$APPDIR/usr/share/tesseract-ocr/4.00/tessdata
export LD_LIBRARY_PATH=$APPDIR/usr/lib
$APPDIR/usr/bin/checks-parser \$APPDIR/usr/bin/checks-parser

View File

@ -1,31 +1,71 @@
FROM checks_parser_base_ubuntu FROM ubuntu:20.04
RUN DEBIAN_FRONTEND=noninteractive apt update # Installing dependencies
RUN DEBIAN_FRONTEND=noninteractive apt install -y wget git cmake make gcc g++ fuse libboost-regex-dev RUN apt update
ARG pkgname=$name RUN DEBIAN_FRONTEND=noninteractive apt install -y qtbase5-dev openssl libmbedtls-dev tesseract-ocr tesseract-ocr-rus libopencv-dev libzbar-dev qttools5-dev nlohmann-json3-dev libcurl4-openssl-dev libtesseract-dev libqrencode-dev
ARG pkgver=$version RUN DEBIAN_FRONTEND=noninteractive apt install -y wget git cmake make gcc g++ fuse
ARG revision
# For gcc 12
RUN DEBIAN_FRONTEND=noninteractive apt install -y libmpc-dev libmpfr-dev libgmp-dev
# The program uses std::regex_constants::multiline and some other C++17 things that are not found in gcc for Ubuntu Focal. Thus, we should compile it.
WORKDIR / WORKDIR /
RUN wget https://mirror.linux-ia64.org/gnu/gcc/releases/gcc-12.3.0/gcc-12.3.0.tar.gz
RUN tar xf gcc-12.3.0.tar.gz
WORKDIR /gcc-12.3.0
RUN ./configure --disable-multilib
RUN make -j $(nproc) && make install
WORKDIR /
RUN rm -rf gcc-12.3.0
ENTRYPOINT bash
# Download linuxdeployqt # Download linuxdeployqt
RUN wget https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage -O /usr/bin/linuxdeployqt && \ RUN wget https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage -O /usr/bin/linuxdeployqt && \
chmod +x /usr/bin/linuxdeployqt chmod +x /usr/bin/linuxdeployqt
# Prepare AppDir and its files # Prepare AppDir and its files
WORKDIR /app WORKDIR /appimage
RUN mkdir -p AppDir
RUN mkdir -p AppDir/usr/bin
COPY deploy/appimage/checks-parser.desktop AppDir COPY deploy/appimage/checks-parser.desktop AppDir
COPY assets/icons/icon.png AppDir/checks-parser.png COPY icon.png AppDir/checks-parser.png
COPY deploy/appimage/AppRun AppDir COPY deploy/appimage/AppRun AppDir
RUN chmod +x AppDir/AppRun RUN chmod +x AppDir/AppRun
RUN cp build/checks-parser /app/AppDir/usr/bin
RUN PATH=/usr/lib/qt5/bin/:$PATH linuxdeployqt --appimage-extract-and-run AppDir/usr/bin/checks-parser -no-copy-copyright-files -appimage #Copy only necessities
RUN mkdir -p /output COPY assets ./assets
RUN echo "cp Checks_parser-x86_64.AppImage /output/Checks_parser-x86_64_${pkgver}-${revision}.AppImage" > /deploy.sh COPY check ./check
RUN chmod +x /deploy.sh COPY exceptions ./exceptions
ENTRYPOINT ["bash", "/deploy.sh"] COPY goods ./goods
COPY image ./image
COPY image_redactor ./image_redactor
COPY output ./output
COPY parser ./parser
COPY settings ./settings
COPY scenes ./scenes
COPY net ./net
COPY translations ./translations
COPY utils ./utils
COPY ./*cpp ./*.h ./*.ui ./*.qrc CMakeLists.txt .
RUN mkdir build
WORKDIR /appimage/build
RUN cmake -DBUILD_TRANSLATIONS=on .. && make -j 8
WORKDIR /appimage/AppDir/usr/bin
RUN cp /appimage/build/checks-parser .
WORKDIR /appimage
RUN LD_LIBRARY_PATH=LD_LIBRARY_PATH=/usr/local/lib64 linuxdeployqt AppDir/usr/bin/checks-parser -no-copy-copyright-files -appimage
ENTRYPOINT bash

31
deploy/appimage/deploy.sh Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/env bash
rm -rf AppDir
mkdir -p AppDir
mkdir -p AppDir/usr/bin
mkdir -p AppDir/usr/lib
mkdir -p AppDir/usr/share/tesseract-ocr/4.00/tessdata
cp -r /usr/share/tesseract-ocr/4.00/tessdata/* AppDir/usr/share/tesseract-ocr/4.00/tessdata
cp ../../checks-parser AppDir/usr/bin
echo \
"[Desktop Entry]
Name=Checks parser
Exec=usr/bin/checks-parser
Icon=checks-parser
Type=Application
Categories=Utility;" \
> AppDir/checks-parser.desktop
echo \
"#!/bin/bash
export TESSDATA_PREFIX=\$APPDIR/usr/share/tesseract-ocr/4.00/tessdata
\$APPDIR/usr/bin/checks-parser" \
> AppDir/AppRun
cp ../../icon.png AppDir/checks-parser.png
chmod +x AppDir/AppRun
cp ../../checks-parser AppDir/usr/bin
linuxdeployqt AppDir/usr/bin/checks-parser -no-copy-copyright-files -appimage

View File

@ -1,19 +1,19 @@
# Maintainer: Leca <leca@foxarmy.org> # Maintainer: Leca <leca@foxarmy.org>
pkgname=$pkgname pkgname=checks-parser-git
pkgver=$pkgver pkgver=alpha_0.0.2
pkgrel=$pkgrel pkgrel=1
epoch= epoch=
pkgdesc="Utility for parsing checks(receipts) to csv" pkgdesc="Utility for parsing checks(receipts) to csv"
arch=('x86_64') arch=('x86_64')
url="https://git.foxarmy.org/leca/checks-parser" url="https://git.foxarmy.org/leca/checks-parser"
license=('GPL-3.0-or-later') license=('GPL-3.0-or-later')
groups=() groups=()
depends=('qt6-base' 'opencv' 'nlohmann-json' 'qrencode' 'boost') depends=('qt5-base' 'opencv' 'zbar' 'nlohmann-json' 'tesseract')
makedepends=('cmake' 'make' 'gcc' 'git' 'qt6-tools') makedepends=('cmake' 'make' 'gcc' 'git' 'qt5-tools')
checkdepends=() checkdepends=()
optdepends=() optdepends=('tesseract-data-rus: scan russian checks with OCR')
provides=() provides=()
conflicts=("checks-parser-bin") conflicts=()
replaces=() replaces=()
backup=() backup=()
options=() options=()
@ -25,15 +25,12 @@ sha256sums=('SKIP')
build() { build() {
cd "$pkgname" cd "$pkgname"
mkdir build && cd build cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr .
cmake -DBUILD_TRANSLATIONS=on ..
make -j ${nproc} make -j ${nproc}
} }
package() { package() {
cd "$pkgname" cd "$pkgname"
cd build
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
##install -Dm755
make DESTDIR="$pkgdir/" PREFIX="/usr" install make DESTDIR="$pkgdir/" PREFIX="/usr" install
} }

View File

@ -1,28 +0,0 @@
FROM archlinux:latest
ARG pkgname=$name
ARG pkgver=$version
ARG revision
RUN mkdir -p /output
RUN pacman --noconfirm -Syu base base-devel sudo
RUN echo "sudo cp ${pkgname}-bin-${pkgver}-$revision-x86_64.pkg.tar.zst /output" > /deploy.sh
RUN chmod +x /deploy.sh
RUN useradd -m builder
RUN usermod -a -G wheel builder
RUN chown -R builder:builder /output
RUN echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers
USER builder
WORKDIR /home/builder/checks-parser
COPY deploy/archlinux/bin/PKGBUILD PKGBUILD
RUN sed -i "s|\$pkgname|${pkgname}-bin|g" PKGBUILD
RUN sed -i "s|\$pkgver|${pkgver}|g" PKGBUILD
RUN sed -i "s|\$pkgrel|$revision|g" PKGBUILD
RUN makepkg -s --noconfirm
#ENTRYPOINT ["bash"]
ENTRYPOINT ["bash", "/deploy.sh"]

View File

@ -1,40 +0,0 @@
# Maintainer: Leca <leca@foxarmy.org>
pkgname=$pkgname
pkgver=$pkgver
pkgrel=$pkgrel
epoch=
pkgdesc="Utility for parsing checks(receipts) to csv"
arch=('x86_64')
url="https://git.foxarmy.org/leca/checks-parser"
license=('GPL-3.0-or-later')
groups=()
depends=('qt6-base' 'opencv' 'nlohmann-json' 'qrencode' 'boost')
makedepends=('cmake' 'make' 'gcc' 'git' 'qt6-tools')
checkdepends=()
optdepends=()
provides=()
conflicts=("checks-parser-git")
replaces=()
backup=()
options=()
install=
changelog=
_releasesurl=https://git.foxarmy.org/leca/checks-parser/releases/download/$pkgver
source=("checks-parser-bin::https+$releaseurl/checks-parser-git-${pkgver}_nightly-$pkgrel-x86_64.pkg.tar.zst")
noextract=()
sha256sums=('SKIP')
build() {
cd "$pkgname"
mkdir build && cd build
cmake -DBUILD_TRANSLATIONS=on ..
make -j ${nproc}
}
package() {
cd "$pkgname"
cd build
install -Dm644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
##install -Dm755
make DESTDIR="$pkgdir/" PREFIX="/usr" install
}

View File

@ -1,28 +0,0 @@
FROM archlinux:latest
ARG pkgname=$name
ARG pkgver=$version
ARG revision
RUN mkdir -p /output
RUN pacman --noconfirm -Syu base base-devel sudo
RUN echo "sudo cp ${pkgname}-git-${pkgver}_nightly-$revision-x86_64.pkg.tar.zst /output" > /deploy.sh
RUN chmod +x /deploy.sh
RUN useradd -m builder
RUN usermod -a -G wheel builder
RUN chown -R builder:builder /output
RUN echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers
USER builder
WORKDIR /home/builder/checks-parser
COPY deploy/archlinux/git/PKGBUILD PKGBUILD
RUN sed -i "s|\$pkgname|${pkgname}-git|g" PKGBUILD
RUN sed -i "s|\$pkgver|${pkgver}_nightly|g" PKGBUILD
RUN sed -i "s|\$pkgrel|$revision|g" PKGBUILD
RUN makepkg -s --noconfirm
#ENTRYPOINT ["bash"]
ENTRYPOINT ["bash", "/deploy.sh"]

View File

@ -1,30 +0,0 @@
FROM archlinux:latest
ARG pkgname=$name
ARG pkgver=$version
ARG revision
RUN mkdir -p /output
RUN pacman --noconfirm -Syu base base-devel sudo
RUN chmod +x /deploy.sh
# RUN useradd -m builder
# RUN usermod -a -G wheel builder
# RUN chown -R builder:builder /output
# RUN echo "%wheel ALL=(ALL:ALL) NOPASSWD: ALL" > /etc/sudoers
# USER builder
# WORKDIR /home/builder/checks-parser
COPY deploy/archlinux/bin/PKGBUILD PKGBUILD
RUN sed -i "s|\$pkgname|${pkgname}-bin|g" PKGBUILD
RUN sed -i "s|\$pkgver|${pkgver}|g" PKGBUILD
RUN sed -i "s|\$pkgrel|$revision|g" PKGBUILD
RUN makepkg -s --noconfirm
RUN echo "sudo cp ${pkgname}-bin-${pkgver}-$revision-x86_64.pkg.tar.zst /output" > /deploy.sh
ENTRYPOINT ["bash", "/deploy.sh"]

View File

@ -1,33 +0,0 @@
FROM ubuntu:24.04
# Installing dependencies
RUN apt update
RUN DEBIAN_FRONTEND=noninteractive apt install -y qt6-base-dev qt6-tools-dev openssl libmbedtls-dev libopencv-dev nlohmann-json3-dev libcurl4-openssl-dev libqrencode-dev
RUN DEBIAN_FRONTEND=noninteractive apt install -y wget git cmake make gcc g++ fuse libboost-regex-dev
WORKDIR /app
#Copy only necessities
COPY assets ./assets
COPY check ./check
COPY exceptions ./exceptions
COPY goods ./goods
COPY image_redactor ./image_redactor
COPY output ./output
COPY settings ./settings
COPY scenes ./scenes
COPY net ./net
COPY translations ./translations
COPY http_server ./http_server
COPY utils ./utils
COPY widgets ./widgets
COPY email_parser ./email_parser
COPY ./*.h ./*cpp ./*.ui ./*.qrc CMakeLists.txt .
WORKDIR /app/build
RUN cmake -DBUILD_TRANSLATIONS=on .. && make -j 8
ENTRYPOINT ["bash"]

View File

@ -1,29 +0,0 @@
FROM checks_parser_base_ubuntu
#for envsubst
RUN DEBIAN_FRONTEND=noninteractive apt install -y gettext
ARG pkgname=$name
ARG pkgver=$version
ARG revision
ARG pkg=${pkgname}_$pkgver-$revision
WORKDIR /app
RUN mkdir -p $pkg/DEBIAN
RUN mkdir -p $pkg/usr/bin
RUN mkdir -p /output
COPY deploy/debian/control $pkg/DEBIAN/control
COPY deploy/debian/copy.sh .
RUN envsubst < $pkg/DEBIAN/control | tee $pkg/DEBIAN/control.output && mv $pkg/DEBIAN/control.output $pkg/DEBIAN/control
RUN envsubst < copy.sh | tee copy.sh.output && mv copy.sh.output copy.sh
RUN chmod +x copy.sh
RUN cp /app/build/checks-parser $pkg/usr/bin
RUN dpkg-deb --build $pkg
ENTRYPOINT ["./copy.sh"]

View File

@ -1,10 +0,0 @@
Package: checks-parser
Version: 3.0-1
Section: utils
Priority: optional
Architecture: amd64
Homepage: https://git.foxarmy.org/checks-parser
Depends: libc, qtbase5-dev, openssl, libmbedtls-dev, tesseract-ocr, tesseract-ocr-rus, libopencv-dev, libzbar-dev, qttools5-dev, nlohmann-json3-dev, libcurl4-openssl-dev, libtesseract-dev, libqrencode-dev
Maintainer: Leca <leca@foxarmy.org>
Description: Utility for parsing checks(receipts) to csv
Utility for extraction of content of a check(receipt) using plaintext, OCR or request to FTS (Federal Taxation Service)

View File

@ -1,2 +0,0 @@
#!/usr/bin/env bash
cp $pkg.deb /output

30
deploy/debian/package.sh Executable file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env bash
pkgname="checks-parser"
pkgver="3.0"
revision="1"
pkg=${pkgname}_$pkgver-$revision
mkdir $pkg
mkdir -p $pkg/usr/bin
cp ../../checks-parser $pkg/usr/bin
mkdir $pkg/DEBIAN
echo \
"Package: $pkgname
Version: $pkgver-$revision
Section: utils
Priority: optional
Architecture: amd64
Homepage: https://git.foxarmy.org/checks-parser
Depends: libc, qtbase5-dev, openssl, libmbedtls-dev, tesseract-ocr, tesseract-ocr-rus, libopencv-dev, libzbar-dev, qttools5-dev, nlohmann-json3-dev, libcurl4-openssl-dev, libtesseract-dev
Maintainer: Leca <leca@foxarmy.org>
Description: Utility for parsing checks(receipts) to csv
Utility for extraction of content of a check(receipt) using plaintext, OCR or request to FTS (Federal Taxation Service)"\
> $pkg/DEBIAN/control
dpkg-deb --build $pkg
rm -rf $pkg

View File

@ -1,9 +0,0 @@
FROM checks_parser_base_ubuntu
RUN DEBIAN_FRONTEND=noninteractive apt install -y autoconf automake autopoint bash bison bzip2 flex g++ g++-multilib gettext git gperf intltool libc6-dev-i386 libgdk-pixbuf2.0-dev libltdl-dev libgl-dev libpcre3-dev libssl-dev libtool-bin libxml-parser-perl lzip make openssl p7zip-full patch perl python3 python3-distutils python3-mako python3-packaging python3-pkg-resources python3-setuptools python-is-python3 ruby sed sqlite3 unzip wget xz-utils
WORKDIR /
RUN git clone https://github.com/mxe/mxe.git
RUN cd mxe && make qt

View File

@ -1,46 +0,0 @@
services:
base_ubuntu:
image: checks_parser_base_ubuntu:latest
build:
context: .
dockerfile: deploy/base-docker/ubuntu/Dockerfile
appimage:
image: checks_parser_appimage:latest
env_file: .env
build:
dockerfile: deploy/appimage/Dockerfile
context: .
args:
- pkgname=$name
- pkgver=$version
- revision=$revision
volumes:
- ./binaries:/output
depends_on:
- base_ubuntu
debian:
image: checks_parser_debian:latest
env_file: .env
build:
dockerfile: deploy/debian/Dockerfile
context: .
args:
- pkgname=$name
- pkgver=$version
- revision=$revision
volumes:
- ./binaries:/output
depends_on:
- base_ubuntu
archlinux:
image: checks_parser_base_arch:latest
env_file: .env
build:
dockerfile: deploy/archlinux/git/Dockerfile
context: .
args:
- pkgname=$name
- pkgver=$version
- revision=$revision
volumes:
- ./binaries:/output

View File

@ -1,180 +0,0 @@
#include "utils/utils.h"
#include <email_parser/emailparser.h>
#include <utils/utils.h>
#include <check/check.h>
#include <boost/regex.hpp>
#include <sstream>
#include <iostream>
#include <bits/stdc++.h>
#include <fstream>
#if __GNUC__ < 8 && __clang_major__ < 17
# include <experimental/filesystem>
using namespace std::experimental::filesystem;
#else
# include <filesystem>
using namespace std::filesystem;
#endif
std::string EmailParser::get_payload_in_email(std::string &email_content) {
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)) {
// return email_content.substr(smatch.position(), email_content.length());
// }
// return "";
}
std::multimap<std::string, std::string> EmailParser::parse_email_content_types(std::string path) {
std::ifstream input_file(path, std::ios::in);
std::string line = "";
std::multimap<std::string, std::string> 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<std::string> 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<int> EmailParser::find_base64_blocks_in_email(std::string &email_content) {
// std::string glued_together;
// for (auto c : email_content) {
// if (c == '\n') continue;
// glued_together.push_back(c);
// }
// boost::regex base64_regex("^[-A-Za-z0-9+/]*={0,3}$");
// }
EmailParser::EmailParser() {
}
std::vector<int> search_content_types_in_email_content(std::string& email_content) {
std::vector<int> 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]
// 1.2 If found 1, try decoding it, if it's not a QR code, go to [2]
// 1.3 Loop through every found entry. If not found in any, go to [2]
//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<int> 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;
// std::cout << payload << std::endl;
// if (payload == "")
// return c;
// return c;
}
Check EmailParser::parse_file(std::string path) {
// std::vector<std::string> 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<std::tuple<int, int>> message_parts_positions;
// while (contents.find("--") > 0) {
// }
return Check();
std::multimap<std::string, std::string> 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();
}

View File

@ -1,19 +0,0 @@
#ifndef CHECKS_PARSER_EMAIL_PARSER
#define CHECKS_PARSER_EMAIL_PARSER
#include <check/check.h>
#include <map>
class EmailParser {
std::string get_payload_in_email(std::string &email_content);
std::multimap<std::string, std::string> parse_email_content_types(std::string path);
// std::vector<int> 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

45
emailtextscene.cpp Normal file
View File

@ -0,0 +1,45 @@
#include "emailtextscene.h"
#include "ui_emailtextscene.h"
#include <QMessageBox>
#include <iostream>
#include <outputdialog.h>
#include <check/check.h>
EmailTextScene::EmailTextScene(QWidget *parent)
: QWidget(parent)
, ui(new Ui::EmailTextScene) {
ui->setupUi(this);
auto modules = parser.get_modules_names();
for (auto &module : modules) {
ui->store_combo_box->addItem(QString::fromStdString(module));
}
}
EmailTextScene::~EmailTextScene() {
delete ui;
}
void EmailTextScene::on_parse_button_clicked() {
std::wstring checkContent = ui->check_content->toPlainText().toStdWString();
parser.set_module(parser.search_modules()[ui->store_combo_box->currentIndex()]);
std::vector<Goods> goods = parser.parse(checkContent);
if (goods.size() == 0) {
QMessageBox infoDialog;
infoDialog.setText(tr("An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer."));
infoDialog.setIcon(QMessageBox::Critical);
infoDialog.setWindowTitle(tr("Error in parsing"));
infoDialog.exec();
return;
}
Check check;
check.add_goods(goods);
OutputDialog d(this, check);
d.show();
d.exec();
}

27
emailtextscene.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef EMAILTEXTSCENE_H
#define EMAILTEXTSCENE_H
#include "parser/parser.h"
#include <QWidget>
namespace Ui {
class EmailTextScene;
}
class EmailTextScene : public QWidget
{
Q_OBJECT
public:
explicit EmailTextScene(QWidget *parent = nullptr);
~EmailTextScene();
private slots:
void on_parse_button_clicked();
private:
Ui::EmailTextScene *ui;
Parser parser;
};
#endif // EMAILTEXTSCENE_H

View File

@ -1,13 +1,11 @@
#include "goods.h" #include "goods.h"
#include <string> #include <string>
#include <QString>
#include <QObject>
Goods::Goods() { } Goods::Goods(std::string name, double price_per_unit, double quantity) {
this->name = name;
Goods::Goods(std::string name, double price_per_unit, std::string net_weight, double quantity) : this->price_per_unit = price_per_unit;
name(name), price_per_unit(price_per_unit), this->quantity = quantity;
net_weight(net_weight), quantity(quantity) {} }
double Goods::calculate_total_price() { double Goods::calculate_total_price() {
return this->price_per_unit * this->quantity; return this->price_per_unit * this->quantity;
@ -17,59 +15,12 @@ std::string Goods::get_name() { return this->name; }
double Goods::get_quantity() { return this->quantity; } double Goods::get_quantity() { return this->quantity; }
std::string Goods::get_net_weight() { return this->net_weight; }
double Goods::get_price_per_unit() { return this->price_per_unit; } double Goods::get_price_per_unit() { return this->price_per_unit; }
void Goods::set_name(std::string name) { this->name = name; } void Goods::set_name(std::string name) { this->name = name; }
void Goods::set_name(QString name) { this->name = name.toStdString(); }
void Goods::set_quantity(double quantity) { this->quantity = quantity; } void Goods::set_quantity(double quantity) { this->quantity = quantity; }
void Goods::set_net_weight(std::string net_weight) { this->net_weight = net_weight; }
void Goods::set_net_weight(QString net_weight) { this->net_weight = net_weight.toStdString(); }
void Goods::set_price_per_unit(double price_per_unit) { void Goods::set_price_per_unit(double price_per_unit) {
this->price_per_unit = price_per_unit; this->price_per_unit = price_per_unit;
} }
Q_DECLARE_METATYPE(Goods)
QDataStream &operator<<(QDataStream &in, Goods &goods) {
in << QString::fromStdString(goods.get_name()) << goods.get_quantity() << QString::fromStdString(goods.get_net_weight()) << goods.get_price_per_unit();
return in;
}
QDataStream &operator>>(QDataStream &out, Goods &goods) {
QString name, net_weight;
double quantity, price_per_unit;
out >> name >> quantity >> net_weight >> price_per_unit;
goods.set_name(name);
goods.set_quantity(quantity);
goods.set_net_weight(net_weight);
goods.set_price_per_unit(price_per_unit);
return out;
}
QDataStream &operator<<(QDataStream &stream, std::vector<Goods> &goods) {
stream << (unsigned int )goods.size();
for (Goods &g : goods) {
stream << g;
}
return stream;
}
QDataStream &operator>>(QDataStream &stream, std::vector<Goods> &goods) {
unsigned int size;
stream >> size;
for (unsigned int i = 0 ; i < size; i ++) {
Goods g = Goods();
stream >> g;
goods.push_back(g);
}
return stream;
}

View File

@ -1,38 +1,24 @@
#ifndef GOODS_H #ifndef GOODS_H
#define GOODS_H #define GOODS_H
#include <QDataStream>
#include <string> #include <string>
#include <vector>
class Goods { class Goods
{
std::string name; std::string name;
double quantity; // by weight or by the piece double quantity; // by weight or by the piece
std::string net_weight; // will contain values like "5мл" or "10г"
double price_per_unit; double price_per_unit;
public: public:
Goods(); Goods(std::string, double, double);
Goods(std::string name, double quantity, std::string net_weight,
double price_per_unit);
double calculate_total_price(); double calculate_total_price();
std::string get_name(); std::string get_name();
double get_quantity(); double get_quantity();
std::string get_net_weight();
double get_price_per_unit(); double get_price_per_unit();
void set_name(std::string); void set_name(std::string);
void set_name(QString);
void set_quantity(double); void set_quantity(double);
void set_net_weight(std::string);
void set_net_weight(QString);
void set_price_per_unit(double); void set_price_per_unit(double);
}; };
QDataStream &operator<<(QDataStream &, Goods &);
QDataStream &operator>>(QDataStream &, Goods &);
QDataStream &operator<<(QDataStream &, std::vector<Goods> &);
QDataStream &operator>>(QDataStream &, std::vector<Goods> &);
#endif // GOODS_H #endif // GOODS_H

View File

@ -1,113 +0,0 @@
#include "http_server.h"
#include <qobjectdefs.h>
#include <unistd.h>
#include <utils/utils.h>
#include <iostream>
#include <mainwindow.h>
#include <thread>
void HttpServer::generateRandomPort() {
port = rand() % (65535 - 1024) + 1024;
}
HttpServer::HttpServer(QWidget *caller) : caller(caller) {
started = false;
port = 8080;
}
HttpServer::~HttpServer() {
started = false;
shutdown(serverSocket, SHUT_RDWR);
close(serverSocket);
for (auto &thread : clientHandlersThreads) {
thread.join();
}
listenClientsThread.join();
}
int HttpServer::start() {
unsigned short number_of_retries = 0;
while (number_of_retries < 10) {
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0) {
std::cerr << QObject::tr("Could not obtain socket.").toStdString() << std::endl;
number_of_retries ++;
continue;
}
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons(port);
serverAddress.sin_addr.s_addr = INADDR_ANY;
if (bind(serverSocket, (sockaddr *)&serverAddress, sizeof(serverAddress)) < 0) {
std::cerr << QObject::tr("Port ").toStdString() << port << QObject::tr(" seems to be occupied. Trying to generate another one").toStdString() << std::endl;
number_of_retries ++;
generateRandomPort();
continue;
}
if (listen(serverSocket, 1) < 0) {
std::cerr << QObject::tr("Could not listen port.").toStdString() << std::endl;
number_of_retries ++;
generateRandomPort();
continue;
}
started = true;
std::cout << QObject::tr("Listening on port: ").toStdString() << port << std::endl;
listenClientsThread = std::thread(&HttpServer::acceptClients, this);
return 0;
}
return -1;
}
void HttpServer::handleClient(int clientSocket) {
std::string localIp;
try {
localIp = get_local_ip_address();
} catch(std::exception e) {
std::cerr << e.what() << std::endl;
close(clientSocket);
return;
}
char buffer[256] = {0};
recv(clientSocket, buffer, 256, 0);
std::string response = "HTTP/1.1 301 Moved Permanently\r\n"
"Content-Length: 0\r\n"
"Keep-Alive: timeout=5, max=100\r\n"
"Location: binaryeye://scan/?ret=http://" + get_local_ip_address() + ":" + std::to_string(port) + "/?result={RESULT}\r\n"
"\r\n";
if (send(clientSocket, response.c_str(), response.length(), 0) < 0) {
std::cerr << response.c_str() << std::endl;
std::cerr << response.length() << std::endl;
std::cerr << QObject::tr("Could not send message").toStdString() << std::endl;
}
emit ((MainWindow *)caller)->httpNewMessage(QString::fromStdString(std::string(buffer)));
}
void HttpServer::acceptClients() {
while (true) {
if (!started) return;
int clientSocket = accept(serverSocket, nullptr, nullptr);
if (!started) return;
clientHandlersThreads.push_back (
std::thread(&HttpServer::handleClient, this, clientSocket)
);
}
}
unsigned short HttpServer::getPort() {
return port;
}
bool HttpServer::isStarted() {
return started;
}

View File

@ -1,37 +0,0 @@
#ifndef CHECKS_PARSER_HTTP_SERVER
#define CHECKS_PARSER_HTTP_SERVER
#include <netinet/in.h>
#include <QWidget>
#include <thread>
#include <vector>
class HttpServer {
private:
unsigned short port;
int serverSocket;
sockaddr_in serverAddress;
QWidget* caller;
std::thread listenClientsThread;
std::vector<std::thread> clientHandlersThreads;
bool started;
void generateRandomPort();
public:
HttpServer(QWidget *caller);
~HttpServer();
int start();
void stop();
void handleClient(int clientSocket);
void acceptClients();
unsigned short getPort();
bool isStarted();
};
#endif //CHECKS_PARSER_HTTP_SERVER

View File

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 138 KiB

View File

Before

Width:  |  Height:  |  Size: 649 KiB

After

Width:  |  Height:  |  Size: 649 KiB

22
image/checkimage.cpp Normal file
View File

@ -0,0 +1,22 @@
#include <opencv2/imgcodecs.hpp>
#include <string>
#include <tesseract/baseapi.h>
#include "checkimage.h"
CheckImage::CheckImage(std::string path) {
this->path = path;
}
std::string CheckImage::parse_text() {
std::string result;
tesseract::TessBaseAPI *ocr = new tesseract::TessBaseAPI();
ocr->Init(NULL, "rus", tesseract::OEM_LSTM_ONLY);
ocr->SetPageSegMode(tesseract::PSM_AUTO);
cv::Mat im = cv::imread(this->path, cv::IMREAD_COLOR);
ocr->SetImage(im.data, im.cols, im.rows, 3, im.step);
result = std::string(ocr->GetUTF8Text());
return result;
}

15
image/checkimage.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef CHECKIMAGE_H
#define CHECKIMAGE_H
#include <string>
class CheckImage {
std::string path;
public:
CheckImage(std::string path);
std::string parse_text();
};
#endif // CHECKIMAGE_H

View File

@ -32,7 +32,8 @@ void ImageRedactor::wheelEvent(QWheelEvent *event) {
void ImageRedactor::mousePressEvent(QMouseEvent *event) void ImageRedactor::mousePressEvent(QMouseEvent *event)
{ {
// Pan the image // Pan the image
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton)
{
setDragMode(QGraphicsView::ScrollHandDrag); setDragMode(QGraphicsView::ScrollHandDrag);
} }
} }
@ -40,7 +41,8 @@ void ImageRedactor::mousePressEvent(QMouseEvent *event)
void ImageRedactor::mouseReleaseEvent(QMouseEvent *event) void ImageRedactor::mouseReleaseEvent(QMouseEvent *event)
{ {
// Reset the drag mode // Reset the drag mode
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton)
{
setDragMode(QGraphicsView::NoDrag); setDragMode(QGraphicsView::NoDrag);
} }
} }

201
main.cpp
View File

@ -1,12 +1,9 @@
#include "email_parser/emailparser.h" #include "mainwindow.h"
#include <mainwindow.h> #include "net/net.h"
#include <net/net.h> #include "settings/settings.h"
#include <settings/settings.h> #include "utils/utils.h"
#include <utils/utils.h>
#include <QApplication> #include <QApplication>
#ifdef BUILD_OFD_MODE #include <curl/curl.h>
# include <curl/curl.h>
#endif
#include <iostream> #include <iostream>
#if __GNUC__ < 8 && __clang_major__ < 17 #if __GNUC__ < 8 && __clang_major__ < 17
# include <experimental/filesystem> # include <experimental/filesystem>
@ -19,24 +16,23 @@
#include <QFile> #include <QFile>
#include <QStackedLayout> #include <QStackedLayout>
#include <QTextStream> #include <QTextStream>
#ifdef BUILD_TRANSLATIONS #include <QTranslator>
# include <QTranslator> #include <emailtextscene.h>
#endif #include <ocrscene.h>
#include <settingsdialog.h> #include <ofdscene.h>
#ifdef BUILD_EMAIL_MODE #include <qpushbutton.h>
// #include <vmime/vmime.hpp> #include <parser/parser.h>
#endif
#include <QPushButton>
#include <utils/base64.h> static QWidget *loadUI(QWidget *parent, std::string filename) {
QUiLoader loader;
QFile file(QString::fromStdString(filename));
file.open(QIODevice::ReadOnly);
return loader.load(&file, parent);
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
curl_global_init(CURL_GLOBAL_ALL);
qRegisterMetaType<Check>("Check");
std::string program_data_path = get_path_relative_to_home(".local/share/checks_parser");
create_directories(program_data_path);
srand(time(0)); srand(time(0));
QApplication app(argc, argv); QApplication app(argc, argv);
@ -46,14 +42,10 @@ int main(int argc, char *argv[]) {
Settings s(settings_file_path); Settings s(settings_file_path);
#ifdef BUILD_TRANSLATIONS
QTranslator translator; QTranslator translator;
QString lang = "en_US"; QString lang = "en_US";
bool languageSettingPresent = false; if (s.get_all_settings().contains("language")) {
languageSettingPresent = s.get_all_settings().find("language") != s.get_all_settings().end();
if (languageSettingPresent) {
lang = QString::fromStdString(s.get_all_settings()["language"]); lang = QString::fromStdString(s.get_all_settings()["language"]);
} else if (translator.load(":/translation/"+QLocale::system().name()+".qm")) { } else if (translator.load(":/translation/"+QLocale::system().name()+".qm")) {
lang = QLocale::system().name(); lang = QLocale::system().name();
@ -61,16 +53,155 @@ int main(int argc, char *argv[]) {
lang = QString::fromStdString("en_US"); lang = QString::fromStdString("en_US");
} }
std::cout << QObject::tr("Using locale: ").toStdString() << lang.toStdString() << std::endl; std::cout << "Using locale: " << lang.toStdString() << std::endl;
if (!translator.load(":/translation/" + lang + ".qm")) { translator.load(":/translation/" + lang + ".qm");
std::cerr << "Could not load translation!!" << std::endl;
}
app.installTranslator(&translator); app.installTranslator(&translator);
#endif
MainWindow w;
QUiLoader loader;
QWidget *window = new QWidget();
QStackedLayout *sceneLayout = new QStackedLayout;
// Main Window setup
QWidget *mainwindowscene = loadUI(window, ":/scenes/scenes/mainwindow.ui");
// Main Window buttons setup
QPushButton *text_from_email_button = ((MainWindow *)mainwindowscene)->findChild<QPushButton *>("text_from_email_button");
QPushButton *ocr_button = ((MainWindow *)mainwindowscene)->findChild<QPushButton *>("ocr_button");
QPushButton *ofd_button = ((MainWindow *)mainwindowscene)->findChild<QPushButton *>("ofd_button");
QObject::connect(text_from_email_button, &QPushButton::clicked, [&]() {
// Text from email scene
sceneLayout->setCurrentIndex(1);
sceneLayout->widget(1)->show();
});
QObject::connect(ocr_button, &QPushButton::clicked, [&]() {
// OCR scene
sceneLayout->setCurrentIndex(2);
sceneLayout->widget(2)->show();
});
QObject::connect(ofd_button, &QPushButton::clicked, [&]() {
// OFD scene
sceneLayout->setCurrentIndex(3);
sceneLayout->widget(3)->show();
});
EmailTextScene *emailTextScene = new EmailTextScene();
OCRScene *ocrscene = new OCRScene();
OFDScene *ofdscene = new OFDScene();
sceneLayout->addWidget(mainwindowscene);
sceneLayout->addWidget(emailTextScene);
sceneLayout->addWidget(ocrscene);
sceneLayout->addWidget(ofdscene);
//Setting all back buttons
for (uint32_t sceneIndex = 0; sceneIndex < sceneLayout->count(); sceneIndex ++) {
auto scene = sceneLayout->widget(sceneIndex);
QPushButton *back_button = scene->findChild<QPushButton *>("back_button");
if (back_button == nullptr) continue;
QObject::connect(back_button, &QPushButton::clicked, [&]() {
sceneLayout->setCurrentIndex(0);
});
}
window->setLayout(sceneLayout);
window->show();
app.exec();
return 0;
// QApplication app(argc, argv);
// QWidget *window = new QWidget;
// QStackedLayout *stackedLayout = new QStackedLayout;
// QWidget *scene1 = new QWidget;
// QWidget *scene2 = new QWidget;
// // Add some widgets to each scene
// QPushButton *button1 = new QPushButton("Switch to Scene 2");
// scene1->setLayout(new QVBoxLayout);
// scene1->layout()->addWidget(button1);
// QPushButton *button2 = new QPushButton("Switch to Scene 1");
// scene2->setLayout(new QVBoxLayout);
// scene2->layout()->addWidget(button2);
// // Add the scenes to the stacked layout
// stackedLayout->addWidget(scene1);
// stackedLayout->addWidget(scene2);
// // Set the layout of the window
// window->setLayout(stackedLayout);
// // Connect the buttons to switch scenes
// QObject::connect(button1, &QPushButton::clicked, [&]() {
// stackedLayout->setCurrentIndex(1);
// });
// QObject::connect(button2, &QPushButton::clicked, [&]() {
// stackedLayout->setCurrentIndex(0);
// });
// window->show();
// app.exec();
// return 0;
curl_global_init(CURL_GLOBAL_ALL);
std::string program_data_path = get_path_relative_to_home(".local/share/checks_parser");
create_directories(program_data_path);
// std::string settings_file_path =
// get_path_relative_to_home(".local/share/checks_parser/settings.json");
// Settings s(settings_file_path);
Net n;
Parser p;
std::vector<std::string> stores_updates = p.check_updates();
for (const std::string &update : stores_updates) {
std::cout << "Downloading "
<< s.get_setting("stores_modules_url") + update << " to "
<< get_path_relative_to_home(s.get_setting("stores_modules_dir") +
"/" + update)
<< std::endl;
n.get_file(s.get_setting("stores_modules_url") + "/" + update,
get_path_relative_to_home(s.get_setting("stores_modules_dir") +
"/" + update));
}
QApplication a(argc, argv);
// QTranslator translator;
// QString lang = "en_US";
// if (s.get_all_settings().contains("language")) {
// lang = QString::fromStdString(s.get_all_settings()["language"]);
// } else if (translator.load(":/translation/"+QLocale::system().name()+".qm")) {
// lang = QLocale::system().name();
// } else {
// lang = QString::fromStdString("en_US");
// }
// std::cout << "Using locale: " << lang.toStdString() << std::endl;
// translator.load(":/translation/" + lang + ".qm");
// a.installTranslator(&translator);
MainWindow w;
w.update();
w.show(); w.show();
return app.exec(); return a.exec();
} }

View File

@ -1,318 +1,15 @@
#include <mainwindow.h> #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include <iostream>
#include <QFileDialog>
#include <QMessageBox>
#include <QPixmap>
#include <settingsdialog.h>
#include <string>
#include <boost/regex.hpp>
#ifdef BUILD_OFD_LOCAL_QR_SCAN
# include <adjustpicturedialog.h>
#include <checkqueuetablemodel.h>
#endif
#include <outputdialog.h>
#include <solvecaptchadialog.h>
#include <net/net.h>
#include <utils/utils.h>
#include <exceptions/ofdrequestexception.h>
#ifdef BUILD_OFD_BINARYEYE_SCAN
# include <qrencode.h>
# include <stdlib.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netinet/in.h>
#endif
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QWidget(parent) : QWidget(parent)
, ui(new Ui::MainWindow) { , ui(new Ui::MainWindow) {
ui->setupUi(this); ui->setupUi(this);
ui->stop_server_button->hide();
ui->checks_to_parse_label->hide(); std::cout << "test" << std::endl;
ui->checkQueueTable->hide();
ui->deleteSelectedButton->hide();
ui->parse_button->hide();
model = new CheckQueueTableModel(&checks, this);
ui->checkQueueTable->setModel(model);
ui->checkQueueTable->viewport()->setAcceptDrops(true);
ui->checkQueueTable->setDragDropMode(QAbstractItemView::DragDrop);
ui->checkQueueTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
#ifdef BUILD_OFD_BINARYEYE_SCAN
QObject::connect(this, &MainWindow::httpErrorOccured, this, &MainWindow::notifyHttpServerFailure);
connect(this, SIGNAL(httpNewMessage(QString)), this, SLOT(httpNewMessageHandler(QString)));
#endif
#ifndef BUILD_EMAIL_MODE
ui->parse_email_button->hide();
ui->or_label_2->hide();
#endif
#ifndef BUILD_OFD_BINARYEYE_SCAN
ui->or_label_2->hide();
ui->binary_eye_button->hide();
#endif
#ifndef BUILD_OFD_LOCAL_QR_SCAN
ui->or_label_1->hide();
ui->choose_image_button->hide();
#endif
QObject::connect(ui->settings_button, &QPushButton::clicked, [&]() {
SettingsDialog d;
d.show();
d.exec();
});
}
#ifdef BUILD_OFD_BINARYEYE_SCAN
void MainWindow::startHttpServer() {
server = new HttpServer(this);
if (server->start() < 0) {
emit httpErrorOccured();
}
}
void MainWindow::on_binary_eye_button_clicked() {
httpServerThread = new std::thread(&MainWindow::startHttpServer, this);
ui->binary_eye_button->setEnabled(false);
ui->stop_server_button->show();
while (!server->isStarted()) {}
std::string localIp;
try {
localIp = get_local_ip_address();
} catch(std::exception e) {
std::cerr << e.what() << std::endl;
return;
}
std::string connectionString = "binaryeye://scan?ret=http://" + localIp + ":" + std::to_string(server->getPort()) + "/?result={RESULT}";
generate_qr_code(connectionString);
QMessageBox infoDialog = QMessageBox();
infoDialog.setText(QString::fromStdString(connectionString));
infoDialog.setIconPixmap(QPixmap(QString::fromStdString(get_path_relative_to_home(".local/share/checks_parser/binaryeye_connection.png"))).scaled(400, 400, Qt::KeepAspectRatio));
infoDialog.setWindowTitle(tr("QR code for binaryeye to connect"));
infoDialog.addButton(tr("I've scanned"), QMessageBox::ButtonRole::AcceptRole);
infoDialog.exec();
}
void MainWindow::notifyHttpServerFailure() {
QMessageBox infoDialog = QMessageBox();
infoDialog.setText(tr("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."));
infoDialog.setIcon(QMessageBox::Warning);
infoDialog.setWindowTitle(tr("Could not start http server."));
infoDialog.exec();
}
void MainWindow::on_stop_server_button_clicked() {
delete server;
ui->stop_server_button->hide();
ui->binary_eye_button->setEnabled(true);
}
void MainWindow::httpNewMessageHandler(QString message) {
std::string parametersString = split(message.toStdString(), " ")[1];
//erase /?result= from the string
parametersString.erase(0, parametersString.find("=") + 1);
//TODO: punycode %26 %3D
parametersString = boost::regex_replace(parametersString, boost::regex("%26"), "&");
parametersString = boost::regex_replace(parametersString, boost::regex("%3D"), "=");
std::vector<std::string> parameters = split(parametersString, "&");
std::map<std::string, std::string> paramsMap;
for (auto &parameter : parameters) {
std::vector<std::string> values = split(parameter, "=");
paramsMap.insert(std::pair<std::string, std::string> (values[0], values[1]));
}
emit onDataDecode(paramsMap);
}
#endif //ifdef BUILD_OFD_BINARYEYE_SCAN
#ifdef BUILD_OFD_LOCAL_QR_SCAN
void MainWindow::on_choose_image_button_clicked() {
QString filename = QFileDialog::getOpenFileName();
if (filename == "") {
QMessageBox infoDialog;
infoDialog.setText(tr("Please, select a picture where QR code that contains info about check is present"));
infoDialog.setIcon(QMessageBox::Critical);
infoDialog.setWindowTitle(tr("Picture was not selected"));
infoDialog.exec();
return;
}
ui->info_label->setText(tr("Selected image: ") + filename);
AdjustPictureDialog dialog = AdjustPictureDialog(this, filename.toStdString());
connect(&dialog, &AdjustPictureDialog::decodedData, this, &MainWindow::onDataDecode);
dialog.exec();
}
#endif //ifdef BUILD_OFD_LOCAL_QR_SCAN
void MainWindow::onDataDecode(std::map<std::string, std::string> data) {
ui->fn_line_edit->setText(QString::fromStdString(data["fn"]));
ui->fd_line_edit->setText(QString::fromStdString(data["i"]));
ui->fi_line_edit->setText(QString::fromStdString(data["fp"]));
QString extractedDateTime = QString::fromStdString(data["t"]);
//TODO: some QRs contain datetime in format yyyyMMddThhmmss. Perhaps there is more different formats, should write function to detect them.
QDateTime datetime = QDateTime::fromString(extractedDateTime, "yyyyMMddThhmm");
if (datetime == QDateTime::fromString(extractedDateTime, "20000101T1200")) {
datetime = QDateTime::fromString(extractedDateTime, "yyyyMMddThhmmss");
}
ui->purchase_datetime_edit->setDateTime(datetime);
int type = std::stoi(data["n"]);
ui->operation_type_combo_box->setCurrentIndex(type - 1);
std::string total = data["s"];
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;
}
unsigned int newRowIndex = checks.size();
model->insertRows(newRowIndex, 1);
checks.at(newRowIndex) = *new_check;
emit model->dataChanged(model->index(newRowIndex, 0), model->index(newRowIndex, 1));
delete new_check;
if (checks.size() > 0) {
ui->checkQueueTable->show();
ui->checks_to_parse_label->show();
ui->deleteSelectedButton->show();
ui->parse_button->show();
}
}
Check *MainWindow::parse_new_check() {
Net net;
std::string solved_captcha = "";
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();
Check* check = new Check();
try {
std::string check_content = net.fetch_check_data_from_ofdru(
ui->fn_line_edit->text().toStdString(),
ui->fd_line_edit->text().toStdString(),
ui->fi_line_edit->text().toStdString(),
ui->purchase_datetime_edit->dateTime().toString(Qt::ISODate).toStdString(),
ui->operation_type_combo_box->currentIndex() + 1,
// In the request to ofd.ru, total is in a format with 2 last digits represent decimal part of a number.
ui->total_spin_box->text().toDouble() * 100,
solved_captcha
);
(*check) = parseOfdRuAnswer(check_content);
check->set_date(ui->purchase_datetime_edit->dateTime().toString(Qt::ISODate).toStdString());
check->set_fn(ui->fn_line_edit->text().toStdString());
check->set_fd(ui->fd_line_edit->text().toStdString());
check->set_fi(ui->fi_line_edit->text().toStdString());
check->set_operation_type(OperationType(ui->operation_type_combo_box->currentIndex() + 1));
return check;
} catch(OfdRequestException e) {
if (!strcmp(e.what(), "Incorrect captcha")) {
QMessageBox infoDialog;
infoDialog.setText(tr("Captcha was not solved correctly!"));
infoDialog.setIcon(QMessageBox::Critical);
infoDialog.setWindowTitle(tr("Captcha is incorrect"));
infoDialog.exec();
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 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 nullptr;
}
}
}
return nullptr;
} }
MainWindow::~MainWindow() { MainWindow::~MainWindow() {
delete ui; delete ui;
} }
void MainWindow::on_deleteSelectedButton_clicked() {
QItemSelectionModel *select = ui->checkQueueTable->selectionModel();
std::vector<unsigned int> to_delete_positions = {};
for (auto &row : select->selectedIndexes()) {
if (row.column() != 0) continue;
to_delete_positions.push_back(row.row());
}
std::sort(to_delete_positions.begin(), to_delete_positions.end(), std::greater<unsigned int>());
for (unsigned int position : to_delete_positions) {
model->removeRows(position, 1);
}
emit model->dataChanged(model->index(checks.size(), 0), model->index(checks.size() + to_delete_positions.size(), 1));
ui->checkQueueTable->clearSelection();
}

219
mainwindow.cpp.old Normal file
View File

@ -0,0 +1,219 @@
#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include "check/check.h"
#include "exceptions/ofdrequestexception.h"
#include "goods/goods.h"
#include "outputdialog.h"
#include "adjustpicturedialog.h"
#include "settingsdialog.h"
#include "solvecaptchadialog.h"
#include <QFileDialog>
#include <QMessageBox>
#include "image/checkimage.h"
#include "utils/utils.h"
#include <opencv2/objdetect.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <zbar.h>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
// this->setupStoresList();
}
MainWindow::~MainWindow() { delete ui; }
// void MainWindow::setupStoresList() {
// parser = *(new Parser());
// std::vector<std::string> modules_names = parser.search_modules();
// for (std::string name : modules_names) {
// StoreModule m(name);
// std::wstring module_name = m.get_name();
// QString s = QString::fromStdWString(module_name);
// ui->storeType->addItem(s);
// }
// }
// std::string MainWindow::makeRequestToOfd(std::string captcha) {
// std::string checkContent = Net().fetch_check_data_from_ofdru(
// ui->fn_edit->text().toStdString(),
// ui->fd_edit->text().toStdString(),
// ui->fi_edit->text().toStdString(),
// ui->dateTimeEdit->dateTime().toString(Qt::ISODate).toStdString(),
// ui->fundIncomeCombo->currentIndex() + 1,
// // In the request to ofd.ru, total is in a strange format, like a string of a format where 2 last digits represent decimal part of a number.
// ui->total_edit->text().toDouble() * 100,
// captcha);
// return checkContent;
// }
// void MainWindow::on_parseButton_clicked() {
// QString s;
// switch (ui->tabWidget->currentIndex()) {
// case 0:
// s = ui->checkContent->toPlainText();
// break;
// case 1:
// s = ui->checkContentFromImage->toPlainText();
// break;
// case 2:
// Net().get_captcha_from_ofdru();
// std::string solved_captcha = "";
// bool success = true;
// bool is_captcha_solved = true;
// do {
// SolveCaptchaDialog dialog = SolveCaptchaDialog(this, &solved_captcha);
// dialog.exec();
// is_captcha_solved = true;
// try {
// std::string check_content = makeRequestToOfd(solved_captcha);
// check = parseOfdRuAnswer(check_content);
// } 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;
// } 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;
// } 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;
// }
// }
// } while (!is_captcha_solved);
// if (success) {
// OutputDialog d = OutputDialog(this, check);
// d.exec();
// }
// return;
// }
// std::wstring check_plaintext = s.toStdWString();
// parser.set_module(parser.search_modules()[0]);
// std::vector<Goods> c = parser.parse(check_plaintext);
// if (c.size() == 0) {
// QMessageBox infoDialog;
// infoDialog.setText(tr("An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer."));
// infoDialog.setIcon(QMessageBox::Critical);
// infoDialog.setWindowTitle(tr("Error in parsing"));
// infoDialog.exec();
// return;
// }
// for (auto& g : c) {
// check.add_goods(g);
// }
// OutputDialog d = OutputDialog(this, check);
// d.show();
// d.exec();
// }
// void MainWindow::on_storeType_currentIndexChanged(int index) {
// std::string module = parser.search_modules()[index];
// parser.set_module(module);
// }
// void MainWindow::on_preferencesButton_clicked() {
// SettingsDialog s = SettingsDialog();
// s.show();
// s.exec();
// }
// void MainWindow::on_chooseImageButton_ofd_clicked() {
// QString filename = QFileDialog::getOpenFileName();
// if (filename == "") {
// QMessageBox infoDialog;
// infoDialog.setText(tr("Please, select a picture where QR code that contains info about check is present"));
// infoDialog.setIcon(QMessageBox::Critical);
// infoDialog.setWindowTitle(tr("Picture was not selected"));
// infoDialog.exec();
// return;
// }
// std::string new_text = "Selected: " + filename.toStdString();
// ui->pathLabel_ofd->setText(QString::fromStdString(new_text));
// AdjustPictureDialog dialog = AdjustPictureDialog(this, filename.toStdString());
// connect(&dialog, &AdjustPictureDialog::decodedData, this, &MainWindow::onDecodedData);
// dialog.exec();
// ui->picture_ofd->setPixmap(QPixmap(filename));
// ui->picture_ofd->setScaledContents(true);
// }
// void MainWindow::onDecodedData(std::string data) {
// std::string delimiter = "&";
// std::vector<std::string> dataSplit = split(data, delimiter);
// std::cout << data << std::endl;
// ui->fn_edit->setText(QString::fromStdString(dataSplit[2]));
// ui->fd_edit->setText(QString::fromStdString(dataSplit[3]));
// ui->fi_edit->setText(QString::fromStdString(dataSplit[4]));
// QString extractedDateTime = QString::fromStdString(split(dataSplit[0], "=")[1]);
// QDateTime datetime = QDateTime::fromString(extractedDateTime, "yyyyMMddThhmm");
// ui->dateTimeEdit->setDateTime(datetime);
// int type = std::stoi(split(dataSplit[5], "=")[1]);
// ui->fundIncomeCombo->setCurrentIndex(type - 1);
// std::string total = split(dataSplit[1], "=")[1];
// ui->total_edit->setText(QString::fromStdString(total));
// }
// void MainWindow::on_chooseImageButton_ocr_clicked()
// {
// QString filename = QFileDialog::getOpenFileName();
// if (filename == "") {
// QMessageBox infoDialog;
// infoDialog.setText(tr("Please, select a picture to scan"));
// infoDialog.setIcon(QMessageBox::Critical);
// infoDialog.setWindowTitle(tr("Picture was not selected"));
// infoDialog.exec();
// return;
// }
// std::string new_text = "Selected: " + filename.toStdString();
// ui->pathLabel_ocr->setText(QString::fromStdString(new_text));
// CheckImage i(filename.toStdString());
// std::string parsed = i.parse_text();
// ui->picture_ocr->setPixmap(QPixmap(filename));
// ui->picture_ocr->setScaledContents(true);
// ui->checkContentFromImage->setPlainText(QString::fromStdString(parsed));
// }

View File

@ -2,13 +2,8 @@
#define MAINWINDOW_H #define MAINWINDOW_H
#include <QWidget> #include <QWidget>
#include <QEvent> #include <qevent.h>
#include <thread> #include <QtUiTools/quiloader.h>
#include <checkqueuetablemodel.h>
#include <http_server/http_server.h>
#include <check/check.h>
namespace Ui { namespace Ui {
class MainWindow; class MainWindow;
@ -21,50 +16,10 @@ class MainWindow : public QWidget
public: public:
explicit MainWindow(QWidget *parent = nullptr); explicit MainWindow(QWidget *parent = nullptr);
~MainWindow(); ~MainWindow();
Check *parse_new_check();
CheckQueueTableModel *model;
#ifdef BUILD_OFD_BINARYEYE_SCAN
void startHttpServer();
#endif
signals:
void httpNewMessage(QString message);
void httpErrorOccured();
void deleteCheckFromList(Check&);
private slots:
#ifdef BUILD_OFD_LOCAL_QR_SCAN
void on_choose_image_button_clicked();
#endif
void onDataDecode(std::map<std::string, std::string>);
void on_parse_button_clicked();
#ifdef BUILD_OFD_BINARYEYE_SCAN
void on_binary_eye_button_clicked();
void notifyHttpServerFailure();
void on_stop_server_button_clicked();
void httpNewMessageHandler(QString message);
// void deleteCheckFromListHandler(Check&);
#endif
#ifdef BUILD_EMAIL_MODE
void on_parse_email_button_clicked();
#endif
void on_add_new_check_button_clicked();
void on_deleteSelectedButton_clicked();
private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
std::vector<Check> checks; private slots:
#ifdef BUILD_OFD_BINARYEYE_SCAN private:
std::thread *httpServerThread;
HttpServer *server = NULL;
#endif
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

44
mainwindow.h.old Normal file
View File

@ -0,0 +1,44 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "check/check.h"
#include "parser/parser.h"
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT
Check check;
Parser parser;
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
// void setupStoresList();
// Check get_check();
// void onDecodedData(std::string);
// std::string makeRequestToOfd(std::string captcha);
private slots:
// void on_parseButton_clicked();
// void on_storeType_currentIndexChanged(int index);
// void on_preferencesButton_clicked();
// void on_chooseImageButton_ofd_clicked();
// void on_chooseImageButton_ocr_clicked();
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

View File

@ -3,8 +3,5 @@
<file>assets/icons/email-text.svg</file> <file>assets/icons/email-text.svg</file>
<file>assets/icons/OCR.svg</file> <file>assets/icons/OCR.svg</file>
<file>assets/icons/OFD.svg</file> <file>assets/icons/OFD.svg</file>
<file>assets/icons/icon.png</file>
<file>assets/icons/icon.svg</file>
<file>assets/icons/settings.svg</file>
</qresource> </qresource>
</RCC> </RCC>

8
modules/magnit.json Normal file
View File

@ -0,0 +1,8 @@
{
"name":"Магнит",
"goods_name_regex": "([\\(\\)\\%\\*a-zA-Z0-9\u0401\u0451\u0410-\u044f \\.\\-\/]{17,100})",
"goods_price_regex": "[0-9]{0,4}[^%]\\.[0-9]{2} ",
"goods_quantity_regex": "([0-9]{0,4}[^%]\\.[0-9]{3} )|(\t\\d )",
"check_start_regex": "",
"check_end_regex":""
}

8
modules/pyaterochka.json Normal file
View File

@ -0,0 +1,8 @@
{
"name":"Пятёрочка",
"goods_name_regex": "([\\(\\)\\%\\*a-zA-Z0-9\u0401\u0451\u0410-\u044f \\.\\-\/]{17,100})",
"goods_price_regex": "[0-9]{0,4}[^%]\\.[0-9]{2} ",
"goods_quantity_regex": "([0-9]{0,4}[^%]\\.[0-9]{3} )|(\t\\d )",
"check_start_regex": "КАССОВЫЙ ЧЕК\nприход",
"check_end_regex": "Итог\\:.{0,3}[0-9]{0,6}\\.[0-9]{2}"
}

View File

@ -1,15 +1,11 @@
#include <net/net.h> #include "net.h"
#include <curl/curl.h> #include <curl/curl.h>
#include <utils/utils.h> #include "../utils/utils.h"
#include <iostream> #include <iostream>
#include <vector>
#include <regex>
Net::Net() {} struct data {};
size_t writeCallback(void* contents, size_t size, size_t nmemb, void* userp) {
size_t totalSize = size * nmemb;
((std::string*)userp)->append(std::string((char*)contents));
return totalSize;
}
size_t write_data(void *buffer, size_t size, size_t nmemb, void *filename) { size_t write_data(void *buffer, size_t size, size_t nmemb, void *filename) {
FILE *f = fopen(((std::string *)filename)->c_str(), "w"); FILE *f = fopen(((std::string *)filename)->c_str(), "w");
@ -20,6 +16,44 @@ size_t write_data(void *buffer, size_t size, size_t nmemb, void *filename) {
return written; return written;
} }
Net::Net() {}
void write_modules(void *buffer, size_t size, size_t nmemb, void *modules) {
std::vector<std::string> *modules_vector =
(std::vector<std::string> *)modules;
std::string to_parse = std::string((char*)buffer);
std::regex r("(?!\\\")\\w+\\.json(?!\\\")", std::regex::collate);
std::smatch res;
std::string::const_iterator search(to_parse.cbegin());
while (std::regex_search(search, to_parse.cend(), res, r)) {
modules_vector->push_back(res[0]);
search = res.suffix().first;
}
}
size_t writeCallback(void* contents, size_t size, size_t nmemb, void* userp) {
size_t totalSize = size * nmemb;
((std::string*)userp)->append(std::string((char*)contents));
return totalSize;
}
std::vector<std::string> Net::get_all_modules(std::string url) {
CURL *handle = curl_easy_init();
curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_modules);
std::vector<std::string> modules {};
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &modules);
curl_easy_perform(handle);
return modules;
}
void Net::get_file(std::string url, std::string filename) { void Net::get_file(std::string url, std::string filename) {
CURL *handle = curl_easy_init(); CURL *handle = curl_easy_init();

View File

@ -5,12 +5,12 @@
#include <vector> #include <vector>
size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp); size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp);
size_t writeCallback(void* contents, size_t size, size_t nmemb, void* userp);
class Net class Net
{ {
public: public:
Net(); Net();
std::vector<std::string> get_all_modules(std::string url);
void get_file(std::string url, std::string filename); void get_file(std::string url, std::string filename);
std::string fetch_check_data_from_ofdru(std::string fn, std::string fd, std::string fi, std::string datetime, int operation, int total, std::string captcha); std::string fetch_check_data_from_ofdru(std::string fn, std::string fd, std::string fi, std::string datetime, int operation, int total, std::string captcha);
void get_captcha_from_ofdru(); void get_captcha_from_ofdru();

73
ocrscene.cpp Normal file
View File

@ -0,0 +1,73 @@
#include "ocrscene.h"
#include "ui_ocrscene.h"
#include <QFileDialog>
#include <QMessageBox>
#include <outputdialog.h>
#include <image/checkimage.h>
#include <check/check.h>
OCRScene::OCRScene(QWidget *parent)
: QWidget(parent)
, ui(new Ui::OCRScene) {
ui->setupUi(this);
auto modules = parser.get_modules_names();
for (auto &module : modules) {
ui->store_combo_box->addItem(QString::fromStdString(module));
}
}
OCRScene::~OCRScene() {
delete ui;
}
void OCRScene::on_parse_button_clicked() {
std::wstring checkContent = ui->check_text_edit->toPlainText().toStdWString();
parser.set_module(parser.search_modules()[ui->store_combo_box->currentIndex()]);
std::vector<Goods> goods = parser.parse(checkContent);
if (goods.size() == 0) {
QMessageBox infoDialog;
infoDialog.setText(tr("An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer."));
infoDialog.setIcon(QMessageBox::Critical);
infoDialog.setWindowTitle(tr("Error in parsing"));
infoDialog.exec();
return;
}
Check check;
check.add_goods(goods);
OutputDialog d(this, check);
d.show();
d.exec();
}
void OCRScene::on_choose_image_button_clicked() {
QString filename = QFileDialog::getOpenFileName();
if (filename == "") {
QMessageBox infoDialog;
infoDialog.setText(tr("Please, select a picture to scan"));
infoDialog.setIcon(QMessageBox::Critical);
infoDialog.setWindowTitle(tr("Picture was not selected"));
infoDialog.exec();
return;
}
std::string new_text = "Selected: " + filename.toStdString();
ui->path_to_image_label->setText(tr("Path to image: ")+ filename);
CheckImage i(filename.toStdString());
std::string parsed = i.parse_text();
ui->check_text_edit->setPlainText(QString::fromStdString(parsed));
}

29
ocrscene.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef OCRSCENE_H
#define OCRSCENE_H
#include "parser/parser.h"
#include <QWidget>
namespace Ui {
class OCRScene;
}
class OCRScene : public QWidget
{
Q_OBJECT
public:
explicit OCRScene(QWidget *parent = nullptr);
~OCRScene();
private slots:
void on_parse_button_clicked();
void on_choose_image_button_clicked();
private:
Ui::OCRScene *ui;
Parser parser;
};
#endif // OCRSCENE_H

216
ofdscene.cpp Normal file
View File

@ -0,0 +1,216 @@
#include "ofdscene.h"
#include "ui_ofdscene.h"
#include "utils/utils.h"
#include <QFileDialog>
#include <QMessageBox>
#include <adjustpicturedialog.h>
#include <httplib.h>
#include <outputdialog.h>
#include <qpixmap.h>
#include <solvecaptchadialog.h>
#include <net/net.h>
#include <exceptions/ofdrequestexception.h>
#include <bits/basic_string.h>
#include <qrencode.h>
#include <bits/basic_string.h>
OFDScene::OFDScene(QWidget *parent)
: QWidget(parent)
, ui(new Ui::OFDScene) {
ui->setupUi(this);
QObject::connect(this, &OFDScene::httpErrorOccured, this, &OFDScene::notifyHttpServerFailure);
}
OFDScene::~OFDScene() {
delete ui;
}
void OFDScene::startHttpServer() {
std::string localIp = "";
try {
localIp = get_local_ip_address();
} catch(std::exception e) {
std::cerr << e.what() << std::endl;
return;
}
unsigned short number_of_retries = 0;
do {
if (number_of_retries == 10) {
emit httpErrorOccured();
return;
}
this->port = rand() % (65535 - 1024) + 1024;
std::string connectionString = "binaryeye://scan/?ret=http://"+ localIp +":"+ std::to_string(port) +"/?result={RESULT}";
server.Get("/", [&](const httplib::Request &req, httplib::Response &res){
std::map<std::string, std::string> paramsMap;
if (req.params.size() < 1) {
res.set_redirect(connectionString, 301);
std::cerr << "Too few params: " << req.params.size() << std::endl;
return;
}
std::string result = req.params.find("result")->second;
std::vector<std::string> dataSplit = split(result, "&");
for (std::string &pair : dataSplit) {
std::vector<std::string> values = split(pair, "=");
paramsMap.insert(std::pair<std::string, std::string>(values[0], values[1]));
}
emit onDataDecode(paramsMap);
res.set_redirect(connectionString, 301);
});
std::cerr << "Listening on port: " << this->port << std::endl;
if (!server.listen("0.0.0.0", this->port)) {
std::cerr << "Random port seems to be occupied. Trying to generate another one" << std::endl;
number_of_retries ++;
continue;
}
} while(true);
}
void OFDScene::on_choose_image_button_clicked() {
QString filename = QFileDialog::getOpenFileName();
if (filename == "") {
QMessageBox infoDialog;
infoDialog.setText(tr("Please, select a picture where QR code that contains info about check is present"));
infoDialog.setIcon(QMessageBox::Critical);
infoDialog.setWindowTitle(tr("Picture was not selected"));
infoDialog.exec();
return;
}
ui->info_label->setText(tr("Selected image: ") + filename);
AdjustPictureDialog dialog = AdjustPictureDialog(this, filename.toStdString());
connect(&dialog, &AdjustPictureDialog::decodedData, this, &OFDScene::onDataDecode);
dialog.exec();
}
void OFDScene::onDataDecode(std::map<std::string, std::string> data) {
ui->fn_line_edit->setText(QString::fromStdString(data["fn"]));
ui->fd_line_edit->setText(QString::fromStdString(data["i"]));
ui->fi_line_edit->setText(QString::fromStdString(data["fp"]));
QString extractedDateTime = QString::fromStdString(data["t"]);
//TODO: some QRs contain datetime in format yyyyMMddThhmmss. Perhaps there is more different formats, should write function to detect them.
QDateTime datetime = QDateTime::fromString(extractedDateTime, "yyyyMMddThhmm");
if (datetime == QDateTime::fromString(extractedDateTime, "20000101T1200")) {
datetime = QDateTime::fromString(extractedDateTime, "yyyyMMddThhmmss");
}
ui->purchase_datetime_edit->setDateTime(datetime);
int type = std::stoi(data["n"]);
ui->operation_type_combo_box->setCurrentIndex(type - 1);
std::string total = data["s"];
ui->total_spin_box->setValue(std::stod(total));
}
void OFDScene::on_parse_button_clicked() {
Net net;
net.get_captcha_from_ofdru();
std::string solved_captcha = "";
bool success = true;
bool is_captcha_solved = true;
Check check;
do {
SolveCaptchaDialog dialog = SolveCaptchaDialog(this, &solved_captcha);
dialog.exec();
is_captcha_solved = true;
try {
std::string check_content = net.fetch_check_data_from_ofdru(
ui->fn_line_edit->text().toStdString(),
ui->fd_line_edit->text().toStdString(),
ui->fi_line_edit->text().toStdString(),
ui->purchase_datetime_edit->dateTime().toString(Qt::ISODate).toStdString(),
ui->operation_type_combo_box->currentIndex() + 1,
// In the request to ofd.ru, total is in a format with 2 last digits represent decimal part of a number.
ui->total_spin_box->text().toDouble() * 100,
solved_captcha);
check = parseOfdRuAnswer(check_content);
} 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;
} 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;
} 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;
}
}
} while (!is_captcha_solved);
if (success) {
OutputDialog d = OutputDialog(this, check);
d.exec();
}
}
void OFDScene::on_binary_eye_button_clicked() {
http_thread = new std::thread(&OFDScene::startHttpServer, this);
ui->binary_eye_button->setEnabled(false);
while (!server.is_running());
std::string localIp;
try {
localIp = get_local_ip_address();
} catch(std::exception e) {
std::cerr << e.what() << std::endl;
return;
}
std::string connectionString = "binaryeye://scan?ret=http://" + localIp + ":" + std::to_string(port) + "/?result={RESULT}";
generate_qr_code(connectionString);
QMessageBox infoDialog = QMessageBox();
infoDialog.setText(QString::fromStdString(connectionString));
infoDialog.setIconPixmap(QPixmap(QString::fromStdString(get_path_relative_to_home(".local/share/checks_parser/binaryeye_connection.png"))).scaled(400, 400, Qt::KeepAspectRatio));
infoDialog.setWindowTitle(tr("QR code for binaryeye to connect"));
infoDialog.setButtonText(1, tr("I've scanned"));
infoDialog.exec();
}
void OFDScene::notifyHttpServerFailure() {
QMessageBox infoDialog = QMessageBox();
infoDialog.setText(tr("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."));
infoDialog.setIcon(QMessageBox::Warning);
infoDialog.setWindowTitle(tr("Could not start http server."));
infoDialog.exec();
}
unsigned int OFDScene::getPort() {
return port;
}

43
ofdscene.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef OFDSCENE_H
#define OFDSCENE_H
#include <QWidget>
#include <httplib.h>
#include <thread>
namespace Ui {
class OFDScene;
}
class OFDScene : public QWidget
{
Q_OBJECT
public:
explicit OFDScene(QWidget *parent = nullptr);
~OFDScene();
void startHttpServer();
unsigned int getPort();
private slots:
void on_choose_image_button_clicked();
void onDataDecode(std::map<std::string, std::string>);
void on_parse_button_clicked();
void on_binary_eye_button_clicked();
void notifyHttpServerFailure();
signals:
void httpErrorOccured();
private:
Ui::OFDScene *ui;
std::thread *http_thread;
unsigned int port;
httplib::Server server;
};
#endif // OFDSCENE_H

401
old_mainwindow.ui Normal file
View File

@ -0,0 +1,401 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>817</width>
<height>659</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QComboBox" name="storeType">
<property name="geometry">
<rect>
<x>90</x>
<y>10</y>
<width>211</width>
<height>31</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="storeTypeLabel">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>Store type</string>
</property>
</widget>
<widget class="QPushButton" name="parseButton">
<property name="geometry">
<rect>
<x>30</x>
<y>560</y>
<width>80</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Parse</string>
</property>
</widget>
<widget class="QPushButton" name="preferencesButton">
<property name="geometry">
<rect>
<x>730</x>
<y>0</y>
<width>81</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>Preferences</string>
</property>
</widget>
<widget class="QTabWidget" name="tabWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>801</width>
<height>511</height>
</rect>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="Text">
<attribute name="title">
<string>Text</string>
</attribute>
<widget class="QLabel" name="checkContentLabel">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>101</width>
<height>18</height>
</rect>
</property>
<property name="text">
<string>Check content</string>
</property>
</widget>
<widget class="QPlainTextEdit" name="checkContent">
<property name="geometry">
<rect>
<x>0</x>
<y>30</y>
<width>611</width>
<height>441</height>
</rect>
</property>
</widget>
</widget>
<widget class="QWidget" name="OCR">
<attribute name="title">
<string>OCR</string>
</attribute>
<widget class="QPushButton" name="chooseImageButton_ocr">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>80</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Choose</string>
</property>
</widget>
<widget class="QPlainTextEdit" name="checkContentFromImage">
<property name="geometry">
<rect>
<x>0</x>
<y>60</y>
<width>511</width>
<height>401</height>
</rect>
</property>
</widget>
<widget class="QLabel" name="pathLabel_ocr">
<property name="geometry">
<rect>
<x>100</x>
<y>0</y>
<width>381</width>
<height>18</height>
</rect>
</property>
<property name="text">
<string>Path to image: </string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>0</x>
<y>30</y>
<width>571</width>
<height>18</height>
</rect>
</property>
<property name="text">
<string>Here is recognised check text. Please, edit it if something's wrong:</string>
</property>
</widget>
<widget class="QLabel" name="picture_ocr">
<property name="geometry">
<rect>
<x>490</x>
<y>10</y>
<width>291</width>
<height>421</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
</widget>
<widget class="QWidget" name="OFD">
<attribute name="title">
<string>OFD</string>
</attribute>
<widget class="QLabel" name="picture_ofd">
<property name="geometry">
<rect>
<x>490</x>
<y>10</y>
<width>291</width>
<height>421</height>
</rect>
</property>
<property name="text">
<string/>
</property>
</widget>
<widget class="QLabel" name="pathLabel_ofd">
<property name="geometry">
<rect>
<x>100</x>
<y>0</y>
<width>381</width>
<height>18</height>
</rect>
</property>
<property name="text">
<string>Path to image: </string>
</property>
</widget>
<widget class="QPushButton" name="chooseImageButton_ofd">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>80</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Choose</string>
</property>
</widget>
<widget class="QLineEdit" name="fn_edit">
<property name="geometry">
<rect>
<x>180</x>
<y>50</y>
<width>261</width>
<height>26</height>
</rect>
</property>
<property name="inputMask">
<string>0000000000000000</string>
</property>
</widget>
<widget class="QLabel" name="fn_label">
<property name="geometry">
<rect>
<x>10</x>
<y>50</y>
<width>161</width>
<height>21</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FN (Fiscal Number)</string>
</property>
</widget>
<widget class="QLabel" name="fd_label">
<property name="geometry">
<rect>
<x>10</x>
<y>90</y>
<width>161</width>
<height>21</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FD (Fiscal Document)</string>
</property>
</widget>
<widget class="QLineEdit" name="fd_edit">
<property name="geometry">
<rect>
<x>180</x>
<y>90</y>
<width>261</width>
<height>26</height>
</rect>
</property>
<property name="inputMask">
<string>0000000000</string>
</property>
</widget>
<widget class="QLabel" name="fi_label">
<property name="geometry">
<rect>
<x>10</x>
<y>130</y>
<width>161</width>
<height>21</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FI (Fiscal Identifier)</string>
</property>
</widget>
<widget class="QLineEdit" name="fi_edit">
<property name="geometry">
<rect>
<x>180</x>
<y>130</y>
<width>261</width>
<height>26</height>
</rect>
</property>
<property name="inputMask">
<string>0000000000</string>
</property>
</widget>
<widget class="QDateTimeEdit" name="dateTimeEdit">
<property name="geometry">
<rect>
<x>10</x>
<y>170</y>
<width>194</width>
<height>27</height>
</rect>
</property>
</widget>
<widget class="QComboBox" name="fundIncomeCombo">
<property name="geometry">
<rect>
<x>10</x>
<y>210</y>
<width>191</width>
<height>26</height>
</rect>
</property>
<item>
<property name="text">
<string>Funds income</string>
</property>
</item>
<item>
<property name="text">
<string>Funds return</string>
</property>
</item>
<item>
<property name="text">
<string>Funds spend</string>
</property>
</item>
<item>
<property name="text">
<string>Spends return</string>
</property>
</item>
</widget>
<widget class="QLineEdit" name="total_edit">
<property name="geometry">
<rect>
<x>90</x>
<y>250</y>
<width>113</width>
<height>26</height>
</rect>
</property>
<property name="inputMask">
<string/>
</property>
</widget>
<widget class="QLabel" name="total_label">
<property name="geometry">
<rect>
<x>10</x>
<y>250</y>
<width>66</width>
<height>18</height>
</rect>
</property>
<property name="text">
<string>Total</string>
</property>
</widget>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>817</width>
<height>33</height>
</rect>
</property>
<widget class="QMenu" name="menuchecks_parser">
<property name="title">
<string>checks parser</string>
</property>
</widget>
<addaction name="menuchecks_parser"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -2,12 +2,58 @@
OutputOptions::OutputOptions() {} OutputOptions::OutputOptions() {}
void OutputOptions::add_or_update_column(Column &column) {
if (column_exist(column.type)) {
update_column(column, find_column(column.type));
} else {
this->order.push_back(column);
}
}
void OutputOptions::update_column(Column &column, unsigned short index) {
this->order[index] = column;
}
void OutputOptions::remove_column(unsigned short index) {
this->order.erase(this->order.begin() + index);
}
void OutputOptions::remove_column(ColumnType t) {
this->order.erase(this->order.begin() + find_column(t));
}
unsigned short OutputOptions::find_column(ColumnType t) {
for (unsigned short i = 0; i < this->order.size(); i++) {
if (this->order[i].type == t)
return i;
}
return -1;
}
bool OutputOptions::column_exist(ColumnType t) {
for (unsigned short i = 0; i < this->order.size(); i++) {
if (this->order[i].type == t)
return true;
}
return false;
}
Column &OutputOptions::get_column(ColumnType t) {
return this->order[find_column(t)];
}
std::vector<Column> &OutputOptions::get_columns() { return this->order; }
void OutputOptions::set_print_header(bool value) { this->print_header = value; } void OutputOptions::set_print_header(bool value) { this->print_header = value; }
bool OutputOptions::get_print_header() { return this->print_header; } bool OutputOptions::get_print_header() { return this->print_header; }
void OutputOptions::set_print_total(bool value) { this->print_total = value; } void OutputOptions::set_print_total(bool value) { this->print_total = value; }
bool OutputOptions::get_print_total() { return this->print_total; } 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; } void OutputOptions::set_path(std::string path) { this->path = path; }
std::string &OutputOptions::get_path() { return this->path; } std::string &OutputOptions::get_path() { return this->path; }

View File

@ -3,7 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#if __GNUC__ <= 8 && __clang_major__ < 17 #if __GNUC__ < 8
# include <experimental/filesystem> # include <experimental/filesystem>
#else #else
# include <filesystem> # include <filesystem>
@ -14,8 +14,7 @@
#include "../net/net.h" #include "../net/net.h"
#include "../settings/settings.h" #include "../settings/settings.h"
enum ColumnType { enum class ColumnType {
date,
goods_name, goods_name,
goods_price_per_unit, goods_price_per_unit,
goods_quantity, goods_quantity,
@ -23,36 +22,45 @@ enum ColumnType {
goods_total goods_total
}; };
// Q_DECLARE_METATYPE(ColumnType); struct Column { // Example:
ColumnType type; // goods_name
// struct Column { // Example: std::string name; // "Товар"
// ColumnType type; // goods_name unsigned int position; // "0" <-- 0 = "A", 1 = "B", etc.. column letter in
// std::string name; // "Товар" // table processor (i.e. excel or libreoffice)
// unsigned int position; // "0" <-- 0 = "A", 1 = "B", etc.. column letter in } typedef Column;
// // table processor (i.e. excel or libreoffice) enum class OutputFormat { csv, ods, xlsx, plaintext } typedef OutputFormat;
// } typedef Column;
class OutputOptions { class OutputOptions {
std::vector<Column> order;
bool print_header; bool print_header;
bool print_total; bool print_total;
OutputFormat format;
std::string path; std::string path;
public: public:
OutputOptions(); OutputOptions();
void add_or_update_column(Column &);
void update_column(Column&, unsigned short);
void remove_column(unsigned short); void remove_column(unsigned short);
void remove_column(ColumnType); void remove_column(ColumnType);
unsigned short find_column(ColumnType); unsigned short find_column(ColumnType);
bool column_exist(ColumnType); bool column_exist(ColumnType);
Column& get_column(ColumnType);
std::vector<Column>& get_columns();
void set_print_header(bool); void set_print_header(bool);
bool get_print_header(); bool get_print_header();
void set_print_total(bool); void set_print_total(bool);
bool get_print_total(); bool get_print_total();
void set_output_format(OutputFormat);
OutputFormat get_output_format();
void set_path(std::string); void set_path(std::string);
std::string &get_path(); std::string &get_path();
}; };
#endif // OUTPUT_OPTIONS_H #endif // OUTPUT_OPTIONS_H

View File

@ -3,91 +3,95 @@
#include "output/output_options.h" #include "output/output_options.h"
#include "ui_outputdialog.h" #include "ui_outputdialog.h"
#include <QFileDialog> #include <QFileDialog>
#include <QLineEdit>
#include <QMainWindow> #include <QMainWindow>
#include <QStandardItemModel>
#include <fstream> #include <fstream>
#include <outputcolumn.h>
#include <outputcolumnmodel.h>
#include "settings/settings.h" #include "settings/settings.h"
#include "utils/utils.h" #include "utils/utils.h"
#include <map>
OutputDialog::OutputDialog(QWidget *parent, std::vector<Check> *checks) OutputDialog::OutputDialog(QWidget *parent, Check &check)
: QDialog(parent), ui(new Ui::OutputDialog), checks(checks), : QDialog(parent), ui(new Ui::OutputDialog), check(check),
options(OutputOptions()) { options(OutputOptions()) {
settings = new Settings(get_path_relative_to_home(".local/share/checks_parser/settings.json"));
Settings settings(get_path_relative_to_home(".local/share/checks_parser/settings.json"));
ui->setupUi(this); ui->setupUi(this);
for (Check &c : *checks) { ui->tableWidget->item(0, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_name"]["name"]));
std::cout << "Check: " << c.get_date() << " " << c.get_total() << std::endl; ui->tableWidget->item(0, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_name"]["position"]));
for (Goods &g : c.get_goods()) {
std::cout << g.get_name() << " " << g.get_net_weight() << " " << g.get_price_per_unit() << " " << g.get_quantity() << std::endl;
}
}
columns = new std::vector<OutputColumn>; 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"]));
OutputColumnModel *model = new OutputColumnModel(columns, this); 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->listView->setModel(model); 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"]));
for (unsigned short i = 0; i < 6; i ++) ui->tableWidget->item(4, 1)->setText(QString::fromStdString(settings.get_all_settings()["output_order"]["goods_total"]["name"]));
columns->push_back(OutputColumn(tr("Кто здесь?"), ColumnType::date)); ui->tableWidget->item(4, 0)->setText(QString::number((int)settings.get_all_settings()["output_order"]["goods_total"]["position"]));
for (auto &column : column_names) {
std::string name = settings->get_all_settings()["output_order"][column.first]["name"];
unsigned short position = settings->get_all_settings()["output_order"][column.first]["position"];
ColumnType type = column.second;
columns->at(position - 1) = (OutputColumn(QString::fromStdString(name), type));
} ui->printHeaderCheckBox->setChecked(settings.get_all_settings()["print_header"]);
ui->printTotalCheckBox->setChecked(settings.get_all_settings()["print_total"]);
for (unsigned short i = 0; i < 6; i ++)
emit model->dataChanged(model->index(i, 0), model->index(i, 0));
ui->printHeaderCheckBox->setChecked(settings->get_all_settings()["print_header"]);
ui->printTotalCheckBox->setChecked(settings->get_all_settings()["print_total"]);
} }
OutputDialog::~OutputDialog() { OutputDialog::~OutputDialog() { delete ui; }
delete settings;
delete ui; bool compare_position(Column &c1, Column &c2) {
return c1.position < c2.position;
} }
void OutputDialog::on_buttonBox_accepted() { void OutputDialog::on_buttonBox_accepted() {
std::ofstream output_file(this->options.get_path()); std::ofstream output_file(this->options.get_path());
print_header(&output_file); for (int i = 0; i < ui->tableWidget->rowCount(); i++) {
int position = ui->tableWidget->item(i, 0)->text().toInt();
std::string name = ui->tableWidget->item(i, 1)->text().toStdString();
for (Check &check : *checks) { Column c;
int row_number = 0; c.type = static_cast<ColumnType>(i);
for (auto it = check.get_goods().begin(); it != check.get_goods().end(); it++, row_number++) { c.position = position;
for (int i = 0; i < columns->size(); i ++) { c.name = name;
OutputColumn &column = columns->at(i);
switch (column.get_column_type()) { this->options.add_or_update_column(c);
case ColumnType::date: }
if (row_number == 0) output_file << check.get_date();
break; std::sort(this->options.get_columns().begin(),
this->options.get_columns().end(), compare_position);
if (options.get_print_header()) {
for (auto &column : this->options.get_columns()) {
output_file << column.name
<< (column.position == this->options.get_columns().size()
? ""
: ",");
}
output_file << std::endl;
}
for (auto goods : this->check.get_goods()) {
for (auto &column : this->options.get_columns()) {
switch (column.type) {
case ColumnType::goods_name: case ColumnType::goods_name:
output_file << it->get_name(); output_file << goods.get_name();
break; break;
case ColumnType::goods_price_per_unit: case ColumnType::goods_price_per_unit:
output_file << std::fixed << std::setprecision(2) << it->get_price_per_unit(); output_file << std::fixed << std::setprecision(2) << goods.get_price_per_unit();
break; break;
case ColumnType::goods_quantity: case ColumnType::goods_quantity:
output_file << std::fixed << std::setprecision(2) << it->get_quantity(); output_file << std::fixed << std::setprecision(2) << goods.get_quantity();
break; break;
case ColumnType::goods_net_weight: case ColumnType::goods_net_weight:
output_file << it->get_net_weight(); output_file << "TODO";
// TODO
break; break;
case ColumnType::goods_total: case ColumnType::goods_total:
output_file << std::fixed << std::setprecision(2) << it->calculate_total_price(); output_file << std::fixed << std::setprecision(2) << goods.calculate_total_price();
break; break;
} }
if (i < columns->size() - 1) { if (column.position != this->options.get_columns().size()) {
output_file << ","; output_file << ",";
} else { } else {
output_file << "\n"; output_file << "\n";
@ -98,9 +102,22 @@ void OutputDialog::on_buttonBox_accepted() {
if (this->options.get_print_total()) { if (this->options.get_print_total()) {
output_file << "Total: " << std::fixed << std::setprecision(2) << check.calculae_total_price() << std::endl; output_file << "Total: " << std::fixed << std::setprecision(2) << check.calculae_total_price() << std::endl;
} }
}
output_file.close(); output_file.close();
save_settings(); }
void update_settings(OutputOptions &options, ColumnType t, std::string name,
int value) {
Column column;
column.type = t;
column.name = name;
column.position = value;
if (value) {
options.add_or_update_column(column);
} else {
options.remove_column(t);
}
} }
void OutputDialog::on_chooseFileButton_clicked() { void OutputDialog::on_chooseFileButton_clicked() {
@ -116,26 +133,3 @@ void OutputDialog::on_printHeaderCheckBox_stateChanged(int value) {
void OutputDialog::on_printTotalCheckBox_stateChanged(int value) { void OutputDialog::on_printTotalCheckBox_stateChanged(int value) {
this->options.set_print_total(value); this->options.set_print_total(value);
} }
void OutputDialog::print_header(std::ofstream *output_file) {
if (options.get_print_header()) {
for (unsigned int i = 0; i < columns->size(); i ++) {
OutputColumn column = columns->at(i);
(*output_file) << column.get_text().toStdString()
<< (i == columns->size() - 1
? ""
: ",");
}
*output_file << std::endl;
}
}
void OutputDialog::save_settings() {
for (int i = 0; i < columns->size(); i ++) {
OutputColumn &column = columns->at(i);
std::string key = find_key_by_value(column_names, column.get_column_type());
settings->get_all_settings()["output_order"][key]["name"] = column.get_text().toStdString();
settings->get_all_settings()["output_order"][key]["position"] = i + 1;
}
settings->flush();
}

View File

@ -5,7 +5,6 @@
#include "output/output_options.h" #include "output/output_options.h"
#include <QComboBox> #include <QComboBox>
#include <QDialog> #include <QDialog>
#include <outputcolumn.h>
namespace Ui { namespace Ui {
class OutputDialog; class OutputDialog;
@ -15,12 +14,10 @@ class OutputDialog : public QDialog {
Q_OBJECT Q_OBJECT
OutputOptions options; OutputOptions options;
std::vector<Check> *checks; Check &check;
std::vector<OutputColumn> *columns;
Settings *settings;
public: public:
explicit OutputDialog(QWidget *parent = nullptr, std::vector<Check> *checks = nullptr); explicit OutputDialog(QWidget *parent = nullptr, Check & = *(new Check()));
~OutputDialog(); ~OutputDialog();
private slots: private slots:
@ -34,10 +31,6 @@ private slots:
private: private:
Ui::OutputDialog *ui; Ui::OutputDialog *ui;
void print_header(std::ofstream *output_file);
void save_settings();
}; };
#endif // OUTPUTDIALOG_H #endif // OUTPUTDIALOG_H

93
parser/module.cpp Normal file
View File

@ -0,0 +1,93 @@
#include "module.h"
#include <fstream>
#include <nlohmann/json.hpp>
#include <regex>
#include <string>
#include "../utils/utils.h"
StoreModule::StoreModule() {}
StoreModule::StoreModule(std::string path) {
std::ifstream settings_file(path);
nlohmann::json settings = nlohmann::json::parse(settings_file);
this->name = from_utf8(settings["name"]);
this->goods_name_regex = from_utf8(settings["goods_name_regex"]);
this->goods_price_regex = from_utf8(settings["goods_price_regex"]);
this->goods_quantity_regex = from_utf8(settings["goods_quantity_regex"]);
this->check_start_regex = from_utf8(settings["check_start_regex"]);
this->check_end_regex = from_utf8(settings["check_end_regex"]);
#ifdef DEBUG
std::wcout << "Name: " << this->name << std::endl;
std::wcout << "Goods name regex: " << this->goods_name_regex << std::endl;
std::wcout << "Goods price regex: " << this->goods_price_regex << std::endl;
std::wcout << "Goods quantity regex: " << this->goods_quantity_regex
<< std::endl;
std::wcout << "Check start regex: " << this->check_start_regex << std::endl;
std::wcout << "Check end regex: " << this->check_end_regex << std::endl;
#endif
}
std::vector<std::string> StoreModule::parse_name(std::wstring str) {
std::vector<std::string> result;
std::wregex r(this->goods_name_regex, std::regex_constants::multiline);
for (std::wsregex_iterator it{str.begin(), str.end(), r}, end{}; it != end;
it++) {
result.push_back(to_utf8(it->str()));
}
return result;
}
std::vector<std::string> StoreModule::parse_price(std::wstring str) {
std::vector<std::string> result;
std::wregex r(this->goods_price_regex, std::regex::collate);
for (std::wsregex_iterator it{str.begin(), str.end(), r}, end{}; it != end;
it++) {
result.push_back(to_utf8(it->str()));
}
return result;
}
std::vector<std::string> StoreModule::parse_quantity(std::wstring str) {
std::vector<std::string> result;
std::wregex r(this->goods_quantity_regex, std::regex::collate);
for (std::wsregex_iterator it{str.begin(), str.end(), r}, end{}; it != end;
it++) {
result.push_back(to_utf8(it->str()));
}
return result;
}
std::wstring StoreModule::trim_check(std::wstring& check) {
unsigned int start_pos;
unsigned int end_pos;
std::wregex start_regex(this->check_start_regex, std::regex::collate);
std::wregex end_regex(this->check_end_regex, std::regex::collate);
for (std::wsregex_iterator it{check.begin(), check.end(), start_regex}, end{};
it != end; it++) {
start_pos = it->position() + it->str().size();
break;
}
check = check.substr(start_pos, check.size());
for (std::wsregex_iterator it{check.begin(), check.end(), end_regex}, end{};
it != end; it++) {
end_pos = it->position() - 1;
break;
}
check = check.substr(0, end_pos);
return check;
}
std::wstring StoreModule::get_name() { return this->name; }

28
parser/module.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef STORE_MODULE_H
#define STORE_MODULE_H
#include <string>
#include <vector>
class StoreModule {
std::string path;
std::wstring name;
std::wstring goods_name_regex;
std::wstring goods_price_regex;
std::wstring goods_quantity_regex;
std::wstring check_start_regex;
std::wstring check_end_regex;
public:
StoreModule(std::string);
StoreModule();
std::vector<std::string> parse_name(std::wstring);
std::vector<std::string> parse_price(std::wstring);
std::vector<std::string> parse_quantity(std::wstring);
std::wstring trim_check(std::wstring&);
std::wstring get_name();
};
#endif // STORE_MODULE_H

143
parser/parser.cpp Normal file
View File

@ -0,0 +1,143 @@
#include "parser.h"
#include "../goods/goods.h"
#include "../net/net.h"
#include "../settings/settings.h"
#include "../utils/utils.h"
#include <iostream>
#include <fstream>
#if __GNUC__ < 8 && __clang_major__ < 17
# include <experimental/filesystem>
using namespace std::experimental;
using namespace std::experimental::filesystem;
#else
# include <filesystem>
using namespace std::filesystem;
#endif
static void dumpVectorsToStdErr(std::vector<std::string> &goods_names, std::vector<std::string> &goods_prices, std::vector<std::string>& goods_quantities) {
std::cerr << goods_names.size() << " " << goods_prices.size() << " " << goods_quantities.size() << std::endl;
std::cerr << "Found goods names: ";
for (auto &goods_name : goods_names) {
std::cerr << goods_name << " ";
}
std::cerr << std::endl;
std::cerr << "Found goods prices: ";
for (auto &goods_price : goods_prices) {
std::cerr << goods_price << " ";
}
std::cerr << std::endl;
std::cerr << "Found goods names: ";
for (auto &goods_quantity : goods_quantities) {
std::cerr << goods_quantity << " ";
}
std::cerr << std::endl;
}
Parser::Parser() {}
std::vector<std::string> Parser::search_modules() {
Settings s(get_path_relative_to_home(".local/share/checks_parser/settings.json"));
std::string path = get_path_relative_to_home(s.get_setting("stores_modules_dir"));//std::string(std::getenv("HOME")) + "/" + STORES_MODULES_DIR;
directory_entry modules_dir(path);
if (!exists(modules_dir)) {
create_directories(path);
std::cout << "No modules directory found. Created one at " << path
<< std::endl;
std::cout << "Please, download modules to that directory from my git."
<< std::endl;
}
std::vector<std::string> modules_files;
for (auto &file : directory_iterator(path)) {
modules_files.push_back(file.path());
}
return modules_files;
}
std::vector<std::string> Parser::get_modules_names() {
std::vector<std::string> modules = this->search_modules();
std::vector<std::string> names = {};
for (std::string &modulePath : modules) {
std::ifstream inputFile(modulePath);
nlohmann::json module = nlohmann::json::parse(inputFile);
std::string name = module["name"];
names.push_back(name);
}
return names;
}
void Parser::set_module(std::string path) { module = StoreModule(path); }
std::vector<Goods> Parser::parse(std::wstring check_plaintext) {
std::vector<Goods> result;
module.trim_check(check_plaintext);
std::vector<std::string> goods_names = module.parse_name(check_plaintext);
std::vector<std::string> goods_prices = module.parse_price(check_plaintext);
std::vector<std::string> goods_quantities =
module.parse_quantity(check_plaintext);
if (goods_names.size() != goods_prices.size() ||
goods_names.size() != goods_quantities.size() ||
goods_prices.size() != goods_quantities.size()) {
dumpVectorsToStdErr(goods_names, goods_prices, goods_quantities);
//Error. Amount of names, prices or quantities are not equal. That means, that some regex(es) has mismatched.
return {};
}
short goods_amount = goods_names.size();
for (short i = 0; i < goods_amount; i++) {
Goods goods(goods_names[i], std::stof(goods_prices[i]), std::stof(goods_quantities[i]));
result.push_back(goods);
}
return result;
}
std::vector<std::string> Parser::check_updates() {
std::cout << "Checking updates for stores modules" << std::endl;
Settings s(get_path_relative_to_home(".local/share/checks_parser/settings.json"));
std::string path = get_path_relative_to_home(s.get_setting("stores_modules_dir"));
std::vector<std::string> to_download;
std::vector<std::string> stored_modules;
directory_entry modules_dir(path);
if (!exists(modules_dir)) {
create_directories(path);
}
for (const auto& file : directory_iterator(path)) {
if (!is_regular_file(file)) continue;
stored_modules.push_back(file.path().filename());
std::cout << file.path().filename() << " detected store module" << std::endl;
}
Net n;
std::cerr << "Downloading modules list from: " << s.get_setting("stores_modules_url") << std::endl;
std::vector<std::string> remote_modules = n.get_all_modules(s.get_setting("stores_modules_url"));
if (stored_modules.empty()) {
std::cout << "I need to download everything" << std::endl;
to_download = remote_modules;
} else {
for (const std::string& module : remote_modules) {
if (!vector_contains_element(stored_modules, module)) {
std::cout << "I need to download store module " << module << std::endl;
to_download.push_back(module);
}
}
}
return to_download;
}

27
parser/parser.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef PARSER_H
#define PARSER_H
#include "../goods/goods.h"
#include "module.h"
#include <string>
#include <vector>
class Parser {
StoreModule module;
public:
Parser();
std::vector<std::string> search_modules();
std::vector<std::string> get_modules_names();
std::vector<std::string> check_updates();
void set_module(std::string);
std::vector<Goods> parse(std::wstring);
};
#endif // PARSER_H

View File

@ -1,8 +1,10 @@
<RCC> <RCC>
<qresource prefix="/scenes"> <qresource prefix="/scenes">
<file>scenes/outputdialog.ui</file> <file>scenes/outputdialog.ui</file>
<file>scenes/emailtextscene.ui</file>
<file>scenes/ocrscene.ui</file>
<file>scenes/mainwindow.ui</file> <file>scenes/mainwindow.ui</file>
<file>scenes/settingsdialog.ui</file> <file>scenes/ofdscene.ui</file>
<file>scenes/solvecaptchadialog.ui</file> <file>scenes/solvecaptchadialog.ui</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@ -13,42 +13,65 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Dialog</string> <string>Dialog</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <widget class="QDialogButtonBox" name="buttonBox">
<item row="1" column="0" colspan="2"> <property name="geometry">
<widget class="ImageRedactor" name="graphicsView"/> <rect>
</item> <x>450</x>
<item row="2" column="0"> <y>450</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QSlider" name="contrastSlider"> <widget class="QSlider" name="contrastSlider">
<property name="geometry">
<rect>
<x>10</x>
<y>460</y>
<width>591</width>
<height>16</height>
</rect>
</property>
<property name="value"> <property name="value">
<number>50</number> <number>50</number>
</property> </property>
<property name="orientation"> <property name="orientation">
<enum>Qt::Orientation::Horizontal</enum> <enum>Qt::Horizontal</enum>
</property> </property>
</widget> </widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>511</width>
<height>18</height>
</rect>
</property>
<property name="text"> <property name="text">
<string>Please, zoom to qr code and adjust contrast so that qr code looks sharp</string> <string>Please, zoom to qr code and adjust contrast so that qr code looks sharp</string>
</property> </property>
</widget> </widget>
</item> <widget class="ImageRedactor" name="graphicsView">
<item row="3" column="0"> <property name="geometry">
<widget class="QDialogButtonBox" name="buttonBox"> <rect>
<property name="orientation"> <x>5</x>
<enum>Qt::Orientation::Horizontal</enum> <y>21</y>
</property> <width>801</width>
<property name="standardButtons"> <height>421</height>
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set> </rect>
</property> </property>
</widget> </widget>
</item> <zorder>buttonBox</zorder>
</layout>
<zorder>label</zorder> <zorder>label</zorder>
<zorder>contrastSlider</zorder> <zorder>contrastSlider</zorder>
<zorder>graphicsView</zorder> <zorder>graphicsView</zorder>
<zorder>buttonBox</zorder>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

80
scenes/emailtextscene.ui Normal file
View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>EmailTextScene</class>
<widget class="QWidget" name="EmailTextScene">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1077</width>
<height>608</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="check_content_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Check content</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QPushButton" name="parse_button">
<property name="text">
<string>Parse</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="store_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Store:</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QTextEdit" name="check_content"/>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="back_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Back</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="store_combo_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>987</width> <width>1039</width>
<height>426</height> <height>693</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -26,325 +26,93 @@
<string>Form</string> <string>Form</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="8" column="2" colspan="4"> <item row="2" column="0">
<widget class="QDoubleSpinBox" name="total_spin_box"> <layout class="QGridLayout" name="root_layout">
<property name="maximum"> <property name="verticalSpacing">
<double>4294967296.000000000000000</double> <number>6</number>
</property> </property>
</widget> <item row="0" column="1">
</item> <widget class="QPushButton" name="ocr_button">
<item row="10" column="2" colspan="4"> <property name="toolTip">
<widget class="QPushButton" name="add_new_check_button"> <string>Optical Character Recognition</string>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Add to queue</string>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="fd_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FD (Fiscal Document)</string>
</property>
</widget>
</item>
<item row="7" column="2" colspan="4">
<widget class="QComboBox" name="operation_type_combo_box">
<item>
<property name="text">
<string>Funds income</string>
</property>
</item>
<item>
<property name="text">
<string>Funds return</string>
</property>
</item>
<item>
<property name="text">
<string>Funds spend</string>
</property>
</item>
<item>
<property name="text">
<string>Spends return</string>
</property>
</item>
</widget>
</item>
<item row="0" column="5">
<widget class="QPushButton" name="stop_server_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Stop server</string>
</property>
</widget>
</item>
<item row="5" column="2" colspan="4">
<widget class="QLineEdit" name="fi_line_edit"/>
</item>
<item row="6" column="0">
<widget class="QLabel" name="datetime_label">
<property name="text">
<string>Date and time of purchase</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QLabel" name="or_label_1">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>or</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="2" colspan="4">
<widget class="QLineEdit" name="fd_line_edit"/>
</item>
<item row="2" column="0" colspan="6">
<widget class="QLabel" name="info_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
</widget> <property name="icon">
</item> <iconset>
<item row="0" column="7"> <normaloff>:/icons/assets/icons/OCR.svg</normaloff>:/icons/assets/icons/OCR.svg</iconset>
<widget class="QLabel" name="checks_to_parse_label">
<property name="text">
<string>Checks to parse</string>
</property> </property>
<property name="alignment"> <property name="iconSize">
<set>Qt::AlignmentFlag::AlignCenter</set> <size>
</property> <width>128</width>
</widget> <height>128</height>
</item> </size>
<item row="10" column="7" colspan="2">
<widget class="QPushButton" name="parse_button">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Parse queue</string>
</property>
</widget>
</item>
<item row="1" column="4">
<widget class="QLabel" name="or_label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>or</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="choose_image_button">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>QR image</string>
</property>
</widget>
</item>
<item row="3" column="2" colspan="4">
<widget class="QLineEdit" name="fn_line_edit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="5">
<widget class="QPushButton" name="binary_eye_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Scan QR using phone</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="0" column="0">
<widget class="QPushButton" name="settings_button"> <widget class="QPushButton" name="text_from_email_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Settings</string>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QPushButton" name="clear_button">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="toolTip">
<string>Clear data</string> <string>Text from E-Mail</string>
</property> </property>
</widget> <property name="autoFillBackground">
</item>
<item row="1" column="7" rowspan="8" colspan="2">
<widget class="QTableView" name="checkQueueTable">
<property name="editTriggers">
<set>QAbstractItemView::EditTrigger::DoubleClicked|QAbstractItemView::EditTrigger::EditKeyPressed</set>
</property>
<property name="tabKeyNavigation">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDropMode::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::DropAction::MoveAction</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SelectionMode::MultiSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="6" column="2" colspan="4">
<widget class="QDateTimeEdit" name="purchase_datetime_edit"/>
</item>
<item row="1" column="3">
<widget class="QPushButton" name="parse_email_button">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Parse an E-Mail</string> <string/>
</property> </property>
</widget> <property name="icon">
</item> <iconset>
<item row="5" column="0"> <normaloff>:/icons/assets/icons/email-text.svg</normaloff>:/icons/assets/icons/email-text.svg</iconset>
<widget class="QLabel" name="fi_label">
<property name="text">
<string>FI (Fiscal Identifier)</string>
</property> </property>
</widget> <property name="iconSize">
</item>
<item row="8" column="0">
<widget class="QLabel" name="total_label">
<property name="text">
<string>Total</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="operation_type_label">
<property name="text">
<string>Operation type</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="fn_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FN (Fiscal Number)</string>
</property>
</widget>
</item>
<item row="0" column="8">
<widget class="QPushButton" name="deleteSelectedButton">
<property name="text">
<string>Delete selected</string>
</property>
</widget>
</item>
<item row="1" column="6">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Policy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size> <size>
<width>40</width> <width>128</width>
<height>20</height> <height>128</height>
</size> </size>
</property> </property>
</spacer> <property name="autoRepeat">
<bool>false</bool>
</property>
<property name="autoExclusive">
<bool>false</bool>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>false</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QPushButton" name="ofd_button">
<property name="text">
<string/>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/assets/icons/OFD.svg</normaloff>:/icons/assets/icons/OFD.svg</iconset>
</property>
<property name="iconSize">
<size>
<width>128</width>
<height>128</height>
</size>
</property>
</widget>
</item>
</layout>
</item> </item>
</layout> </layout>
</widget> </widget>

127
scenes/ocrscene.ui Normal file
View File

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OCRScene</class>
<widget class="QWidget" name="OCRScene">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>992</width>
<height>634</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="topMargin">
<number>8</number>
</property>
<item row="1" column="2">
<widget class="QComboBox" name="store_combo_box">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="5" column="0" colspan="3">
<widget class="QPushButton" name="parse_button">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Parse</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QPushButton" name="choose_image_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Choose</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="3">
<widget class="QPushButton" name="back_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Back</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QLabel" name="instructions_label">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Recognized text will be shown below as soon as image will be processed. Please, edit it</string>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QLabel" name="path_to_image_label">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Path to image:</string>
</property>
</widget>
</item>
<item row="4" column="0" colspan="3">
<widget class="QTextEdit" name="check_text_edit"/>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="store_label">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Store:</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

194
scenes/ofdscene.ui Normal file
View File

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>OFDScene</class>
<widget class="QWidget" name="OFDScene">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>894</width>
<height>625</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SizeConstraint::SetDefaultConstraint</enum>
</property>
<item row="8" column="0">
<widget class="QLabel" name="total_label">
<property name="text">
<string>Total</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QPushButton" name="back_button">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Back</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="or_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>or</string>
</property>
</widget>
</item>
<item row="8" column="2">
<widget class="QDoubleSpinBox" name="total_spin_box">
<property name="maximum">
<double>4294967296.000000000000000</double>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="fd_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FD (Fiscal Document)</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="datetime_label">
<property name="text">
<string>Date and time of purchase</string>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QComboBox" name="operation_type_combo_box">
<item>
<property name="text">
<string>Funds income</string>
</property>
</item>
<item>
<property name="text">
<string>Funds return</string>
</property>
</item>
<item>
<property name="text">
<string>Funds spend</string>
</property>
</item>
<item>
<property name="text">
<string>Spends return</string>
</property>
</item>
</widget>
</item>
<item row="4" column="2">
<widget class="QLineEdit" name="fd_line_edit"/>
</item>
<item row="2" column="0" colspan="3">
<widget class="QLabel" name="info_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QLineEdit" name="fn_line_edit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="5" column="2">
<widget class="QLineEdit" name="fi_line_edit"/>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="binary_eye_button">
<property name="text">
<string>Use your phone as a QR code scanner</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="fn_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>FN (Fiscal Number)</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="fi_label">
<property name="text">
<string>FI (Fiscal Identifier)</string>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QDateTimeEdit" name="purchase_datetime_edit"/>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="choose_image_button">
<property name="text">
<string>Choose image on your PC</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="operation_type_label">
<property name="text">
<string>Operation type</string>
</property>
</widget>
</item>
<item row="9" column="0" colspan="3">
<widget class="QPushButton" name="parse_button">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Parse</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -21,55 +21,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="0" column="1">
<widget class="QCheckBox" name="printTotalCheckBox">
<property name="text">
<string>Print total</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="chooseFileButton"> <widget class="QPushButton" name="chooseFileButton">
<property name="text"> <property name="text">
<string>Choose</string> <string>Choose</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0" colspan="3">
<widget class="QListView" name="listView">
<property name="editTriggers">
<set>QAbstractItemView::EditTrigger::DoubleClicked|QAbstractItemView::EditTrigger::EditKeyPressed</set>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDropMode::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::DropAction::TargetMoveAction</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property>
</widget>
</item>
<item row="4" column="2" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QCheckBox" name="printHeaderCheckBox"> <widget class="QCheckBox" name="printHeaderCheckBox">
<property name="text"> <property name="text">
@ -77,6 +35,118 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="QCheckBox" name="printTotalCheckBox">
<property name="text">
<string>Print total</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="3">
<widget class="QTableWidget" name="tableWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<row>
<property name="text">
<string>Goods name</string>
</property>
</row>
<row>
<property name="text">
<string>Goods price</string>
</property>
</row>
<row>
<property name="text">
<string>Goods quantity</string>
</property>
</row>
<row>
<property name="text">
<string>Goods net weight</string>
</property>
</row>
<row>
<property name="text">
<string>Goods total</string>
</property>
</row>
<column>
<property name="text">
<string>position</string>
</property>
</column>
<column>
<property name="text">
<string>name</string>
</property>
</column>
<item row="0" column="0">
<property name="text">
<string>1</string>
</property>
</item>
<item row="0" column="1">
<property name="text">
<string>Name</string>
</property>
</item>
<item row="1" column="0">
<property name="text">
<string>2</string>
</property>
</item>
<item row="1" column="1">
<property name="text">
<string>Price</string>
</property>
</item>
<item row="2" column="0">
<property name="text">
<string>3</string>
</property>
</item>
<item row="2" column="1">
<property name="text">
<string>Quantity</string>
</property>
</item>
<item row="3" column="0">
<property name="text">
<string>4</string>
</property>
</item>
<item row="3" column="1">
<property name="text">
<string>Net weight</string>
</property>
</item>
<item row="4" column="0">
<property name="text">
<string>5</string>
</property>
</item>
<item row="4" column="1">
<property name="text">
<string>Total price</string>
</property>
</item>
</widget>
</item>
<item row="4" column="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok</set>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -1,167 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>settingsdialog</class>
<widget class="QDialog" name="settingsdialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>693</width>
<height>616</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QScrollArea" name="scrollArea">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>673</width>
<height>554</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="1">
<widget class="QCheckBox" name="printHeaderCheckBox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="print_total_label">
<property name="text">
<string>Print total</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QComboBox" name="languageComboBox">
<item>
<property name="text">
<string>en_US</string>
</property>
</item>
<item>
<property name="text">
<string>ru_RU</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="print_header_label">
<property name="text">
<string>Print header</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="printTotalCheckBox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="language_label">
<property name="text">
<string>Language</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<widget class="QListView" name="outputOptionsListView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDropMode::InternalMove</enum>
</property>
<property name="defaultDropAction">
<enum>Qt::DropAction::TargetMoveAction</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectionBehavior::SelectRows</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Save</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>settingsdialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>settingsdialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -2,8 +2,9 @@
#include <fstream> #include <fstream>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <string> #include <string>
#include "../utils/utils.h"
#if __GNUC__ <= 8 && __clang_major__ < 17 #if __GNUC__ < 8 && __clang_major__ < 17
# include <experimental/filesystem> # include <experimental/filesystem>
using namespace std::experimental; using namespace std::experimental;
using namespace std::experimental::filesystem; using namespace std::experimental::filesystem;
@ -19,31 +20,31 @@ Settings::Settings(std::string path) {
std::ofstream output(path); std::ofstream output(path);
nlohmann::json settings = R"({ nlohmann::json settings = R"({
"ofds_modules_dir":".local/share/checks_parser/modules/ofd",
"stores_modules_dir":".local/share/checks_parser/modules/stores",
"ofds_modules_url":"https://foxarmy.org/checks-parser/modules/ofd/",
"stores_modules_url":"https://foxarmy.org/checks-parser/modules/stores/",
"print_header": true, "print_header": true,
"print_total": true, "print_total": true,
"output_order": { "output_order": {
"date" : {
"position":1,
"name": "Date"
},
"goods_name": { "goods_name": {
"position":2, "position":1,
"name":"Goods name" "name":"Goods name"
}, },
"goods_price_per_unit": { "goods_price_per_unit": {
"position":3, "position":2,
"name":"Goods price per unit" "name":"Goods price per unit"
}, },
"goods_quantity": { "goods_quantity": {
"position":4, "position":3,
"name":"Goods quantity" "name":"Goods quantity"
}, },
"goods_net_weight": { "goods_net_weight": {
"position":5, "position":4,
"name":"Goods net weight" "name":"Goods net weight"
}, },
"goods_total": { "goods_total": {
"position":6, "position":5,
"name":"Goods total" "name":"Goods total"
} }
} }
@ -59,6 +60,9 @@ Settings::Settings(std::string path) {
nlohmann::json settings = nlohmann::json::parse(input); nlohmann::json settings = nlohmann::json::parse(input);
this->settings = settings; 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) { void Settings::write_setting(std::string setting, std::string value) {

View File

@ -1,13 +1,9 @@
#include "settingsdialog.h" #include "settingsdialog.h"
#include "settings/settings.h"
#include "ui_settingsdialog.h" #include "ui_settingsdialog.h"
#include "utils/utils.h"
#include <settings/settings.h>
#include <utils/utils.h>
#include <iostream>
#include <QMessageBox> #include <QMessageBox>
#include <outputcolumnmodel.h>
SettingsDialog::SettingsDialog(QWidget *parent) SettingsDialog::SettingsDialog(QWidget *parent)
: QDialog(parent), ui(new Ui::settingsdialog), : QDialog(parent), ui(new Ui::settingsdialog),
@ -16,43 +12,118 @@ SettingsDialog::SettingsDialog(QWidget *parent)
ui->setupUi(this); ui->setupUi(this);
columns = new std::vector<OutputColumn>; ui->OFDModulesDirEdit->setText(QString::fromStdString(settings.get_all_settings()["ofds_modules_dir"]));
ui->OFDModulesURLEdit->setText(QString::fromStdString(settings.get_all_settings()["ofds_modules_url"]));
ui->storesModulesDirEdit->setText(QString::fromStdString(settings.get_all_settings()["stores_modules_dir"]));
ui->storesModulesURLEdit->setText(QString::fromStdString(settings.get_all_settings()["stores_modules_url"]));
OutputColumnModel *model = new OutputColumnModel(columns, this); ui->goodsNamePositionSpin->setValue(this->settings.get_all_settings()["output_order"]["goods_name"]["position"]);
ui->goodsNameAliasEdit->setText(QString::fromStdString(this->settings.get_all_settings()["output_order"]["goods_name"]["name"]));
ui->outputOptionsListView->setModel(model); ui->goodsPricePerUnitPositionSpin->setValue(this->settings.get_all_settings()["output_order"]["goods_price_per_unit"]["position"]);
ui->goodsPricePerUnitAliasEdit->setText(QString::fromStdString(this->settings.get_all_settings()["output_order"]["goods_price_per_unit"]["name"]));
for (unsigned short i = 0; i < 6; i ++) ui->goodsQuantityPositionSpin->setValue(this->settings.get_all_settings()["output_order"]["goods_quantity"]["position"]);
columns->push_back(OutputColumn(tr("Кто здесь?"), ColumnType::date)); ui->goodsQuantityAliasEdit->setText(QString::fromStdString(this->settings.get_all_settings()["output_order"]["goods_quantity"]["name"]));
for (auto &column : column_names) { ui->goodsNetWeightPositionSpin->setValue(this->settings.get_all_settings()["output_order"]["goods_net_weight"]["position"]);
std::string name = settings.get_all_settings()["output_order"][column.first]["name"]; ui->goodsNetWeightAliasEdit->setText(QString::fromStdString(this->settings.get_all_settings()["output_order"]["goods_net_weight"]["name"]));
unsigned short position = settings.get_all_settings()["output_order"][column.first]["position"];
ColumnType type = column.second; ui->goodsTotalPositionSpin->setValue(this->settings.get_all_settings()["output_order"]["goods_total"]["position"]);
columns->at(position - 1) = (OutputColumn(QString::fromStdString(name), type)); ui->goodsTotalAliasEdit->setText(QString::fromStdString(this->settings.get_all_settings()["output_order"]["goods_total"]["name"]));
}
ui->printHeaderCheckBox->setChecked(this->settings.get_all_settings()["print_header"]);
ui->printTotalCheckBox->setChecked(this->settings.get_all_settings()["print_total"]);
ui->printHeaderCheckBox->setChecked(settings.get_all_settings()["print_header"]);
ui->printTotalCheckBox->setChecked(settings.get_all_settings()["print_total"]);
int currentLanguageIndex = 0; int currentLanguageIndex = 0;
bool languageSettingPresent = false; if (!this->settings.get_all_settings().contains("language")) {
languageSettingPresent = settings.get_all_settings().find("language") != settings.get_all_settings().end();
if (languageSettingPresent) {
currentLanguageIndex = ui->languageComboBox->findText(QString::fromStdString(this->settings.get_all_settings()["language"]));
} else {
currentLanguageIndex = ui->languageComboBox->findText(QLocale::system().name()); currentLanguageIndex = ui->languageComboBox->findText(QLocale::system().name());
if (currentLanguageIndex < 0) { if (currentLanguageIndex < 0) {
currentLanguageIndex = ui->languageComboBox->findText("en_US"); currentLanguageIndex = ui->languageComboBox->findText("en_US");
} }
} else {
currentLanguageIndex = ui->languageComboBox->findText(QString::fromStdString(this->settings.get_all_settings()["language"]));
} }
ui->languageComboBox->setCurrentIndex(currentLanguageIndex); ui->languageComboBox->setCurrentIndex(currentLanguageIndex);
} }
SettingsDialog::~SettingsDialog() { delete ui; }
void SettingsDialog::on_OFDModulesDirEdit_editingFinished() {
this->settings.alter_setting("ofds_modules_dir",
ui->OFDModulesDirEdit->text().toStdString());
}
void SettingsDialog::on_storesModulesDirEdit_editingFinished() {
this->settings.alter_setting("stores_modules_dir",
ui->storesModulesDirEdit->text().toStdString());
}
void SettingsDialog::on_OFDModulesURLEdit_editingFinished() {
this->settings.alter_setting("ofds_modules_url",
ui->OFDModulesURLEdit->text().toStdString());
}
void SettingsDialog::on_storesModulesURLEdit_editingFinished() {
this->settings.alter_setting("stores_modules_url",
ui->storesModulesURLEdit->text().toStdString());
}
void SettingsDialog::on_goodsNamePositionSpin_valueChanged(int value) {
this->settings.get_all_settings()["output_order"]["goods_name"]["position"] =
value;
}
void SettingsDialog::on_goodsNameAliasEdit_editingFinished() {
this->settings.get_all_settings()["output_order"]["goods_name"]["name"] =
ui->goodsNameAliasEdit->text().toStdString();
}
void SettingsDialog::on_goodsPricePerUnitPositionSpin_valueChanged(int value) {
this->settings
.get_all_settings()["output_order"]["goods_price_per_unit"]["position"] =
value;
}
void SettingsDialog::on_goodsPricePerUnitAliasEdit_editingFinished() {
this->settings.get_all_settings()["output_order"]["goods_price_per_unit"]["name"] =
ui->goodsPricePerUnitAliasEdit->text().toStdString();
}
void SettingsDialog::on_goodsQuantityPositionSpin_valueChanged(int value) {
this->settings
.get_all_settings()["output_order"]["goods_quantity"]["position"] = value;
}
void SettingsDialog::on_goodsQuantityAliasEdit_editingFinished() {
this->settings.get_all_settings()["output_order"]["goods_quantity"]["name"] =
ui->goodsQuantityAliasEdit->text().toStdString();
}
void SettingsDialog::on_goodsNetWeightPositionSpin_valueChanged(int value) {
this->settings
.get_all_settings()["output_order"]["goods_net_weight"]["position"] =
value;
}
void SettingsDialog::on_goodsNetWeightAliasEdit_editingFinished() {
this->settings.get_all_settings()["output_order"]["goods_net_weight"]["name"] =
ui->goodsNetWeightAliasEdit->text().toStdString();
}
void SettingsDialog::on_goodsTotalPositionSpin_valueChanged(int value) {
this->settings
.get_all_settings()["output_order"]["goods_total"]["position"] =
value;
}
void SettingsDialog::on_goodsTotalAliasEdit_editingFinished() {
this->settings.get_all_settings()["output_order"]["goods_total"]["name"] =
ui->goodsTotalAliasEdit->text().toStdString();
}
void SettingsDialog::on_printHeaderCheckBox_stateChanged(int value) { void SettingsDialog::on_printHeaderCheckBox_stateChanged(int value) {
this->settings.get_all_settings()["print_header"] = (value? true : false); this->settings.get_all_settings()["print_header"] = (value? true : false);
} }
@ -61,25 +132,12 @@ void SettingsDialog::on_printTotalCheckBox_stateChanged(int value) {
this->settings.get_all_settings()["print_total"] = (value? true : false); this->settings.get_all_settings()["print_total"] = (value? true : false);
} }
void SettingsDialog::on_buttonBox_accepted() { void SettingsDialog::on_buttonBox_accepted() { this->settings.flush(); }
for (int i = 0; i < columns->size(); i ++) {
OutputColumn &column = columns->at(i);
std::string key = find_key_by_value(column_names, column.get_column_type());
settings.get_all_settings()["output_order"][key]["name"] = column.get_text().toStdString();
settings.get_all_settings()["output_order"][key]["position"] = i + 1;
}
this->settings.flush();
}
void SettingsDialog::on_buttonBox_rejected() { this->close(); } void SettingsDialog::on_buttonBox_rejected() { this->close(); }
void SettingsDialog::on_languageComboBox_currentTextChanged(const QString &changed) { void SettingsDialog::on_languageComboBox_currentTextChanged(const QString &changed) {
bool languageSettingPresent = false; if (this->settings.get_all_settings().contains("language")) {
languageSettingPresent = settings.get_all_settings().find("language") != settings.get_all_settings().end();
if (languageSettingPresent) {
if (changed == QString::fromStdString(this->settings.get_all_settings()["language"])) return; if (changed == QString::fromStdString(this->settings.get_all_settings()["language"])) return;
} else { } else {
if (changed == QLocale::system().name()) return; if (changed == QLocale::system().name()) return;
@ -94,6 +152,3 @@ void SettingsDialog::on_languageComboBox_currentTextChanged(const QString &chang
infoDialog.exec(); infoDialog.exec();
} }
SettingsDialog::~SettingsDialog() { delete ui; }

View File

@ -2,8 +2,7 @@
#define SETTINGSDIALOG_H #define SETTINGSDIALOG_H
#include <QDialog> #include <QDialog>
#include <outputcolumn.h> #include "settings/settings.h"
#include <settings/settings.h>
namespace Ui { namespace Ui {
class settingsdialog; class settingsdialog;
@ -12,25 +11,51 @@ class settingsdialog;
class SettingsDialog : public QDialog { class SettingsDialog : public QDialog {
Q_OBJECT Q_OBJECT
Settings settings; Settings settings;
std::vector<OutputColumn> *columns;
public: public:
explicit SettingsDialog(QWidget *parent = nullptr); explicit SettingsDialog(QWidget *parent = nullptr);
~SettingsDialog(); ~SettingsDialog();
private slots: private slots:
void on_printTotalCheckBox_stateChanged(int arg1); void on_OFDModulesDirEdit_editingFinished();
void on_printHeaderCheckBox_stateChanged(int arg1);
void on_buttonBox_accepted(); void on_buttonBox_accepted();
void on_storesModulesDirEdit_editingFinished();
void on_OFDModulesURLEdit_editingFinished();
void on_storesModulesURLEdit_editingFinished();
void on_goodsNamePositionSpin_valueChanged(int arg1);
void on_goodsNameAliasEdit_editingFinished();
void on_goodsPricePerUnitPositionSpin_valueChanged(int arg1);
void on_goodsPricePerUnitAliasEdit_editingFinished();
void on_goodsQuantityPositionSpin_valueChanged(int arg1);
void on_goodsQuantityAliasEdit_editingFinished();
void on_goodsNetWeightPositionSpin_valueChanged(int arg1);
void on_goodsNetWeightAliasEdit_editingFinished();
void on_goodsTotalPositionSpin_valueChanged(int arg1);
void on_goodsTotalAliasEdit_editingFinished();
void on_printHeaderCheckBox_stateChanged(int arg1);
void on_printTotalCheckBox_stateChanged(int arg1);
void on_buttonBox_rejected(); void on_buttonBox_rejected();
void on_languageComboBox_currentTextChanged(const QString &arg1); void on_languageComboBox_currentTextChanged(const QString &arg1);
private: private:
Ui::settingsdialog *ui; Ui::settingsdialog *ui;
public:
bool settingsExist();
}; };
#endif // SETTINGSDIALOG_H #endif // SETTINGSDIALOG_H

306
settingsdialog.ui Normal file
View File

@ -0,0 +1,306 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>settingsdialog</class>
<widget class="QDialog" name="settingsdialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>599</width>
<height>799</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>310</x>
<y>740</y>
<width>251</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Save</set>
</property>
</widget>
<widget class="QScrollArea" name="scrollArea">
<property name="geometry">
<rect>
<x>10</x>
<y>0</y>
<width>541</width>
<height>741</height>
</rect>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>539</width>
<height>739</height>
</rect>
</property>
<widget class="QWidget" name="gridLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>531</width>
<height>731</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="15" column="1">
<widget class="QCheckBox" name="printTotalCheckBox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_15">
<property name="text">
<string>Goods net weight position</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="goodsNamePositionSpin"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Stores modules url</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Goods quantity position</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="OFDModulesDirEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>OFD modules url</string>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Print header</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QSpinBox" name="goodsPricePerUnitPositionSpin"/>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="storesModulesURLEdit"/>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Goods quantity alias</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>OFD modules directory</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="goodsNameAliasEdit"/>
</item>
<item row="15" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Print total</string>
</property>
</widget>
</item>
<item row="13" column="1">
<widget class="QLineEdit" name="goodsTotalAliasEdit"/>
</item>
<item row="8" column="1">
<widget class="QSpinBox" name="goodsQuantityPositionSpin"/>
</item>
<item row="11" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Goods net weight alias</string>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QSpinBox" name="goodsNetWeightPositionSpin"/>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="OFDModulesURLEdit"/>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Goods total alias</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="storesModulesDirEdit"/>
</item>
<item row="7" column="1">
<widget class="QLineEdit" name="goodsPricePerUnitAliasEdit"/>
</item>
<item row="9" column="1">
<widget class="QLineEdit" name="goodsQuantityAliasEdit"/>
</item>
<item row="11" column="1">
<widget class="QLineEdit" name="goodsNetWeightAliasEdit"/>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Goods name position</string>
</property>
</widget>
</item>
<item row="14" column="1">
<widget class="QCheckBox" name="printHeaderCheckBox">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Goods name alias</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Goods price per unit position</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Stores modules directory</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>Goods total position</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="OFDModulesDirChooseButton">
<property name="text">
<string>Choose</string>
</property>
</widget>
</item>
<item row="12" column="1">
<widget class="QSpinBox" name="goodsTotalPositionSpin"/>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Goods price per unit alias</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="storedModulesDirChooseButton">
<property name="text">
<string>Choose</string>
</property>
</widget>
</item>
<item row="16" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="16" column="1">
<widget class="QComboBox" name="languageComboBox">
<item>
<property name="text">
<string>en_US</string>
</property>
</item>
<item>
<property name="text">
<string>ru_RU</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>settingsdialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>settingsdialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -11,6 +11,7 @@ SolveCaptchaDialog::SolveCaptchaDialog(QWidget *parent, std::string* solved_capt
ui->setupUi(this); ui->setupUi(this);
QString captcha_path = QString::fromStdString(get_path_relative_to_home(".local/share/checks_parser/captcha.jpg")); QString captcha_path = QString::fromStdString(get_path_relative_to_home(".local/share/checks_parser/captcha.jpg"));
std::cout << captcha_path.toStdString() << std::endl;
ui->captcha_picture->setPixmap(captcha_path); ui->captcha_picture->setPixmap(captcha_path);
ui->captcha_picture->setScaledContents(true); ui->captcha_picture->setScaledContents(true);
} }

View File

@ -9,17 +9,17 @@
<translation>Dialog</translation> <translation>Dialog</translation>
</message> </message>
<message> <message>
<location filename="../scenes/adjustpicturedialog.ui" line="33"/> <location filename="../scenes/adjustpicturedialog.ui" line="58"/>
<source>Please, zoom to qr code and adjust contrast so that qr code looks sharp</source> <source>Please, zoom to qr code and adjust contrast so that qr code looks sharp</source>
<translation>Please, zoom to qr code and adjust contrast so that qr code looks sharp</translation> <translation>Please, zoom to qr code and adjust contrast so that qr code looks sharp</translation>
</message> </message>
<message> <message>
<location filename="../adjustpicturedialog.cpp" line="42"/> <location filename="../adjustpicturedialog.cpp" line="39"/>
<source>QR code was not detected on that image. Please edit it again or enter data manually</source> <source>QR code was not detected on that image. Please edit it again or enter data manually</source>
<translation>QR code was not detected on that image. Please edit it again or enter data manually</translation> <translation>QR code was not detected on that image. Please edit it again or enter data manually</translation>
</message> </message>
<message> <message>
<location filename="../adjustpicturedialog.cpp" line="44"/> <location filename="../adjustpicturedialog.cpp" line="41"/>
<source>No QR code</source> <source>No QR code</source>
<translation>No QR code</translation> <translation>No QR code</translation>
</message> </message>
@ -27,36 +27,43 @@
<context> <context>
<name>EmailTextScene</name> <name>EmailTextScene</name>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="14"/>
<source>Form</source> <source>Form</source>
<translation type="vanished">Form</translation> <translation>Form</translation>
</message> </message>
<message> <message>
<source>Store type</source> <source>Store type</source>
<translation type="obsolete">Store type</translation> <translation type="obsolete">Store type</translation>
</message> </message>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="26"/>
<source>Check content</source> <source>Check content</source>
<translation type="vanished">Check content</translation> <translation>Check content</translation>
</message> </message>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="33"/>
<source>Parse</source> <source>Parse</source>
<translation type="vanished">Parse</translation> <translation>Parse</translation>
</message> </message>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="46"/>
<source>Store:</source> <source>Store:</source>
<translation type="vanished">Store:</translation> <translation>Store:</translation>
</message> </message>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="62"/>
<source>Back</source> <source>Back</source>
<translation type="vanished">Back</translation> <translation>Back</translation>
</message> </message>
<message> <message>
<location filename="../emailtextscene.cpp" line="31"/>
<source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source> <source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation type="vanished">An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</translation> <translation>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</translation>
</message> </message>
<message> <message>
<location filename="../emailtextscene.cpp" line="33"/>
<source>Error in parsing</source> <source>Error in parsing</source>
<translation type="vanished">Error in parsing</translation> <translation>Error in parsing</translation>
</message> </message>
</context> </context>
<context> <context>
@ -112,397 +119,51 @@
<translation type="vanished">0000000000000000</translation> <translation type="vanished">0000000000000000</translation>
</message> </message>
<message> <message>
<location filename="../scenes/mainwindow.ui" line="322"/>
<source>FN (Fiscal Number)</source> <source>FN (Fiscal Number)</source>
<translatorcomment>FN = Фискальный Номер</translatorcomment> <translatorcomment>FN = Фискальный Номер</translatorcomment>
<translation>FN (Fiscal Number)</translation> <translation type="vanished">FN (Fiscal Number)</translation>
</message> </message>
<message> <message>
<location filename="../scenes/mainwindow.ui" line="58"/>
<source>FD (Fiscal Document)</source> <source>FD (Fiscal Document)</source>
<translatorcomment>FD = Фискальный Документ</translatorcomment> <translatorcomment>FD = Фискальный Документ</translatorcomment>
<translation>FD (Fiscal Document)</translation> <translation type="vanished">FD (Fiscal Document)</translation>
</message> </message>
<message> <message>
<source>0000000000</source> <source>0000000000</source>
<translation type="vanished">000000000</translation> <translation type="vanished">000000000</translation>
</message> </message>
<message> <message>
<source>Back</source>
<translation type="obsolete">Back</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="95"/>
<source>Stop server</source>
<translation>Stop server</translation>
</message>
<message>
<source>Choose image on your PC</source>
<translation type="vanished">Choose image on your PC</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="118"/>
<location filename="../scenes/mainwindow.ui" line="173"/>
<source>or</source>
<translation>or</translation>
</message>
<message>
<source>Use your phone as a QR code scanner</source>
<translation type="vanished">Use your phone as a QR code scanner</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="295"/>
<source>FI (Fiscal Identifier)</source> <source>FI (Fiscal Identifier)</source>
<translatorcomment>FI = Фискальный Признак</translatorcomment> <translatorcomment>FI = Фискальный Признак</translatorcomment>
<translation>FI (Fiscal Identifier)</translation> <translation type="vanished">FI (Fiscal Identifier)</translation>
</message> </message>
<message> <message>
<location filename="../scenes/mainwindow.ui" line="329"/>
<source>Delete selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Add new check</source>
<translation type="vanished">Add new check</translation>
</message>
<message>
<source>Clear</source>
<translation type="vanished">Clear</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="105"/>
<source>Date and time of purchase</source>
<translation>Date and time of purchase</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="309"/>
<source>Operation type</source>
<translation>Operation type</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="288"/>
<source>Parse an E-Mail</source>
<translation>Parse an E-Mail</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="45"/>
<source>Add to queue</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="66"/>
<source>Funds income</source> <source>Funds income</source>
<translatorcomment>Приход средств</translatorcomment> <translatorcomment>Приход средств</translatorcomment>
<translation>Funds income</translation> <translation type="vanished">Funds income</translation>
</message> </message>
<message> <message>
<location filename="../scenes/mainwindow.ui" line="71"/>
<source>Funds return</source> <source>Funds return</source>
<translatorcomment>Возврат средств</translatorcomment> <translatorcomment>Возврат средств</translatorcomment>
<translation>Funds return</translation> <translation type="vanished">Funds return</translation>
</message> </message>
<message> <message>
<location filename="../scenes/mainwindow.ui" line="76"/>
<source>Funds spend</source> <source>Funds spend</source>
<translatorcomment>Расход средств</translatorcomment> <translatorcomment>Расход средств</translatorcomment>
<translation>Funds spend</translation> <translation type="vanished">Funds spend</translation>
</message> </message>
<message> <message>
<location filename="../scenes/mainwindow.ui" line="81"/>
<source>Spends return</source> <source>Spends return</source>
<translatorcomment>Возврат расхода</translatorcomment> <translatorcomment>Возврат расхода</translatorcomment>
<translation>Spends return</translation> <translation type="vanished">Spends return</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="144"/>
<source>Checks to parse</source>
<translation>Checks to parse</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="160"/>
<source>Parse queue</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="186"/>
<source>QR image</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="212"/>
<source>Scan QR using phone</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="225"/>
<source>Settings</source>
<translation>Settings</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="238"/>
<source>Clear data</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="302"/>
<source>Total</source>
<translation>Total</translation>
</message>
<message>
<source>checks parser</source>
<translation type="vanished">checks parser</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="106"/>
<source>QR code for binaryeye to connect</source>
<translation>QR code for binaryeye to connect</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="107"/>
<source>I&apos;ve scanned</source>
<translation>I&apos;ve scanned</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="113"/>
<source>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&apos;t lucky, please, contact the developer.</source>
<translation>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&apos;t lucky, please, contact the developer.</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="115"/>
<source>Could not start http server.</source>
<translation>Could not start http server.</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="161"/>
<source>Selected image: </source>
<translation>Selected image: </translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="194"/>
<source>This feature is under development. Wait it to appear in next updates.</source>
<translation>This feature is under development. Wait for it to appear in next updates.</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="196"/>
<source>Under development</source>
<translation>Under development</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="207"/>
<source>Please, add check(s) to parse</source>
<translation>Please, add check(s) to parse</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="209"/>
<source>No checks to parse</source>
<translation>No checks to parse</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="275"/>
<source>Captcha was not solved correctly!</source>
<translation>Captcha was not solved correctly!</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="277"/>
<source>Captcha is incorrect</source>
<translation>Captcha is incorrect</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="282"/>
<source>Internal server error. Please, try again later.</source>
<translation>Internal server error. Please, try again later.</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="284"/>
<source>Internal server error</source>
<translation>Internal server error</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="289"/>
<source>Check not found. Please, ensure correctness of entered data.</source>
<translation>Check not found. Please, ensure correctness of entered data.</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="291"/>
<source>Check was not found</source>
<translation>Check was not found</translation>
</message>
<message>
<source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation type="vanished">An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</translation>
</message>
<message>
<source>Error in parsing</source>
<translation type="vanished">Error in parsing</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="154"/>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation>Please, select a picture where QR code that contains info about check is present</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="156"/>
<source>Picture was not selected</source>
<translation>Picture was not selected</translation>
</message>
<message>
<source>Please, select a picture to scan</source>
<translation type="vanished">Please, select a picture to scan</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="26"/>
<source>Form</source>
<translation>Form</translation>
</message>
<message>
<source>Optical Character Recognition</source>
<translation type="vanished">Optical Character Recognition</translation>
</message>
<message>
<source>Text from E-Mail</source>
<translation type="vanished">Text from E-Mail</translation>
</message>
</context>
<context>
<name>OCRScene</name>
<message>
<source>Form</source>
<translation type="vanished">Form</translation>
</message>
<message>
<source>Choose</source>
<translation type="vanished">Choose</translation>
</message>
<message>
<source>Path to image:</source>
<translation type="vanished">Path to image:</translation>
</message>
<message>
<source>Store:</source>
<translation type="vanished">Store:</translation>
</message>
<message>
<source>Recognized text will be shown below as soon as image will be processed. Please, edit it</source>
<translation type="vanished">Recognized text will be shown below as soon as image will be processed. Please, edit it</translation>
</message>
<message>
<source>Back</source>
<translation type="vanished">Back</translation>
</message>
<message>
<source>Parse</source>
<translation type="vanished">Parse</translation>
</message>
<message>
<source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation type="vanished">An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</translation>
</message>
<message>
<source>Error in parsing</source>
<translation type="vanished">Error in parsing</translation>
</message>
<message>
<source>Please, select a picture to scan</source>
<translation type="vanished">Please, select a picture to scan</translation>
</message>
<message>
<source>Picture was not selected</source>
<translation type="vanished">Picture was not selected</translation>
</message>
<message>
<source>Path to image: </source>
<translation type="vanished">Path to image: </translation>
</message>
</context>
<context>
<name>OFDScene</name>
<message>
<source>Form</source>
<translation type="vanished">Form</translation>
</message> </message>
<message> <message>
<source>Total</source> <source>Total</source>
<translation type="vanished">Total</translation> <translation type="vanished">Total</translation>
</message> </message>
<message> <message>
<source>Back</source> <source>checks parser</source>
<translation type="vanished">Back</translation> <translation type="vanished">checks parser</translation>
</message>
<message>
<source>or</source>
<translation type="vanished">or</translation>
</message>
<message>
<source>FD (Fiscal Document)</source>
<translation type="vanished">FD (Fiscal Document)</translation>
</message>
<message>
<source>Date and time of purchase</source>
<translation type="vanished">Date and time of purchase</translation>
</message>
<message>
<source>Stop server</source>
<translation type="vanished">Stop server</translation>
</message>
<message>
<source>Funds income</source>
<translation type="vanished">Funds income</translation>
</message>
<message>
<source>Funds return</source>
<translation type="vanished">Funds return</translation>
</message>
<message>
<source>Funds spend</source>
<translation type="vanished">Funds spend</translation>
</message>
<message>
<source>Spends return</source>
<translation type="vanished">Spends return</translation>
</message>
<message>
<source>Use your phone as a QR code scanner</source>
<translation type="vanished">Use your phone as a QR code scanner</translation>
</message>
<message>
<source>FN (Fiscal Number)</source>
<translation type="vanished">FN (Fiscal Number)</translation>
</message>
<message>
<source>FI (Fiscal Identifier)</source>
<translation type="vanished">FI (Fiscal Identifier)</translation>
</message>
<message>
<source>Choose image on your PC</source>
<translation type="vanished">Choose image on your PC</translation>
</message>
<message>
<source>Operation type</source>
<translation type="vanished">Operation type</translation>
</message>
<message>
<source>Parse</source>
<translation type="vanished">Parse</translation>
</message>
<message>
<source>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&apos;t lucky, please, contact the developer.</source>
<translation type="vanished">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&apos;t lucky, please, contact the developer.</translation>
</message>
<message>
<source>Could not start http server.</source>
<translation type="vanished">Could not start http server.</translation>
</message>
<message>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation type="vanished">Please, select a picture where QR code that contains info about check is present</translation>
</message>
<message>
<source>Picture was not selected</source>
<translation type="vanished">Picture was not selected</translation>
</message>
<message>
<source>Selected image: </source>
<translation type="vanished">Selected image: </translation>
</message> </message>
<message> <message>
<source>Captcha was not solved correctly!</source> <source>Captcha was not solved correctly!</source>
@ -525,16 +186,250 @@
<translation type="vanished">Check not found. Please, ensure correctness of entered data.</translation> <translation type="vanished">Check not found. Please, ensure correctness of entered data.</translation>
</message> </message>
<message> <message>
<source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation type="vanished">An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</translation>
</message>
<message>
<source>Error in parsing</source>
<translation type="vanished">Error in parsing</translation>
</message>
<message>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation type="vanished">Please, select a picture where QR code that contains info about check is present</translation>
</message>
<message>
<source>Picture was not selected</source>
<translation type="vanished">Picture was not selected</translation>
</message>
<message>
<source>Please, select a picture to scan</source>
<translation type="vanished">Please, select a picture to scan</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="26"/>
<source>Form</source>
<translation>Form</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="37"/>
<source>Optical Character Recognition</source>
<translation>Optical Character Recognition</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="63"/>
<source>Text from E-Mail</source>
<translation>Text from E-Mail</translation>
</message>
</context>
<context>
<name>OCRScene</name>
<message>
<location filename="../scenes/ocrscene.ui" line="20"/>
<source>Form</source>
<translation>Form</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="64"/>
<source>Choose</source>
<translation>Choose</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="103"/>
<source>Path to image:</source>
<translation>Path to image:</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="119"/>
<source>Store:</source>
<translation>Store:</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="90"/>
<source>Recognized text will be shown below as soon as image will be processed. Please, edit it</source>
<translation>Recognized text will be shown below as soon as image will be processed. Please, edit it</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="77"/>
<source>Back</source>
<translation>Back</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="45"/>
<source>Parse</source>
<translation>Parse</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="36"/>
<source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="38"/>
<source>Error in parsing</source>
<translation>Error in parsing</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="56"/>
<source>Please, select a picture to scan</source>
<translation>Please, select a picture to scan</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="58"/>
<source>Picture was not selected</source>
<translation>Picture was not selected</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="64"/>
<source>Path to image: </source>
<translation>Path to image: </translation>
</message>
</context>
<context>
<name>OFDScene</name>
<message>
<location filename="../scenes/ofdscene.ui" line="14"/>
<source>Form</source>
<translation>Form</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="23"/>
<source>Total</source>
<translation>Total</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="36"/>
<source>Back</source>
<translation>Back</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="49"/>
<source>or</source>
<translation>or</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="69"/>
<source>FD (Fiscal Document)</source>
<translation>FD (Fiscal Document)</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="76"/>
<source>Date and time of purchase</source>
<translation>Date and time of purchase</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="84"/>
<source>Funds income</source>
<translation>Funds income</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="89"/>
<source>Funds return</source>
<translation>Funds return</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="94"/>
<source>Funds spend</source>
<translation>Funds spend</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="99"/>
<source>Spends return</source>
<translation>Spends return</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="136"/>
<source>Use your phone as a QR code scanner</source>
<translation>Use your phone as a QR code scanner</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="149"/>
<source>FN (Fiscal Number)</source>
<translation>FN (Fiscal Number)</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="156"/>
<source>FI (Fiscal Identifier)</source>
<translation>FI (Fiscal Identifier)</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="166"/>
<source>Choose image on your PC</source>
<translation>Choose image on your PC</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="173"/>
<source>Operation type</source>
<translation>Operation type</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="186"/>
<source>Parse</source>
<translation>Parse</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="205"/>
<source>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&apos;t lucky, please, contact the developer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="207"/>
<source>Could not start http server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="84"/>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation>Please, select a picture where QR code that contains info about check is present</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="86"/>
<source>Picture was not selected</source>
<translation>Picture was not selected</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="91"/>
<source>Selected image: </source>
<translation>Selected image: </translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="150"/>
<source>Captcha was not solved correctly!</source>
<translation>Captcha was not solved correctly!</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="152"/>
<source>Captcha is incorrect</source>
<translation>Captcha is incorrect</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="157"/>
<source>Internal server error. Please, try again later.</source>
<translation>Internal server error. Please, try again later.</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="159"/>
<source>Internal server error</source>
<translation>Internal server error</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="164"/>
<source>Check not found. Please, ensure correctness of entered data.</source>
<translation>Check not found. Please, ensure correctness of entered data.</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="166"/>
<source>Check was not found</source> <source>Check was not found</source>
<translation type="vanished">Check was not found</translation> <translation>Check was not found</translation>
</message> </message>
<message> <message>
<location filename="../ofdscene.cpp" line="198"/>
<source>QR code for binaryeye to connect</source> <source>QR code for binaryeye to connect</source>
<translation type="vanished">QR code for binaryeye to connect</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ofdscene.cpp" line="199"/>
<source>I&apos;ve scanned</source> <source>I&apos;ve scanned</source>
<translation type="vanished">I&apos;ve scanned</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>123 123</source> <source>123 123</source>
@ -554,126 +449,115 @@
<translation>Path to export: </translation> <translation>Path to export: </translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="34"/> <location filename="../scenes/outputdialog.ui" line="27"/>
<source>Choose</source> <source>Choose</source>
<translation>Choose</translation> <translation>Choose</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="76"/> <location filename="../scenes/outputdialog.ui" line="34"/>
<source>Print header</source> <source>Print header</source>
<translation>Print header</translation> <translation>Print header</translation>
</message> </message>
<message> <message>
<source>Date</source> <location filename="../scenes/outputdialog.ui" line="55"/>
<translation type="vanished">Date</translation>
</message>
<message>
<source>Goods name</source> <source>Goods name</source>
<translation type="vanished">Goods name</translation> <translation>Goods name</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="60"/>
<source>Goods price</source> <source>Goods price</source>
<translation type="vanished">Goods price</translation> <translation>Goods price</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="65"/>
<source>Goods quantity</source> <source>Goods quantity</source>
<translation type="vanished">Goods quantity</translation> <translation>Goods quality</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="70"/>
<source>Goods net weight</source> <source>Goods net weight</source>
<translation type="vanished">Goods net weight</translation> <translation>Goods net weight</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="75"/>
<source>Goods total</source> <source>Goods total</source>
<translation type="vanished">Goods total</translation> <translation>Goods total</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="80"/>
<source>position</source> <source>position</source>
<translation type="vanished">position</translation> <translation>position</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="85"/>
<source>name</source> <source>name</source>
<translation type="vanished">name</translation> <translation>name</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="90"/>
<source>1</source> <source>1</source>
<translation type="vanished">1</translation> <translation>1</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="95"/>
<source>Name</source> <source>Name</source>
<translation type="vanished">Name</translation> <translation>Name</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="100"/>
<source>2</source> <source>2</source>
<translation type="vanished">2</translation> <translation>2</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="105"/>
<source>Price</source> <source>Price</source>
<translation type="vanished">Price</translation> <translation>Price</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="110"/>
<source>3</source> <source>3</source>
<translation type="vanished">3</translation> <translation>3</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="115"/>
<source>Quantity</source> <source>Quantity</source>
<translation type="vanished">Quantity</translation> <translation>Quantity</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="120"/>
<source>4</source> <source>4</source>
<translation type="vanished">4</translation> <translation>4</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="125"/>
<source>Net weight</source> <source>Net weight</source>
<translation type="vanished">Net Weight</translation> <translation>Net Weight</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="130"/>
<source>5</source> <source>5</source>
<translation type="vanished">5</translation> <translation>5</translation>
</message>
<message>
<source>6</source>
<translation type="vanished">6</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="135"/>
<source>Total price</source> <source>Total price</source>
<translation type="vanished">Total price</translation> <translation>Total price</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="27"/> <location filename="../scenes/outputdialog.ui" line="41"/>
<source>Print total</source> <source>Print total</source>
<translation>Print total</translation> <translation>Print total</translation>
</message> </message>
<message>
<location filename="../outputdialog.cpp" line="37"/>
<source>Кто здесь?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../main.cpp" line="64"/>
<source>Using locale: </source>
<translation>Using locale: </translation>
</message>
</context> </context>
<context> <context>
<name>SettingsDialog</name> <name>SettingsDialog</name>
<message> <message>
<location filename="../settingsdialog.cpp" line="26"/>
<source>Кто здесь?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog.cpp" line="91"/>
<source>You need to restart program to apply language changes</source> <source>You need to restart program to apply language changes</source>
<translation>You need to restart program to apply language changes</translation> <translation type="vanished">You need to restart program to apply language changes</translation>
</message> </message>
<message> <message>
<location filename="../settingsdialog.cpp" line="93"/>
<source>Restart required</source> <source>Restart required</source>
<translation>Restart required</translation> <translation type="vanished">Restart required</translation>
</message> </message>
</context> </context>
<context> <context>
@ -684,12 +568,12 @@
<translation>Dialog</translation> <translation>Dialog</translation>
</message> </message>
<message> <message>
<location filename="../solvecaptchadialog.cpp" line="22"/> <location filename="../solvecaptchadialog.cpp" line="23"/>
<source>Please, enter a valid captcha</source> <source>Please, enter a valid captcha</source>
<translation>Please, enter a valid captcha</translation> <translation>Please, enter a valid captcha</translation>
</message> </message>
<message> <message>
<location filename="../solvecaptchadialog.cpp" line="24"/> <location filename="../solvecaptchadialog.cpp" line="25"/>
<source>No captcha</source> <source>No captcha</source>
<translation>No captcha</translation> <translation>No captcha</translation>
</message> </message>
@ -697,9 +581,8 @@
<context> <context>
<name>settingsdialog</name> <name>settingsdialog</name>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="14"/>
<source>Dialog</source> <source>Dialog</source>
<translation>Dialog</translation> <translation type="vanished">Dialog</translation>
</message> </message>
<message> <message>
<source>Goods name position</source> <source>Goods name position</source>
@ -709,41 +592,25 @@
<source>Goods price per unit alias</source> <source>Goods price per unit alias</source>
<translation type="vanished">Goods price per unit alias</translation> <translation type="vanished">Goods price per unit alias</translation>
</message> </message>
<message>
<source>Date name position</source>
<translation type="vanished">Date name position</translation>
</message>
<message>
<location filename="../scenes/settingsdialog.ui" line="85"/>
<source>Language</source>
<translation>Language</translation>
</message>
<message>
<source>Date name alias</source>
<translation type="vanished">Date name alias</translation>
</message>
<message> <message>
<source>TextLabel</source> <source>TextLabel</source>
<translation type="vanished">Language</translation> <translation type="vanished">Language</translation>
</message> </message>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="58"/>
<source>en_US</source> <source>en_US</source>
<translation>en_US</translation> <translation type="vanished">en_US</translation>
</message> </message>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="63"/>
<source>ru_RU</source> <source>ru_RU</source>
<translation>ru_RU</translation> <translation type="vanished">ru_RU</translation>
</message> </message>
<message> <message>
<source>Choose</source> <source>Choose</source>
<translation type="vanished">Choose</translation> <translation type="vanished">Choose</translation>
</message> </message>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="71"/>
<source>Print header</source> <source>Print header</source>
<translation>Print header</translation> <translation type="vanished">Print header</translation>
</message> </message>
<message> <message>
<source>Goods net weight alias</source> <source>Goods net weight alias</source>
@ -794,9 +661,8 @@
<translation type="vanished">Goods quantity position</translation> <translation type="vanished">Goods quantity position</translation>
</message> </message>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="50"/>
<source>Print total</source> <source>Print total</source>
<translation>Print total</translation> <translation type="vanished">Print total</translation>
</message> </message>
</context> </context>
</TS> </TS>

View File

@ -9,17 +9,17 @@
<translation>Диалог</translation> <translation>Диалог</translation>
</message> </message>
<message> <message>
<location filename="../scenes/adjustpicturedialog.ui" line="33"/> <location filename="../scenes/adjustpicturedialog.ui" line="58"/>
<source>Please, zoom to qr code and adjust contrast so that qr code looks sharp</source> <source>Please, zoom to qr code and adjust contrast so that qr code looks sharp</source>
<translation>Пожалуйста, приблизьте QR код и настройте контраст, чтобы он читался</translation> <translation>Пожалуйста, приблизьте QR код и настройте контраст, чтобы он читался</translation>
</message> </message>
<message> <message>
<location filename="../adjustpicturedialog.cpp" line="42"/> <location filename="../adjustpicturedialog.cpp" line="39"/>
<source>QR code was not detected on that image. Please edit it again or enter data manually</source> <source>QR code was not detected on that image. Please edit it again or enter data manually</source>
<translation>QR код не найден на этом изображении. Пожалуйста, попытайтесь снова или введите данные вручную</translation> <translation>QR код не найден на этом изображении. Пожалуйста, попытайтесь снова или введите данные вручную</translation>
</message> </message>
<message> <message>
<location filename="../adjustpicturedialog.cpp" line="44"/> <location filename="../adjustpicturedialog.cpp" line="41"/>
<source>No QR code</source> <source>No QR code</source>
<translation>QR код не найден</translation> <translation>QR код не найден</translation>
</message> </message>
@ -27,36 +27,43 @@
<context> <context>
<name>EmailTextScene</name> <name>EmailTextScene</name>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="14"/>
<source>Form</source> <source>Form</source>
<translation type="vanished">Форма</translation> <translation>Форма</translation>
</message> </message>
<message> <message>
<source>Store type</source> <source>Store type</source>
<translation type="obsolete">Магазин</translation> <translation type="obsolete">Магазин</translation>
</message> </message>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="26"/>
<source>Check content</source> <source>Check content</source>
<translation type="vanished">Контент чека</translation> <translation>Контент чека</translation>
</message> </message>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="33"/>
<source>Parse</source> <source>Parse</source>
<translation type="vanished">Парсить</translation> <translation>Парсить</translation>
</message> </message>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="46"/>
<source>Store:</source> <source>Store:</source>
<translation type="vanished">Магазин:</translation> <translation>Магазин:</translation>
</message> </message>
<message> <message>
<location filename="../scenes/emailtextscene.ui" line="62"/>
<source>Back</source> <source>Back</source>
<translation type="vanished">Назад</translation> <translation>Назад</translation>
</message> </message>
<message> <message>
<location filename="../emailtextscene.cpp" line="31"/>
<source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source> <source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation type="vanished">Произошла ошибка. Чек был прочитан неверно. Размеры векторов различаются. Пожалуйста, сообщите об этом разработчику.</translation> <translation>Произошла ошибка. Чек был прочитан неверно. Размеры векторов различаются. Пожалуйста, сообщите об этом разработчику.</translation>
</message> </message>
<message> <message>
<location filename="../emailtextscene.cpp" line="33"/>
<source>Error in parsing</source> <source>Error in parsing</source>
<translation type="vanished">Ошибка в парсинге</translation> <translation>Ошибка в парсинге</translation>
</message> </message>
</context> </context>
<context> <context>
@ -112,325 +119,23 @@
<translation type="vanished">0000000000000000</translation> <translation type="vanished">0000000000000000</translation>
</message> </message>
<message> <message>
<location filename="../scenes/mainwindow.ui" line="322"/>
<source>FN (Fiscal Number)</source> <source>FN (Fiscal Number)</source>
<translatorcomment>Фискальный Норма</translatorcomment> <translatorcomment>Фискальный Норма</translatorcomment>
<translation>ФН</translation> <translation type="vanished">ФН</translation>
</message> </message>
<message> <message>
<location filename="../scenes/mainwindow.ui" line="58"/>
<source>FD (Fiscal Document)</source> <source>FD (Fiscal Document)</source>
<translatorcomment>Фискальный Документ</translatorcomment> <translatorcomment>Фискальный Документ</translatorcomment>
<translation>ФД</translation> <translation type="vanished">ФД</translation>
</message> </message>
<message> <message>
<source>0000000000</source> <source>0000000000</source>
<translation type="vanished">000000000</translation> <translation type="vanished">000000000</translation>
</message> </message>
<message> <message>
<source>Back</source>
<translation type="obsolete">Назад</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="95"/>
<source>Stop server</source>
<translation>Остановить сервер</translation>
</message>
<message>
<source>Choose image on your PC</source>
<translation type="vanished">Выбрать изображение на компьютере</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="118"/>
<location filename="../scenes/mainwindow.ui" line="173"/>
<source>or</source>
<translation>или</translation>
</message>
<message>
<source>Use your phone as a QR code scanner</source>
<translation type="vanished">Использовать телефон как сканнер QR</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="295"/>
<source>FI (Fiscal Identifier)</source> <source>FI (Fiscal Identifier)</source>
<translatorcomment>Фискальный Признак</translatorcomment> <translatorcomment>Фискальный Признак</translatorcomment>
<translation>ФП</translation> <translation type="vanished">ФП</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="329"/>
<source>Delete selected</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="105"/>
<source>Date and time of purchase</source>
<translation>Дата и время покупки</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="309"/>
<source>Operation type</source>
<translation>Тип операции</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="288"/>
<source>Parse an E-Mail</source>
<translation>Парсить E-Mail</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="45"/>
<source>Add to queue</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="66"/>
<source>Funds income</source>
<translation>Приход средств</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="71"/>
<source>Funds return</source>
<translation>Возврат средств</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="76"/>
<source>Funds spend</source>
<translation>Расход средств</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="81"/>
<source>Spends return</source>
<translation>Возврат расхода</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="144"/>
<source>Checks to parse</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="160"/>
<source>Parse queue</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="186"/>
<source>QR image</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="212"/>
<source>Scan QR using phone</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="225"/>
<source>Settings</source>
<translation>Настройки</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="238"/>
<source>Clear data</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="302"/>
<source>Total</source>
<translation>Итого</translation>
</message>
<message>
<source>checks parser</source>
<translation type="vanished">Парсер чеков</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="106"/>
<source>QR code for binaryeye to connect</source>
<translation>QR код для подключения BinaryEye</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="107"/>
<source>I&apos;ve scanned</source>
<translation>Просканировал</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="113"/>
<source>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&apos;t lucky, please, contact the developer.</source>
<translation>Не смог поднять HTTP сервер. 10 раз подряд случайно выбранный порт был занят. Либо Вам следует бежать за лоттерейным билетом, или в программе баг. Если лотерейный билет не был выигрышным, пожалуйста, сообщите разработчику.</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="115"/>
<source>Could not start http server.</source>
<translation>Не получилось запустить HTTP сервер.</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="161"/>
<source>Selected image: </source>
<translation>Выбранное изображение: </translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="194"/>
<source>This feature is under development. Wait it to appear in next updates.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="196"/>
<source>Under development</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="207"/>
<source>Please, add check(s) to parse</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="209"/>
<source>No checks to parse</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="275"/>
<source>Captcha was not solved correctly!</source>
<translation>Капча была решена неверно!</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="277"/>
<source>Captcha is incorrect</source>
<translation>Капча введена неверно</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="282"/>
<source>Internal server error. Please, try again later.</source>
<translation>Внутренняя ошибка сервера. Пожалуйста, попробуйте снова позже.</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="284"/>
<source>Internal server error</source>
<translation>Внутренняя ошибка сервера</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="289"/>
<source>Check not found. Please, ensure correctness of entered data.</source>
<translation>Чек не найден. Пожалуйста, убедитесь в правильности введённых данных.</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="291"/>
<source>Check was not found</source>
<translation>Чек не найден</translation>
</message>
<message>
<source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation type="vanished">Произошла ошибка. Чек был прочитан неверно. Размеры векторов различаются. Пожалуйста, сообщите об этом разработчику.</translation>
</message>
<message>
<source>Error in parsing</source>
<translation type="vanished">Ошибка в парсинге</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="154"/>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation>Пожалуйста, выберете изображение, содержащее QR код с информацией о чеке</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="156"/>
<source>Picture was not selected</source>
<translation>Изображение не было выбрано</translation>
</message>
<message>
<source>Please, select a picture to scan</source>
<translation type="vanished">Пожалуйста, выберете изображение для сканирования</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="26"/>
<source>Form</source>
<translation>Форма</translation>
</message>
<message>
<source>Optical Character Recognition</source>
<translation type="vanished">Оптическое распознавание символов</translation>
</message>
<message>
<source>Text from E-Mail</source>
<translation type="vanished">Текст из электронного письма</translation>
</message>
</context>
<context>
<name>OCRScene</name>
<message>
<source>Form</source>
<translation type="vanished">Форма</translation>
</message>
<message>
<source>Choose</source>
<translation type="vanished">Выбрать</translation>
</message>
<message>
<source>Path to image:</source>
<translation type="vanished">Путь к изображению:</translation>
</message>
<message>
<source>Store:</source>
<translation type="vanished">Магазин:</translation>
</message>
<message>
<source>Recognized text will be shown below as soon as image will be processed. Please, edit it</source>
<translation type="vanished">Распознанный текст будет показан ниже как только изображение обработается. Пожалуйста, отредактируйте</translation>
</message>
<message>
<source>Back</source>
<translation type="vanished">Назад</translation>
</message>
<message>
<source>Parse</source>
<translation type="vanished">Парсить</translation>
</message>
<message>
<source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation type="vanished">Произошла ошибка. Чек был прочитан неверно. Размеры векторов различаются. Пожалуйста, сообщите об этом разработчику.</translation>
</message>
<message>
<source>Error in parsing</source>
<translation type="vanished">Ошибка в парсинге</translation>
</message>
<message>
<source>Please, select a picture to scan</source>
<translation type="vanished">Пожалуйста, выберете изображение для сканирования</translation>
</message>
<message>
<source>Picture was not selected</source>
<translation type="vanished">Изображение не было выбрано</translation>
</message>
<message>
<source>Path to image: </source>
<translation type="vanished">Путь к изображению: </translation>
</message>
</context>
<context>
<name>OFDScene</name>
<message>
<source>Form</source>
<translation type="vanished">Форма</translation>
</message>
<message>
<source>Total</source>
<translation type="vanished">Итого</translation>
</message>
<message>
<source>Back</source>
<translation type="vanished">Назад</translation>
</message>
<message>
<source>or</source>
<translation type="vanished">или</translation>
</message>
<message>
<source>FD (Fiscal Document)</source>
<translation type="vanished">ФД</translation>
</message>
<message>
<source>Date and time of purchase</source>
<translation type="vanished">Дата и время покупки</translation>
</message>
<message>
<source>Stop server</source>
<translation type="vanished">Остановить сервер</translation>
</message> </message>
<message> <message>
<source>Funds income</source> <source>Funds income</source>
@ -449,48 +154,12 @@
<translation type="vanished">Возврат расхода</translation> <translation type="vanished">Возврат расхода</translation>
</message> </message>
<message> <message>
<source>Use your phone as a QR code scanner</source> <source>Total</source>
<translation type="vanished">Использовать телефон как сканнер QR</translation> <translation type="vanished">Итого</translation>
</message> </message>
<message> <message>
<source>FN (Fiscal Number)</source> <source>checks parser</source>
<translation type="vanished">ФН</translation> <translation type="vanished">Парсер чеков</translation>
</message>
<message>
<source>FI (Fiscal Identifier)</source>
<translation type="vanished">ФП</translation>
</message>
<message>
<source>Choose image on your PC</source>
<translation type="vanished">Выбрать изображение на компьютере</translation>
</message>
<message>
<source>Operation type</source>
<translation type="vanished">Тип операции</translation>
</message>
<message>
<source>Parse</source>
<translation type="vanished">Парсить</translation>
</message>
<message>
<source>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&apos;t lucky, please, contact the developer.</source>
<translation type="vanished">Не смог поднять HTTP сервер. 10 раз подряд случайно выбранный порт был занят. Либо Вам следует бежать за лоттерейным билетом, или в программе баг. Если лотерейный билет не был выигрышным, пожалуйста, сообщите разработчику.</translation>
</message>
<message>
<source>Could not start http server.</source>
<translation type="vanished">Не получилось запустить HTTP сервер.</translation>
</message>
<message>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation type="vanished">Пожалуйста, выберете изображение, содержащее QR код с информацией о чеке</translation>
</message>
<message>
<source>Picture was not selected</source>
<translation type="vanished">Изображение не было выбрано</translation>
</message>
<message>
<source>Selected image: </source>
<translation type="vanished">Выбранное изображение: </translation>
</message> </message>
<message> <message>
<source>Captcha was not solved correctly!</source> <source>Captcha was not solved correctly!</source>
@ -517,12 +186,250 @@
<translation type="vanished">Чек не найден</translation> <translation type="vanished">Чек не найден</translation>
</message> </message>
<message> <message>
<source>QR code for binaryeye to connect</source> <source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation type="vanished">QR код для подключения BinaryEye</translation> <translation type="vanished">Произошла ошибка. Чек был прочитан неверно. Размеры векторов различаются. Пожалуйста, сообщите об этом разработчику.</translation>
</message> </message>
<message> <message>
<source>Error in parsing</source>
<translation type="vanished">Ошибка в парсинге</translation>
</message>
<message>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation type="vanished">Пожалуйста, выберете изображение, содержащее QR код с информацией о чеке</translation>
</message>
<message>
<source>Picture was not selected</source>
<translation type="vanished">Изображение не было выбрано</translation>
</message>
<message>
<source>Please, select a picture to scan</source>
<translation type="vanished">Пожалуйста, выберете изображение для сканирования</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="26"/>
<source>Form</source>
<translation>Форма</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="37"/>
<source>Optical Character Recognition</source>
<translation>Оптическое распознавание символов</translation>
</message>
<message>
<location filename="../scenes/mainwindow.ui" line="63"/>
<source>Text from E-Mail</source>
<translation>Текст из электронного письма</translation>
</message>
</context>
<context>
<name>OCRScene</name>
<message>
<location filename="../scenes/ocrscene.ui" line="20"/>
<source>Form</source>
<translation>Форма</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="64"/>
<source>Choose</source>
<translation>Выбрать</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="103"/>
<source>Path to image:</source>
<translation>Путь к изображению:</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="119"/>
<source>Store:</source>
<translation>Магазин:</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="90"/>
<source>Recognized text will be shown below as soon as image will be processed. Please, edit it</source>
<translation>Распознанный текст будет показан ниже как только изображение обработается. Пожалуйста, отредактируйте</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="77"/>
<source>Back</source>
<translation>Назад</translation>
</message>
<message>
<location filename="../scenes/ocrscene.ui" line="45"/>
<source>Parse</source>
<translation>Парсить</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="36"/>
<source>An error has occured. Check was matched incorrectly. Vector sizes are different. Please, contact the developer.</source>
<translation>Произошла ошибка. Чек был прочитан неверно. Размеры векторов различаются. Пожалуйста, сообщите об этом разработчику.</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="38"/>
<source>Error in parsing</source>
<translation>Ошибка в парсинге</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="56"/>
<source>Please, select a picture to scan</source>
<translation>Пожалуйста, выберете изображение для сканирования</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="58"/>
<source>Picture was not selected</source>
<translation>Изображение не было выбрано</translation>
</message>
<message>
<location filename="../ocrscene.cpp" line="64"/>
<source>Path to image: </source>
<translation>Путь к изображению: </translation>
</message>
</context>
<context>
<name>OFDScene</name>
<message>
<location filename="../scenes/ofdscene.ui" line="14"/>
<source>Form</source>
<translation>Форма</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="23"/>
<source>Total</source>
<translation>Итого</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="36"/>
<source>Back</source>
<translation>Назад</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="49"/>
<source>or</source>
<translation>или</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="69"/>
<source>FD (Fiscal Document)</source>
<translation>ФД</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="76"/>
<source>Date and time of purchase</source>
<translation>Дата и время покупки</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="84"/>
<source>Funds income</source>
<translation>Приход средств</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="89"/>
<source>Funds return</source>
<translation>Возврат средств</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="94"/>
<source>Funds spend</source>
<translation>Расход средств</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="99"/>
<source>Spends return</source>
<translation>Возврат расхода</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="136"/>
<source>Use your phone as a QR code scanner</source>
<translation>Использовать телефон как сканнер QR</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="149"/>
<source>FN (Fiscal Number)</source>
<translation>ФН</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="156"/>
<source>FI (Fiscal Identifier)</source>
<translation>ФП</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="166"/>
<source>Choose image on your PC</source>
<translation>Выбрать изображение на компьютере</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="173"/>
<source>Operation type</source>
<translation>Тип операции</translation>
</message>
<message>
<location filename="../scenes/ofdscene.ui" line="186"/>
<source>Parse</source>
<translation>Парсить</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="205"/>
<source>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&apos;t lucky, please, contact the developer.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="207"/>
<source>Could not start http server.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="84"/>
<source>Please, select a picture where QR code that contains info about check is present</source>
<translation>Пожалуйста, выберете изображение, содержащее QR код с информацией о чеке</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="86"/>
<source>Picture was not selected</source>
<translation>Изображение не было выбрано</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="91"/>
<source>Selected image: </source>
<translation>Выбранное изображение: </translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="150"/>
<source>Captcha was not solved correctly!</source>
<translation>Капча была решена неверно!</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="152"/>
<source>Captcha is incorrect</source>
<translation>Капча введена неверно</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="157"/>
<source>Internal server error. Please, try again later.</source>
<translation>Внутренняя ошибка сервера. Пожалуйста, попробуйте снова позже.</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="159"/>
<source>Internal server error</source>
<translation>Внутренняя ошибка сервера</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="164"/>
<source>Check not found. Please, ensure correctness of entered data.</source>
<translation>Чек не найден. Пожалуйста, убедитесь в правильности введённых данных.</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="166"/>
<source>Check was not found</source>
<translation>Чек не найден</translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="198"/>
<source>QR code for binaryeye to connect</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ofdscene.cpp" line="199"/>
<source>I&apos;ve scanned</source> <source>I&apos;ve scanned</source>
<translation type="vanished">Просканировал</translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<source>123 123</source> <source>123 123</source>
@ -542,122 +449,115 @@
<translation>Путь для экспорта: </translation> <translation>Путь для экспорта: </translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="34"/> <location filename="../scenes/outputdialog.ui" line="27"/>
<source>Choose</source> <source>Choose</source>
<translation>Выбрать</translation> <translation>Выбрать</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="76"/> <location filename="../scenes/outputdialog.ui" line="34"/>
<source>Print header</source> <source>Print header</source>
<translation>Печатать заголовок</translation> <translation>Печатать заголовок</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="55"/>
<source>Goods name</source> <source>Goods name</source>
<translation type="vanished">Имя товара</translation> <translation>Имя товара</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="60"/>
<source>Goods price</source> <source>Goods price</source>
<translation type="vanished">Цена товара</translation> <translation>Цена товара</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="65"/>
<source>Goods quantity</source> <source>Goods quantity</source>
<translation type="vanished">Количество товара</translation> <translation>Количество товара</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="70"/>
<source>Goods net weight</source> <source>Goods net weight</source>
<translation type="vanished">Масса нетто товара</translation> <translation>Масса нетто товара</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="75"/>
<source>Goods total</source> <source>Goods total</source>
<translation type="vanished">Всего за товар</translation> <translation>Всего за товар</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="80"/>
<source>position</source> <source>position</source>
<translation type="vanished">позиция</translation> <translation>позиция</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="85"/>
<source>name</source> <source>name</source>
<translation type="vanished">алиас</translation> <translation>алиас</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="90"/>
<source>1</source> <source>1</source>
<translation type="vanished">1</translation> <translation>1</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="95"/>
<source>Name</source> <source>Name</source>
<translation type="vanished">Имя</translation> <translation>Имя</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="100"/>
<source>2</source> <source>2</source>
<translation type="vanished">2</translation> <translation>2</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="105"/>
<source>Price</source> <source>Price</source>
<translation type="vanished">Цена</translation> <translation>Цена</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="110"/>
<source>3</source> <source>3</source>
<translation type="vanished">3</translation> <translation>3</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="115"/>
<source>Quantity</source> <source>Quantity</source>
<translation type="vanished">Количество</translation> <translation>Количество</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="120"/>
<source>4</source> <source>4</source>
<translation type="vanished">4</translation> <translation>4</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="125"/>
<source>Net weight</source> <source>Net weight</source>
<translation type="vanished">Масса нетто</translation> <translation>Масса нетто</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="130"/>
<source>5</source> <source>5</source>
<translation type="vanished">5</translation> <translation>5</translation>
</message>
<message>
<source>6</source>
<translation type="obsolete">6</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="135"/>
<source>Total price</source> <source>Total price</source>
<translation type="vanished">Всего</translation> <translation>Всего</translation>
</message> </message>
<message> <message>
<location filename="../scenes/outputdialog.ui" line="27"/> <location filename="../scenes/outputdialog.ui" line="41"/>
<source>Print total</source> <source>Print total</source>
<translation>Печатать Итого</translation> <translation>Печатать Итого</translation>
</message> </message>
<message>
<location filename="../outputdialog.cpp" line="37"/>
<source>Кто здесь?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
<message>
<location filename="../main.cpp" line="64"/>
<source>Using locale: </source>
<translation>Использую локаль: </translation>
</message>
</context> </context>
<context> <context>
<name>SettingsDialog</name> <name>SettingsDialog</name>
<message> <message>
<location filename="../settingsdialog.cpp" line="26"/>
<source>Кто здесь?</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsdialog.cpp" line="91"/>
<source>You need to restart program to apply language changes</source> <source>You need to restart program to apply language changes</source>
<translation>Требуется перезагрузить программу, чтобы применить изменения языка</translation> <translation type="vanished">Требуется перезагрузить программу, чтобы применить изменения языка</translation>
</message> </message>
<message> <message>
<location filename="../settingsdialog.cpp" line="93"/>
<source>Restart required</source> <source>Restart required</source>
<translation>Требуется перезагрузка</translation> <translation type="vanished">Требуется перезагрузка</translation>
</message> </message>
</context> </context>
<context> <context>
@ -668,12 +568,12 @@
<translation>Диалог</translation> <translation>Диалог</translation>
</message> </message>
<message> <message>
<location filename="../solvecaptchadialog.cpp" line="22"/> <location filename="../solvecaptchadialog.cpp" line="23"/>
<source>Please, enter a valid captcha</source> <source>Please, enter a valid captcha</source>
<translation>Пожалуйста, введите верную капчу</translation> <translation>Пожалуйста, введите верную капчу</translation>
</message> </message>
<message> <message>
<location filename="../solvecaptchadialog.cpp" line="24"/> <location filename="../solvecaptchadialog.cpp" line="25"/>
<source>No captcha</source> <source>No captcha</source>
<translation>Нет капчи</translation> <translation>Нет капчи</translation>
</message> </message>
@ -681,9 +581,8 @@
<context> <context>
<name>settingsdialog</name> <name>settingsdialog</name>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="14"/>
<source>Dialog</source> <source>Dialog</source>
<translation>Диалог</translation> <translation type="vanished">Диалог</translation>
</message> </message>
<message> <message>
<source>Goods name position</source> <source>Goods name position</source>
@ -693,33 +592,25 @@
<source>Goods price per unit alias</source> <source>Goods price per unit alias</source>
<translation type="vanished">Алиас цены товара</translation> <translation type="vanished">Алиас цены товара</translation>
</message> </message>
<message>
<location filename="../scenes/settingsdialog.ui" line="85"/>
<source>Language</source>
<translation>Язык</translation>
</message>
<message> <message>
<source>TextLabel</source> <source>TextLabel</source>
<translation type="vanished">Язык</translation> <translation type="vanished">Язык</translation>
</message> </message>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="58"/>
<source>en_US</source> <source>en_US</source>
<translation>en_US</translation> <translation type="vanished">en_US</translation>
</message> </message>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="63"/>
<source>ru_RU</source> <source>ru_RU</source>
<translation>ru_RU</translation> <translation type="vanished">ru_RU</translation>
</message> </message>
<message> <message>
<source>Choose</source> <source>Choose</source>
<translation type="vanished">Выбрать</translation> <translation type="vanished">Выбрать</translation>
</message> </message>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="71"/>
<source>Print header</source> <source>Print header</source>
<translation>Печатать заголовок</translation> <translation type="vanished">Печатать заголовок</translation>
</message> </message>
<message> <message>
<source>Goods net weight alias</source> <source>Goods net weight alias</source>
@ -770,9 +661,8 @@
<translation type="vanished">Позиция количества товара</translation> <translation type="vanished">Позиция количества товара</translation>
</message> </message>
<message> <message>
<location filename="../scenes/settingsdialog.ui" line="50"/>
<source>Print total</source> <source>Print total</source>
<translation>Печатать Итого</translation> <translation type="vanished">Печатать Итого</translation>
</message> </message>
</context> </context>
</TS> </TS>

View File

@ -1,79 +0,0 @@
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include <utils/base64.h>
#include <string>
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (std::isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}

View File

@ -1,29 +0,0 @@
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include <string>
std::string base64_decode(std::string const& encoded_string);

View File

@ -1,32 +1,24 @@
#include "output/output_options.h" #include "utils.h"
#include <utils/utils.h>
#ifdef BUILD_OFD_BINARYEYE_SCAN #include <arpa/inet.h>
# include <arpa/inet.h>
# include <qrencode.h>
# include <ifaddrs.h>
# include <netinet/in.h>
#endif
#ifdef BUILD_OFD_MODE
# include <exceptions/ofdrequestexception.h>
# include <fstream>
#endif
#include <codecvt> #include <codecvt>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <locale> #include <locale>
#if defined(BUILD_OCR_MODE) || defined(BUILD_OFD_MODE) #include <opencv2/core/mat.hpp>
# include <opencv2/core/mat.hpp> #include <opencv2/imgcodecs.hpp>
# include <opencv2/imgcodecs.hpp> #include <opencv2/imgproc.hpp>
# include <opencv2/imgproc.hpp> #include <qrencode.h>
#endif #include <regex>
#include <string> #include <string>
#include "../exceptions/ofdrequestexception.h"
#include "settings/settings.h"
#include <QWidget> #include <QWidget>
#include <fstream>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <opencv2/opencv.hpp>
#include <boost/regex.hpp>
#include <net/net.h>
#include <settings/settings.h>
#ifdef BUILD_OFD_BINARYEYE_SCAN
std::string get_local_ip_address() { std::string get_local_ip_address() {
struct ifaddrs * ifAddrStruct=NULL; struct ifaddrs * ifAddrStruct=NULL;
struct ifaddrs * ifa=NULL; struct ifaddrs * ifa=NULL;
@ -52,7 +44,6 @@ std::string get_local_ip_address() {
throw std::runtime_error(QWidget::tr("Could not find any usable local IP address. If you beleive that this is problem with the program, please, contact the developer.").toStdString()); throw std::runtime_error(QWidget::tr("Could not find any usable local IP address. If you beleive that this is problem with the program, please, contact the developer.").toStdString());
} }
#endif
std::string to_utf8(std::wstring wide_string) { std::string to_utf8(std::wstring wide_string) {
static std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv; static std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;
@ -75,36 +66,9 @@ bool vector_contains_element(const std::vector<T>& vector, const T& to_find) {
} }
return false; return false;
} }
template<class T>
bool areAllSizesEqual(const std::vector<T>& v1, const std::vector<T>& v2,
const std::vector<T>& v3, const std::vector<T>& v4) {
return (v1.size() == v2.size() && v2.size() == v3.size() && v3.size() == v4.size());
}
// template<typename K, typename V>
// K find_key_by_value(std::map<K, V> &m, V value) {
// for (auto& entry : m) {
// if (value == entry.second) {
// return entry.first;
// }
// }
// return K();
// }
std::string find_key_by_value(const std::map<std::string, ColumnType> &m, ColumnType value) {
for (auto& entry : m) {
if (value == entry.second) {
return entry.first;
}
}
return "";
}
//ужас //ужас
template bool vector_contains_element<std::string>(const std::vector<std::string>& vector, const std::string& to_find); template bool vector_contains_element<std::string>(const std::vector<std::string>& vector, const std::string& to_find);
template bool areAllSizesEqual(const std::vector<std::string>& v1, const std::vector<std::string>& v2,
const std::vector<std::string>& v3, const std::vector<std::string>& v4);
// template std::string find_key_by_value(std::map<std::string, ColumnType> &, ColumnType);
std::vector<std::string> split(std::string s, std::string delimiter) { std::vector<std::string> split(std::string s, std::string delimiter) {
std::vector<std::string> result; std::vector<std::string> result;
@ -120,30 +84,16 @@ std::vector<std::string> split(std::string s, std::string delimiter) {
return result; return result;
} }
std::vector<std::wstring> split(std::wstring s, std::wstring delimiter) {
std::vector<std::wstring> result;
size_t pos = 0;
std::wstring token;
while ((pos = s.find(delimiter)) != std::string::npos) {
token = s.substr(0, pos);
result.push_back(token);
s.erase(0, pos + delimiter.length());
}
result.push_back(s);
return result;
}
std::wstring substring_from_to(std::wstring& text, std::wstring from, std::wstring to) { std::wstring substring_from_to(std::wstring& text, std::wstring from, std::wstring to) {
unsigned int start_pos = 0; unsigned int start_pos = 0;
unsigned int end_pos = 0; unsigned int end_pos = 0;
std::wstring substring; std::wstring substring;
boost::wregex start_regex(from); std::wregex start_regex(from);
boost::wregex end_regex(to); std::wregex end_regex(to);
for (boost::wsregex_iterator it{text.begin(), text.end(), start_regex}, end{}; for (std::wsregex_iterator it{text.begin(), text.end(), start_regex}, end{};
it != end; it++) { it != end; it++) {
start_pos = it->position() + it->str().size(); start_pos = it->position() + it->str().size();
break; break;
@ -152,7 +102,7 @@ std::wstring substring_from_to(std::wstring& text, std::wstring from, std::wstri
if(text == from_utf8("")) return text; if(text == from_utf8("")) return text;
substring = text.substr(start_pos, text.size()); substring = text.substr(start_pos, text.size());
for (boost::wsregex_iterator it{substring.begin(), substring.end(), end_regex}, end{}; for (std::wsregex_iterator it{substring.begin(), substring.end(), end_regex}, end{};
it != end; it++) { it != end; it++) {
end_pos = it->position(); end_pos = it->position();
break; break;
@ -164,7 +114,7 @@ std::wstring substring_from_to(std::wstring& text, std::wstring from, std::wstri
return substring; return substring;
} }
#ifdef BUILD_OFD_MODE
std::wstring trim_html_response(std::wstring& check) { std::wstring trim_html_response(std::wstring& check) {
std::wstring begin_check_marker = from_utf8("<!-- Products -->"); std::wstring begin_check_marker = from_utf8("<!-- Products -->");
std::wstring end_check_marker = from_utf8("<!-- \\/Products -->"); std::wstring end_check_marker = from_utf8("<!-- \\/Products -->");
@ -174,23 +124,23 @@ std::wstring trim_html_response(std::wstring& check) {
} }
std::vector<std::wstring> find_in_html(std::string& html, std::string regex, std::string html_start, std::string html_end) { std::vector<std::wstring> find_in_html(std::string& html, std::string regex, std::string html_start, std::string html_end) {
boost::regex searching_regex(regex); std::regex searching_regex(regex);
std::vector<std::wstring> parsed; std::vector<std::wstring> parsed;
for (boost::sregex_iterator it{html.begin(), html.end(), searching_regex}, end{}; for (std::sregex_iterator it{html.begin(), html.end(), searching_regex}, end{};
it != end; it++) { it != end; it++) {
std::wstring found_entry = from_utf8(it->str()); std::wstring found_entry = from_utf8(it->str());
// std::cout << "Found: " << to_utf8(found_entry) << std::endl; std::wcout << "Found: " << found_entry << std::endl;
std::wstring extracted = substring_from_to(found_entry, from_utf8(html_start), from_utf8(html_end)); std::wstring extracted = substring_from_to(found_entry, from_utf8(html_start), from_utf8(html_end));
// std::cout << "Extracted: " << to_utf8(extracted) << std::endl; std::wcout << "Extracted: " << extracted << std::endl;
parsed.push_back(extracted); parsed.push_back(extracted);
} }
return parsed; return parsed;
} }
std::vector<std::wstring> find_products_in_html(std::string html) { std::vector<std::wstring> find_products_in_html(std::string html) {
return find_in_html(html, "<div class=\"ifw-col ifw-col-1 text-left\"><b>.{2,100}<\\/b><\\/div>", "<div class=\"ifw-col ifw-col-1 text-left\"><b>", "<\\/b><\\/div>"); return find_in_html(html, "<div class=\"ifw-col ifw-col-1 text-left\"><b>.*<\\/b><\\/div>", "<div class=\"ifw-col ifw-col-1 text-left\"><b>", "<\\/b><\\/div>");
} }
std::vector<std::wstring> find_amounts_in_html(std::string html) { std::vector<std::wstring> find_amounts_in_html(std::string html) {
@ -202,29 +152,6 @@ std::vector<std::wstring> find_amounts_in_html(std::string html) {
return founds; return founds;
} }
std::vector<std::wstring> find_net_weights_in_names(std::vector<std::wstring> &names) {
std::vector<std::wstring> result;
for (std::wstring &name : names ) {
boost::wregex regexp(from_utf8("((\\d+(\\.|,)?\\d{0,}((м|)л|(к|м|)г|т|ц|шт|(pc|)s|(m|k|)g|(m|)l|t))(\\s|\\t){0,})+"), boost::regex_constants::collate);
bool found = false;
for (boost::wsregex_iterator it{name.begin(), name.end(), regexp}, end{}; it != end;
it++) {
result.push_back(it->str());
found = true;
name.erase(it->position(), it->str().length());
break;
}
if (!found) {
result.push_back(from_utf8("?"));
}
}
for (auto &entry : result) {
std::replace(entry.begin(), entry.end(), ',', '.');
}
return result;
}
std::vector<std::wstring> find_prices_in_html(std::string html) { std::vector<std::wstring> find_prices_in_html(std::string html) {
std::vector<std::wstring> founds = find_in_html(html, "X <\\/span><span>\\d+(\\.|,)\\d{2}<\\/span>", "X <\\/span><span>", "<\\/span>"); std::vector<std::wstring> founds = find_in_html(html, "X <\\/span><span>\\d+(\\.|,)\\d{2}<\\/span>", "X <\\/span><span>", "<\\/span>");
for (auto &found : founds) { for (auto &found : founds) {
@ -234,7 +161,7 @@ std::vector<std::wstring> find_prices_in_html(std::string html) {
return founds; return founds;
} }
void dumpVectorsToStderr(std::vector<std::wstring> &products, std::vector<std::wstring> &amounts, std::vector<std::wstring> &net_weights, std::vector<std::wstring> &prices) { void dumpVectorsToStderr(std::vector<std::wstring> &products, std::vector<std::wstring> &amounts, std::vector<std::wstring> &prices) {
std::cerr << "Products: "; std::cerr << "Products: ";
for (auto &product : products) { for (auto &product : products) {
std::cerr << to_utf8(product) << "|[]|"; std::cerr << to_utf8(product) << "|[]|";
@ -247,12 +174,6 @@ void dumpVectorsToStderr(std::vector<std::wstring> &products, std::vector<std::w
} }
std::cerr << std::endl; std::cerr << std::endl;
std::cerr << "Net weights: ";
for (auto &net_weight : net_weights) {
std::wcerr << net_weight << " ";
}
std::cerr << std::endl;
std::cerr << "Prices: "; std::cerr << "Prices: ";
for (auto &price : prices) { for (auto &price : prices) {
std::wcerr << price << " "; std::wcerr << price << " ";
@ -267,7 +188,6 @@ Check parseOfdRuAnswer(std::string html) {
std::vector<std::wstring> products = find_products_in_html(trimmed); std::vector<std::wstring> products = find_products_in_html(trimmed);
std::vector<std::wstring> amounts = find_amounts_in_html(trimmed); std::vector<std::wstring> amounts = find_amounts_in_html(trimmed);
std::vector<std::wstring> net_weights = find_net_weights_in_names(products);
std::vector<std::wstring> prices = find_prices_in_html(trimmed); std::vector<std::wstring> prices = find_prices_in_html(trimmed);
if ((products.size() + amounts.size() + prices.size()) == 0) { if ((products.size() + amounts.size() + prices.size()) == 0) {
@ -282,7 +202,7 @@ Check parseOfdRuAnswer(std::string html) {
} }
if ((products.size() + amounts.size() + prices.size())/products.size() != 3) { if ((products.size() + amounts.size() + prices.size())/products.size() != 3) {
dumpVectorsToStderr(products, amounts, net_weights, prices); dumpVectorsToStderr(products, amounts, prices);
//TOOD: make new setting "app_home" and get all path using it. //TOOD: make new setting "app_home" and get all path using it.
std::ofstream error_log(get_path_relative_to_home(".local/share/checks_parser/error_log.txt"), std::ios_base::app); std::ofstream error_log(get_path_relative_to_home(".local/share/checks_parser/error_log.txt"), std::ios_base::app);
error_log << trimmed << std::endl; error_log << trimmed << std::endl;
@ -293,17 +213,13 @@ Check parseOfdRuAnswer(std::string html) {
Check c; Check c;
for (int i = 0; i < products.size(); i ++) { for (int i = 0; i < products.size(); i ++) {
// std::cout << "Adding to check: "; Goods goods(to_utf8(products[i]), std::stod(prices[i]), std::stod(amounts[i]));
// std::cout << to_utf8(products[i]) << " " << to_utf8(prices[i]) << " " << to_utf8(net_weights[i]) << " " << to_utf8(amounts[i]) << std::endl;
Goods goods(to_utf8(products[i]), std::stod(prices[i]), to_utf8(net_weights[i]), std::stod(amounts[i]));
c.add_goods(goods); c.add_goods(goods);
} }
return c; return c;
} }
#endif // ifdef BUILD_OFD_MODE
#ifdef BUILD_OFD_BINARYEYE_SCAN
void generate_qr_code(std::string data) { void generate_qr_code(std::string data) {
QRcode *qrCode = QRcode_encodeString(data.c_str(), 2, QR_ECLEVEL_L, QR_MODE_8, 1); QRcode *qrCode = QRcode_encodeString(data.c_str(), 2, QR_ECLEVEL_L, QR_MODE_8, 1);
if (qrCode == NULL) { if (qrCode == NULL) {
@ -328,18 +244,3 @@ void generate_qr_code(std::string data) {
cv::imwrite(get_path_relative_to_home(".local/share/checks_parser/binaryeye_connection.png"), qrCodeImage); cv::imwrite(get_path_relative_to_home(".local/share/checks_parser/binaryeye_connection.png"), qrCodeImage);
QRcode_free(qrCode); QRcode_free(qrCode);
} }
#endif // ifdef BUILD_OFD_BINARYEYE_SCAN
#ifdef BUILD_EMAIL_MODE
std::vector<std::string> read_file(std::string path) {
std::ifstream stream(path);
std::vector<std::string> lines;
std::string buffer;
while(getline(stream, buffer)) {
lines.push_back(buffer);
}
stream.close();
return lines;
}
#endif // ifdef BUILD_EMAIL_MODE

View File

@ -3,50 +3,23 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include "../check/check.h" #include "../check/check.h"
#include "output/output_options.h"
std::string get_local_ip_address();
std::string to_utf8(std::wstring wide_string); std::string to_utf8(std::wstring wide_string);
std::wstring from_utf8(std::string string); std::wstring from_utf8(std::string string);
std::string get_path_relative_to_home(std::string path); std::string get_path_relative_to_home(std::string path);
const std::map<std::string, ColumnType> column_names = {
{"date", ColumnType::date},
{"goods_name", ColumnType::goods_name},
{"goods_price_per_unit", ColumnType::goods_price_per_unit},
{"goods_quantity", ColumnType::goods_quantity},
{"goods_net_weight", ColumnType::goods_net_weight},
{"goods_total", ColumnType::goods_total}
};
// template <typename K, typename V>
// K find_key_by_value(std::map<K, V> &m, V value);
std::string find_key_by_value(const std::map<std::string, ColumnType> &m, ColumnType value);
template <typename T> template <typename T>
bool vector_contains_element(const std::vector<T> &vector, const T &to_find); bool vector_contains_element(const std::vector<T> &vector, const T &to_find);
template <class T>
bool areAllSizesEqual(const std::vector<T>& v1, const std::vector<T>& v2,
const std::vector<T>& v3, const std::vector<T>& v4);
std::vector<std::string> split(std::string, std::string); std::vector<std::string> split(std::string, std::string);
std::vector<std::wstring> split(std::wstring s, std::wstring delimiter);
#ifdef BUILD_OFD_MODE
Check parseOfdRuAnswer(std::string); Check parseOfdRuAnswer(std::string);
std::wstring trim_html_response(std::wstring& check); std::wstring trim_html_response(std::wstring& check);
#endif
#ifdef BUILD_OFD_BINARYEYE_SCAN
void generate_qr_code(std::string data); void generate_qr_code(std::string data);
std::string get_local_ip_address();
#endif
void fetch_and_download_modules();
#ifdef BUILD_EMAIL_MODE
std::vector<std::string> read_file(std::string path);
#endif
#endif // UTILS_H #endif // UTILS_H

View File

@ -1,229 +0,0 @@
#include "checkqueuetablemodel.h"
#include <QMimeData>
#include <QIODevice>
#include <QDateTime>
#include <QVector>
CheckQueueTableModel::CheckQueueTableModel(std::vector<Check> *checks, QObject *parent)
: checks(checks), QAbstractTableModel{parent}
{}
int CheckQueueTableModel::rowCount(const QModelIndex &parent) const { return checks->size(); }
int CheckQueueTableModel::columnCount(const QModelIndex &parent) const { return 2; }
QVariant CheckQueueTableModel::data(const QModelIndex &index, int role) const {
if (!index.isValid() || index.row() >= checks->size())
return QVariant();
if (role != Qt::DisplayRole) return QVariant();
Check& c = checks->at(index.row());
switch (index.column()) {
case 0:
return QVariant::fromValue(QString::fromStdString(c.get_date()));
break;
case 1:
return QVariant::fromValue(c.get_total());
break;
}
return QVariant();
}
bool CheckQueueTableModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (role == Qt::EditRole) {
if (!index.isValid() || index.row() >= checks->size())
return false;
unsigned int row = index.row();
Check &c = checks->at(row);
switch (index.column()) {
case 0:
c.set_date(value.value<std::string>());
break;
case 1:
c.set_total(value.value<double>());
break;
emit dataChanged(index, index, {role});
}
return true;
}
return false;
}
QVariant CheckQueueTableModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
switch (section) {
case 0:
return tr("Date&Time");
case 1:
return tr("Total");
}
} else if (role == Qt::DisplayRole && orientation == Qt::Vertical) {
return section + 1;
}
return QVariant();
}
Qt::ItemFlags CheckQueueTableModel::flags(const QModelIndex &index) const {
auto flags = QAbstractItemModel::flags(index);
if (index.isValid())
flags |= Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled;
else
flags |= Qt::ItemIsDropEnabled;
return flags;
}
Qt::DropActions CheckQueueTableModel::supportedDropActions() const {
return Qt::DropActions() | Qt::MoveAction;
}
bool CheckQueueTableModel::insertRows(int position, int rows, const QModelIndex &index) {
beginInsertRows(QModelIndex(), position, position+rows-1);
for (int i = 0; i < rows; ++i)
checks->emplace(checks->begin() + position, Check());
endInsertRows();
return true;
}
bool CheckQueueTableModel::removeRows(int position, int rows, const QModelIndex &index) {
beginRemoveRows(QModelIndex(), position, position+rows-1);
for (int row = 0; row < rows; ++row)
checks->erase(std::next(checks->begin(), position));
endRemoveRows();
return true;
}
QStringList CheckQueueTableModel::mimeTypes() const {
QStringList types;
types << CheckQueueTableModel::MimeType;
return types;
}
bool CheckQueueTableModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int, const QModelIndex &) {
if (action != Qt::MoveAction || !data->hasFormat(CheckQueueTableModel::MimeType))
return false;
if (row > checks->size()) return false;
return true;
}
QMimeData* CheckQueueTableModel::mimeData(const QModelIndexList &indexes) const {
QMimeData* mimeData = new QMimeData;
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
for (const QModelIndex &i : indexes) {
if (i.isValid() && i.column() == 0) {
Check &c = checks->at(i.row());
QString date = QString::fromStdString(c.get_date()),
fn = QString::fromStdString(c.get_fn()),
fd = QString::fromStdString(c.get_fd()),
fi = QString::fromStdString(c.get_fi());
double total = c.get_total();
OperationType type = c.get_operationType();
std::vector<Goods> goods = c.get_goods();
stream << date << total << type << fn << fd << fi << goods;
}
}
mimeData->setData(CheckQueueTableModel::MimeType, encodedData);
return mimeData;
}
bool CheckQueueTableModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
if (!canDropMimeData(data, action, row, column, parent)) return false;
if (action == Qt::IgnoreAction) return true;
else if (action != Qt::MoveAction) return false;
if (row > checks->size()) return false;
QByteArray encodedData = data->data(CheckQueueTableModel::MimeType);
QDataStream stream(&encodedData, QIODevice::ReadOnly);
std::vector<Check> newItems;
int rows = 0;
while (!stream.atEnd()) {
QString date, fn, fd, fi;
double total;
OperationType type;
std::vector<Goods> goods;
stream >> date >> total >> type >> fn >> fd >> fi >> goods;
Check c = Check(
date.toStdString(),
total,
type,
fn.toStdString(),
fd.toStdString(),
fi.toStdString(),
goods
);
newItems.push_back(c);
++rows;
}
insertRows(row, rows, QModelIndex());
for (Check item : newItems) {
// (*checks)[row] = std::move(item);
Check &c = checks->at(row);
c.set_date(item.get_date());
c.set_total(item.get_total());
c.set_fn(item.get_fn());
c.set_fd(item.get_fd());
c.set_fi(item.get_fi());
c.set_operation_type(item.get_operationType());
for (Goods &g : item.get_goods()) {
c.add_goods(g);
}
emit dataChanged(index(row, 0), index(row, 1));
row++;
}
return true;
}
void CheckQueueTableModel::sort(int column, Qt::SortOrder order) {
beginResetModel();
switch (column) {
case 0:
std::sort(checks->begin(), checks->end(),
[&](const Check& a, const Check& b) {
if (order == Qt::AscendingOrder) {
return compare(a, b, column);
} else {
return !compare(a, b, column);
}
});
break;
case 1:
std::sort(checks->begin(), checks->end(),
[&](const Check& a, const Check& b) {
if (order == Qt::AscendingOrder) {
return compare(a, b, column);
} else {
return !compare(a, b, column);
}
});
}
endResetModel();
}
bool CheckQueueTableModel::compare(const Check &check_a, const Check &check_b, int column) {
switch (column) {
case 0:
return
QDateTime::fromString(QString::fromStdString(((Check &)check_a).get_date()), "yyyyMMddThhmm")
>
QDateTime::fromString(QString::fromStdString(((Check &)check_b).get_date()), "yyyyMMddThhmm");
break;
case 1:
return ((Check &)check_a).get_total() > ((Check &)check_b).get_total();
break;
default: return false;
}
}

View File

@ -1,41 +0,0 @@
#ifndef CHECKQUEUETABLEMODEL_H
#define CHECKQUEUETABLEMODEL_H
#include <QAbstractTableModel>
#include <check/check.h>
class CheckQueueTableModel : public QAbstractTableModel
{
Q_OBJECT
static constexpr const char* MimeType = "application/check.queue.model";
public:
explicit CheckQueueTableModel(std::vector<Check> *checks, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
Qt::DropActions supportedDropActions() const override;
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
QStringList mimeTypes() const override;
bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int, int, const QModelIndex &);
QMimeData* mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
void sort(int column, Qt::SortOrder order) override;
private:
std::vector<Check> *checks;
bool compare(const Check& check_a, const Check& check_b, int column);
signals:
void editCompleted(const QString &);
};
#endif // CHECKQUEUETABLEMODEL_H

View File

@ -1,47 +0,0 @@
#include "checkqueuetableview.h"
CheckQueueTableView::CheckQueueTableView(QWidget *parent)
: QTableView(parent), m_dropRow(0) {
setSelectionMode(QAbstractItemView::SingleSelection);
setSelectionBehavior(QAbstractItemView::SelectRows);
setDragEnabled(true);
setAcceptDrops(true);
setDragDropMode(QAbstractItemView::DragDrop);
setDefaultDropAction(Qt::MoveAction);
setDragDropOverwriteMode(false);
setDropIndicatorShown(true);
}
int CheckQueueTableView::selectedRow() const {
QItemSelectionModel *selection = selectionModel();
return selection->hasSelection() ? selection->selectedRows().front().row() : -1;
}
void CheckQueueTableView::reset() {
QTableView::reset();
QObject::connect(model(), &QAbstractTableModel::rowsInserted, this, [this](const QModelIndex &parent, int first, int last) {
Q_UNUSED(parent)
Q_UNUSED(last)
m_dropRow = first;
});
}
void CheckQueueTableView::dropEvent(QDropEvent *e) {
if (e->source() != this || e->dropAction() != Qt::MoveAction)
return;
int dragRow = selectedRow();
QTableView::dropEvent(e); // m_dropRow is set by inserted row
if (m_dropRow > dragRow)
--m_dropRow;
QMetaObject::invokeMethod(this,
std::bind(&CheckQueueTableView::selectRow, this, m_dropRow),
Qt::QueuedConnection); // Postpones selection
}

View File

@ -1,19 +0,0 @@
#ifndef CHECKQUEUETABLEVIEW_H
#define CHECKQUEUETABLEVIEW_H
#include <QTableView>
#include <QDropEvent>
class CheckQueueTableView : public QTableView
{
Q_OBJECT
int m_dropRow;
public:
CheckQueueTableView(QWidget *parent = nullptr);
Q_INVOKABLE int selectedRow() const;
void reset();
void dropEvent(QDropEvent *e);
};
#endif // CHECKQUEUETABLEVIEW_H

View File

@ -1,17 +0,0 @@
#include "outputcolumn.h"
#include <QHBoxLayout>
OutputColumn::OutputColumn() { }
OutputColumn::OutputColumn(QString text, ColumnType type)
: text(text), type(type) { }
void OutputColumn::set_column_type(ColumnType type) { this->type = type; }
ColumnType OutputColumn::get_column_type() { return type; }
void OutputColumn::set_text(const QString &text) { this->text = text; }
QString OutputColumn::get_text() { return text; }
Q_DECLARE_METATYPE(OutputColumn)

View File

@ -1,23 +0,0 @@
#ifndef OUTPUTCOLUMN_H
#define OUTPUTCOLUMN_H
#include "output/output_options.h"
#include <QLineEdit>
#include <QStandardItem>
class OutputColumn
{
QString text;
ColumnType type;
public:
OutputColumn();
OutputColumn(QString text, ColumnType type);
void set_column_type(ColumnType type);
ColumnType get_column_type();
void set_text(const QString &text);
QString get_text();
};
#endif // OUTPUTCOLUMN_H

View File

@ -1,149 +0,0 @@
#include "outputcolumnmodel.h"
#include <QObject>
#include <QMimeData>
#include <QIODevice>
#include <outputcolumn.h>
OutputColumnModel::OutputColumnModel(std::vector<OutputColumn> *columns, QObject *parent)
: columns(columns), QAbstractListModel{parent} { }
int OutputColumnModel::rowCount(const QModelIndex &parent) const {
return columns->size();
}
QVariant OutputColumnModel::data(const QModelIndex &index, int role) const {
if (!index.isValid() || index.row() >= columns->size())
return QVariant();
if (role == Qt::DisplayRole)
return QVariant::fromValue(columns->at(index.row()).get_text());
else if (role == 0x0101)
return QVariant::fromValue(columns->at(index.row()).get_column_type());
return QVariant();
}
QVariant OutputColumnModel::headerData(int section, Qt::Orientation orientation, int role) const {
if (role != Qt::DisplayRole)
return QVariant();
if (orientation == Qt::Horizontal)
return QObject::tr("Column type alias");
else
return QStringLiteral("Position %1").arg(section);
}
bool OutputColumnModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (index.isValid()) {
OutputColumn &column = columns->at(index.row());
if (role == 0x102) {
OutputColumn data = value.value<OutputColumn>();
column.set_text(data.get_text());
column.set_column_type(data.get_column_type());
} else {
QString data = value.value<QString>();
column.set_text(data);
}
(*columns)[index.row()] = column;
emit dataChanged(index, index, {role});
return true;
}
return false;
}
Qt::DropActions OutputColumnModel::supportedDropActions() const {
return Qt::MoveAction;
}
bool OutputColumnModel::insertRows(int position, int rows, const QModelIndex &index) {
beginInsertRows(QModelIndex(), position, position+rows-1);
for (int row = 0; row < rows; ++row)
columns->emplace(columns->begin() + position, OutputColumn("Чё зыришь глаза пузыришь?", ColumnType::date));
endInsertRows();
return true;
}
bool OutputColumnModel::removeRows(int position, int rows, const QModelIndex &parent) {
beginRemoveRows(QModelIndex(), position, position+rows-1);
for (int row = 0; row < rows; ++row)
columns->erase(std::next(columns->begin(), position));
endRemoveRows();
return true;
}
QStringList OutputColumnModel::mimeTypes() const {
QStringList types;
types << OutputColumnModel::MimeType;
return types;
}
bool OutputColumnModel::canDropMimeData(const QMimeData *data, Qt::DropAction action, int, int, const QModelIndex &) {
if ( action != Qt::MoveAction || !data->hasFormat(OutputColumnModel::MimeType))
return false;
return true;
}
Qt::ItemFlags OutputColumnModel::flags (const QModelIndex & index) const {
Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);
if (index.isValid())
return Qt::ItemIsDragEnabled | Qt::ItemIsEditable | defaultFlags;
else
return Qt::ItemIsDropEnabled | defaultFlags;
}
QMimeData* OutputColumnModel::mimeData(const QModelIndexList &indexes) const {
QMimeData* mimeData = new QMimeData;
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
for (const QModelIndex &index : indexes) {
if (index.isValid()) {
QString text = data(index, Qt::DisplayRole).toString();
ColumnType type = ColumnType(data(index, 0x0101).toInt());
stream << text << type;
}
}
mimeData->setData(OutputColumnModel::MimeType, encodedData);
return mimeData;
}
bool OutputColumnModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) {
if (!canDropMimeData(data, action, row, column, parent))
return false;
if (action == Qt::IgnoreAction)
return true;
else if (action != Qt::MoveAction)
return false;
QByteArray encodedData = data->data(OutputColumnModel::MimeType);
QDataStream stream(&encodedData, QIODevice::ReadOnly);
std::vector<OutputColumn> newItems;
int rows = 0;
while (!stream.atEnd()) {
QString text;
ColumnType type;
stream >> text >> type;
newItems.push_back(OutputColumn(text, type));
++rows;
}
insertRows(row, rows, QModelIndex());
for (const OutputColumn &column : newItems) {
QModelIndex idx = index(row, 0, QModelIndex());
setData(idx, QVariant::fromValue(column), 0x102);
row++;
}
return true;
}

View File

@ -1,37 +0,0 @@
#ifndef OUTPUTCOLUMNMODEL_H
#define OUTPUTCOLUMNMODEL_H
#include <QLineEdit>
#include <QAbstractListModel>
#include <outputcolumn.h>
class OutputColumnModel : public QAbstractListModel
{
Q_OBJECT
static constexpr const char* MimeType = "application/output.column.model";
std::vector<OutputColumn> *columns;
public:
OutputColumnModel(std::vector<OutputColumn> *columns, QObject *parent = nullptr);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags (const QModelIndex& index) const override;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole) override;
Qt::DropActions supportedDropActions() const override;
bool insertRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
bool removeRows(int position, int rows, const QModelIndex &index = QModelIndex()) override;
QStringList mimeTypes() const override;
bool canDropMimeData(const QMimeData *, Qt::DropAction, int, int, const QModelIndex&);
QMimeData* mimeData(const QModelIndexList &indexes) const override;
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override;
};
#endif // OUTPUTCOLUMNMODEL_H