Compare commits

...

191 commits

Author SHA1 Message Date
8a5038e462 README 2024-10-01 14:39:54 +02:00
068a50e289 Push to new version 2024-10-01 14:08:34 +02:00
da89a0d2c7 Fix translations and style for Windows 2024-10-01 14:00:36 +02:00
429d66dd4d user manual updated 2024-09-30 15:03:17 +02:00
37c8446ea4 user manual updated 2024-09-30 14:39:50 +02:00
a896e0d40e Push to new version 2024-09-30 14:17:17 +02:00
3668e2484f try to make singleapplication flatpak-compatible (2) 2024-09-30 10:47:03 +02:00
52dc7f1974 try to make singleapplication flatpak-compatible 2024-09-30 10:26:14 +02:00
ab7b14f12e get rid of fmt 2024-05-22 13:09:44 +02:00
0c7f071b9d include dir for boost added. 2024-03-05 14:38:02 +01:00
d0c50c9b9a qsingleapplication version bumped 2024-01-23 11:25:07 +01:00
a17cb22e05 2023 → 2024 2024-01-23 11:22:51 +01:00
205dac5326 nlohmann updateted to 3.11.3 2024-01-23 11:20:39 +01:00
b0d2d6b284 update to version 1.8 2024-01-23 10:46:49 +01:00
7f11ba4e5d new version 1.8.0 2024-01-23 10:42:04 +01:00
32d0ec7749 Allow import of multiple sales at once. 2024-01-23 10:39:43 +01:00
54e5c70447 Changed minimum versions 2024-01-23 10:06:01 +01:00
379fd4a73c Lib references for Windows updated 2023-09-21 15:00:18 +02:00
8a94b53379 Push boost version. 2023-05-02 11:07:15 +02:00
d9b13d0e1a README updated. 2023-05-02 11:03:22 +02:00
6f885dd64e more on metainfo 2023-04-28 10:29:39 +02:00
1a698a6cd2 metainfo added 2023-04-28 10:12:10 +02:00
ac9ae8b34e Add metainfo 2023-04-28 10:08:02 +02:00
4385624988 Merge branch 'master' of ssh://git.rustysoft.de/martin/kima2 2023-04-26 12:47:45 +02:00
4cfd8d5572 2022 → 2023 2023-04-26 12:47:41 +02:00
528ae1ff31 use QUrl::fromLocalFile 2023-04-26 12:46:35 +02:00
649c26db4b Updated for Windows installation 2023-04-26 08:53:13 +02:00
13a1d26d96 description updated 2023-04-26 08:36:03 +02:00
f898844c7c remove obsolete lstdc++fs flag 2023-04-25 16:19:27 +02:00
e6b71a7e4d Get rid of csv-parser 2023-04-25 16:11:34 +02:00
f4b4ccbbea Revert "Remove fmt dependency"
This reverts commit 8efdf7b6fe.
2023-04-25 15:22:06 +02:00
e79a81797c Increase version number 2023-04-21 21:35:46 +02:00
8efdf7b6fe Remove fmt dependency 2023-04-21 21:33:59 +02:00
a46e8d89c9 remove xlnt stuff 2023-01-18 19:41:53 +01:00
3e293ba447 updated 2023-01-18 17:08:09 +01:00
245e41bbf0 Push to new version. 2023-01-18 17:06:02 +01:00
b0444112a8 seller menu simplified. 2023-01-18 17:05:21 +01:00
c4ccd43b45 Push to new version 2023-01-18 16:46:44 +01:00
16745a248c Update manual 2023-01-18 16:46:30 +01:00
19ea7f27de Excel stuff removed. 2023-01-18 16:35:28 +01:00
42bf036f85 Merge branch 'master' of ssh://git.rustysoft.de/martin/kima2 2022-09-27 09:01:55 +02:00
9d7492c745 Version 1.6.1 2022-09-27 09:01:27 +02:00
9fcfb8e3ba Loading translation improved. 2022-09-26 20:47:23 +02:00
a23de4dcf0 README updated 2022-09-26 14:04:57 +02:00
77f45fa55f improved Arch Linux build file 2022-09-26 13:48:48 +02:00
7299ae7c66 Fix db filename 2022-09-26 12:40:21 +02:00
1720dbba8b cpack: dependency fix for Windows 2022-09-26 12:29:53 +02:00
66e2e69c3e LICENSE: 2021 -> 2022 2022-09-26 12:24:45 +02:00
78aafab63b cpack: dependency fix for Windows 2022-09-26 12:23:50 +02:00
72e1a69dae prepare for 1.6.0 2022-09-26 12:10:57 +02:00
7e38da7276 Update DLLs for Qt6 2022-09-26 11:22:11 +02:00
144f61d6d4 Do not use named locales on Windows. 2022-09-26 11:21:03 +02:00
85574ff08a replace boost date_time with chrono 2022-09-26 10:14:57 +02:00
c44d8b352a manual updated 2022-09-26 09:09:15 +02:00
be9a7be38d nlohmann_json and singleapplication updated 2022-09-26 09:05:38 +02:00
104464f781 2022-09-24 20:43:32 +02:00
3a132e69ae Add one additional seller.
fixes #24
2022-09-24 20:41:15 +02:00
01577d02a0 Do not ignore seller entries with empty names.
fixes #23
2022-09-24 20:25:33 +02:00
ecd1111391 Externen Link aktualisiert. 2022-09-24 14:56:22 +02:00
944edc277a ReportDialog: Reduce lines per page
fixes #22
2022-09-24 14:41:14 +02:00
0427bd4077 move to qt6 2022-07-07 17:16:51 +02:00
2b6628bdf8 Using fmt for currency 2022-07-07 16:53:44 +02:00
6944051c31 code formatting 2022-07-07 15:37:33 +02:00
5f7d91a18e Copyright year updated 2022-07-07 15:32:13 +02:00
2b7c099f5e Adopt to C++20 2022-07-07 15:31:32 +02:00
acc3095e60 code formatting 2022-07-07 15:21:46 +02:00
d677dfd628 clang-format updated 2022-07-07 15:14:30 +02:00
e0ffd47430 nlohmann_json: update to v3.10.5 2022-07-07 15:07:42 +02:00
3e6e587df8 member variables renamed 2022-07-07 15:03:39 +02:00
21571215bc 2022-07-07 15:03:13 +02:00
52e0f1636e Hide .cache (clangd) 2022-07-07 15:03:04 +02:00
38b8865cea push to new singleapplication version 2021-09-27 14:53:09 +02:00
20c7a99578 2021-09-22 07:54:53 +02:00
3572d22715 2021-09-22 07:53:19 +02:00
453fd8cdb5 cpack: new Windows dependencies added 2021-09-21 10:49:54 +02:00
911b05b487 cpack: new Windows dependencies added 2021-09-21 10:36:43 +02:00
9e511c684d subproject updated 2021-09-20 08:46:51 +02:00
b9bb9f1a8c new version 1.5.3 2021-09-20 08:05:37 +02:00
329c3a1540 Manual updated 2021-09-20 08:04:39 +02:00
8feb122c11 subprojects updated 2021-09-20 07:44:22 +02:00
d573aed916 Merge branch 'master' of ssh://brodbeck-online.de:60022/martin/kima2-cpp 2021-09-20 07:40:34 +02:00
83b6cd1d26 2021-09-20 07:40:05 +02:00
059ae5ca66 2020 → 2021 2021-09-20 07:39:37 +02:00
edf9cb1a2c Revert "2020 → 2021"
This reverts commit b8b5d8b140.
2021-09-19 19:59:51 +02:00
b8b5d8b140 2021 → 2021 2021-09-19 19:58:49 +02:00
072af76de1 ... 2021-09-19 19:53:20 +02:00
92ecc92bf7 Fix umlaut-crash on linux machines 2021-09-19 19:52:32 +02:00
e48fbe82e0 csv-parser updated 2021-09-17 21:16:08 +02:00
8ef821dcc0 singleapplication updated 2021-09-17 21:13:21 +02:00
c65e7be415 new version 1.5.2 2020-10-19 07:50:30 +02:00
b9f09fcd4c ... 2020-10-15 16:50:37 +02:00
a156c5331a Added warning message when Excel import fails. 2020-10-15 16:43:00 +02:00
9e6e2eb936 Merge branch 'master' of ssh://brodbeck-online.de:60022/martin/kima2-cpp into master 2020-10-15 15:49:02 +02:00
8b299bacc3 Set default style on Windows to "Fusion". 2020-10-15 15:47:05 +02:00
eb5434f374 Merge branch 'master' of ssh://brodbeck-online.de:60022/martin/kima2-cpp into master 2020-09-04 09:52:57 +02:00
a547b6f3c3 push csv-parser subproject 2020-08-28 19:10:01 +02:00
26804ba03e push singleapplication subproject 2020-08-28 19:07:16 +02:00
305d41f2e1 Added missing dlls to Windows deployment 2020-04-17 10:22:52 +02:00
8374175087 2019 -> 2020 2020-02-15 09:55:01 +01:00
01bf3c29a2 [Windows] undefine DELETE macro if defined 2020-02-12 16:13:16 +01:00
ff9dc9b05e Fix cpack for windows (dlls) 2020-02-12 16:12:40 +01:00
e41ce5a117 Push to version 1.5.1 2020-02-12 14:06:59 +01:00
2d06de9907 Fix Excel import 2020-02-12 14:04:15 +01:00
77791e142c more on build scripts 2019-10-17 16:03:12 +02:00
a8181729f9 more on meson 2019-10-14 19:32:14 +02:00
5aa4e3a5f0 typo 2019-10-11 12:49:32 +02:00
77226f294b rpm builds now 2019-10-11 12:48:54 +02:00
03f1c7cca5 Initial commit of rpm spec file 2019-10-11 11:09:54 +02:00
b9d4375f5a improved build instructions 2019-10-11 10:01:21 +02:00
f37cd75502 using system json lib if available
(works on Arch Linux)
2019-10-11 09:09:18 +02:00
b016452a06 adjust cmake files to changed filesystem 2019-10-10 15:00:09 +02:00
7ca04f79ce more on meson 2019-10-10 14:52:00 +02:00
e987692c7a add files to be installed to meson 2019-10-10 14:42:11 +02:00
d13f9d2824 meson is now working ... somehow 2019-10-10 13:36:48 +02:00
2faa2fa019 3rdparty renamed to subprojects 2019-10-10 13:36:18 +02:00
fc308c644c Merge branch 'master' into meson 2019-10-10 08:13:30 +02:00
e89728846c restructure libs 2019-10-10 08:09:16 +02:00
ad895abd35 initial meson files added 2019-10-09 16:31:29 +02:00
ec0b7cbf0d code cleanup 2019-10-09 10:36:51 +02:00
9fd29d588f code cleanup 2019-10-07 14:08:01 +02:00
710b4cf9fb disable ctor 2019-10-07 13:58:10 +02:00
98fdffe5fb having fun with virtual dtors 2019-10-07 10:51:13 +02:00
847e8aa8ba message improved 2019-10-07 10:33:46 +02:00
9e2d377195 json-import: show number of sellers 2019-10-07 10:29:14 +02:00
acc8408b0a Fix file system problem (umlauts) on Linux 2019-10-07 08:32:51 +02:00
99104a932f code cleanup 2019-10-07 08:32:37 +02:00
f0ec980e8d code cleanup 2019-10-06 10:47:15 +02:00
4f9151d85b reduce compiler warnings 2019-10-06 10:22:11 +02:00
df972f68fc manual updated 2019-10-04 16:02:10 +02:00
57bfe3af62 Show information dialog about outdated database file 2019-10-04 15:50:13 +02:00
a33b9896b3 Add entity files to git 2019-10-04 15:22:15 +02:00
63f9822f34 get rid of warning message 2019-10-04 15:21:55 +02:00
69982fc931 sellers: get rid of uuid 2019-10-04 15:15:43 +02:00
46d6468e5c files renamed 2019-10-04 14:05:19 +02:00
dd23da5f72 Push to new version 1.4.2 2019-10-01 11:55:28 +02:00
c171a6350d message improved 2019-10-01 11:47:53 +02:00
bc5c63fe0d Merge branch 'KSM-18' of martin/kima2-cpp into master 2019-10-01 11:42:05 +02:00
106846a127 Allows to open cvs files with umlauts
fixes #18
2019-10-01 11:38:41 +02:00
04c553e657 confirmation text improved 2019-10-01 08:33:36 +02:00
2a5cf16998 code beautifying 2019-09-30 10:39:00 +02:00
857f706443 added kdevelop project file 2019-09-30 10:31:52 +02:00
1a82a4147b Show correct last price when cancelling sold items 2019-09-30 10:28:03 +02:00
ddb06c73a8 push to new version 2019-09-28 15:06:22 +02:00
0b369e1c8e fixes issue #17 2019-09-28 15:02:55 +02:00
52cff89735 Fixes issue #16 2019-09-28 14:24:07 +02:00
fe9c69b88a manual updated 2019-09-27 09:03:39 +02:00
3d52357480 push to new version 1.4.0 2019-09-27 09:00:32 +02:00
75fb3753e1 read until seller no. 500 2019-09-27 09:00:14 +02:00
8ff8cbd6d2 use semicolon as delimiter 2019-09-27 08:56:24 +02:00
44a736fd79 manuel updated 2019-09-27 08:48:29 +02:00
ffdece92b4 added importing from csv (continued and finished) 2019-09-27 08:37:18 +02:00
8265212c11 added importing from csv 2019-09-26 16:41:39 +02:00
780e37f565 added importing from csv 2019-09-26 16:41:28 +02:00
79f1065868 push to new version 1.3.1 2019-09-26 08:58:50 +02:00
b9d502dcde user manual updated 2019-09-26 08:56:07 +02:00
6702459b0d push SingleApplication to 3.0.16 2019-09-26 08:49:49 +02:00
083767bfd4 Push json lib to v3.7.0 2019-09-26 08:45:53 +02:00
6b25c7c9cf re-adding END_ROW to prevent crash (?!) 2019-09-26 08:43:36 +02:00
fb98c3786e Merge branch 'master' of ssh://brodbeck-online.de:60022/martin/kima2-cpp 2019-09-16 10:52:31 +02:00
dc2b31a5f9 Use correct Qt-dlls 2019-09-16 10:51:36 +02:00
1ea26f1e6c push to v1.3.0 2019-09-16 10:35:26 +02:00
43b5ec1fc0 Merge branch 'master' of ssh://brodbeck-online.de:60022/martin/kima2-cpp 2019-07-24 14:15:50 +02:00
07b734521b read excel file until end 2019-07-24 14:12:31 +02:00
9b7f5568d7 import from excel until EOF 2019-07-12 15:53:19 +02:00
e8ce908a7f Push to new version 1.2.0 2019-06-24 14:42:49 +02:00
4621274c4e Updated singleapplicatioUpdated singleapplicationn 2019-06-24 14:38:16 +02:00
080ac45f5b Update to latest json library version (3.6.1) 2019-06-24 14:36:28 +02:00
2c243a6563 Manual updated 2019-06-24 14:33:56 +02:00
d2680c52af cmake module path simplified 2019-04-12 12:49:09 +02:00
7a2e8c6fc3 install prefix fixed 2019-04-12 12:48:33 +02:00
8e3ad5dc53 use correct translation file 2019-04-10 13:29:51 +02:00
193cb0d8ce fix submodule 2019-04-10 12:50:00 +02:00
ecc45811d8 try singleapplication 2019-04-10 12:04:18 +02:00
5a3b6fff82 Re-enable resize column on sales view 2019-04-08 10:49:49 +02:00
b9ec6f983e code beautifying 2019-04-08 10:48:24 +02:00
d3e56e629d Revert resize column because application crashes 2019-04-05 15:10:23 +02:00
1797a860d1 ignore .vscode/ipch/* 2019-04-05 15:09:24 +02:00
96c89da080 using new version 3.6.0 2019-04-04 14:24:08 +02:00
abe570e28c push to new version 2019-04-04 14:22:08 +02:00
b2dc79207d manual updated 2019-04-04 14:21:51 +02:00
e97cbf1287 year updated 2019-02-25 13:32:34 +01:00
cfdb319c14 user manual update 2019-02-25 13:28:31 +01:00
e5c66463c3 [Fix #13] Font for price dialog increased 2019-02-25 09:56:55 +01:00
32da117468 [Fix #12] Excel-Import: Show a message with number of imported sellers 2019-02-25 09:26:21 +01:00
2fb42d5365 typo 2019-02-25 09:04:52 +01:00
45fe7ea8bd [Enh #11] Import from Excel: trim names 2019-02-25 08:57:10 +01:00
65f5da45ec SalesView: Resize columns to content 2019-02-25 08:27:51 +01:00
f32f0afd6a using nlohmann_json v3.5.0 2019-02-21 12:36:24 +01:00
838525942e bump to new version 1.0.0 2018-11-07 16:08:53 +01:00
7a8c7a9d0b make sure that only one instance of the application runs 2018-11-07 16:02:07 +01:00
6c51f8cec5 change to nlohman json version 3.4.0 2018-10-31 15:39:59 +01:00
78 changed files with 1471 additions and 998 deletions

View file

@ -1,27 +1,38 @@
---
Language: Cpp Language: Cpp
# BasedOnStyle: LLVM # BasedOnStyle: LLVM
AccessModifierOffset: -2 AccessModifierOffset: -4
AlignAfterOpenBracket: Align AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false AlignArrayOfStructures: None
AlignConsecutiveDeclarations: false AlignConsecutiveMacros: None
AlignConsecutiveAssignments: None
AlignConsecutiveBitFields: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Right AlignEscapedNewlines: Right
AlignOperands: true AlignOperands: Align
AlignTrailingComments: true AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true BinPackArguments: true
BinPackParameters: true BinPackParameters: true
BraceWrapping: BraceWrapping:
AfterClass: true AfterCaseLabel: false
AfterControlStatement: false AfterClass: false
AfterControlStatement: Never
AfterEnum: false AfterEnum: false
AfterFunction: false AfterFunction: false
AfterNamespace: false AfterNamespace: false
@ -31,13 +42,21 @@ BraceWrapping:
AfterExternBlock: false AfterExternBlock: false
BeforeCatch: false BeforeCatch: false
BeforeElse: false BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false IndentBraces: false
SplitEmptyFunction: true SplitEmptyFunction: true
SplitEmptyRecord: true SplitEmptyRecord: true
SplitEmptyNamespace: true SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None BreakBeforeBinaryOperators: None
BreakBeforeBraces: Linux BreakBeforeConceptDeclarations: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterFunction: true
AfterNamespace: true
BreakBeforeInheritanceComma: false BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon BreakConstructorInitializers: BeforeColon
@ -50,35 +69,56 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4
Cpp11BracedListStyle: true Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false DerivePointerAlignment: false
DisableFormat: false DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true FixNamespaceComments: true
ForEachMacros: ForEachMacros:
- foreach - foreach
- Q_FOREACH - Q_FOREACH
- BOOST_FOREACH - BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve IncludeBlocks: Preserve
IncludeCategories: IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2 Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)' - Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3 Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*' - Regex: '.*'
Priority: 1 Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$' IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequires: false
IndentWidth: 4 IndentWidth: 4
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave JavaScriptQuotes: Leave
JavaScriptWrapImports: true JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
MacroBlockBegin: '' MacroBlockBegin: ''
MacroBlockEnd: '' MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 1
NamespaceIndentation: None NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2 ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2 PenaltyBreakAssignment: 2
@ -86,24 +126,57 @@ PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300 PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120 PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000 PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000 PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60 PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left PenaltyIndentedWhitespace: 0
PointerAlignment: Right
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: true ReflowComments: true
SortIncludes: true ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: true SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements SpaceBeforeParens: ControlStatements
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1 SpacesBeforeTrailingComments: 1
SpacesInAngles: false SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false SpacesInParentheses: false
SpacesInSquareBrackets: false SpacesInSquareBrackets: false
Standard: Auto SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8 TabWidth: 8
UseCRLF: false
UseTab: Never UseTab: Never
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME
...

3
.gitignore vendored
View file

@ -1 +1,4 @@
build build
.vscode/ipch/*
.kdev4
.cache

7
.gitmodules vendored
View file

@ -1,3 +1,6 @@
[submodule "3rdparty/nlohmann_json"] [submodule "subprojects/nlohmann_json"]
path = 3rdparty/nlohmann_json path = subprojects/nlohmann_json
url = https://github.com/nlohmann/json.git url = https://github.com/nlohmann/json.git
[submodule "subprojects/singleapplication"]
path = subprojects/singleapplication/singleapplication.git
url = https://github.com/itay-grudev/SingleApplication.git

3
.vscode/launch.json vendored
View file

@ -21,7 +21,8 @@
"text": "-enable-pretty-printing", "text": "-enable-pretty-printing",
"ignoreFailures": true "ignoreFailures": true
} }
] ],
"visualizerFile": "/home/brodbemn/.config/Code - OSS/User/workspaceStorage/d64ec049841ecb3d43e402bb3c167cb5/tonka3000.qtvsctools/qt.natvis.xml"
} }
] ]
} }

42
.vscode/settings.json vendored
View file

@ -65,9 +65,47 @@
"condition_variable": "cpp", "condition_variable": "cpp",
"mutex": "cpp", "mutex": "cpp",
"hash_map": "cpp", "hash_map": "cpp",
"future": "cpp" "future": "cpp",
"bit": "cpp",
"compare": "cpp",
"concepts": "cpp",
"forward_list": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"unordered_set": "cpp",
"iterator": "cpp",
"memory_resource": "cpp",
"random": "cpp",
"semaphore": "cpp",
"stop_token": "cpp",
"__bit_reference": "cpp",
"__bits": "cpp",
"__config": "cpp",
"__debug": "cpp",
"__errc": "cpp",
"__hash_table": "cpp",
"__locale": "cpp",
"__mutex_base": "cpp",
"__node_handle": "cpp",
"__split_buffer": "cpp",
"__threading_support": "cpp",
"__tree": "cpp",
"__tuple": "cpp",
"__verbose_abort": "cpp",
"format": "cpp",
"ios": "cpp",
"locale": "cpp"
}, },
"C_Cpp.clang_format_path": "/usr/bin/clang-format", "C_Cpp.clang_format_path": "/usr/bin/clang-format",
"cmake.configureOnOpen": true, "cmake.configureOnOpen": true,
"C_Cpp.configurationWarnings": "Disabled" "C_Cpp.configurationWarnings": "Disabled",
"files.watcherExclude": {
"**/.git/objects/**": true,
"**/.git/subtree-cache/**": true,
"**/node_modules/*/**": true,
"**/.hg/store/**": true,
".flatpak/**": true,
"_build/**": true
}
} }

@ -1 +0,0 @@
Subproject commit f1768a540a7b7c5cc30cdcd6be9e9ef91083719b

View file

@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 3.8) cmake_minimum_required(VERSION 3.10)
project(kima2 VERSION 0.13.1) project(kima2 VERSION 1.8.3)
set(CMAKE_MODULE_PATH "${CMAKE_HOME_DIRECTORY}/cmake" ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH "${CMAKE_HOME_DIRECTORY}/cmake")
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
#include(InstallRequiredSystemLibraries) #include(InstallRequiredSystemLibraries)
@ -12,10 +12,12 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(MSVC) if(MSVC)
add_compile_options(/W4 /WX) add_compile_options(/W4 /WX)
else() else()
add_compile_options(-Wall -Wextra -pedantic -Woverloaded-virtual -Wredundant-decls -Wshadow) #add_compile_options(-Wall -Wextra -pedantic -Woverloaded-virtual -Wredundant-decls -Wshadow)
add_compile_options(-Wall -Wextra)
endif() endif()
configure_file(config.h.in ${PROJECT_BINARY_DIR}/config.h) configure_file(config.h.in ${PROJECT_BINARY_DIR}/config.h)
configure_file(de.rustysoft.kima2.metainfo.xml.in ${PROJECT_BINARY_DIR}/de.rustysoft.kima2.metainfo.xml)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
@ -27,14 +29,14 @@ if(KIMA2_USE_EXTERNAL_JSON)
find_package(nlohmann_json REQUIRED) find_package(nlohmann_json REQUIRED)
endif() endif()
add_subdirectory(3rdparty) add_subdirectory(subprojects)
add_subdirectory(src) add_subdirectory(src)
if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE MATCHES Debug) #if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE MATCHES Debug)
include(CTest) # include(CTest)
enable_testing() # enable_testing()
add_subdirectory(test) # add_subdirectory(test)
endif() #endif()
# CPack # CPack
@ -78,6 +80,8 @@ else(WIN32 AND NOT UNIX)
DESTINATION share/${PROJECT_NAME}) DESTINATION share/${PROJECT_NAME})
install(FILES "${CMAKE_SOURCE_DIR}/misc/kima2.svg" install(FILES "${CMAKE_SOURCE_DIR}/misc/kima2.svg"
DESTINATION share/icons/hicolor/scalable/apps) DESTINATION share/icons/hicolor/scalable/apps)
install(FILES de.rustysoft.kima2.metainfo.xml
DESTINATION share/metainfo)
endif (WIN32 AND NOT UNIX) endif (WIN32 AND NOT UNIX)
if( MINGW ) if( MINGW )
@ -86,18 +90,19 @@ if( MINGW )
set( CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS set( CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS
${MINGW_PATH}/libstdc++-6.dll ${MINGW_PATH}/libstdc++-6.dll
${MINGW_PATH}/libgcc_s_seh-1.dll ${MINGW_PATH}/libgcc_s_seh-1.dll
${MINGW_PATH}/Qt5Core.dll ${MINGW_PATH}/Qt6Core.dll
${MINGW_PATH}/Qt5Gui.dll ${MINGW_PATH}/Qt6Gui.dll
${MINGW_PATH}/Qt5Widgets.dll ${MINGW_PATH}/Qt6Widgets.dll
${MINGW_PATH}/Qt5PrintSupport.dll ${MINGW_PATH}/Qt6PrintSupport.dll
${MINGW_PATH}/Qt6Network.dll
${MINGW_PATH}/libwinpthread-1.dll ${MINGW_PATH}/libwinpthread-1.dll
${MINGW_PATH}/libsqlite3-0.dll ${MINGW_PATH}/libsqlite3-0.dll
${MINGW_PATH}/libusb-1.0.dll ${MINGW_PATH}/libusb-1.0.dll
${MINGW_PATH}/libxlnt.dll ${MINGW_PATH}/libicuuc75.dll
${MINGW_PATH}/libicuuc62.dll ${MINGW_PATH}/libicuin75.dll
${MINGW_PATH}/libicuin62.dll ${MINGW_PATH}/libicudt75.dll
${MINGW_PATH}/libicudt62.dll
${MINGW_PATH}/libpcre2-16-0.dll ${MINGW_PATH}/libpcre2-16-0.dll
${MINGW_PATH}/libpcre2-8-0.dll
${MINGW_PATH}/zlib1.dll ${MINGW_PATH}/zlib1.dll
${MINGW_PATH}/libharfbuzz-0.dll ${MINGW_PATH}/libharfbuzz-0.dll
${MINGW_PATH}/libpng16-16.dll ${MINGW_PATH}/libpng16-16.dll
@ -107,20 +112,26 @@ if( MINGW )
${MINGW_PATH}/libbz2-1.dll ${MINGW_PATH}/libbz2-1.dll
${MINGW_PATH}/libintl-8.dll ${MINGW_PATH}/libintl-8.dll
${MINGW_PATH}/libpcre-1.dll ${MINGW_PATH}/libpcre-1.dll
${MINGW_PATH}/libdouble-conversion.dll
${MINGW_PATH}/libzstd.dll
${MINGW_PATH}/libmd4c.dll
${MINGW_PATH}/libbrotlicommon.dll
${MINGW_PATH}/libbrotlidec.dll
#${MINGW_PATH}/libfmt.dll
${MINGW_PATH}/libb2-1.dll
${MINGW_PATH}/libiconv-2.dll) ${MINGW_PATH}/libiconv-2.dll)
install(FILES ${MINGW_PATH}/../share/qt5/plugins/platforms/qwindows.dll install(FILES ${MINGW_PATH}/../share/qt6/plugins/platforms/qwindows.dll
${MINGW_PATH}/../share/qt5/plugins/platforms/qminimal.dll ${MINGW_PATH}/../share/qt6/plugins/platforms/qminimal.dll
DESTINATION bin/platforms) DESTINATION bin/platforms)
install(FILES ${MINGW_PATH}/../share/qt5/plugins/printsupport/windowsprintersupport.dll #install(FILES ${MINGW_PATH}/../share/qt6/plugins/printsupport/windowsprintersupport.dll
DESTINATION bin/printsupport) # DESTINATION bin/printsupport)
install(FILES ${MINGW_PATH}/../share/qt5/translations/qtbase_de.qm install(FILES ${MINGW_PATH}/../share/qt6/translations/qtbase_de.qm
${MINGW_PATH}/../share/qt5/translations/qt_de.qm ${MINGW_PATH}/../share/qt6/translations/qt_de.qm
${MINGW_PATH}/../share/qt5/translations/qt_help_de.qm ${MINGW_PATH}/../share/qt6/translations/qt_help_de.qm
${MINGW_PATH}/../share/qt5/translations/qtmultimedia_de.qm ${MINGW_PATH}/../share/qt6/translations/qtmultimedia_de.qm
${MINGW_PATH}/../share/qt5/translations/qtquick1_de.qm DESTINATION bin/share/qt6/translations)
${MINGW_PATH}/../share/qt5/translations/qtscript_de.qm install(FILES ${MINGW_PATH}/../share/qt6/plugins/styles/qmodernwindowsstyle.dll
${MINGW_PATH}/../share/qt5/translations/qtxmlpatterns_de.qm DESTINATION bin/styles)
DESTINATION bin/translations)
endif( MINGW ) endif( MINGW )
include(InstallRequiredSystemLibraries) include(InstallRequiredSystemLibraries)

View file

@ -1,4 +1,4 @@
Copyright © 2018 Martin Brodbeck Copyright © 2018-2024 Martin Brodbeck
Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der
zugehörigen Dokumentationen (die "Software") erhält, die Erlaubnis erteilt, zugehörigen Dokumentationen (die "Software") erhält, die Erlaubnis erteilt,

View file

@ -13,19 +13,16 @@ Verkaufsdaten nach dem Verkaufsende auszutauschen.
Ebenso können über einen ESC/POS-Drucker Quittungen ausgestellt werden. Ebenso können über einen ESC/POS-Drucker Quittungen ausgestellt werden.
## Installation ## Installation
Auf [rustysoft.de](https://www.rustysoft.de/?01_kima2) werden verschiedene Installationspakete (Arch Linux, Auf [rustysoft.de](https://www.rustysoft.de/software/kima2/) werden die Installationsmöglichkeiten (Flatpak, Windows-Installer) erläutert. Bitte die Hinweise dort beachten.
Ubuntu, Windows) angeboten. Bitte die Hinweise dort beachten.
### Selbst compilieren ## Selbst compilieren
KIMA2 benötigt folgende Libraries: KIMA2 benötigt folgende Libraries:
* Qt5 * Qt 6
* boost >= 1.62 * boost >= 1.80
* libusb-1.0 * libusb-1.0
* xlnt >= 1.3.0
* nlohmann-json (als 3rdparty submodule vorhanden) * nlohmann-json (als 3rdparty submodule vorhanden)
Da Features aus C++17 verwendet werden sowie std::filesystem, sollte als Compiler mindestens Da Features aus C++20 sowie von neueren Compilern verwendet werden, sollte als Compiler mindestens GCC 14 verwendet werden.
GCC 8 verwendet werden.
Die Installationsschritte unter Linux sind wie folgt: Die Installationsschritte unter Linux sind wie folgt:
``` ```

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>de.rustysoft.kima2</id>
<name>KIMA2</name>
<summary>A small cash point program for childrens stuff markets</summary>
<metadata_license>MIT</metadata_license>
<project_license>GPL-3.0-or-later</project_license>
<releases>
<release version="@PROJECT_VERSION@" type="stable" date="2024-01-23" />
</releases>
<description>
<p>
A small cash point program for children's stuff markets. German language only.
</p>
</description>
<launchable type="desktop-id">de.rustysoft.kima2.desktop</launchable>
<screenshots>
<screenshot type="default">
<image>https://rustysoft.de/images/software/kima2/screenshot.png</image>
</screenshot>
</screenshots>
</component>

4
kima2.kdev4 Normal file
View file

@ -0,0 +1,4 @@
[Project]
CreatedFrom=CMakeLists.txt
Manager=KDevCMakeManager
Name=kima2

Binary file not shown.

Binary file not shown.

33
meson.build Normal file
View file

@ -0,0 +1,33 @@
project('kima2', 'cpp', default_options : ['cpp_std=c++20'], version : '1.8.0')
conf_data = configuration_data()
conf_data.set('PROJECT_VERSION', '"' + meson.project_version() + '"')
configure_file(output : 'config.h',
configuration : conf_data)
configuration_inc = include_directories('.')
#csv = cmake.subproject('csv-parser')
#csv_lib = csv.dependency('csv')
nlohmann_lib = dependency('nlohmann_json', version : '>=3.5.0', required : false)
if not nlohmann_lib.found()
nlohmann_inc = include_directories('subprojects/nlohmann_json/single_include')
nlohmann_lib = declare_dependency(include_directories : nlohmann_inc)
endif
csv_inc = include_directories('subprojects/csv-parser/single_include')
csv_dep = declare_dependency(include_directories : csv_inc)
singleapp_proj = subproject('singleapplication')
singleapp_lib = singleapp_proj.get_variable('singleapp_lib')
singleapp_dep = singleapp_proj.get_variable('singleapp_dep')
subdir('src')
if build_machine.system() == 'linux'
install_data('misc/kima2.svg', install_dir : get_option('datadir') / 'icons/hicolor/scalable/apps')
install_data('misc/kima2.desktop', install_dir : get_option('datadir') / 'applications')
install_data('manual/Benutzerhandbuch.pdf', install_dir : get_option('datadir') / 'kima2')
endif

View file

@ -1,29 +1,35 @@
# Maintainer: Martin Brodbeck <martin at brodbeck-online dot de> # Maintainer: Martin Brodbeck <martin at brodbeck-online dot de>
pkgname=kima2 pkgname=kima2
pkgver=0.13.0 pkgver=1.7.1
pkgrel=1 pkgrel=1
pkgdesc="A small cash point program for children's things markets (German only)" pkgdesc="A small cash point program for children's things markets (German only)"
arch=('i686' 'x86_64') arch=('i686' 'x86_64')
url="http://www.rustysoft.de/?01_kima2" url="http://www.rustysoft.de/software/kima2"
license=('custom') license=('custom')
depends=('glibc' 'libusb' 'qt5-base' 'sqlite3' 'xlnt') depends=('glibc' 'libusb' 'qt6-base' 'sqlite3')
makedepends=('boost>=1.62') makedepends=('boost>=1.62')
source=($pkgname-$pkgver.tar.gz) source=(git+https://git.rustysoft.de/martin/kima2)
md5sums=('') sha256sums=('SKIP')
build() { build() {
if [ ! -d $pkgname/build ]; then cd $pkgname
mkdir $pkgname/build
fi
cd $pkgname/build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$pkgdir/usr -DKIMA2_USE_EXTERNAL_JSON=ON .. git checkout v$pkgver
git submodule init
git submodule update
if [ -d build ]; then
rm -rf build
fi
mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr -DKIMA2_USE_EXTERNAL_JSON=OFF ..
make make
} }
package() { package() {
cd $pkgname/build cd $pkgname/build
make install make DESTDIR="$pkgdir" install
cd .. cd ..
install -D -m644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE" install -D -m644 LICENSE "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE"
} }

View file

@ -3,8 +3,8 @@ Type=Application
Name=KIMA2 Name=KIMA2
GenericName=Cash Point Program GenericName=Cash Point Program
GenericName[de]=Kassenprogramm GenericName[de]=Kassenprogramm
Comment=A small cash point program Comment=A small cash point program for children's stuff markets
Comment[de]=Ein kleines Kassenprogramm Comment[de]=Ein kleines Kassenprogramm für Kindersachenmärkte
Exec=kima2 Exec=kima2
Icon=kima2 Icon=kima2
Categories=Office; Categories=Office;

43
misc/kima2.spec Normal file
View file

@ -0,0 +1,43 @@
Name: kima2
Version: 1.6.0
Release: 1%{?dist}
Summary: A small cash point program for children's things markets
License: custom
Source0: %{name}-%{version}.tar.gz
BuildRequires: meson
BuildRequires: gcc-c++
#BuildRequires: pkgconfig(nlohmann_json)
BuildRequires: boost-date-time
BuildRequires: sqlite-devel
BuildRequires: libusb-devel
BuildRequires: qt5-qtdeclarative-devel
#BuildRequires: pkgconfig(pthreads)
%description
%prep
%autosetup
%build
%meson
%meson_build
%install
%meson_install
%check
%meson_test
%files
%{_bindir}/kima2
%{_datadir}/applications/kima2.desktop
%{_datadir}/icons/hicolor/scalable/apps/kima2.svg
%{_datadir}/kima2/Benutzerhandbuch.pdf
%changelog
* Fri Oct 11 2019 Martin Brodbeck <infor@rustysoft.de> - dev builds
-

View file

@ -1,34 +1,34 @@
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
find_package(Boost 1.62 COMPONENTS date_time REQUIRED) find_package(Boost 1.78 REQUIRED)
find_package(SQLite3 REQUIRED) find_package(SQLite3 REQUIRED)
if (MINGW) # Because csv-parser needs threads:
find_package(XLNT REQUIRED STATIC)
else (MINGW) find_package(fmt)
find_package(PkgConfig REQUIRED)
pkg_check_modules(XLNT REQUIRED xlnt>=1.3)
endif (MINGW)
set(CORE_SOURCES set(CORE_SOURCES
database.cpp database.cpp
entity.cpp entity.cpp
entityint.cpp
entityuuid.cpp
seller.cpp seller.cpp
article.cpp article.cpp
sale.cpp sale.cpp
marketplace.cpp marketplace.cpp
excelreader.cpp csvreader.cpp
jsonutil.cpp jsonutil.cpp
utils.cpp utils.cpp
) )
add_library(core STATIC ${CORE_SOURCES})
add_library(core STATIC ${CORE_SOURCES})
#target_include_directories(core PRIVATE ${PROJECT_SOURCE_DIR}/subprojects/csv-parser/single_include)
if (WIN32) if (WIN32)
target_link_libraries(core PRIVATE printer Boost::boost Boost::date_time sqlite3 nlohmann_json::nlohmann_json ${XLNT_LIBRARY}) target_link_libraries(core PRIVATE sqlite3 nlohmann_json::nlohmann_json)
target_link_libraries(core PRIVATE bcrypt) target_link_libraries(core PRIVATE bcrypt)
else() else()
target_link_libraries(core PRIVATE printer Boost::boost Boost::date_time sqlite3 nlohmann_json::nlohmann_json ${XLNT_LIBRARIES}) target_link_libraries(core PRIVATE sqlite3 nlohmann_json::nlohmann_json)
endif() endif()
target_include_directories(core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(core PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..)

View file

@ -1,44 +1,38 @@
#include "article.h" #include "article.h"
#include "seller.h"
#include "utils.h" #include "utils.h"
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
// Article::Article() : Entity() {} Article::Article(int price) : m_price(price) {}
Article::Article(int price) : price_(price) {} void Article::setArticleNo(int articleNo) { m_articleNo = articleNo; }
// Article::Article(std::shared_ptr<Seller> sellerPtr) : Entity() { sellerPtr_ = sellerPtr; } void Article::setPrice(int price) { m_price = price; }
void Article::setArticleNo(int articleNo) { articleNo_ = articleNo; } void Article::setDescription(const std::string &description) { m_description = description; }
void Article::setPrice(int price) { price_ = price; } void Article::setSale(Sale *salePtr) { m_salePtr = salePtr; }
void Article::setDescription(const std::string& description) { description_ = description; } void Article::setSeller(Seller *sellerPtr) { m_sellerPtr = sellerPtr; }
void Article::setSale(Sale* salePtr) { salePtr_ = salePtr; } bool Article::isSold() { return m_salePtr ? true : false; }
void Article::setSeller(Seller* sellerPtr) { sellerPtr_ = sellerPtr; } std::string Article::getDescription() { return m_description; }
bool Article::isSold() { return salePtr_ ? true : false; } Seller *Article::getSeller() { return m_sellerPtr; }
Sale *Article::getSale() { return m_salePtr; }
std::string Article::getDescription() { return description_; } int Article::getPrice() const { return m_price; }
Seller* Article::getSeller() { return sellerPtr_; } std::string Article::getPriceAsString() const { return formatCentAsEuroString(m_price); }
Sale* Article::getSale() { return salePtr_; }
int Article::getPrice() const { return price_; } int Article::getArticleNo() const { return m_articleNo; }
std::string Article::getPriceAsString() const
{
return formatCentAsEuroString(price_);
}
int Article::getArticleNo() const { return articleNo_; }
std::string Article::getCompleteArticleNo() const std::string Article::getCompleteArticleNo() const
{ {
std::stringstream artNoStream; std::stringstream artNoStream;
artNoStream << sourceNo_ << "K" << std::setfill('0') << std::setw(5) << articleNo_; artNoStream << m_sourceNo << "K" << std::setfill('0') << std::setw(5) << m_articleNo;
return artNoStream.str(); return artNoStream.str();
} }

View file

@ -1,9 +1,7 @@
#ifndef ARTICLE_H #ifndef ARTICLE_H
#define ARTICLE_H #define ARTICLE_H
#include "entity.h" #include "entityuuid.h"
//#include "sale.h"
//#include "seller.h"
#include <memory> #include <memory>
#include <string> #include <string>
@ -11,34 +9,35 @@
class Seller; class Seller;
class Sale; class Sale;
class Article : public Entity class Article : public EntityUuid
{ {
public: public:
Article() = default; Article() = default;
Article(int price); Article(int price);
//virtual ~Article() = default; Article(const Article &) = delete;
virtual ~Article() = default;
void setArticleNo(int articleNo); void setArticleNo(int articleNo);
void setPrice(int price); void setPrice(int price);
void setDescription(const std::string& description); void setDescription(const std::string &description);
bool isSold(); bool isSold();
void setSale(Sale* salePtr); void setSale(Sale *salePtr);
void setSeller(Seller* sellerPtr); void setSeller(Seller *sellerPtr);
int getArticleNo() const; int getArticleNo() const;
std::string getCompleteArticleNo() const; std::string getCompleteArticleNo() const;
std::string getDescription(); std::string getDescription();
Seller* getSeller(); Seller *getSeller();
Sale* getSale(); Sale *getSale();
int getPrice() const; int getPrice() const;
std::string getPriceAsString() const; std::string getPriceAsString() const;
private: private:
Seller* sellerPtr_{}; Seller *m_sellerPtr{};
Sale* salePtr_{}; Sale *m_salePtr{};
int articleNo_{}; int m_articleNo{};
int price_{}; int m_price{};
std::string description_{}; std::string m_description{};
}; };
#endif #endif

92
src/core/csvreader.cpp Normal file
View file

@ -0,0 +1,92 @@
#include "csvreader.h"
#include "utils.h"
#include <fstream>
// #include <csv.hpp>
#include <boost/algorithm/string.hpp>
#ifdef DELETE
#undef DELETE
#endif
namespace fs = std::filesystem;
std::size_t CsvReader::readSellersFromFile(const fs::path &filePath, Marketplace *market)
{
#if defined(_WIN64) || defined(_WIN32)
// Windows: Somhow this is necessary in order to open file names with umlauts
auto wide = filePath.wstring();
std::string fileName(wide.begin(), wide.end());
std::ifstream infile(fileName);
#else
// csv::CSVReader csvReader(filePath.string(), format);
std::ifstream infile(filePath.string());
#endif
for (auto &seller : market->getSellers()) {
seller->setState(Seller::State::DELETE);
}
market->storeToDb(true);
std::string line;
while (getline(infile, line)) {
std::vector<std::string> strs;
boost::split(strs, line, boost::is_any_of(";"));
auto seller = std::make_unique<Seller>();
try {
int sellerNo = std::stoi(strs[0]);
seller->setSellerNo(sellerNo);
} catch (std::invalid_argument const &ex) {
continue;
}
if (isNumber(strs[1]))
seller->setNumArticlesOffered(std::stoi(strs[1]));
else
seller->setNumArticlesOffered(0);
// If both, first name and last name, are empty, use N. N.
// Else, use the real values.
if (strs[2].empty() && strs[2].empty()) {
seller->setFirstName("N.");
seller->setLastName("N.");
} else {
std::string firstName = strs[2];
seller->setFirstName(trim(firstName));
std::string lastName = strs[3];
seller->setLastName(trim(lastName));
}
market->getSellers().push_back(std::move(seller));
}
// Add one additional seller "RESERVE RESERVE"
auto seller = std::make_unique<Seller>();
seller->setSellerNo(market->getNextSellerNo());
seller->setFirstName("RESERVE");
seller->setLastName("RESERVE");
market->getSellers().push_back(std::move(seller));
// If there was no special seller "Sonderkonto" in import data, then create one
auto specialSeller = market->findSellerWithSellerNo(0);
if (!specialSeller) {
auto seller = std::make_unique<Seller>();
seller->setSellerNo(0);
seller->setLastName("Sonderkonto");
seller->setFirstName("Sonderkonto");
seller->setNumArticlesOffered(0);
market->getSellers().push_back(std::move(seller));
}
market->sortSellers();
market->storeToDb();
return market->getSellers().size() - 1; // minus 1 because we don't count the "special" seller
}

19
src/core/csvreader.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef CSV_READER_H
#define CSV_READER_H
#include "marketplace.h"
#include "seller.h"
#include <filesystem>
#include <memory>
#include <string>
#include <vector>
class CsvReader
{
public:
static std::size_t readSellersFromFile(const std::filesystem::path &filePath,
Marketplace *market);
};
#endif

View file

@ -1,13 +1,13 @@
#include "database.h" #include "database.h"
#include <chrono>
#include <filesystem> #include <filesystem>
#include <format>
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#include <vector> #include <vector>
#include "boost/date_time/posix_time/posix_time.hpp" Database::Database(const std::string &dbname)
Database::Database(const std::string& dbname)
{ {
dbname_ = dbname; dbname_ = dbname;
init(); init();
@ -28,7 +28,7 @@ Database::Database()
if (!fs::exists(dbpath)) { if (!fs::exists(dbpath)) {
try { try {
fs::create_directories(dbpath); fs::create_directories(dbpath);
} catch (fs::filesystem_error& err) { } catch (fs::filesystem_error &err) {
throw err; throw err;
} }
} }
@ -45,8 +45,11 @@ void Database::newDb()
fs::path sourcePath = dbname_; fs::path sourcePath = dbname_;
fs::path destPath = sourcePath.parent_path() / sourcePath.stem(); fs::path destPath = sourcePath.parent_path() / sourcePath.stem();
destPath += std::string("_") +=
boost::posix_time::to_iso_string(boost::posix_time::second_clock::local_time()) += ".db"; auto chronoTime = std::chrono::system_clock::now();
std::string timeString = std::format("{0:%FT%H-%M-%S}", chronoTime);
destPath += std::string("_") += timeString += ".db";
fs::copy_file(sourcePath, destPath, fs::copy_options::overwrite_existing); fs::copy_file(sourcePath, destPath, fs::copy_options::overwrite_existing);
@ -57,13 +60,13 @@ void Database::newDb()
Database::~Database() { sqlite3_close(db_); } Database::~Database() { sqlite3_close(db_); }
void Database::exec(const std::string& sql) void Database::exec(const std::string &sql)
{ {
char* errMsg; char *errMsg;
const int errCode = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &errMsg); const int errCode = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &errMsg);
if (errCode) { if (errCode) {
std::string errMsgString(errMsg); // Make a C++ string of the errMsg, so that we can call std::string errMsgString(errMsg); // Make a C++ string of the errMsg, so that we can call
// sqlite3_free() before throwing the exception // sqlite3_free() before throwing the exception
sqlite3_free(errMsg); sqlite3_free(errMsg);
throw std::runtime_error("Error in SQL execution: " + errMsgString); throw std::runtime_error("Error in SQL execution: " + errMsgString);
} }
@ -75,11 +78,10 @@ void Database::createNew()
std::string sqlCreateKima2{"CREATE TABLE IF NOT EXISTS kima2 (" std::string sqlCreateKima2{"CREATE TABLE IF NOT EXISTS kima2 ("
"version INTEGER NOT NULL);" "version INTEGER NOT NULL);"
"INSERT INTO kima2 (version) VALUES (2);"}; "INSERT INTO kima2 (version) VALUES (3);"};
sqlStrings.push_back(sqlCreateKima2); sqlStrings.push_back(sqlCreateKima2);
std::string sqlCreateSellers{"CREATE TABLE IF NOT EXISTS sellers (" std::string sqlCreateSellers{"CREATE TABLE IF NOT EXISTS sellers ("
"id TEXT PRIMARY KEY NOT NULL, " "seller_no INTEGER PRIMARY KEY NOT NULL, "
"seller_no INTEGER, "
"first_name TEXT, " "first_name TEXT, "
"last_name TEXT, " "last_name TEXT, "
"num_offered_articles INTEGER, " "num_offered_articles INTEGER, "
@ -89,13 +91,13 @@ void Database::createNew()
std::string sqlCreateArticles{ std::string sqlCreateArticles{
"CREATE TABLE IF NOT EXISTS articles (" "CREATE TABLE IF NOT EXISTS articles ("
"id TEXT PRIMARY KEY NOT NULL, " "id TEXT PRIMARY KEY NOT NULL, "
"seller_id TEXT NOT NULL, " "seller_no TEXT NOT NULL, "
"source_no INTEGER NOT NULL, " "source_no INTEGER NOT NULL, "
"article_no INTEGER NOT NULL, " "article_no INTEGER NOT NULL, "
"description TEXT, " "description TEXT, "
"price INTEGER NOT NULL, " "price INTEGER NOT NULL, "
"UNIQUE (source_no, article_no), " "UNIQUE (source_no, article_no), "
"FOREIGN KEY (seller_id) REFERENCES sellers(id) ON DELETE CASCADE, " "FOREIGN KEY (seller_no) REFERENCES sellers(seller_no) ON DELETE CASCADE, "
"CHECK (article_no BETWEEN 0 AND 99999)" "CHECK (article_no BETWEEN 0 AND 99999)"
");"}; ");"};
sqlStrings.push_back(sqlCreateArticles); sqlStrings.push_back(sqlCreateArticles);
@ -115,13 +117,13 @@ void Database::createNew()
sqlStrings.push_back(sqlCreateSalesItems); sqlStrings.push_back(sqlCreateSalesItems);
std::string sqlInitialEntries{ std::string sqlInitialEntries{
"INSERT OR IGNORE INTO sellers (id, seller_no, first_name, last_name, " "INSERT OR IGNORE INTO sellers (seller_no, first_name, last_name, "
"num_offered_articles) VALUES " "num_offered_articles) VALUES "
"('11111111-1111-1111-1111-111111111111', 0, 'Sonderkonto', 'Sonderkonto', 0)"}; "(0, 'Sonderkonto', 'Sonderkonto', 0)"};
sqlStrings.push_back(sqlInitialEntries); sqlStrings.push_back(sqlInitialEntries);
beginTransaction(); beginTransaction();
for (const auto& sql : sqlStrings) { for (const auto &sql : sqlStrings) {
exec(sql); exec(sql);
} }
endTransaction(); endTransaction();
@ -130,13 +132,15 @@ void Database::createNew()
void Database::updateDbToVer2() void Database::updateDbToVer2()
{ {
beginTransaction(); beginTransaction();
exec("INSERT OR IGNORE INTO sellers (id, seller_no, first_name, last_name, " exec("INSERT OR IGNORE INTO sellers (seller_no, first_name, last_name, "
"num_offered_articles) VALUES " "num_offered_articles) VALUES "
"('11111111-1111-1111-1111-111111111111', 0, 'Sonderkonto', 'Sonderkonto', 0)"); "(0, 'Sonderkonto', 'Sonderkonto', 0)");
exec("UPDATE kima2 SET version = 2"); exec("UPDATE kima2 SET version = 3");
endTransaction(); endTransaction();
} }
void Database::updateDbToVer3() { newDb(); }
void Database::init() void Database::init()
{ {
const int errCode = sqlite3_open(dbname_.c_str(), &db_); const int errCode = sqlite3_open(dbname_.c_str(), &db_);
@ -152,12 +156,19 @@ void Database::init()
switch (version) { switch (version) {
case 0: case 0:
createNew(); createNew();
initResult_ = InitResult::OK;
break; break;
case 1: case 1:
updateDbToVer2(); updateDbToVer3();
initResult_ = InitResult::OUTDATED_REPLACED;
break;
case 2:
updateDbToVer3();
initResult_ = InitResult::OUTDATED_REPLACED;
break; break;
default: default:
// Do nothing because we are up-to-date. // Do nothing because we are up-to-date.
initResult_ = InitResult::OK;
break; break;
} }
} }
@ -165,7 +176,7 @@ void Database::init()
int Database::getVersion() int Database::getVersion()
{ {
int retCode{}; int retCode{};
sqlite3_stmt* stmt; sqlite3_stmt *stmt;
// Check if there's already a kima2 table available. // Check if there's already a kima2 table available.
// If not, return version == 0. // If not, return version == 0.
@ -212,29 +223,26 @@ void Database::beginTransaction() { exec("BEGIN TRANSACTION"); }
void Database::endTransaction() { exec("END TRANSACTION"); } void Database::endTransaction() { exec("END TRANSACTION"); }
unsigned int Database::storeSellers(std::vector<std::unique_ptr<Seller>>& sellers, bool onlyDelete) unsigned int Database::storeSellers(std::vector<std::unique_ptr<Seller>> &sellers, bool onlyDelete)
{ {
int retCode{}; int retCode{};
int count{}; int count{};
sqlite3_stmt* stmt; sqlite3_stmt *stmt;
beginTransaction(); beginTransaction();
for (auto& seller : sellers) { for (auto &seller : sellers) {
if (seller->getState() == Seller::State::NEW && !onlyDelete) { if (seller->getState() == Seller::State::NEW && !onlyDelete) {
retCode = sqlite3_prepare_v2( retCode = sqlite3_prepare_v2(
db_, db_,
"INSERT INTO sellers" "INSERT INTO sellers"
" (id, seller_no, first_name, last_name, num_offered_articles)" " (seller_no, first_name, last_name, num_offered_articles)"
" VALUES (:uuid, :seller_no, :first_name, :last_name, :num_offered_articles)", " VALUES (:seller_no, :first_name, :last_name, :num_offered_articles)",
-1, &stmt, nullptr); -1, &stmt, nullptr);
if (retCode != SQLITE_OK) if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_)); throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
boost::uuids::to_string(seller->getUuid()).c_str(), -1,
SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_no"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_no"),
seller->getSellerNo()); seller->getSellerNo());
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":first_name"), sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":first_name"),
@ -260,15 +268,13 @@ unsigned int Database::storeSellers(std::vector<std::unique_ptr<Seller>>& seller
"UPDATE sellers SET" "UPDATE sellers SET"
" seller_no = :seller_no, first_name = :first_name," " seller_no = :seller_no, first_name = :first_name,"
" last_name = :last_name, num_offered_articles = :num_offered_articles" " last_name = :last_name, num_offered_articles = :num_offered_articles"
" WHERE id = :uuid", " WHERE seller_no = :id",
-1, &stmt, nullptr); -1, &stmt, nullptr);
if (retCode != SQLITE_OK) if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_)); throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":id"), seller->getId());
boost::uuids::to_string(seller->getUuid()).c_str(), -1,
SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_no"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_no"),
seller->getSellerNo()); seller->getSellerNo());
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":first_name"), sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":first_name"),
@ -291,15 +297,13 @@ unsigned int Database::storeSellers(std::vector<std::unique_ptr<Seller>>& seller
} else if (seller->getState() == Seller::State::DELETE) { } else if (seller->getState() == Seller::State::DELETE) {
count += static_cast<int>(seller->getArticles(false).size()); count += static_cast<int>(seller->getArticles(false).size());
retCode = retCode = sqlite3_prepare_v2(db_, "DELETE FROM sellers WHERE seller_no = :id", -1,
sqlite3_prepare_v2(db_, "DELETE FROM sellers WHERE id = :uuid", -1, &stmt, nullptr); &stmt, nullptr);
if (retCode != SQLITE_OK) if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_)); throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":id"), seller->getId());
boost::uuids::to_string(seller->getUuid()).c_str(), -1,
SQLITE_TRANSIENT);
retCode = sqlite3_step(stmt); retCode = sqlite3_step(stmt);
@ -322,12 +326,12 @@ unsigned int Database::storeSellers(std::vector<std::unique_ptr<Seller>>& seller
// Everything went fine, so we can now update our objects // Everything went fine, so we can now update our objects
sellers.erase(std::remove_if(sellers.begin(), sellers.end(), sellers.erase(std::remove_if(sellers.begin(), sellers.end(),
[](const std::unique_ptr<Seller>& seller) { [](const std::unique_ptr<Seller> &seller) {
return (seller->getState() == Seller::State::DELETE); return (seller->getState() == Seller::State::DELETE);
}), }),
sellers.end()); sellers.end());
for (auto& seller : sellers) { for (auto &seller : sellers) {
seller->cleanupArticles(); seller->cleanupArticles();
seller->setState(Seller::State::OK); seller->setState(Seller::State::OK);
} }
@ -335,18 +339,18 @@ unsigned int Database::storeSellers(std::vector<std::unique_ptr<Seller>>& seller
return count; return count;
} }
unsigned int Database::storeArticles(std::vector<Article*> articles) unsigned int Database::storeArticles(std::vector<Article *> articles)
{ {
int retCode{}; int retCode{};
int count{}; int count{};
sqlite3_stmt* stmt; sqlite3_stmt *stmt;
for (auto& article : articles) { for (auto &article : articles) {
if (article->getState() == Article::State::NEW) { if (article->getState() == Article::State::NEW) {
retCode = sqlite3_prepare_v2( retCode = sqlite3_prepare_v2(
db_, db_,
"INSERT INTO articles" "INSERT INTO articles"
" (id, seller_id, source_no, article_no, description, price)" " (id, seller_no, source_no, article_no, description, price)"
" VALUES (:uuid, :seller_id, :source_no, :article_no, :desc, :price)", " VALUES (:uuid, :seller_id, :source_no, :article_no, :desc, :price)",
-1, &stmt, nullptr); -1, &stmt, nullptr);
@ -356,9 +360,8 @@ unsigned int Database::storeArticles(std::vector<Article*> articles)
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"), sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
boost::uuids::to_string(article->getUuid()).c_str(), -1, boost::uuids::to_string(article->getUuid()).c_str(), -1,
SQLITE_TRANSIENT); SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":seller_id"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_id"),
boost::uuids::to_string(article->getSeller()->getUuid()).c_str(), -1, article->getSeller()->getId());
SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":source_no"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":source_no"),
article->getSourceNo()); article->getSourceNo());
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":article_no"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":article_no"),
@ -382,7 +385,7 @@ unsigned int Database::storeArticles(std::vector<Article*> articles)
retCode = sqlite3_prepare_v2( retCode = sqlite3_prepare_v2(
db_, db_,
"UPDATE articles SET" "UPDATE articles SET"
" seller_id = seller_id, source_no = :source_no, article_no = :article_no," " seller_no = :seller_id, source_no = :source_no, article_no = :article_no,"
" description = :desc, price = :price" " description = :desc, price = :price"
" WHERE id = :uuid", " WHERE id = :uuid",
-1, &stmt, nullptr); -1, &stmt, nullptr);
@ -393,9 +396,8 @@ unsigned int Database::storeArticles(std::vector<Article*> articles)
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"), sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":uuid"),
boost::uuids::to_string(article->getUuid()).c_str(), -1, boost::uuids::to_string(article->getUuid()).c_str(), -1,
SQLITE_TRANSIENT); SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":seller_id"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_id"),
boost::uuids::to_string(article->getSeller()->getUuid()).c_str(), -1, article->getSeller()->getId());
SQLITE_TRANSIENT);
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":source_no"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":source_no"),
article->getSourceNo()); article->getSourceNo());
sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":article_no"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":article_no"),
@ -442,18 +444,18 @@ unsigned int Database::storeArticles(std::vector<Article*> articles)
return count; return count;
} }
unsigned int Database::storeSales(std::vector<std::unique_ptr<Sale>>& sales) unsigned int Database::storeSales(std::vector<std::unique_ptr<Sale>> &sales)
{ {
int retCode{}; int retCode{};
int count{}; int count{};
sqlite3_stmt* stmt; sqlite3_stmt *stmt;
if (sales.size() == 0) if (sales.size() == 0)
return 0; return 0;
beginTransaction(); beginTransaction();
for (auto& sale : sales) { for (auto &sale : sales) {
if (sale->getState() == Sale::State::NEW) { if (sale->getState() == Sale::State::NEW) {
retCode = sqlite3_prepare_v2(db_, retCode = sqlite3_prepare_v2(db_,
"INSERT INTO sales" "INSERT INTO sales"
@ -483,7 +485,7 @@ unsigned int Database::storeSales(std::vector<std::unique_ptr<Sale>>& sales)
++count; ++count;
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
for (const auto& article : sale->getArticles()) { for (const auto &article : sale->getArticles()) {
retCode = sqlite3_prepare_v2(db_, retCode = sqlite3_prepare_v2(db_,
"INSERT INTO sales_items" "INSERT INTO sales_items"
" (sale_id, article_id)" " (sale_id, article_id)"
@ -536,24 +538,24 @@ unsigned int Database::storeSales(std::vector<std::unique_ptr<Sale>>& sales)
// Everything went fine, so we can now update our objects // Everything went fine, so we can now update our objects
sales.erase( sales.erase(
std::remove_if(sales.begin(), sales.end(), std::remove_if(sales.begin(), sales.end(),
[](const auto& sale) { return (sale->getState() == Sale::State::DELETE); }), [](const auto &sale) { return (sale->getState() == Sale::State::DELETE); }),
sales.end()); sales.end());
for (auto& sale : sales) { for (auto &sale : sales) {
sale->setState(Sale::State::OK); sale->setState(Sale::State::OK);
} }
return count; return count;
} }
unsigned int Database::loadSellers(std::vector<std::unique_ptr<Seller>>& sellers) unsigned int Database::loadSellers(std::vector<std::unique_ptr<Seller>> &sellers)
{ {
int retCode{}; int retCode{};
int count{}; int count{};
sqlite3_stmt* stmt; sqlite3_stmt *stmt;
retCode = sqlite3_prepare_v2(db_, retCode = sqlite3_prepare_v2(db_,
"SELECT id, seller_no, first_name, last_name, " "SELECT seller_no, first_name, last_name, "
"num_offered_articles FROM sellers ORDER BY seller_no", "num_offered_articles FROM sellers ORDER BY seller_no",
-1, &stmt, nullptr); -1, &stmt, nullptr);
if (retCode != SQLITE_OK) if (retCode != SQLITE_OK)
@ -566,11 +568,10 @@ unsigned int Database::loadSellers(std::vector<std::unique_ptr<Seller>>& sellers
while (retCode != SQLITE_DONE) { while (retCode != SQLITE_DONE) {
++count; ++count;
auto seller = std::make_unique<Seller>(); auto seller = std::make_unique<Seller>();
seller->setUuidFromString(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0))); seller->setSellerNo(sqlite3_column_int(stmt, 0));
seller->setSellerNo(sqlite3_column_int(stmt, 1)); seller->setFirstName(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1)));
seller->setFirstName(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))); seller->setLastName(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2)));
seller->setLastName(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3))); seller->setNumArticlesOffered(sqlite3_column_int(stmt, 3));
seller->setNumArticlesOffered(sqlite3_column_int(stmt, 4));
seller->setState(Seller::State::OK); seller->setState(Seller::State::OK);
sellers.push_back(std::move(seller)); sellers.push_back(std::move(seller));
@ -579,29 +580,29 @@ unsigned int Database::loadSellers(std::vector<std::unique_ptr<Seller>>& sellers
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
for (auto& seller : sellers) { for (auto &seller : sellers) {
retCode = sqlite3_prepare_v2(db_, retCode = sqlite3_prepare_v2(db_,
"SELECT id, source_no, article_no, description, price" "SELECT id, source_no, article_no, description, price"
" FROM articles" " FROM articles"
" WHERE seller_id = :seller_uuid" " WHERE seller_no = :seller_id"
" ORDER BY article_no", " ORDER BY article_no",
-1, &stmt, nullptr); -1, &stmt, nullptr);
if (retCode != SQLITE_OK) if (retCode != SQLITE_OK)
throw std::runtime_error(sqlite3_errmsg(db_)); throw std::runtime_error(sqlite3_errmsg(db_));
sqlite3_bind_text(stmt, sqlite3_bind_parameter_index(stmt, ":seller_uuid"), sqlite3_bind_int(stmt, sqlite3_bind_parameter_index(stmt, ":seller_id"), seller->getId());
boost::uuids::to_string(seller->getUuid()).c_str(), -1, SQLITE_TRANSIENT);
retCode = sqlite3_step(stmt); retCode = sqlite3_step(stmt);
while (retCode != SQLITE_DONE) { while (retCode != SQLITE_DONE) {
++count; ++count;
auto article = std::make_unique<Article>(); auto article = std::make_unique<Article>();
article->setUuidFromString(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0))); article->setUuidFromString(
reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0)));
article->setSeller(seller.get()); article->setSeller(seller.get());
article->setSourceNo(sqlite3_column_int(stmt, 1)); article->setSourceNo(sqlite3_column_int(stmt, 1));
article->setArticleNo(sqlite3_column_int(stmt, 2)); article->setArticleNo(sqlite3_column_int(stmt, 2));
article->setDescription(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 3))); article->setDescription(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 3)));
article->setPrice(sqlite3_column_int(stmt, 4)); article->setPrice(sqlite3_column_int(stmt, 4));
article->setState(Article::State::OK); article->setState(Article::State::OK);
@ -616,12 +617,12 @@ unsigned int Database::loadSellers(std::vector<std::unique_ptr<Seller>>& sellers
return count; return count;
} }
unsigned int Database::loadSales(std::vector<std::unique_ptr<Sale>>& sales, unsigned int Database::loadSales(std::vector<std::unique_ptr<Sale>> &sales,
std::vector<std::unique_ptr<Seller>>& sellers) std::vector<std::unique_ptr<Seller>> &sellers)
{ {
int retCode{}; int retCode{};
int count{}; int count{};
sqlite3_stmt* stmt; sqlite3_stmt *stmt;
retCode = sqlite3_prepare_v2(db_, retCode = sqlite3_prepare_v2(db_,
"SELECT id, source_no, sold_at" "SELECT id, source_no, sold_at"
@ -634,13 +635,13 @@ unsigned int Database::loadSales(std::vector<std::unique_ptr<Sale>>& sales,
sales.clear(); sales.clear();
std::map<std::string, Sale*> saleMap; std::map<std::string, Sale *> saleMap;
while (retCode != SQLITE_DONE) { while (retCode != SQLITE_DONE) {
++count; ++count;
auto sale = std::make_unique<Sale>(); auto sale = std::make_unique<Sale>();
sale->setUuidFromString(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0))); sale->setUuidFromString(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0)));
sale->setSourceNo(sqlite3_column_int(stmt, 1)); sale->setSourceNo(sqlite3_column_int(stmt, 1));
sale->setTimestamp(reinterpret_cast<const char*>(sqlite3_column_text(stmt, 2))); sale->setTimestamp(reinterpret_cast<const char *>(sqlite3_column_text(stmt, 2)));
sale->setState(Sale::State::OK); sale->setState(Sale::State::OK);
saleMap.insert(std::make_pair(sale->getUuidAsString(), sale.get())); saleMap.insert(std::make_pair(sale->getUuidAsString(), sale.get()));
sales.push_back(std::move(sale)); sales.push_back(std::move(sale));
@ -650,8 +651,8 @@ unsigned int Database::loadSales(std::vector<std::unique_ptr<Sale>>& sales,
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
std::map<std::string, Article*> artMap; std::map<std::string, Article *> artMap;
for (const auto& seller : sellers) { for (const auto &seller : sellers) {
for (const auto article : seller->getArticles(false)) { for (const auto article : seller->getArticles(false)) {
artMap.insert(std::make_pair(article->getUuidAsString(), article)); artMap.insert(std::make_pair(article->getUuidAsString(), article));
} }
@ -667,8 +668,8 @@ unsigned int Database::loadSales(std::vector<std::unique_ptr<Sale>>& sales,
retCode = sqlite3_step(stmt); retCode = sqlite3_step(stmt);
while (retCode != SQLITE_DONE) { while (retCode != SQLITE_DONE) {
saleMap[reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0))]->addArticle( saleMap[reinterpret_cast<const char *>(sqlite3_column_text(stmt, 0))]->addArticle(
artMap[reinterpret_cast<const char*>(sqlite3_column_text(stmt, 1))]); artMap[reinterpret_cast<const char *>(sqlite3_column_text(stmt, 1))]);
retCode = sqlite3_step(stmt); retCode = sqlite3_step(stmt);
} }
@ -681,7 +682,7 @@ unsigned int Database::loadSales(std::vector<std::unique_ptr<Sale>>& sales,
void Database::updateCashPointNo(int oldCashPointNo, int newCashPointNo) void Database::updateCashPointNo(int oldCashPointNo, int newCashPointNo)
{ {
int retCode{}; int retCode{};
sqlite3_stmt* stmt; sqlite3_stmt *stmt;
// Check if the new no ist already in use // Check if the new no ist already in use
retCode = sqlite3_prepare_v2(db_, "SELECT COUNT() FROM articles WHERE source_no = :source_no", retCode = sqlite3_prepare_v2(db_, "SELECT COUNT() FROM articles WHERE source_no = :source_no",

View file

@ -10,32 +10,36 @@
class Database class Database
{ {
public: public:
explicit Database(const std::string& dbname); enum class InitResult { OK, OUTDATED_REPLACED };
explicit Database(const std::string &dbname);
Database(); Database();
~Database(); ~Database();
Database(const Database&) = delete; Database(const Database &) = delete;
Database& operator=(const Database&) = delete; Database &operator=(const Database &) = delete;
void exec(const std::string& sql); void exec(const std::string &sql);
unsigned int storeSellers(std::vector<std::unique_ptr<Seller>>& sellers, unsigned int storeSellers(std::vector<std::unique_ptr<Seller>> &sellers,
bool onlyDelete = false); bool onlyDelete = false);
unsigned int loadSellers(std::vector<std::unique_ptr<Seller>>& sellers); unsigned int loadSellers(std::vector<std::unique_ptr<Seller>> &sellers);
unsigned int storeSales(std::vector<std::unique_ptr<Sale>>& sales); unsigned int storeSales(std::vector<std::unique_ptr<Sale>> &sales);
unsigned int loadSales(std::vector<std::unique_ptr<Sale>>& sales, unsigned int loadSales(std::vector<std::unique_ptr<Sale>> &sales,
std::vector<std::unique_ptr<Seller>>& sellers); std::vector<std::unique_ptr<Seller>> &sellers);
void updateCashPointNo(int oldCashPointNo, int newCashPointNo); void updateCashPointNo(int oldCashPointNo, int newCashPointNo);
void newDb(); void newDb();
InitResult getInitResult() { return initResult_; }
private: private:
sqlite3* db_{nullptr}; sqlite3 *db_{nullptr};
std::string dbname_; std::string dbname_;
void init(); void init();
void beginTransaction(); void beginTransaction();
void endTransaction(); void endTransaction();
void createNew(); void createNew();
int getVersion(); int getVersion();
unsigned int storeArticles(std::vector<Article*> articles); unsigned int storeArticles(std::vector<Article *> articles);
void updateDbToVer2(); void updateDbToVer2();
void updateDbToVer3();
InitResult initResult_{InitResult::OK};
}; };
#endif // DATABASE_H #endif // DATABASE_H

View file

@ -1,33 +1,3 @@
#include "entity.h" #include "entity.h"
#include <iostream> Entity::State Entity::getState() const { return m_state; }
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
Entity::~Entity() = default;
void Entity::createUuid()
{
static boost::uuids::random_generator generator{};
uuid_ = generator();
}
void Entity::setUuidFromString(const std::string& uuidString)
{
boost::uuids::string_generator generator{};
uuid_ = generator(uuidString);
}
Entity::State Entity::getState() const
{
return state_;
}
void Entity::setSourceNo(int sourceNo) {
sourceNo_ = sourceNo;
}
int Entity::getSourceNo() const {
return sourceNo_;
}

View file

@ -1,35 +1,16 @@
#ifndef ENTITY_H #ifndef ENTITY_H
#define ENTITY_H #define ENTITY_H
#include <string>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
class Entity class Entity
{ {
public: public:
enum class State { NEW, UPDATE, DELETE, OK }; enum class State { NEW, UPDATE, DELETE, OK };
virtual ~Entity() = default;
// Entity() = default; void setState(State state) { m_state = state; }
virtual ~Entity() = 0;
void createUuid();
void setUuidFromString(const std::string& uuidString);
void setState(State state) { state_ = state; }
void setSourceNo(int sourceNo);
const boost::uuids::uuid& getUuid() const { return uuid_; };
std::string getUuidAsString() const { return boost::uuids::to_string(uuid_); }
virtual State getState() const; virtual State getState() const;
virtual int getSourceNo() const;
protected: private:
int sourceNo_{}; State m_state{State::NEW};
private:
boost::uuids::uuid uuid_{};
State state_{State::NEW};
}; };
#endif // ENTITY_H #endif // ENTITY_H

5
src/core/entityint.cpp Normal file
View file

@ -0,0 +1,5 @@
#include "entityint.h"
EntityInt::EntityInt(int id) { m_id = id; }
void EntityInt::setId(int id) { m_id = id; }

19
src/core/entityint.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef ENTITY_INT_H
#define ENTITY_INT_H
#include "entity.h"
class EntityInt : public Entity
{
public:
EntityInt() = default;
virtual ~EntityInt() = default;
EntityInt(int id);
void setId(int id);
int getId() const { return m_id; };
protected:
int m_id{};
};
#endif // ENTITY_INT_H

22
src/core/entityuuid.cpp Normal file
View file

@ -0,0 +1,22 @@
#include "entityuuid.h"
#include <iostream>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
void EntityUuid::createUuid()
{
static boost::uuids::random_generator generator{};
m_uuid = generator();
}
void EntityUuid::setUuidFromString(const std::string &uuidString)
{
boost::uuids::string_generator generator{};
m_uuid = generator(uuidString);
}
void EntityUuid::setSourceNo(int sourceNo) { m_sourceNo = sourceNo; }
int EntityUuid::getSourceNo() const { return m_sourceNo; }

32
src/core/entityuuid.h Normal file
View file

@ -0,0 +1,32 @@
#ifndef ENTITY_UUID_H
#define ENTITY_UUID_H
#include "entity.h"
#include <string>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
class EntityUuid : public Entity
{
public:
EntityUuid() = default;
virtual ~EntityUuid() = default;
void createUuid();
void setUuidFromString(const std::string &uuidString);
void setSourceNo(int sourceNo);
const boost::uuids::uuid &getUuid() const { return m_uuid; };
std::string getUuidAsString() const { return boost::uuids::to_string(m_uuid); }
virtual int getSourceNo() const;
protected:
int m_sourceNo{};
private:
boost::uuids::uuid m_uuid{};
};
#endif // ENTITY_UUID_H

View file

@ -1,63 +0,0 @@
#include "excelreader.h"
#include <xlnt/xlnt.hpp>
#include <fstream>
namespace fs = std::filesystem;
void ExcelReader::readSellersFromFile(const fs::path& filePath, Marketplace* market)
{
xlnt::workbook wb;
std::ifstream mystream(filePath, std::ios::binary);
if(!mystream.is_open()) {
throw std::runtime_error("Could not open ecxel file");
}
wb.load(mystream);
for (auto& seller : market->getSellers()) {
seller->setState(Seller::State::DELETE);
}
market->storeToDb(true);
auto ws = wb.sheet_by_index(0);
const int START_ROW = 5;
const int END_ROW = 350;
int rowCount{};
for (const auto& row : ws.rows(false)) {
if (rowCount < START_ROW) {
++rowCount;
continue;
} else if (rowCount > END_ROW) {
break;
}
if (row[2].value<std::string>().empty() && row[3].value<std::string>().empty()) {
++rowCount;
continue;
}
auto seller = std::make_unique<Seller>();
seller->createUuid();
seller->setSellerNo(row[0].value<int>());
seller->setNumArticlesOffered(row[1].value<int>());
seller->setFirstName(row[2].value<std::string>());
seller->setLastName(row[3].value<std::string>());
market->getSellers().push_back(std::move(seller));
rowCount++;
}
// If there was no special seller "Sonderkonto" in import data, then create one
auto specialSeller = market->findSellerWithUuid("11111111-1111-1111-1111-111111111111");
if (!specialSeller) {
auto seller = std::make_unique<Seller>();
seller->setUuidFromString("11111111-1111-1111-1111-111111111111");
seller->setSellerNo(0);
seller->setLastName("Sonderkonto");
seller->setFirstName("Sonderkonto");
seller->setNumArticlesOffered(0);
market->getSellers().push_back(std::move(seller));
}
market->sortSellers();
market->storeToDb();
}

View file

@ -1,18 +0,0 @@
#ifndef EXCEL_READER_H
#define EXCEL_READER_H
#include "marketplace.h"
#include "seller.h"
#include <filesystem>
#include <memory>
#include <string>
#include <vector>
class ExcelReader
{
public:
static void readSellersFromFile(const std::filesystem::path& filePath, Marketplace* market);
};
#endif

View file

@ -5,17 +5,15 @@
#include <fstream> #include <fstream>
namespace fs = std::filesystem;
using json = nlohmann::json; using json = nlohmann::json;
void JsonUtil::exportSellers(const std::filesystem::path& filePath, Marketplace* market) void JsonUtil::exportSellers(const std::filesystem::path &filePath, Marketplace *market)
{ {
json root; json root;
std::ofstream file(filePath); std::ofstream file(filePath);
for (const auto& seller : market->getSellers()) { for (const auto &seller : market->getSellers()) {
json newEntry; json newEntry;
newEntry["uuid"] = seller->getUuidAsString();
newEntry["seller_no"] = seller->getSellerNo(); newEntry["seller_no"] = seller->getSellerNo();
newEntry["last_name"] = seller->getLastName(); newEntry["last_name"] = seller->getLastName();
newEntry["first_name"] = seller->getFirstName(); newEntry["first_name"] = seller->getFirstName();
@ -26,9 +24,9 @@ void JsonUtil::exportSellers(const std::filesystem::path& filePath, Marketplace*
file << root.dump(4) << std::endl; file << root.dump(4) << std::endl;
} }
void JsonUtil::importSellers(const std::filesystem::path& filePath, Marketplace* market) std::size_t JsonUtil::importSellers(const std::filesystem::path &filePath, Marketplace *market)
{ {
for (auto& seller : market->getSellers()) { for (auto &seller : market->getSellers()) {
seller->setState(Seller::State::DELETE); seller->setState(Seller::State::DELETE);
} }
market->storeToDb(true); market->storeToDb(true);
@ -38,7 +36,6 @@ void JsonUtil::importSellers(const std::filesystem::path& filePath, Marketplace*
for (auto val : jsonValues["sellers"]) { for (auto val : jsonValues["sellers"]) {
auto seller = std::make_unique<Seller>(); auto seller = std::make_unique<Seller>();
seller->setUuidFromString(val["uuid"]);
seller->setSellerNo(val["seller_no"]); seller->setSellerNo(val["seller_no"]);
seller->setLastName(val["last_name"]); seller->setLastName(val["last_name"]);
seller->setFirstName(val["first_name"]); seller->setFirstName(val["first_name"]);
@ -47,10 +44,9 @@ void JsonUtil::importSellers(const std::filesystem::path& filePath, Marketplace*
} }
// If there was no special seller "Sonderkonto" in import data, then create one // If there was no special seller "Sonderkonto" in import data, then create one
auto specialSeller = market->findSellerWithUuid("11111111-1111-1111-1111-111111111111"); auto specialSeller = market->findSellerWithSellerNo(0);
if (!specialSeller) { if (!specialSeller) {
auto seller = std::make_unique<Seller>(); auto seller = std::make_unique<Seller>();
seller->setUuidFromString("11111111-1111-1111-1111-111111111111");
seller->setSellerNo(0); seller->setSellerNo(0);
seller->setLastName("Sonderkonto"); seller->setLastName("Sonderkonto");
seller->setFirstName("Sonderkonto"); seller->setFirstName("Sonderkonto");
@ -59,18 +55,20 @@ void JsonUtil::importSellers(const std::filesystem::path& filePath, Marketplace*
} }
market->sortSellers(); market->sortSellers();
market->storeToDb(); market->storeToDb();
return market->getSellers().size() - 1; // minus 1 because we don't count the "special" seller
} }
void JsonUtil::exportSales(const std::filesystem::path& filePath, Marketplace* market, int cashPointNo) void JsonUtil::exportSales(const std::filesystem::path &filePath, Marketplace *market,
int cashPointNo)
{ {
json root; json root;
std::ofstream file(filePath); std::ofstream file(filePath);
root["source_no"] = cashPointNo; root["source_no"] = cashPointNo;
for (const auto& sale : market->getSales()) { for (const auto &sale : market->getSales()) {
if (sale->getSourceNo() != cashPointNo) if (sale->getSourceNo() != cashPointNo)
continue; continue;
@ -78,10 +76,10 @@ void JsonUtil::exportSales(const std::filesystem::path& filePath, Marketplace* m
newSale["uuid"] = sale->getUuidAsString(); newSale["uuid"] = sale->getUuidAsString();
newSale["timestamp"] = sale->getTimestamp(); newSale["timestamp"] = sale->getTimestamp();
for (const auto& article : sale->getArticles()) { for (const auto &article : sale->getArticles()) {
json newArticle; json newArticle;
newArticle["uuid"] = article->getUuidAsString(); newArticle["uuid"] = article->getUuidAsString();
newArticle["seller_uuid"] = article->getSeller()->getUuidAsString(); newArticle["seller_no"] = article->getSeller()->getSellerNo();
newArticle["desc"] = article->getDescription(); newArticle["desc"] = article->getDescription();
newArticle["price"] = article->getPrice(); newArticle["price"] = article->getPrice();
// newArticle["source_no"] = article->getSourceNo(); // newArticle["source_no"] = article->getSourceNo();
@ -96,33 +94,36 @@ void JsonUtil::exportSales(const std::filesystem::path& filePath, Marketplace* m
file << root.dump(4) << std::endl; file << root.dump(4) << std::endl;
} }
void JsonUtil::importSales(const std::filesystem::path& filePath, Marketplace* market, int cashPointNo) void JsonUtil::importSales(const std::filesystem::path &filePath, Marketplace *market,
int cashPointNo)
{ {
std::ifstream file(filePath); std::ifstream file(filePath);
json jsonValues = json::parse(file); json jsonValues = json::parse(file);
int source_no = jsonValues["source_no"]; int source_no = jsonValues["source_no"];
if (source_no == cashPointNo) { if (source_no == cashPointNo) {
throw std::runtime_error("Die Kassen-Nr. der zu imporierenden Daten wird von dieser Kasse " std::string ret = "Die Kassen-Nr. ";
"hier bereits verwendet."); ret += std::to_string(source_no);
ret += " der zu imporierenden Daten wird von dieser Kasse hier bereits verwendet.";
throw std::runtime_error(ret);
} }
market->setSalesToDelete(jsonValues["source_no"]); market->setSalesToDelete(jsonValues["source_no"]);
market->storeToDb(); market->storeToDb();
for (const auto& valSale : jsonValues["sales"]) { for (const auto &valSale : jsonValues["sales"]) {
auto sale = std::make_unique<Sale>(); auto sale = std::make_unique<Sale>();
sale->setUuidFromString(valSale["uuid"]); sale->setUuidFromString(valSale["uuid"]);
sale->setSourceNo(jsonValues["source_no"]); sale->setSourceNo(jsonValues["source_no"]);
sale->setTimestamp(valSale["timestamp"]); sale->setTimestamp(valSale["timestamp"]);
for (const auto& valArticle : valSale["articles"]) { for (const auto &valArticle : valSale["articles"]) {
auto article = std::make_unique<Article>(); auto article = std::make_unique<Article>();
article->setUuidFromString(valArticle["uuid"]); article->setUuidFromString(valArticle["uuid"]);
article->setSourceNo(jsonValues["source_no"]); article->setSourceNo(jsonValues["source_no"]);
article->setArticleNo(valArticle["article_no"]); article->setArticleNo(valArticle["article_no"]);
article->setDescription(valArticle["desc"]); article->setDescription(valArticle["desc"]);
article->setPrice(valArticle["price"]); article->setPrice(valArticle["price"]);
auto seller = market->findSellerWithUuid(valArticle["seller_uuid"]); auto seller = market->findSellerWithSellerNo(valArticle["seller_no"]);
if (seller == nullptr) { if (seller == nullptr) {
throw std::runtime_error( throw std::runtime_error(
"Die zu importierenden Daten verweisen auf einen nicht vorhandenen Verkäufer. " "Die zu importierenden Daten verweisen auf einen nicht vorhandenen Verkäufer. "

View file

@ -3,16 +3,18 @@
#include "marketplace.h" #include "marketplace.h"
#include <string>
#include <filesystem> #include <filesystem>
#include <string>
class JsonUtil class JsonUtil
{ {
public: public:
static void exportSellers(const std::filesystem::path& filePath, Marketplace* market); static void exportSellers(const std::filesystem::path &filePath, Marketplace *market);
static void importSellers(const std::filesystem::path& filePath, Marketplace* market); static std::size_t importSellers(const std::filesystem::path &filePath, Marketplace *market);
static void exportSales(const std::filesystem::path& filePath, Marketplace* market, int cashPointNo); static void exportSales(const std::filesystem::path &filePath, Marketplace *market,
static void importSales(const std::filesystem::path& filePath, Marketplace* market, int cashPointNo); int cashPointNo);
static void importSales(const std::filesystem::path &filePath, Marketplace *market,
int cashPointNo);
}; };
#endif #endif

View file

@ -3,19 +3,19 @@
#include "utils.h" #include "utils.h"
#include <algorithm> #include <algorithm>
#include <filesystem>
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <numeric> #include <numeric>
#include <sstream> #include <sstream>
#include <filesystem>
namespace fs = std::filesystem; namespace fs = std::filesystem;
Marketplace::Marketplace() Marketplace::Marketplace()
{ {
auto seller = std::make_unique<Seller>("Max", "Mustermann"); /*auto seller = std::make_unique<Seller>("Max", "Mustermann");
seller->createUuid(); seller->createUuid();
sellers_.push_back(std::move(seller)); sellers_.push_back(std::move(seller)); */
} }
void Marketplace::storeToDb(bool onlyDelete) void Marketplace::storeToDb(bool onlyDelete)
@ -25,22 +25,23 @@ void Marketplace::storeToDb(bool onlyDelete)
db.storeSales(sales_); db.storeSales(sales_);
} }
void Marketplace::loadFromDb() Database::InitResult Marketplace::loadFromDb()
{ {
Database db; Database db;
db.loadSellers(sellers_); db.loadSellers(sellers_);
db.loadSales(sales_, sellers_); db.loadSales(sales_, sellers_);
return db.getInitResult();
} }
SellersVec& Marketplace::getSellers() { return sellers_; } SellersVec &Marketplace::getSellers() { return sellers_; }
SalesVec& Marketplace::getSales() { return sales_; } SalesVec &Marketplace::getSales() { return sales_; }
int Marketplace::getNextSellerNo() int Marketplace::getNextSellerNo()
{ {
auto iter = std::max_element( auto iter = std::max_element(
sellers_.begin(), sellers_.end(), sellers_.begin(), sellers_.end(),
[](const auto& a, const auto& b) -> bool { return a->getSellerNo() < b->getSellerNo(); }); [](const auto &a, const auto &b) -> bool { return a->getSellerNo() < b->getSellerNo(); });
if (iter == sellers_.end()) if (iter == sellers_.end())
return 1; return 1;
return (*iter)->getSellerNo() + 1; return (*iter)->getSellerNo() + 1;
@ -52,14 +53,14 @@ int Marketplace::getNextArticleNo()
int maxArtNoInBasket{0}; int maxArtNoInBasket{0};
auto iter = std::max_element(sellers_.begin(), sellers_.end(), auto iter = std::max_element(sellers_.begin(), sellers_.end(),
[](const auto& a, const auto& b) -> bool { [](const auto &a, const auto &b) -> bool {
return a->getMaxArticleNo() < b->getMaxArticleNo(); return a->getMaxArticleNo() < b->getMaxArticleNo();
}); });
if (iter != sellers_.end()) if (iter != sellers_.end())
maxArtNoInDb = (*iter)->getMaxArticleNo(); maxArtNoInDb = (*iter)->getMaxArticleNo();
auto iter2 = auto iter2 =
std::max_element(basket_.begin(), basket_.end(), [](const auto& a, const auto& b) -> bool { std::max_element(basket_.begin(), basket_.end(), [](const auto &a, const auto &b) -> bool {
return a->getArticleNo() < b->getArticleNo(); return a->getArticleNo() < b->getArticleNo();
}); });
@ -72,13 +73,13 @@ int Marketplace::getNextArticleNo()
int Marketplace::getNumSellersDelete() int Marketplace::getNumSellersDelete()
{ {
int count = std::count_if(sellers_.begin(), sellers_.end(), int count = std::count_if(sellers_.begin(), sellers_.end(),
[](const auto& a) { return a->getState() == Seller::State::DELETE; }); [](const auto &a) { return a->getState() == Seller::State::DELETE; });
return count; return count;
} }
int Marketplace::getNumArticlesSold() int Marketplace::getNumArticlesSold()
{ {
int sum = std::accumulate(sellers_.begin(), sellers_.end(), 0, [](int a, const auto& seller) { int sum = std::accumulate(sellers_.begin(), sellers_.end(), 0, [](int a, const auto &seller) {
return a + seller->numArticlesSold(); return a + seller->numArticlesSold();
}); });
return sum; return sum;
@ -86,7 +87,7 @@ int Marketplace::getNumArticlesSold()
int Marketplace::getNumArticlesOffered() int Marketplace::getNumArticlesOffered()
{ {
int sum = std::accumulate(sellers_.begin(), sellers_.end(), 0, [](int a, const auto& seller) { int sum = std::accumulate(sellers_.begin(), sellers_.end(), 0, [](int a, const auto &seller) {
return a + seller->numArticlesOffered(); return a + seller->numArticlesOffered();
}); });
return sum; return sum;
@ -94,19 +95,10 @@ int Marketplace::getNumArticlesOffered()
void Marketplace::sortSellers() { std::sort(sellers_.begin(), sellers_.end()); } void Marketplace::sortSellers() { std::sort(sellers_.begin(), sellers_.end()); }
Seller* Marketplace::findSellerWithSellerNo(int sellerNo) Seller *Marketplace::findSellerWithSellerNo(int sellerNo)
{ {
auto iter = std::find_if(sellers_.begin(), sellers_.end(), auto iter = std::find_if(sellers_.begin(), sellers_.end(),
[sellerNo](const auto& a) { return a->getSellerNo() == sellerNo; }); [sellerNo](const auto &a) { return a->getSellerNo() == sellerNo; });
if (iter == sellers_.end())
return nullptr;
return (*iter).get();
}
Seller* Marketplace::findSellerWithUuid(const std::string& uuid)
{
auto iter = std::find_if(sellers_.begin(), sellers_.end(),
[uuid](const auto& a) { return a->getUuidAsString() == uuid; });
if (iter == sellers_.end()) if (iter == sellers_.end())
return nullptr; return nullptr;
return (*iter).get(); return (*iter).get();
@ -114,7 +106,8 @@ Seller* Marketplace::findSellerWithUuid(const std::string& uuid)
void Marketplace::addArticleToBasket(std::unique_ptr<Article> article) void Marketplace::addArticleToBasket(std::unique_ptr<Article> article)
{ {
basket_.insert(basket_.begin(), std::move(article)); // article to the beginning of the basket vector basket_.insert(basket_.begin(),
std::move(article)); // article to the beginning of the basket vector
} }
size_t Marketplace::basketSize() { return basket_.size(); } size_t Marketplace::basketSize() { return basket_.size(); }
@ -136,12 +129,12 @@ void Marketplace::finishCurrentSale(std::unique_ptr<Sale> sale)
storeToDb(); storeToDb();
} }
BasketVec& Marketplace::getBasket() { return basket_; } BasketVec &Marketplace::getBasket() { return basket_; }
int Marketplace::getBasketSumInCent() int Marketplace::getBasketSumInCent()
{ {
int sum = std::accumulate(basket_.begin(), basket_.end(), 0, int sum = std::accumulate(basket_.begin(), basket_.end(), 0,
[](int a, const auto& b) { return a + b->getPrice(); }); [](int a, const auto &b) { return a + b->getPrice(); });
return sum; return sum;
} }
@ -155,39 +148,40 @@ std::string Marketplace::getBasketSumAsString()
void Marketplace::removeSale(boost::uuids::uuid uuid) void Marketplace::removeSale(boost::uuids::uuid uuid)
{ {
sales_.erase(std::remove_if(sales_.begin(), sales_.end(), sales_.erase(std::remove_if(sales_.begin(), sales_.end(),
[&uuid](const auto& a) { return a->getUuid() == uuid; }), [&uuid](const auto &a) { return a->getUuid() == uuid; }),
sales_.end()); sales_.end());
} }
void Marketplace::setSalesToDelete(int cashPointNo) void Marketplace::setSalesToDelete(int cashPointNo)
{ {
std::for_each(sales_.begin(), sales_.end(), [cashPointNo](auto& sale) { std::for_each(sales_.begin(), sales_.end(), [cashPointNo](auto &sale) {
if (sale->getSourceNo() == cashPointNo) { if (sale->getSourceNo() == cashPointNo) {
sale->setState(Sale::State::DELETE); sale->setState(Sale::State::DELETE);
for (auto& article : sale->getArticles()) { for (auto &article : sale->getArticles()) {
article->setState(Article::State::DELETE); article->setState(Article::State::DELETE);
} }
} }
}); });
} }
void Marketplace::exportReportToCSV(const fs::path& filePath, int feeInPercent, int maxFeeInEuro) void Marketplace::exportReportToCSV(const fs::path &filePath, int feeInPercent, int maxFeeInEuro)
{ {
const char delimiter = ','; const char delimiter = ';';
std::ofstream file(filePath); std::ofstream file(filePath);
file << "Verk.Nr." << delimiter << "Nachname" << delimiter << "Vorname" << delimiter file << "Verk.Nr." << delimiter << "Nachname" << delimiter << "Vorname" << delimiter
<< "Anz. gemeldet" << delimiter << "Anz. verkauft" << delimiter << "Umsatz" << delimiter << "Anz. gemeldet" << delimiter << "Anz. verkauft" << delimiter << "Umsatz" << delimiter
<< "Auszahlung\n"; << "Auszahlung\n";
for (const auto& seller : sellers_) { for (const auto &seller : sellers_) {
file << seller->getSellerNo() << delimiter file << seller->getSellerNo() << delimiter
<< escapeCsvValue(seller->getLastName(), delimiter) << delimiter << escapeCsvValue(seller->getLastName(), delimiter) << delimiter
<< escapeCsvValue(seller->getFirstName(), delimiter) << delimiter << escapeCsvValue(seller->getFirstName(), delimiter) << delimiter
<< seller->numArticlesOffered() << delimiter << seller->numArticlesSold() << delimiter << seller->numArticlesOffered() << delimiter << seller->numArticlesSold() << delimiter
<< escapeCsvValue(seller->sumAsString(), delimiter) << delimiter << escapeCsvValue(seller->sumAsString(), delimiter) << delimiter
<< escapeCsvValue(paymentAsString(seller->sumInCents(), feeInPercent, maxFeeInEuro * 100), << escapeCsvValue(
delimiter) paymentAsString(seller->sumInCents(), feeInPercent, maxFeeInEuro * 100),
delimiter)
<< "\n"; << "\n";
} }
} }
@ -195,7 +189,7 @@ void Marketplace::exportReportToCSV(const fs::path& filePath, int feeInPercent,
int Marketplace::getOverallSumInCent() int Marketplace::getOverallSumInCent()
{ {
int sum = std::accumulate(sellers_.begin(), sellers_.end(), 0, int sum = std::accumulate(sellers_.begin(), sellers_.end(), 0,
[](int a, const auto& b) { return a + b->sumInCents(); }); [](int a, const auto &b) { return a + b->sumInCents(); });
return sum; return sum;
} }
@ -208,7 +202,7 @@ std::string Marketplace::getOverallSumAsString()
int Marketplace::getOverallPaymentInCent(int percent, int maxFee) int Marketplace::getOverallPaymentInCent(int percent, int maxFee)
{ {
int sum = std::accumulate( int sum = std::accumulate(
sellers_.begin(), sellers_.end(), 0, [percent, maxFee](int a, const auto& b) { sellers_.begin(), sellers_.end(), 0, [percent, maxFee](int a, const auto &b) {
return a + b->sumInCents() - marketFee(b->sumInCents(), percent, maxFee); return a + b->sumInCents() - marketFee(b->sumInCents(), percent, maxFee);
}); });
return sum; return sum;
@ -246,7 +240,7 @@ std::string paymentAsString(int sumInCent, int percent, int maxFeeInCent)
return formatCentAsEuroString(sumInCent - marketFee(sumInCent, percent, maxFeeInCent)); return formatCentAsEuroString(sumInCent - marketFee(sumInCent, percent, maxFeeInCent));
} }
std::string escapeCsvValue(const std::string& value, const char delimiter) std::string escapeCsvValue(const std::string &value, const char delimiter)
{ {
std::stringstream output; std::stringstream output;
bool containsDelim{false}; bool containsDelim{false};
@ -256,7 +250,7 @@ std::string escapeCsvValue(const std::string& value, const char delimiter)
output << '"'; output << '"';
} }
for (auto& symbol : value) { for (auto &symbol : value) {
if (symbol == '"') { if (symbol == '"') {
output << '"' << symbol; output << '"' << symbol;
} else { } else {

View file

@ -2,6 +2,7 @@
#define MARKETPLACE_H #define MARKETPLACE_H
#include "article.h" #include "article.h"
#include "database.h"
#include "sale.h" #include "sale.h"
#include "seller.h" #include "seller.h"
@ -19,26 +20,26 @@ using BasketVec = std::vector<std::unique_ptr<Article>>;
class Marketplace class Marketplace
{ {
public: public:
Marketplace(); Marketplace();
void storeToDb(bool onlyDelete = false); void storeToDb(bool onlyDelete = false);
void loadFromDb(); Database::InitResult loadFromDb();
SellersVec& getSellers(); SellersVec &getSellers();
SalesVec& getSales(); SalesVec &getSales();
int getNextSellerNo(); int getNextSellerNo();
int getNextArticleNo(); int getNextArticleNo();
int getNumSellersDelete(); int getNumSellersDelete();
int getNumArticlesSold(); int getNumArticlesSold();
int getNumArticlesOffered(); int getNumArticlesOffered();
BasketVec& getBasket(); BasketVec &getBasket();
int getBasketSumInCent(); int getBasketSumInCent();
std::string getBasketSumAsString(); std::string getBasketSumAsString();
void sortSellers(); void sortSellers();
Seller* findSellerWithSellerNo(int sellerNo); Seller *findSellerWithSellerNo(int sellerNo);
Seller* findSellerWithUuid(const std::string& uuid); Seller *findSellerWithUuid(const std::string &uuid);
void addArticleToBasket(std::unique_ptr<Article> article); void addArticleToBasket(std::unique_ptr<Article> article);
size_t basketSize(); size_t basketSize();
void finishCurrentSale(std::unique_ptr<Sale> sale); void finishCurrentSale(std::unique_ptr<Sale> sale);
@ -53,12 +54,12 @@ class Marketplace
void clear(); void clear();
void exportReportToCSV(const std::filesystem::path& filePath, int feeInPercent, void exportReportToCSV(const std::filesystem::path &filePath, int feeInPercent,
int maxFeeInEuro); int maxFeeInEuro);
friend class ExcelReader; friend class ExcelReader;
private: private:
SellersVec sellers_; SellersVec sellers_;
SalesVec sales_; SalesVec sales_;
BasketVec basket_; BasketVec basket_;
@ -67,6 +68,6 @@ class Marketplace
double marketFee(int sumInCent, int percent, int maxFeeInCent); double marketFee(int sumInCent, int percent, int maxFeeInCent);
std::string marketFeeAsString(int sumInCent, int percent, int maxFeeInCent); std::string marketFeeAsString(int sumInCent, int percent, int maxFeeInCent);
std::string paymentAsString(int sumInCent, int percent, int maxFeeInCent); std::string paymentAsString(int sumInCent, int percent, int maxFeeInCent);
std::string escapeCsvValue(const std::string& value, const char delimiter); std::string escapeCsvValue(const std::string &value, const char delimiter);
#endif #endif

13
src/core/meson.build Normal file
View file

@ -0,0 +1,13 @@
boost = dependency('boost', modules: ['date_time'], static: true)
xlnt = dependency('xlnt')
sqlite = dependency('sqlite3')
src = ['database.cpp', 'entity.cpp', 'entityint.cpp', 'entityuuid.cpp',
'seller.cpp', 'article.cpp', 'sale.cpp', 'marketplace.cpp',
'excelreader.cpp', 'csvreader.cpp', 'jsonutil.cpp', 'utils.cpp']
core_inc = include_directories('..')
core_lib = static_library('core', src, dependencies: [boost, xlnt, sqlite, nlohmann_lib, csv_dep])
core_dep = declare_dependency(link_with: core_lib, include_directories : core_inc)

View file

@ -3,43 +3,41 @@
#include <numeric> #include <numeric>
void Sale::addArticle(Article* articlePtr) void Sale::addArticle(Article *articlePtr)
{ {
articlePtr->setSale(this); articlePtr->setSale(this);
articles_.push_back(articlePtr); m_articles.push_back(articlePtr);
} }
ArticlesVec& Sale::getArticles() { return articles_; } ArticlesVec &Sale::getArticles() { return m_articles; }
void Sale::removeArticle(const Article* articlePtr) void Sale::removeArticle(const Article *articlePtr)
{ {
/* auto it = std::find_if(articles_.begin(), articles_.end(), auto it = std::find(m_articles.begin(), m_articles.end(), articlePtr);
[&articlePtr](auto art) { return art.get() == articlePtr; }); */
auto it = std::find(articles_.begin(), articles_.end(), articlePtr);
if (it != articles_.end()) { if (it != m_articles.end()) {
(*it)->setSale(nullptr); (*it)->setSale(nullptr);
(*it)->setState( (*it)->setState(
Article::State::DELETE); // since we only have ad-hoc articles, that have all been sold Article::State::DELETE); // since we only have ad-hoc articles, that have all been sold
articles_.erase(it); m_articles.erase(it);
} }
} }
int Sale::sumInCents() int Sale::sumInCents()
{ {
int sum = std::accumulate(articles_.begin(), articles_.end(), 0, int sum = std::accumulate(m_articles.begin(), m_articles.end(), 0,
[](int a, const Article* b) { return a + b->getPrice(); }); [](int a, const Article *b) { return a + b->getPrice(); });
return sum; return sum;
} }
std::string Sale::sumAsString() { return formatCentAsEuroString(sumInCents()); } std::string Sale::sumAsString() { return formatCentAsEuroString(sumInCents()); }
std::string Sale::getTimestamp() const { return timestamp_; } std::string Sale::getTimestamp() const { return m_timestamp; }
void Sale::setTimestamp(const std::string& timestamp) { timestamp_ = timestamp; } void Sale::setTimestamp(const std::string &timestamp) { m_timestamp = timestamp; }
std::string Sale::getTimestampFormatted() const std::string Sale::getTimestampFormatted() const
{ {
boost::posix_time::ptime time = boost::posix_time::from_iso_extended_string(timestamp_); boost::posix_time::ptime time = boost::posix_time::from_iso_extended_string(m_timestamp);
return boost::posix_time::to_simple_string(time); return boost::posix_time::to_simple_string(time);
} }

View file

@ -11,27 +11,30 @@
namespace namespace
{ {
using ArticlesVec = std::vector<Article*>; using ArticlesVec = std::vector<Article *>;
} }
class Sale : public Entity class Sale : public EntityUuid
{ {
public: public:
void addArticle(Article* articlePtr); Sale() = default;
void setTimestamp(const std::string& timestamp); Sale(const Sale &) = delete;
virtual ~Sale() = default;
void addArticle(Article *articlePtr);
void setTimestamp(const std::string &timestamp);
ArticlesVec& getArticles(); ArticlesVec &getArticles();
std::string getTimestamp() const; std::string getTimestamp() const;
std::string getTimestampFormatted() const; std::string getTimestampFormatted() const;
int sumInCents(); int sumInCents();
std::string sumAsString(); std::string sumAsString();
void removeArticle(const Article* articlePtr); void removeArticle(const Article *articlePtr);
private: private:
std::string timestamp_{ std::string m_timestamp{
boost::posix_time::to_iso_extended_string(boost::posix_time::second_clock::local_time())}; boost::posix_time::to_iso_extended_string(boost::posix_time::second_clock::local_time())};
mutable ArticlesVec articles_{}; mutable ArticlesVec m_articles{};
}; };
#endif #endif

View file

@ -5,50 +5,49 @@
#include <numeric> #include <numeric>
#include <sstream> #include <sstream>
Seller::Seller(const std::string& firstName, const std::string& lastName, int sellerNo, Seller::Seller(const std::string &firstName, const std::string &lastName, int sellerNo,
int numArticlesOffered) int numArticlesOffered)
: Entity() : EntityInt(sellerNo)
{ {
firstName_ = firstName; m_firstName = firstName;
lastName_ = lastName; m_lastName = lastName;
sellerNo_ = sellerNo; m_numArticlesOffered = numArticlesOffered;
numArticlesOffered_ = numArticlesOffered;
} }
void Seller::setSellerNo(int seller_no) { sellerNo_ = seller_no; } void Seller::setSellerNo(int seller_no) { setId(seller_no); }
void Seller::setFirstName(const std::string& firstName) { firstName_ = firstName; } void Seller::setFirstName(const std::string &firstName) { m_firstName = firstName; }
void Seller::setLastName(const std::string& lastName) { lastName_ = lastName; } void Seller::setLastName(const std::string &lastName) { m_lastName = lastName; }
void Seller::setNumArticlesOffered(int number) { numArticlesOffered_ = number; } void Seller::setNumArticlesOffered(int number) { m_numArticlesOffered = number; }
void Seller::addArticle(std::unique_ptr<Article> article) void Seller::addArticle(std::unique_ptr<Article> article)
{ {
article->setSeller(this); article->setSeller(this);
articles_.push_back(std::move(article)); m_articles.push_back(std::move(article));
} }
std::string Seller::getFirstName() const { return firstName_; } std::string Seller::getFirstName() const { return m_firstName; }
std::string Seller::getLastName() const { return lastName_; } std::string Seller::getLastName() const { return m_lastName; }
int Seller::getSellerNo() const { return sellerNo_; } int Seller::getSellerNo() const { return getId(); }
std::string Seller::getSellerNoAsString() const std::string Seller::getSellerNoAsString() const
{ {
std::stringstream selNoStr; std::stringstream selNoStr;
selNoStr << std::setfill('0') << std::setw(3) << sellerNo_; selNoStr << std::setfill('0') << std::setw(3) << m_id;
return selNoStr.str(); return selNoStr.str();
; ;
} }
std::vector<Article*> Seller::getArticles(bool onlySold) const std::vector<Article *> Seller::getArticles(bool onlySold) const
{ {
std::vector<Article*> articles; std::vector<Article *> articles;
for (const auto& article : articles_) { for (const auto &article : m_articles) {
if (onlySold && article->isSold()) { if (onlySold && article->isSold()) {
articles.push_back(article.get()); articles.push_back(article.get());
} else if (!onlySold) { } else if (!onlySold) {
@ -58,54 +57,54 @@ std::vector<Article*> Seller::getArticles(bool onlySold) const
return articles; return articles;
} }
Article* Seller::getArticleByUuid(const std::string& uuidString) Article *Seller::getArticleByUuid(const std::string &uuidString)
{ {
auto iter = std::find_if(articles_.begin(), articles_.end(), [&uuidString](const auto& art) { auto iter = std::find_if(m_articles.begin(), m_articles.end(), [&uuidString](const auto &art) {
return art->getUuidAsString() == uuidString; return art->getUuidAsString() == uuidString;
}); });
if (iter == articles_.end()) if (iter == m_articles.end())
return nullptr; return nullptr;
return (*iter).get(); return (*iter).get();
} }
int Seller::numArticlesSold() const { return static_cast<int>(getArticles(true).size()); } int Seller::numArticlesSold() const { return static_cast<int>(getArticles(true).size()); }
int Seller::numArticlesOffered() const { return numArticlesOffered_; } int Seller::numArticlesOffered() const { return m_numArticlesOffered; }
int Seller::getMaxArticleNo() const int Seller::getMaxArticleNo() const
{ {
auto iter = std::max_element( auto iter = std::max_element(
articles_.begin(), articles_.end(), m_articles.begin(), m_articles.end(),
[](const auto& a, const auto& b) -> bool { return a->getArticleNo() < b->getArticleNo(); }); [](const auto &a, const auto &b) -> bool { return a->getArticleNo() < b->getArticleNo(); });
if (iter == articles_.end()) if (iter == m_articles.end())
return 0; return 0;
return (*iter)->getArticleNo(); return (*iter)->getArticleNo();
} }
void Seller::cleanupArticles() void Seller::cleanupArticles()
{ {
articles_.erase(std::remove_if(articles_.begin(), articles_.end(), m_articles.erase(std::remove_if(m_articles.begin(), m_articles.end(),
[](const auto& article) { [](const auto &article) {
return article->getState() == Article::State::DELETE; return article->getState() == Article::State::DELETE;
}), }),
articles_.end()); m_articles.end());
for (auto& article : articles_) { for (auto &article : m_articles) {
article->setState(Article::State::OK); article->setState(Article::State::OK);
} }
} }
int Seller::sumInCents() int Seller::sumInCents()
{ {
int sum = std::accumulate(articles_.begin(), articles_.end(), 0, int sum = std::accumulate(m_articles.begin(), m_articles.end(), 0,
[](int a, const auto& b) { return a + b->getPrice(); }); [](int a, const auto &b) { return a + b->getPrice(); });
return sum; return sum;
} }
std::string Seller::sumAsString() { return formatCentAsEuroString(sumInCents()); } std::string Seller::sumAsString() { return formatCentAsEuroString(sumInCents()); }
bool operator<(const Seller& li, const Seller& re) { return li.sellerNo_ < re.sellerNo_; } bool operator<(const Seller &li, const Seller &re) { return li.m_id < re.m_id; }
bool operator<(const std::unique_ptr<Seller>& li, const std::unique_ptr<Seller>& re) bool operator<(const std::unique_ptr<Seller> &li, const std::unique_ptr<Seller> &re)
{ {
return li->sellerNo_ < re->sellerNo_; return li->m_id < re->m_id;
} }

View file

@ -2,7 +2,7 @@
#define SELLER_H #define SELLER_H
#include "article.h" #include "article.h"
#include "entity.h" #include "entityint.h"
#include <memory> #include <memory>
#include <string> #include <string>
@ -10,17 +10,18 @@
// class Article; // class Article;
class Seller : public Entity class Seller : public EntityInt
{ {
public: public:
Seller() = default; Seller() = default;
// virtual ~Seller() = default; Seller(const Seller &) = delete;
Seller(const std::string& firstName, const std::string& lastName, int sellerNo = 0, virtual ~Seller() = default;
Seller(const std::string &firstName, const std::string &lastName, int sellerNo = 0,
int numArticlesOffered = 0); int numArticlesOffered = 0);
void setSellerNo(int sellerNo); void setSellerNo(int sellerNo);
void setFirstName(const std::string& firstName); void setFirstName(const std::string &firstName);
void setLastName(const std::string& lastName); void setLastName(const std::string &lastName);
void setNumArticlesOffered(int number); void setNumArticlesOffered(int number);
void addArticle(std::unique_ptr<Article> article); void addArticle(std::unique_ptr<Article> article);
void cleanupArticles(); void cleanupArticles();
@ -31,22 +32,20 @@ class Seller : public Entity
std::string getSellerNoAsString() const; std::string getSellerNoAsString() const;
int numArticlesOffered() const; int numArticlesOffered() const;
int numArticlesSold() const; int numArticlesSold() const;
// int numArticlesTotal() const; std::vector<Article *> getArticles(bool onlySold = true) const;
std::vector<Article*> getArticles(bool onlySold = true) const; Article *getArticleByUuid(const std::string &uuidString);
Article* getArticleByUuid(const std::string& uuidString);
int getMaxArticleNo() const; int getMaxArticleNo() const;
int sumInCents(); int sumInCents();
std::string sumAsString(); std::string sumAsString();
friend bool operator<(const Seller& li, const Seller& re); friend bool operator<(const Seller &li, const Seller &re);
friend bool operator<(const std::unique_ptr<Seller>& li, const std::unique_ptr<Seller>& re); friend bool operator<(const std::unique_ptr<Seller> &li, const std::unique_ptr<Seller> &re);
private: private:
int sellerNo_{-1}; int m_numArticlesOffered{};
int numArticlesOffered_{}; std::string m_firstName{};
std::string firstName_{}; std::string m_lastName{};
std::string lastName_{}; std::vector<std::unique_ptr<Article>> m_articles{};
std::vector<std::unique_ptr<Article>> articles_{};
}; };
#endif #endif

View file

@ -1,46 +1,70 @@
#include "utils.h" #include "utils.h"
#include <algorithm>
#include <clocale>
#include <format>
#include <iomanip> #include <iomanip>
#include <numeric> #include <numeric>
#include <iostream>
//using namespace fmt;
std::string formatCentAsEuroString(const int cent, int width) std::string formatCentAsEuroString(const int cent, int width)
{ {
std::stringstream currStream; /*std::stringstream currStream;
try { try {
std::locale myLocale("de_DE.utf8"); std::locale myLocale("de_DE.utf8");
currStream.imbue(myLocale); currStream.imbue(myLocale);
std::cout << ">>> " << fmt::format(myLocale, "{:6.2Lf}", 1.12345) << '\n';
currStream << std::right << std::setw(width) << std::showbase currStream << std::right << std::setw(width) << std::showbase
<< std::put_money(cent, false); << std::put_money(cent, false);
} catch (std::runtime_error& err) { } catch (std::runtime_error &err) {
currStream << std::fixed << std::setw(width >= 4 ? width - 4 : width) currStream << std::fixed << std::setw(width >= 4 ? width - 4 : width)
<< std::setprecision(2) << cent / 100.0L << ""; << std::setprecision(2) << cent / 100.0L << "";
} }
return currStream.str(); return currStream.str();*/
#if defined(_WIN64) || defined(_WIN32)
std::locale myLocale;
#else
std::locale myLocale{"de_DE.utf8"};
#endif
return std::format(myLocale, "{:{}.2Lf} €", cent / 100.0L, width);
} }
std::optional<PrinterDevice> convertToPosPrinterDevice(const std::string& device, std::string &ltrim(std::string &str, const std::string &chars)
const std::string& endpoint)
{ {
if (device.empty()) { str.erase(0, str.find_first_not_of(chars));
return std::nullopt; return str;
} }
PrinterDevice printerDevice; std::string &rtrim(std::string &str, const std::string &chars)
std::string delimiter = ":"; {
try { str.erase(str.find_last_not_of(chars) + 1);
printerDevice.idVendor = std::stoi(device.substr(0, device.find(delimiter)), 0, 16); return str;
printerDevice.idProduct = std::stoi(device.substr(device.find(delimiter) + 1), 0, 16); }
if (endpoint.empty()) {
printerDevice.endpoint = 0x03; std::string &trim(std::string &str, const std::string &chars)
} else { {
printerDevice.endpoint = std::stoi(endpoint, 0, 16); return ltrim(rtrim(str, chars), chars);
} }
} catch (std::exception& ex) { bool case_insensitive_match(std::string s1, std::string s2)
throw ex; {
} // convert s1 and s2 into lower case strings
transform(s1.begin(), s1.end(), s1.begin(), ::tolower);
return printerDevice; transform(s2.begin(), s2.end(), s2.begin(), ::tolower);
if (s1.compare(s2) == 0)
return true; // The strings are same
return false; // not matched
}
bool isNumber(const std::string &str)
{
return !str.empty() && std::find_if(str.begin(), str.end(), [](unsigned char c) {
return !std::isdigit(c);
}) == str.end();
} }

View file

@ -1,14 +1,15 @@
#ifndef UTILS_H #ifndef CORE_UTILS_H
#define UTILS_H #define CORE_UTILS_H
#include "posprinter.h"
#include <locale> #include <locale>
#include <optional> #include <optional>
#include <string> #include <string>
std::string formatCentAsEuroString(const int cent, int width = 10); std::string formatCentAsEuroString(const int cent, int width = 6);
std::optional<PrinterDevice> convertToPosPrinterDevice(const std::string& vendor, std::string &ltrim(std::string &str, const std::string &chars = "\t\n\v\f\r ");
const std::string& endpoint); std::string &rtrim(std::string &str, const std::string &chars = "\t\n\v\f\r ");
std::string &trim(std::string &str, const std::string &chars = "\t\n\v\f\r ");
bool case_insensitive_match(std::string s1, std::string s2);
bool isNumber(const std::string &str);
#endif #endif

View file

@ -8,8 +8,14 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON) set(CMAKE_AUTORCC ON)
# Find the QtWidgets library # Find the QtWidgets library
find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt6 COMPONENTS Widgets Network PrintSupport CONFIG REQUIRED)
find_package(Qt5PrintSupport CONFIG REQUIRED) #find_package(Qt5Widgets CONFIG REQUIRED)
#find_package(Qt5PrintSupport CONFIG REQUIRED)
# For SingleApplication:
#find_package(Qt5Network CONFIG REQUIRED)
set(QAPPLICATION_CLASS QApplication)
add_compile_definitions(QAPPLICATION_CLASS=${QAPPLICATION_CLASS})
set(GUI_SOURCES set(GUI_SOURCES
kima2.cpp kima2.cpp
@ -28,11 +34,16 @@ set(GUI_SOURCES
settingsdialog.cpp settingsdialog.cpp
settingsdialog.ui settingsdialog.ui
../../kima2.qrc ../../kima2.qrc
${PROJECT_SOURCE_DIR}/subprojects/singleapplication/singleapplication.git/singleapplication.cpp
${PROJECT_SOURCE_DIR}/subprojects/singleapplication/singleapplication.git/singleapplication_p.cpp
) )
add_executable(kima2 ${GUI_SOURCES} kima2.rc) add_executable(kima2 ${GUI_SOURCES} kima2.rc)
target_include_directories(kima2 PRIVATE ${PROJECT_BINARY_DIR}) target_include_directories(kima2 PRIVATE ${PROJECT_BINARY_DIR})
target_link_libraries(kima2 core printer Qt5::Widgets Qt5::PrintSupport stdc++fs) target_include_directories(kima2 PRIVATE ${PROJECT_SOURCE_DIR}/subprojects/singleapplication/singleapplication.git)
# target_link_libraries(kima2 core printer Qt5::Widgets Qt5::PrintSupport Qt5::Network stdc++fs)
target_link_libraries(kima2 core printer Qt::Core Qt::PrintSupport Qt::Network)
if(WIN32) if(WIN32)
set_target_properties(kima2 PROPERTIES LINK_FLAGS "-mwindows") set_target_properties(kima2 PROPERTIES LINK_FLAGS "-mwindows")
endif(WIN32) endif(WIN32)

View file

@ -1,17 +1,17 @@
#include "basketmodel.h" #include "basketmodel.h"
#include <QFont> #include <QFont>
#include <QSettings>
#include <QFontDatabase> #include <QFontDatabase>
#include <QSettings>
BasketModel::BasketModel(Marketplace* market, QObject* parent) BasketModel::BasketModel(Marketplace* market, QObject* parent)
: QAbstractTableModel(parent), marketplace_(market) : QAbstractTableModel(parent), m_marketplace(market)
{ {
} }
int BasketModel::rowCount([[maybe_unused]] const QModelIndex& parent) const int BasketModel::rowCount([[maybe_unused]] const QModelIndex& parent) const
{ {
return static_cast<int>(marketplace_->basketSize()); return static_cast<int>(m_marketplace->basketSize());
} }
int BasketModel::columnCount([[maybe_unused]] const QModelIndex& parent) const { return 4; } int BasketModel::columnCount([[maybe_unused]] const QModelIndex& parent) const { return 4; }
@ -22,7 +22,7 @@ QVariant BasketModel::data(const QModelIndex& index, int role) const
QFont myFont; QFont myFont;
QFont myFixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); QFont myFixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
if (myFixedFont.fixedPitch() == false) { if (myFixedFont.fixedPitch() == false) {
myFixedFont.setFamily("monospace"); myFixedFont.setFamily("monospace");
} }
@ -46,10 +46,10 @@ QVariant BasketModel::data(const QModelIndex& index, int role) const
if (role != Qt::DisplayRole) if (role != Qt::DisplayRole)
return QVariant(); return QVariant();
if (marketplace_->basketSize() == 0) if (m_marketplace->basketSize() == 0)
return QVariant(); return QVariant();
Article* article = marketplace_->getBasket().at(index.row()).get(); Article* article = m_marketplace->getBasket().at(index.row()).get();
switch (index.column()) { switch (index.column()) {
case 0: case 0:
@ -92,45 +92,45 @@ QVariant BasketModel::headerData(int section, Qt::Orientation orientation, int r
void BasketModel::addArticle(Seller* seller, int price, const std::string& desc) void BasketModel::addArticle(Seller* seller, int price, const std::string& desc)
{ {
emit beginInsertRows(QModelIndex(), marketplace_->getBasket().size(), emit beginInsertRows(QModelIndex(), m_marketplace->getBasket().size(),
marketplace_->getBasket().size()); m_marketplace->getBasket().size());
auto article = std::make_unique<Article>(price); auto article = std::make_unique<Article>(price);
article->createUuid(); article->createUuid();
article->setDescription(desc); article->setDescription(desc);
article->setArticleNo(marketplace_->getNextArticleNo()); article->setArticleNo(m_marketplace->getNextArticleNo());
article->setSourceNo(QSettings().value("global/cashPointNo").toInt()); article->setSourceNo(QSettings().value("global/cashPointNo").toInt());
article->setSeller(seller); article->setSeller(seller);
marketplace_->addArticleToBasket(std::move(article)); m_marketplace->addArticleToBasket(std::move(article));
emit endInsertRows(); emit endInsertRows();
} }
void BasketModel::finishSale() void BasketModel::finishSale()
{ {
emit beginRemoveRows(QModelIndex(), 0, marketplace_->getBasket().size() - 1); emit beginRemoveRows(QModelIndex(), 0, m_marketplace->getBasket().size() - 1);
auto sale = std::make_unique<Sale>(); auto sale = std::make_unique<Sale>();
sale->createUuid(); sale->createUuid();
sale->setSourceNo(QSettings().value("global/cashPointNo").toInt()); sale->setSourceNo(QSettings().value("global/cashPointNo").toInt());
marketplace_->finishCurrentSale(std::move(sale)); m_marketplace->finishCurrentSale(std::move(sale));
emit endRemoveRows(); emit endRemoveRows();
emit basketDataChanged(); emit basketDataChanged();
} }
void BasketModel::cancelSale() void BasketModel::cancelSale()
{ {
emit beginRemoveRows(QModelIndex(), 0, marketplace_->getBasket().size() - 1); emit beginRemoveRows(QModelIndex(), 0, m_marketplace->getBasket().size() - 1);
marketplace_->getBasket().clear(); m_marketplace->getBasket().clear();
emit endRemoveRows(); emit endRemoveRows();
} }
bool BasketModel::removeRows(int row, int count, const QModelIndex& parent) bool BasketModel::removeRows(int row, int count, const QModelIndex& parent)
{ {
auto article = marketplace_->getBasket().at(row).get(); auto article = m_marketplace->getBasket().at(row).get();
emit beginRemoveRows(parent, row, row + count - 1); emit beginRemoveRows(parent, row, row + count - 1);
marketplace_->getBasket().erase( m_marketplace->getBasket().erase(
std::remove_if(marketplace_->getBasket().begin(), marketplace_->getBasket().end(), std::remove_if(m_marketplace->getBasket().begin(), m_marketplace->getBasket().end(),
[&article](const auto& a) { return a->getUuid() == article->getUuid(); }), [&article](const auto& a) { return a->getUuid() == article->getUuid(); }),
marketplace_->getBasket().end()); m_marketplace->getBasket().end());
emit endRemoveRows(); emit endRemoveRows();
return true; return true;
} }

View file

@ -1,13 +1,13 @@
#ifndef BASKET_MODEL_H #ifndef BASKET_MODEL_H
#define BASKET_MODEL_H #define BASKET_MODEL_H
#include <marketplace.h> #include <core/marketplace.h>
#include <QAbstractTableModel> #include <QAbstractTableModel>
class BasketModel : public QAbstractTableModel class BasketModel : public QAbstractTableModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit BasketModel(Marketplace* market, QObject* parent = nullptr); explicit BasketModel(Marketplace* market, QObject* parent = nullptr);
@ -15,19 +15,16 @@ class BasketModel : public QAbstractTableModel
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
//virtual Qt::ItemFlags flags(const QModelIndex& index) const override;
//virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override;
//virtual bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
void addArticle(Seller* seller, int price, const std::string& desc); void addArticle(Seller* seller, int price, const std::string& desc);
void finishSale(); void finishSale();
void cancelSale(); void cancelSale();
virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override;
signals: signals:
void basketDataChanged(); void basketDataChanged();
private: private:
Marketplace* marketplace_; Marketplace* m_marketplace;
}; };
#endif #endif

View file

@ -6,26 +6,27 @@
#include <QLibraryInfo> #include <QLibraryInfo>
#include <QMessageBox> #include <QMessageBox>
#include <QSettings> #include <QSettings>
#include <QSharedMemory>
#include <QStyleFactory>
#include <QTranslator> #include <QTranslator>
int main(int argc, char* argv[]) #include <singleapplication.h>
#include <stdexcept>
int main(int argc, char *argv[])
{ {
QApplication kimaApp{argc, argv}; SingleApplication kimaApp(argc, argv, false, SingleApplication::Mode::User | SingleApplication::ExcludeAppPath | SingleApplication::ExcludeAppVersion);
// QCoreApplication::setOrganizationName("RustySoft"); // QCoreApplication::setOrganizationName("RustySoft");
QCoreApplication::setOrganizationDomain("rustysoft.de"); QCoreApplication::setOrganizationDomain("rustysoft.de");
QCoreApplication::setApplicationName("kima2"); QCoreApplication::setApplicationName("kima2");
QTranslator qTranslator; QTranslator qtTranslator;
QLocale german(QLocale::German);
#ifdef __linux__ if (qtTranslator.load(QLocale::system(), u"qtbase"_qs, u"_"_qs,
qTranslator.load("qt_" + german.name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); QLibraryInfo::path(QLibraryInfo::TranslationsPath))) {
#endif kimaApp.installTranslator(&qtTranslator);
#ifdef _WIN32 }
qTranslator.load("qt_" + german.name(),
QApplication::applicationDirPath() + QDir::separator() + "translations");
#endif
kimaApp.installTranslator(&qTranslator);
QSettings settings{}; QSettings settings{};
while (!settings.contains("global/cashPointNo")) { while (!settings.contains("global/cashPointNo")) {

View file

@ -1,22 +1,23 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "basketmodel.h" #include "basketmodel.h"
#include "config.h"
#include "jsonutil.h"
#include "pricedialog.h" #include "pricedialog.h"
#include "reportdialog.h" #include "reportdialog.h"
#include "salemodel.h" #include "salemodel.h"
#include "sellerdialog.h" #include "sellerdialog.h"
#include "settingsdialog.h" #include "settingsdialog.h"
#include <config.h>
#include <utils.h> #include <core/csvreader.h>
#include <core/jsonutil.h>
#include <excelreader.h> #include <core/utils.h>
#include <posprinter.h> #include <printer/posprinter.h>
#include <printer/utils.h>
#include <exception> #include <exception>
#include <filesystem> #include <filesystem>
#include <regex> #include <regex>
#include <string>
#include <QFileDialog> #include <QFileDialog>
#include <QMessageBox> #include <QMessageBox>
@ -30,23 +31,31 @@ constexpr int STATUSBAR_TIMEOUT = 5000;
MainWindow::MainWindow() MainWindow::MainWindow()
{ {
ui_.setupUi(this); m_ui.setupUi(this);
marketplace_ = std::make_unique<Marketplace>(); m_marketplace = std::make_unique<Marketplace>();
marketplace_->loadFromDb(); Database::InitResult res = m_marketplace->loadFromDb();
if (res == Database::InitResult::OUTDATED_REPLACED) {
QMessageBox(QMessageBox::Icon::Information, "Datenbankinformation",
"Es wurde eine <b>veraltete</b> Datenbankdatei erkannt.<br />Diese wurde "
"umbenannt und eine <b>neue</b> Datei wurde erstellt.")
.exec();
}
statusBar()->showMessage("Gespeicherte Daten wurden geladen.", STATUSBAR_TIMEOUT); statusBar()->showMessage("Gespeicherte Daten wurden geladen.", STATUSBAR_TIMEOUT);
BasketModel* model = new BasketModel(getMarketplace(), ui_.basketView); BasketModel *model = new BasketModel(getMarketplace(), m_ui.basketView);
ui_.basketView->setModel(model); m_ui.basketView->setModel(model);
ui_.basketView->setColumnHidden(0, true); // hide the uuid m_ui.basketView->setColumnHidden(0, true); // hide the uuid
setWindowTitle("KIMA2 - Kasse Nr. " + QSettings().value("global/cashPointNo").toString()); setWindowTitle("KIMA2 - Kasse Nr. " + QSettings().value("global/cashPointNo").toString());
m_ui.salesView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
setSaleModel(); setSaleModel();
connect(ui_.actionQuit, &QAction::triggered, qApp, QApplication::quit); connect(m_ui.actionQuit, &QAction::triggered, qApp, QApplication::closeAllWindows,
connect(ui_.newAction, &QAction::triggered, this, [=]() { Qt::QueuedConnection);
if (marketplace_->getSellers().size() == 0 && marketplace_->getSales().size() == 0) { connect(m_ui.newAction, &QAction::triggered, this, [this]() {
if (m_marketplace->getSellers().size() == 0 && m_marketplace->getSales().size() == 0) {
return; return;
} }
auto dlgResult = auto dlgResult =
@ -58,46 +67,45 @@ MainWindow::MainWindow()
if (dlgResult == QMessageBox::No) if (dlgResult == QMessageBox::No)
return; return;
delete ui_.salesView->model(); delete m_ui.salesView->model();
dynamic_cast<BasketModel*>(ui_.basketView->model())->cancelSale(); dynamic_cast<BasketModel *>(m_ui.basketView->model())->cancelSale();
marketplace_->clear(); m_marketplace->clear();
setSaleModel(); setSaleModel();
updateStatLabel(); updateStatLabel();
}); });
ui_.sellerNoEdit->installEventFilter(this); m_ui.sellerNoEdit->installEventFilter(this);
ui_.givenSpinBox->installEventFilter(this); m_ui.givenSpinBox->installEventFilter(this);
connect(ui_.actionEditSeller, &QAction::triggered, this, connect(m_ui.actionEditSeller, &QAction::triggered, this,
&MainWindow::onActionEditSellerTriggered); &MainWindow::onActionEditSellerTriggered);
connect(ui_.paidButton, &QPushButton::clicked, this, &MainWindow::onPaidButtonTriggered); connect(m_ui.paidButton, &QPushButton::clicked, this, &MainWindow::onPaidButtonTriggered);
connect(ui_.givenSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, connect(m_ui.givenSpinBox, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this,
&MainWindow::onGivenSpinBoxValueChanged); &MainWindow::onGivenSpinBoxValueChanged);
connect(ui_.basketView->selectionModel(), &QItemSelectionModel::selectionChanged, this, connect(m_ui.basketView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
&MainWindow::onBasketViewSelectionChanged); &MainWindow::onBasketViewSelectionChanged);
connect(ui_.cancelArticleButton, &QPushButton::clicked, this, connect(m_ui.cancelArticleButton, &QPushButton::clicked, this,
&MainWindow::onCancelArticleButtonClicked); &MainWindow::onCancelArticleButtonClicked);
connect(ui_.cancelSaleButton, &QPushButton::clicked, this, connect(m_ui.cancelSaleButton, &QPushButton::clicked, this,
&MainWindow::onCancelSaleButtonClicked); &MainWindow::onCancelSaleButtonClicked);
connect(ui_.printSaleReceiptButton, &QPushButton::clicked, this, connect(m_ui.printSaleReceiptButton, &QPushButton::clicked, this,
&MainWindow::onPrintSaleReceiptButtonClicked); &MainWindow::onPrintSaleReceiptButtonClicked);
connect(ui_.cancelAllArticlesButton, &QPushButton::clicked, this, connect(m_ui.cancelAllArticlesButton, &QPushButton::clicked, this,
&MainWindow::onCancelAllArticlesButtonClicked); &MainWindow::onCancelAllArticlesButtonClicked);
connect(ui_.aboutQtAction, &QAction::triggered, this, &MainWindow::onAboutQt); connect(m_ui.aboutQtAction, &QAction::triggered, this, &MainWindow::onAboutQt);
connect(ui_.aboutAction, &QAction::triggered, this, &MainWindow::onAbout); connect(m_ui.aboutAction, &QAction::triggered, this, &MainWindow::onAbout);
connect(ui_.openManualAction, &QAction::triggered, this, []() { connect(m_ui.openManualAction, &QAction::triggered, this, []() {
auto locations = QStandardPaths::standardLocations(QStandardPaths::DataLocation); auto locations = QStandardPaths::standardLocations(QStandardPaths::AppDataLocation);
for (auto location : locations) { for (auto location : locations) {
if (QFile::exists(location + QString("/Benutzerhandbuch.pdf"))) { if (QFile::exists(location + QString("/Benutzerhandbuch.pdf"))) {
QDesktopServices::openUrl( QDesktopServices::openUrl(
QUrl(QString("file:///") + location + QString("/Benutzerhandbuch.pdf"), QUrl::fromLocalFile(location + QString("/Benutzerhandbuch.pdf")));
QUrl::TolerantMode));
} }
} }
}); });
connect(ui_.licenseAction, &QAction::triggered, this, [=]() { connect(m_ui.licenseAction, &QAction::triggered, this, [this]() {
QString licenseText( QString licenseText(
"Copyright © 2018 Martin Brodbeck\n\n" "Copyright © 2018-2024 Martin Brodbeck\n\n"
"Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der " "Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der "
"zugehörigen Dokumentationen (die \"Software\") erhält, die Erlaubnis erteilt, " "zugehörigen Dokumentationen (die \"Software\") erhält, die Erlaubnis erteilt, "
"sie uneingeschränkt zu nutzen, inklusive und ohne Ausnahme mit dem Recht, " "sie uneingeschränkt zu nutzen, inklusive und ohne Ausnahme mit dem Recht, "
@ -116,32 +124,30 @@ MainWindow::MainWindow()
"SOFTWARE ODER SONSTIGER VERWENDUNG DER SOFTWARE ENTSTANDEN."); "SOFTWARE ODER SONSTIGER VERWENDUNG DER SOFTWARE ENTSTANDEN.");
QMessageBox::information(this, "Lizenzinformation", licenseText); QMessageBox::information(this, "Lizenzinformation", licenseText);
}); });
connect(ui_.reportAction, &QAction::triggered, this, [=]() { ReportDialog(this).exec(); }); connect(m_ui.reportAction, &QAction::triggered, this, [this]() { ReportDialog(this).exec(); });
connect(ui_.configAction, &QAction::triggered, this, [=]() { connect(m_ui.configAction, &QAction::triggered, this, [this]() {
int result = SettingsDialog(this).exec(); int result = SettingsDialog(this).exec();
if (result == QDialog::Accepted) { if (result == QDialog::Accepted) {
delete ui_.salesView->model(); delete m_ui.salesView->model();
marketplace_->loadFromDb(); m_marketplace->loadFromDb();
setSaleModel(); setSaleModel();
} }
this->setWindowTitle("KIMA2 - Kasse Nr. " + this->setWindowTitle("KIMA2 - Kasse Nr. " +
QSettings().value("global/cashPointNo").toString()); QSettings().value("global/cashPointNo").toString());
}); });
connect(ui_.importSellerExcelAction, &QAction::triggered, this, connect(m_ui.importSellerAction, &QAction::triggered, this,
&MainWindow::onImportSellerExcelActionTriggered); &MainWindow::onImportSellerActionTriggered);
connect(ui_.importSellerJsonAction, &QAction::triggered, this, connect(m_ui.exportSalesJsonAction, &QAction::triggered, this,
&MainWindow::onImportSellerJsonActionTriggered);
connect(ui_.exportSellerJsonAction, &QAction::triggered, this,
&MainWindow::onExportSellerJsonActionTriggered);
connect(ui_.exportSalesJsonAction, &QAction::triggered, this,
&MainWindow::onExportSalesJsonActionTriggered); &MainWindow::onExportSalesJsonActionTriggered);
connect(ui_.importSalesJsonAction, &QAction::triggered, this, connect(m_ui.importSalesJsonAction, &QAction::triggered, this,
&MainWindow::onImportSalesJsonActionTriggered); &MainWindow::onImportSalesJsonActionTriggered);
readGeometry(); readGeometry();
setWindowIcon(QIcon(":/misc/kima2.ico")); setWindowIcon(QIcon(":/misc/kima2.ico"));
updateStatLabel(); updateStatLabel();
m_ui.lastPriceLabel1->setText(formatCentAsEuroString(0).c_str());
m_ui.lastPriceLabel2->setText(formatCentAsEuroString(0).c_str());
} }
void MainWindow::onActionEditSellerTriggered() void MainWindow::onActionEditSellerTriggered()
@ -149,11 +155,11 @@ void MainWindow::onActionEditSellerTriggered()
auto dialog = std::make_unique<SellerDialog>(this); auto dialog = std::make_unique<SellerDialog>(this);
int retCode = dialog->exec(); int retCode = dialog->exec();
delete ui_.salesView->model(); delete m_ui.salesView->model();
if (retCode == QDialog::Accepted) { if (retCode == QDialog::Accepted) {
marketplace_->sortSellers(); m_marketplace->sortSellers();
marketplace_->storeToDb(); m_marketplace->storeToDb();
statusBar()->showMessage("Änderungen an den Verkäufer-Stammdaten gespeichert.", statusBar()->showMessage("Änderungen an den Verkäufer-Stammdaten gespeichert.",
STATUSBAR_TIMEOUT); STATUSBAR_TIMEOUT);
} else { } else {
@ -167,43 +173,44 @@ void MainWindow::onActionEditSellerTriggered()
void MainWindow::setSaleModel() void MainWindow::setSaleModel()
{ {
ui_.salesView->setModel(new SaleModel(getMarketplace(), ui_.salesView)); m_ui.salesView->setModel(new SaleModel(getMarketplace(), m_ui.salesView));
ui_.salesView->setColumnHidden(2, true); m_ui.salesView->setColumnHidden(2, true);
ui_.salesView->resizeColumnToContents(0); m_ui.salesView->resizeColumnToContents(0);
m_ui.salesView->resizeColumnToContents(1);
ui_.printSaleReceiptButton->setEnabled(false); m_ui.printSaleReceiptButton->setEnabled(false);
ui_.cancelSaleButton->setEnabled(false); m_ui.cancelSaleButton->setEnabled(false);
connect(static_cast<BasketModel*>(ui_.basketView->model()), &BasketModel::basketDataChanged, connect(static_cast<BasketModel *>(m_ui.basketView->model()), &BasketModel::basketDataChanged,
static_cast<SaleModel*>(ui_.salesView->model()), &SaleModel::onBasketDataChanged); static_cast<SaleModel *>(m_ui.salesView->model()), &SaleModel::onBasketDataChanged);
connect(ui_.salesView->selectionModel(), &QItemSelectionModel::selectionChanged, this, connect(m_ui.salesView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
&MainWindow::onSalesViewSelectionChanged); &MainWindow::onSalesViewSelectionChanged);
} }
void MainWindow::onPaidButtonTriggered() void MainWindow::onPaidButtonTriggered()
{ {
if (marketplace_->basketSize() > 0) { if (m_marketplace->basketSize() > 0) {
QString lastPrice{marketplace_->getBasketSumAsString().c_str()}; QString lastPrice{m_marketplace->getBasketSumAsString().c_str()};
dynamic_cast<BasketModel*>(ui_.basketView->model())->finishSale(); dynamic_cast<BasketModel *>(m_ui.basketView->model())->finishSale();
ui_.salesView->resizeColumnToContents(0); m_ui.salesView->resizeColumnToContents(0);
ui_.lastPriceLabel1->setText(lastPrice); m_ui.lastPriceLabel1->setText(lastPrice);
ui_.lastPriceLabel2->setText(lastPrice); m_ui.lastPriceLabel2->setText(lastPrice);
ui_.basketSumLabel->setText(formatCentAsEuroString(0).c_str()); m_ui.basketSumLabel->setText(formatCentAsEuroString(0).c_str());
ui_.drawbackLabel->setText(formatCentAsEuroString(0).c_str()); m_ui.drawbackLabel->setText(formatCentAsEuroString(0).c_str());
ui_.drawbackContainerWidget->setEnabled(false); m_ui.drawbackContainerWidget->setEnabled(false);
ui_.sellerNoEdit->setFocus(); m_ui.sellerNoEdit->setFocus();
statusBar()->showMessage("Verkaufsvorgang erfolgreich durchgeführt.", STATUSBAR_TIMEOUT); statusBar()->showMessage("Verkaufsvorgang erfolgreich durchgeführt.", STATUSBAR_TIMEOUT);
updateStatLabel(); updateStatLabel();
} }
} }
bool MainWindow::eventFilter(QObject* target, QEvent* event) bool MainWindow::eventFilter(QObject *target, QEvent *event)
{ {
if (target == ui_.sellerNoEdit) { if (target == m_ui.sellerNoEdit) {
if (event->type() == QEvent::KeyPress) { if (event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key::Key_Enter || keyEvent->key() == Qt::Key::Key_Return) { if (keyEvent->key() == Qt::Key::Key_Enter || keyEvent->key() == Qt::Key::Key_Return) {
if (keyEvent->modifiers() == Qt::ControlModifier) { if (keyEvent->modifiers() & Qt::ControlModifier) {
checkSellerNo(true); checkSellerNo(true);
} else { } else {
checkSellerNo(false); checkSellerNo(false);
@ -212,18 +219,18 @@ bool MainWindow::eventFilter(QObject* target, QEvent* event)
return true; return true;
} }
} }
} else if (target == ui_.givenSpinBox) { } else if (target == m_ui.givenSpinBox) {
if (event->type() == QEvent::KeyPress) { if (event->type() == QEvent::KeyPress) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event); QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key::Key_Enter || keyEvent->key() == Qt::Key::Key_Return) { if (keyEvent->key() == Qt::Key::Key_Enter || keyEvent->key() == Qt::Key::Key_Return) {
if (keyEvent->modifiers() == Qt::ControlModifier) { if (keyEvent->modifiers() & Qt::ControlModifier) {
onPaidButtonTriggered(); onPaidButtonTriggered();
return true; return true;
} }
} else if (keyEvent->key() == Qt::Key::Key_Escape) { } else if (keyEvent->key() == Qt::Key::Key_Escape) {
ui_.drawbackLabel->setText(formatCentAsEuroString(0).c_str()); m_ui.drawbackLabel->setText(formatCentAsEuroString(0).c_str());
ui_.drawbackContainerWidget->setEnabled(false); m_ui.drawbackContainerWidget->setEnabled(false);
ui_.sellerNoEdit->setFocus(); m_ui.sellerNoEdit->setFocus();
} }
} }
} }
@ -234,15 +241,15 @@ void MainWindow::checkSellerNo(bool ctrlPressed)
{ {
using std::regex, std::regex_match, std::smatch; using std::regex, std::regex_match, std::smatch;
auto inputText = ui_.sellerNoEdit->text().toStdString(); auto inputText = m_ui.sellerNoEdit->text().toStdString();
if (inputText.empty()) { if (inputText.empty()) {
if (ctrlPressed == false) { if (ctrlPressed == false) {
onPaidButtonTriggered(); onPaidButtonTriggered();
} else if (marketplace_->getBasket().size() > 0) { } else if (m_marketplace->getBasket().size() > 0) {
ui_.drawbackContainerWidget->setEnabled(true); m_ui.drawbackContainerWidget->setEnabled(true);
ui_.givenSpinBox->setFocus(); m_ui.givenSpinBox->setFocus();
ui_.givenSpinBox->selectAll(); m_ui.givenSpinBox->selectAll();
} }
return; return;
} }
@ -251,13 +258,13 @@ void MainWindow::checkSellerNo(bool ctrlPressed)
smatch result; smatch result;
if (!regex_match(inputText, result, pattern)) { if (!regex_match(inputText, result, pattern)) {
ui_.sellerNoEdit->clear(); m_ui.sellerNoEdit->clear();
return; return;
} }
int sellerNo = std::stoi(result[0]); int sellerNo = std::stoi(result[0]);
auto seller = marketplace_->findSellerWithSellerNo(sellerNo); auto seller = m_marketplace->findSellerWithSellerNo(sellerNo);
if (seller) { if (seller) {
PriceDialog priceDialog(this); PriceDialog priceDialog(this);
if (sellerNo == 0) { if (sellerNo == 0) {
@ -267,52 +274,52 @@ void MainWindow::checkSellerNo(bool ctrlPressed)
if (dialogResult == QDialog::Accepted) { if (dialogResult == QDialog::Accepted) {
int price = priceDialog.getPrice(); int price = priceDialog.getPrice();
std::string desc = priceDialog.getDescription(); std::string desc = priceDialog.getDescription();
dynamic_cast<BasketModel*>(ui_.basketView->model())->addArticle(seller, price, desc); dynamic_cast<BasketModel *>(m_ui.basketView->model())->addArticle(seller, price, desc);
ui_.basketView->resizeColumnToContents(1); m_ui.basketView->resizeColumnToContents(1);
ui_.basketSumLabel->setText(marketplace_->getBasketSumAsString().c_str()); m_ui.basketSumLabel->setText(m_marketplace->getBasketSumAsString().c_str());
} }
} }
ui_.sellerNoEdit->clear(); m_ui.sellerNoEdit->clear();
} }
void MainWindow::onGivenSpinBoxValueChanged(double value) void MainWindow::onGivenSpinBoxValueChanged(double value)
{ {
int givenInCent = std::round(value * 100); int givenInCent = std::round(value * 100);
int basketSumInCent = marketplace_->getBasketSumInCent(); int basketSumInCent = m_marketplace->getBasketSumInCent();
int drawback = givenInCent - basketSumInCent; int drawback = givenInCent - basketSumInCent;
ui_.drawbackLabel->setText(formatCentAsEuroString(drawback).c_str()); m_ui.drawbackLabel->setText(formatCentAsEuroString(drawback).c_str());
} }
void MainWindow::onBasketViewSelectionChanged(const QItemSelection& selected, void MainWindow::onBasketViewSelectionChanged(const QItemSelection &selected,
[[maybe_unused]] const QItemSelection& deselected) [[maybe_unused]] const QItemSelection &deselected)
{ {
if (selected.size() > 0) { if (selected.size() > 0) {
ui_.cancelArticleButton->setEnabled(true); m_ui.cancelArticleButton->setEnabled(true);
} else { } else {
ui_.cancelArticleButton->setEnabled(false); m_ui.cancelArticleButton->setEnabled(false);
} }
} }
void MainWindow::onSalesViewSelectionChanged(const QItemSelection& selected, void MainWindow::onSalesViewSelectionChanged(const QItemSelection &selected,
[[maybe_unused]] const QItemSelection& deselected) [[maybe_unused]] const QItemSelection &deselected)
{ {
if (selected.size() > 0) { if (selected.size() > 0) {
ui_.cancelSaleButton->setEnabled(true); m_ui.cancelSaleButton->setEnabled(true);
if (!selected.indexes()[0].parent().isValid()) if (!selected.indexes()[0].parent().isValid())
ui_.printSaleReceiptButton->setEnabled(true); m_ui.printSaleReceiptButton->setEnabled(true);
else else
ui_.printSaleReceiptButton->setEnabled(false); m_ui.printSaleReceiptButton->setEnabled(false);
} else { } else {
ui_.cancelSaleButton->setEnabled(false); m_ui.cancelSaleButton->setEnabled(false);
ui_.printSaleReceiptButton->setEnabled(false); m_ui.printSaleReceiptButton->setEnabled(false);
} }
} }
void MainWindow::onCancelArticleButtonClicked([[maybe_unused]] bool checked) void MainWindow::onCancelArticleButtonClicked([[maybe_unused]] bool checked)
{ {
auto selModel = ui_.basketView->selectionModel(); auto selModel = m_ui.basketView->selectionModel();
if (selModel->hasSelection() == false) if (selModel->hasSelection() == false)
return; return;
@ -329,22 +336,23 @@ void MainWindow::onCancelArticleButtonClicked([[maybe_unused]] bool checked)
// Deleting the rows, beginning with the last one! // Deleting the rows, beginning with the last one!
for (auto iter = indexes.constEnd() - 1; iter >= indexes.constBegin(); --iter) { for (auto iter = indexes.constEnd() - 1; iter >= indexes.constBegin(); --iter) {
ui_.basketView->model()->removeRow(iter->row()); m_ui.basketView->model()->removeRow(iter->row());
} }
ui_.basketSumLabel->setText(marketplace_->getBasketSumAsString().c_str()); // Update basket sum m_ui.basketSumLabel->setText(
ui_.sellerNoEdit->setFocus(); m_marketplace->getBasketSumAsString().c_str()); // Update basket sum
m_ui.sellerNoEdit->setFocus();
} }
void MainWindow::onCancelSaleButtonClicked([[maybe_unused]] bool checked) void MainWindow::onCancelSaleButtonClicked([[maybe_unused]] bool checked)
{ {
auto selModel = ui_.salesView->selectionModel(); auto selModel = m_ui.salesView->selectionModel();
if (selModel->hasSelection() == false) if (selModel->hasSelection() == false)
return; return;
auto dlgResult = auto dlgResult =
QMessageBox(QMessageBox::Icon::Warning, "Sind Sie sicher?", QMessageBox(QMessageBox::Icon::Warning, "Sind Sie sicher?",
"Möchten Sie wirklich stornieren?", "Möchten Sie wirklich aus abgeschlossenen Verkäufen stornieren?",
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, this) QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, this)
.exec(); .exec();
if (dlgResult == QMessageBox::No) if (dlgResult == QMessageBox::No)
@ -355,16 +363,25 @@ void MainWindow::onCancelSaleButtonClicked([[maybe_unused]] bool checked)
// Deleting the rows, beginning with the last one! // Deleting the rows, beginning with the last one!
for (auto iter = indexes.constEnd() - 1; iter >= indexes.constBegin(); --iter) { for (auto iter = indexes.constEnd() - 1; iter >= indexes.constBegin(); --iter) {
ui_.salesView->model()->removeRow(iter->row(), iter->parent()); m_ui.salesView->model()->removeRow(iter->row(), iter->parent());
} }
ui_.salesView->collapseAll(); m_ui.salesView->collapseAll();
QString lastPriceValue(formatCentAsEuroString(0).c_str());
if (m_ui.salesView->model()->rowCount() > 0) {
lastPriceValue =
m_ui.salesView->model()->data(m_ui.salesView->model()->index(0, 1)).toString();
}
m_ui.lastPriceLabel1->setText(lastPriceValue);
m_ui.lastPriceLabel2->setText(lastPriceValue);
updateStatLabel(); updateStatLabel();
} }
void MainWindow::onPrintSaleReceiptButtonClicked([[maybe_unused]] bool checked) void MainWindow::onPrintSaleReceiptButtonClicked([[maybe_unused]] bool checked)
{ {
auto selModel = ui_.salesView->selectionModel(); auto selModel = m_ui.salesView->selectionModel();
if (selModel->hasSelection() == false) if (selModel->hasSelection() == false)
return; return;
@ -373,7 +390,7 @@ void MainWindow::onPrintSaleReceiptButtonClicked([[maybe_unused]] bool checked)
QString posPrinterEndpoint = settings.value("global/posPrinterEndpoint", "").toString(); QString posPrinterEndpoint = settings.value("global/posPrinterEndpoint", "").toString();
auto indexes = selModel->selectedRows(); auto indexes = selModel->selectedRows();
auto& sale = marketplace_->getSales().at(indexes[0].row()); auto &sale = m_marketplace->getSales().at(indexes[0].row());
auto printerDevice = auto printerDevice =
convertToPosPrinterDevice(posPrinterDevice.toStdString(), posPrinterEndpoint.toStdString()); convertToPosPrinterDevice(posPrinterDevice.toStdString(), posPrinterEndpoint.toStdString());
@ -387,12 +404,13 @@ void MainWindow::onPrintSaleReceiptButtonClicked([[maybe_unused]] bool checked)
} }
if (printer->isValid()) if (printer->isValid())
printer->printSaleReceipt(sale.get(), settings.value("global/commune", "Dettingen").toString().toStdString()); printer->printSaleReceipt(
sale.get(), settings.value("global/commune", "Dettingen").toString().toStdString());
} }
void MainWindow::onCancelAllArticlesButtonClicked([[maybe_unused]] bool checked) void MainWindow::onCancelAllArticlesButtonClicked([[maybe_unused]] bool checked)
{ {
if (ui_.basketView->model()->rowCount() == 0) if (m_ui.basketView->model()->rowCount() == 0)
return; return;
auto dlgResult = auto dlgResult =
@ -403,10 +421,11 @@ void MainWindow::onCancelAllArticlesButtonClicked([[maybe_unused]] bool checked)
if (dlgResult == QMessageBox::No) if (dlgResult == QMessageBox::No)
return; return;
dynamic_cast<BasketModel*>(ui_.basketView->model())->cancelSale(); dynamic_cast<BasketModel *>(m_ui.basketView->model())->cancelSale();
ui_.basketSumLabel->setText(marketplace_->getBasketSumAsString().c_str()); // Update basket sum m_ui.basketSumLabel->setText(
ui_.sellerNoEdit->setFocus(); m_marketplace->getBasketSumAsString().c_str()); // Update basket sum
m_ui.sellerNoEdit->setFocus();
} }
void MainWindow::onAboutQt() { QMessageBox::aboutQt(this); } void MainWindow::onAboutQt() { QMessageBox::aboutQt(this); }
@ -422,9 +441,9 @@ void MainWindow::onAbout()
">info@rustysoft.de</a>&gt;</p>"); ">info@rustysoft.de</a>&gt;</p>");
} }
void MainWindow::onImportSellerExcelActionTriggered() void MainWindow::onImportSellerActionTriggered()
{ {
if (!marketplace_->getSales().empty()) { if (!m_marketplace->getSales().empty()) {
QMessageBox(QMessageBox::Icon::Information, "Import nicht möglich", QMessageBox(QMessageBox::Icon::Information, "Import nicht möglich",
"Der Import ist nicht möglich, da schon Verkäufe getätigt wurden.", "Der Import ist nicht möglich, da schon Verkäufe getätigt wurden.",
QMessageBox::StandardButton::Ok, this) QMessageBox::StandardButton::Ok, this)
@ -432,59 +451,31 @@ void MainWindow::onImportSellerExcelActionTriggered()
return; return;
} }
QMessageBox( auto filename =
QMessageBox::Icon::Information, "Bitte beachten", QFileDialog::getOpenFileName(this, "Verkäufer importieren", QString(),
"<b>Achtung:</b> Importieren Sie die Verkäuferdaten nur auf <b>einer (!)</b> " "Alle unterstützte Dateien (*.csv);;CSV Dateien (*.csv)");
"KIMA2-Installation.<br /> "
"Verteilen Sie die Daten auf die anderen Installationen unbedingt über eine JSON-Datei!", if (filename.isEmpty())
QMessageBox::StandardButton::Ok, this) return;
#if defined(_WIN64) || defined(_WIN32)
fs::path filePath(filename.toStdWString());
#else
fs::path filePath(filename.toStdString());
#endif
std::size_t numImported{};
numImported = CsvReader::readSellersFromFile(filePath, m_marketplace.get());
updateStatLabel();
using namespace std::string_literals;
std::ostringstream msg;
msg << "Aus der CSV-Datei wurden <b>"s << std::to_string(numImported)
<< "</b> Verkäufer importiert.";
QMessageBox(QMessageBox::Icon::Information, "Verkäufer erfolgreich importiert",
msg.str().c_str(), QMessageBox::StandardButton::Ok, this)
.exec(); .exec();
auto filename = QFileDialog::getOpenFileName(this, "Verkäufer importieren", QString(),
"Excel Dateien (*.xlsx *.xls)");
if (filename.isEmpty())
return;
fs::path filePath(filename.toStdWString());
ExcelReader::readSellersFromFile(filePath, marketplace_.get());
updateStatLabel();
}
void MainWindow::onImportSellerJsonActionTriggered()
{
if (!marketplace_->getSales().empty()) {
QMessageBox(QMessageBox::Icon::Information, "Import nicht möglich",
"Der Import ist nicht möglich, da schon Verkäufe getätigt wurden.",
QMessageBox::StandardButton::Ok, this)
.exec();
return;
}
auto filename = QFileDialog::getOpenFileName(this, "Verkäufer importieren", QString(),
"JSON Dateien (*.json)");
if (filename.isEmpty())
return;
fs::path filePath(filename.toStdWString());
JsonUtil::importSellers(filePath, marketplace_.get());
updateStatLabel();
}
void MainWindow::onExportSellerJsonActionTriggered()
{
auto filename = QFileDialog::getSaveFileName(
this, "Verkäufer exportieren", QString("kima2_verkaeufer.json"), "JSON Dateien (*.json)");
if (filename.isEmpty())
return;
fs::path filePath(filename.toStdWString());
JsonUtil::exportSellers(filePath, marketplace_.get());
} }
void MainWindow::onExportSalesJsonActionTriggered() void MainWindow::onExportSalesJsonActionTriggered()
@ -499,9 +490,13 @@ void MainWindow::onExportSalesJsonActionTriggered()
if (filename.isEmpty()) if (filename.isEmpty())
return; return;
#if defined(_WIN64) || defined(_WIN32)
fs::path filePath(filename.toStdWString()); fs::path filePath(filename.toStdWString());
#else
fs::path filePath(filename.toStdString());
#endif
JsonUtil::exportSales(filePath, marketplace_.get(), JsonUtil::exportSales(filePath, m_marketplace.get(),
settings.value("global/cashPointNo").toInt()); settings.value("global/cashPointNo").toInt());
} }
@ -509,23 +504,30 @@ void MainWindow::onImportSalesJsonActionTriggered()
{ {
QSettings settings; QSettings settings;
auto filename = QFileDialog::getOpenFileName(this, "Umsätze/Transaktionen importieren", auto filenames = QFileDialog::getOpenFileNames(this, "Umsätze/Transaktionen importieren",
QString(), "JSON Dateien (*.json)"); QString(), "JSON Dateien (*.json)");
if (filename.isEmpty()) if (filenames.isEmpty())
return; return;
fs::path filePath(filename.toStdWString()); for(auto filename: filenames) {
#if defined(_WIN64) || defined(_WIN32)
fs::path filePath(filename.toStdWString());
#else
fs::path filePath(filename.toStdString());
#endif
delete ui_.salesView->model(); delete m_ui.salesView->model();
try { try {
JsonUtil::importSales(filePath, marketplace_.get(), JsonUtil::importSales(filePath, m_marketplace.get(),
settings.value("global/cashPointNo").toInt()); settings.value("global/cashPointNo").toInt());
} catch (std::runtime_error& err) { } catch (std::runtime_error &err) {
QMessageBox(QMessageBox::Icon::Warning, "Import nicht möglich", err.what(), QMessageBox::Ok, QMessageBox(QMessageBox::Icon::Warning, "Import nicht möglich", err.what(), QMessageBox::Ok,
this) this)
.exec(); .exec();
}
} }
setSaleModel(); setSaleModel();
updateStatLabel(); updateStatLabel();
} }
@ -550,7 +552,7 @@ void MainWindow::readGeometry()
settings.endGroup(); settings.endGroup();
} }
void MainWindow::closeEvent(QCloseEvent* event) void MainWindow::closeEvent(QCloseEvent *event)
{ {
writeGeometry(); writeGeometry();
event->accept(); event->accept();
@ -560,9 +562,9 @@ void MainWindow::updateStatLabel()
{ {
std::string statistics("<b>KIMA2 - Version "); std::string statistics("<b>KIMA2 - Version ");
statistics += PROJECT_VERSION; statistics += PROJECT_VERSION;
statistics += "</b><br />Verkäufer: " + std::to_string(marketplace_->getSellers().size() - 1); statistics += "</b><br />Verkäufer: " + std::to_string(m_marketplace->getSellers().size() - 1);
statistics += "<br />Kunden: " + std::to_string(marketplace_->getSales().size()); statistics += "<br />Kunden: " + std::to_string(m_marketplace->getSales().size());
statistics += "<br />Umsatz: " + marketplace_->getOverallSumAsString(); statistics += "<br />Umsatz: " + m_marketplace->getOverallSumAsString();
ui_.statLabel->setText(statistics.c_str()); m_ui.statLabel->setText(statistics.c_str());
} }

View file

@ -3,7 +3,7 @@
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include <marketplace.h> #include <core/marketplace.h>
#include <QMainWindow> #include <QMainWindow>
#include <QtGui/QCloseEvent> #include <QtGui/QCloseEvent>
@ -16,7 +16,7 @@ class MainWindow : public QMainWindow
public: public:
MainWindow(); MainWindow();
Marketplace* getMarketplace() { return marketplace_.get(); } Marketplace* getMarketplace() { return m_marketplace.get(); }
private slots: private slots:
void onBasketViewSelectionChanged(const QItemSelection& selected, void onBasketViewSelectionChanged(const QItemSelection& selected,
@ -29,7 +29,7 @@ class MainWindow : public QMainWindow
void onCancelAllArticlesButtonClicked(bool checked); void onCancelAllArticlesButtonClicked(bool checked);
void onAboutQt(); void onAboutQt();
void onAbout(); void onAbout();
virtual bool eventFilter(QObject *target, QEvent *event) override; virtual bool eventFilter(QObject* target, QEvent* event) override;
protected: protected:
virtual void closeEvent(QCloseEvent* event) override; virtual void closeEvent(QCloseEvent* event) override;
@ -39,9 +39,7 @@ class MainWindow : public QMainWindow
void checkSellerNo(bool ctrlPressed = false); void checkSellerNo(bool ctrlPressed = false);
void onPaidButtonTriggered(); void onPaidButtonTriggered();
void onGivenSpinBoxValueChanged(double value); void onGivenSpinBoxValueChanged(double value);
void onImportSellerExcelActionTriggered(); void onImportSellerActionTriggered();
void onImportSellerJsonActionTriggered();
void onExportSellerJsonActionTriggered();
void onExportSalesJsonActionTriggered(); void onExportSalesJsonActionTriggered();
void onImportSalesJsonActionTriggered(); void onImportSalesJsonActionTriggered();
void setSaleModel(); void setSaleModel();
@ -49,8 +47,8 @@ class MainWindow : public QMainWindow
void readGeometry(); void readGeometry();
void updateStatLabel(); void updateStatLabel();
Ui::MainWindow ui_; Ui::MainWindow m_ui;
std::unique_ptr<Marketplace> marketplace_; std::unique_ptr<Marketplace> m_marketplace;
}; };
#endif #endif

View file

@ -112,7 +112,11 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QDoubleSpinBox" name="givenSpinBox"/> <widget class="QDoubleSpinBox" name="givenSpinBox">
<property name="maximum">
<double>999.990000000000009</double>
</property>
</widget>
</item> </item>
<item> <item>
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
@ -436,16 +440,8 @@ drucken</string>
<property name="title"> <property name="title">
<string>&amp;Verkäufer</string> <string>&amp;Verkäufer</string>
</property> </property>
<widget class="QMenu" name="importSellerMenu">
<property name="title">
<string>Importieren</string>
</property>
<addaction name="importSellerExcelAction"/>
<addaction name="importSellerJsonAction"/>
</widget>
<addaction name="actionEditSeller"/> <addaction name="actionEditSeller"/>
<addaction name="importSellerMenu"/> <addaction name="importSellerAction"/>
<addaction name="exportSellerJsonAction"/>
</widget> </widget>
<widget class="QMenu" name="menuHilfe"> <widget class="QMenu" name="menuHilfe">
<property name="title"> <property name="title">
@ -509,9 +505,9 @@ drucken</string>
<string>Exportieren für andere Kasse (JSON)</string> <string>Exportieren für andere Kasse (JSON)</string>
</property> </property>
</action> </action>
<action name="importSellerExcelAction"> <action name="importSellerActionX">
<property name="text"> <property name="text">
<string>Aus Excel-Datei (initial)</string> <string>Aus CSV-Datei (initial)</string>
</property> </property>
</action> </action>
<action name="importSellerJsonAction"> <action name="importSellerJsonAction">
@ -544,6 +540,11 @@ drucken</string>
<string>Lizenz</string> <string>Lizenz</string>
</property> </property>
</action> </action>
<action name="importSellerAction">
<property name="text">
<string>Importieren (aus CSV-Datei)</string>
</property>
</action>
</widget> </widget>
<resources/> <resources/>
<connections/> <connections/>

23
src/gui/meson.build Normal file
View file

@ -0,0 +1,23 @@
qt5 = import('qt5')
qt5_dep = dependency('qt5', modules: ['Core', 'Gui', 'PrintSupport', 'Network'])
thread_dep = dependency('threads')
src = ['kima2.cpp', 'mainwindow.cpp', 'sellerdialog.cpp', 'sellermodel.cpp',
'pricedialog.cpp', 'basketmodel.cpp', 'salemodel.cpp', 'reportdialog.cpp',
'reportmodel.cpp', 'settingsdialog.cpp']
ui = ['mainwindow.ui', 'sellerdialog.ui', 'pricedialog.ui', 'reportdialog.ui', 'settingsdialog.ui']
processed = qt5.preprocess(moc_headers : ['basketmodel.h', 'mainwindow.h', 'pricedialog.h',
'reportdialog.h', 'sellerdialog.h', 'settingsdialog.h',
'sellermodel.h', 'salemodel.h'],
ui_files : ui,
qresources : '../../kima2.qrc',
dependencies: qt5_dep)
kima2 = executable('kima2', sources : [src, processed],
dependencies : [qt5_dep, singleapp_dep, core_dep, printer_dep, thread_dep],
include_directories : [configuration_inc],
install : true)

View file

@ -6,34 +6,31 @@
PriceDialog::PriceDialog(QWidget* parent, bool forceDesc, Qt::WindowFlags f) : QDialog(parent, f) PriceDialog::PriceDialog(QWidget* parent, bool forceDesc, Qt::WindowFlags f) : QDialog(parent, f)
{ {
forceDesc_ = forceDesc; m_forceDesc = forceDesc;
ui_.setupUi(this); m_ui.setupUi(this);
ui_.priceSpinBox->setFocus(); m_ui.priceSpinBox->setFocus();
} }
int PriceDialog::getPrice() const { return static_cast<int>(ui_.priceSpinBox->value() * 100); } int PriceDialog::getPrice() const { return static_cast<int>(m_ui.priceSpinBox->value() * 100); }
std::string PriceDialog::getDescription() const { return ui_.descEdit->text().toStdString(); } std::string PriceDialog::getDescription() const { return m_ui.descEdit->text().toStdString(); }
void PriceDialog::accept() void PriceDialog::accept()
{ {
if (static_cast<int>(std::round(ui_.priceSpinBox->value() * 100.0L)) % 50 != 0) { if (static_cast<int>(std::round(m_ui.priceSpinBox->value() * 100.0L)) % 50 != 0) {
QMessageBox(QMessageBox::Icon::Warning, "Falsche Preiseingabe", QMessageBox(QMessageBox::Icon::Warning, "Falsche Preiseingabe",
"Es sind nur 0,50 Cent-Schritte erlaubt.", QMessageBox::StandardButton::Ok, "Es sind nur 0,50 Cent-Schritte erlaubt.", QMessageBox::StandardButton::Ok,
this) this)
.exec(); .exec();
} else if (forceDesc_ && ui_.descEdit->text().trimmed().isEmpty()) { } else if (m_forceDesc && m_ui.descEdit->text().trimmed().isEmpty()) {
QMessageBox(QMessageBox::Icon::Warning, "Artikelbeschreibung fehlt", QMessageBox(QMessageBox::Icon::Warning, "Artikelbeschreibung fehlt",
"Da Sie auf das Sonderkonto buchen ist eine Artikelbeschreibung erforderlich.", "Da Sie auf das Sonderkonto buchen ist eine Artikelbeschreibung erforderlich.",
QMessageBox::StandardButton::Ok, this) QMessageBox::StandardButton::Ok, this)
.exec(); .exec();
ui_.descEdit->setFocus(); m_ui.descEdit->setFocus();
} else { } else {
QDialog::accept(); QDialog::accept();
} }
} }
void PriceDialog::setForceDesc(bool force) void PriceDialog::setForceDesc(bool force) { m_forceDesc = force; }
{
forceDesc_ = force;
}

View file

@ -19,8 +19,8 @@ class PriceDialog : public QDialog
private: private:
void on_model_duplicateSellerNo(const QString& message); void on_model_duplicateSellerNo(const QString& message);
virtual void accept() override; virtual void accept() override;
Ui::PriceDialog ui_; Ui::PriceDialog m_ui;
bool forceDesc_; bool m_forceDesc;
}; };
#endif #endif

View file

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>201</width> <width>266</width>
<height>127</height> <height>128</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -18,6 +18,11 @@
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="text"> <property name="text">
<string>Preis</string> <string>Preis</string>
</property> </property>
@ -28,6 +33,11 @@
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QDoubleSpinBox" name="priceSpinBox"> <widget class="QDoubleSpinBox" name="priceSpinBox">
<property name="font">
<font>
<pointsize>16</pointsize>
</font>
</property>
<property name="minimum"> <property name="minimum">
<double>-999.990000000000009</double> <double>-999.990000000000009</double>
</property> </property>
@ -51,6 +61,18 @@
</item> </item>
<item row="1" column="1"> <item row="1" column="1">
<widget class="QLineEdit" name="descEdit"> <widget class="QLineEdit" name="descEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="placeholderText"> <property name="placeholderText">
<string>(optional)</string> <string>(optional)</string>
</property> </property>

View file

@ -2,8 +2,9 @@
#include "mainwindow.h" #include "mainwindow.h"
#include <posprinter.h> #include <core/utils.h>
#include <utils.h> #include <printer/posprinter.h>
#include <printer/utils.h>
#include <filesystem> #include <filesystem>
@ -16,22 +17,22 @@
namespace fs = std::filesystem; namespace fs = std::filesystem;
ReportDialog::ReportDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f) ReportDialog::ReportDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f)
{ {
ui_.setupUi(this); m_ui.setupUi(this);
market_ = dynamic_cast<MainWindow*>(parent)->getMarketplace(); m_market = dynamic_cast<MainWindow *>(parent)->getMarketplace();
model_ = std::make_unique<ReportModel>(market_, ui_.reportView); m_model = std::make_unique<ReportModel>(m_market, m_ui.reportView);
ui_.reportView->setModel(model_.get()); m_ui.reportView->setModel(m_model.get());
ui_.reportView->hideColumn(0); m_ui.reportView->hideColumn(0);
ui_.reportView->setRowHidden(0, true); // hide the special "Sonderkonto" user m_ui.reportView->setRowHidden(0, true); // hide the special "Sonderkonto" user
connect(ui_.exportCsvButton, &QPushButton::clicked, this, connect(m_ui.exportCsvButton, &QPushButton::clicked, this,
&ReportDialog::onExportCsvButtonClicked); &ReportDialog::onExportCsvButtonClicked);
connect(ui_.printReportButton, &QPushButton::clicked, this, connect(m_ui.printReportButton, &QPushButton::clicked, this,
&ReportDialog::onPrintReportButtonClicked); &ReportDialog::onPrintReportButtonClicked);
connect(ui_.printSellerReceiptButton, &QPushButton::clicked, this, connect(m_ui.printSellerReceiptButton, &QPushButton::clicked, this,
&ReportDialog::onPrintSellerReceiptButtonClicked); &ReportDialog::onPrintSellerReceiptButtonClicked);
connect(ui_.reportView->selectionModel(), &QItemSelectionModel::selectionChanged, this, connect(m_ui.reportView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
&ReportDialog::onReportViewSelectionChanged); &ReportDialog::onReportViewSelectionChanged);
} }
@ -47,9 +48,13 @@ void ReportDialog::onExportCsvButtonClicked()
if (filename.isEmpty()) if (filename.isEmpty())
return; return;
#if defined(_WIN64) || defined(_WIN32)
fs::path filePath(filename.toStdWString()); fs::path filePath(filename.toStdWString());
#else
fs::path filePath(filename.toStdString());
#endif
market_->exportReportToCSV(filePath, feeInPercent, maxFeeInEuro); m_market->exportReportToCSV(filePath, feeInPercent, maxFeeInEuro);
} }
void ReportDialog::onPrintReportButtonClicked() void ReportDialog::onPrintReportButtonClicked()
@ -70,8 +75,8 @@ void ReportDialog::onPrintReportButtonClicked()
QPainter painter; QPainter painter;
int height = printer.height(); int height = printer.height();
int width = printer.width(); int width = printer.width();
const double ENTRIES_PER_PAGE = 51; const double ENTRIES_PER_PAGE = 45;
const auto& sellers = market_->getSellers(); const auto &sellers = m_market->getSellers();
unsigned int numPages = std::ceil(sellers.size() / ENTRIES_PER_PAGE); unsigned int numPages = std::ceil(sellers.size() / ENTRIES_PER_PAGE);
painter.begin(&printer); painter.begin(&printer);
@ -102,7 +107,7 @@ void ReportDialog::onPrintReportButtonClicked()
for (unsigned int j = 0; for (unsigned int j = 0;
j < ENTRIES_PER_PAGE && (i - 1) * ENTRIES_PER_PAGE + j < sellers.size(); ++j) { j < ENTRIES_PER_PAGE && (i - 1) * ENTRIES_PER_PAGE + j < sellers.size(); ++j) {
int idx = (i - 1) * ENTRIES_PER_PAGE + j; int idx = (i - 1) * ENTRIES_PER_PAGE + j;
if (sellers.at(idx)->getUuidAsString() == "11111111-1111-1111-1111-111111111111") { if (sellers.at(idx)->getId() == 0) {
continue; continue;
} }
content += QString("%1 %2 %3 %4 %5 %6 %7\n") content += QString("%1 %2 %3 %4 %5 %6 %7\n")
@ -121,7 +126,7 @@ void ReportDialog::onPrintReportButtonClicked()
} }
// pieces booked on the special account "Sonderkonto" // pieces booked on the special account "Sonderkonto"
const auto specialSeller = market_->findSellerWithUuid("11111111-1111-1111-1111-111111111111"); const auto specialSeller = m_market->findSellerWithSellerNo(0);
if (specialSeller && specialSeller->numArticlesSold() > 0) { if (specialSeller && specialSeller->numArticlesSold() > 0) {
printer.newPage(); printer.newPage();
painter.setFont(QFont("Arial", 16, QFont::Bold)); painter.setFont(QFont("Arial", 16, QFont::Bold));
@ -131,7 +136,7 @@ void ReportDialog::onPrintReportButtonClicked()
QString content("Einzelteile ohne Nummer\n=======================\n\n"); QString content("Einzelteile ohne Nummer\n=======================\n\n");
unsigned int lines{0}; unsigned int lines{0};
unsigned int pages{1}; unsigned int pages{1};
for (const auto& article : specialSeller->getArticles(true)) { for (const auto &article : specialSeller->getArticles(true)) {
content += QString("- %1:").arg(article->getDescription().substr(0, 45).c_str(), -45); content += QString("- %1:").arg(article->getDescription().substr(0, 45).c_str(), -45);
content += QString("%1\n").arg(article->getPriceAsString().c_str(), 11); content += QString("%1\n").arg(article->getPriceAsString().c_str(), 11);
++lines; ++lines;
@ -158,8 +163,8 @@ void ReportDialog::onPrintReportButtonClicked()
"Auswertung Kindersachenmarkt"); "Auswertung Kindersachenmarkt");
painter.setFont(fixedFont); painter.setFont(fixedFont);
QString content("Gesamtstatistik\n===============\n\n"); QString content("Gesamtstatistik\n===============\n\n");
int numArticlesOffered = market_->getNumArticlesOffered(); int numArticlesOffered = m_market->getNumArticlesOffered();
int numArticlesSold = market_->getNumArticlesSold(); int numArticlesSold = m_market->getNumArticlesSold();
double percentArticlesSold = double percentArticlesSold =
(static_cast<double>(numArticlesSold) / static_cast<double>(numArticlesOffered)) * 100; (static_cast<double>(numArticlesSold) / static_cast<double>(numArticlesOffered)) * 100;
content += QString("Registrierte Verkäufer: %1\n").arg(sellers.size() - 1, 6); content += QString("Registrierte Verkäufer: %1\n").arg(sellers.size() - 1, 6);
@ -167,14 +172,14 @@ void ReportDialog::onPrintReportButtonClicked()
content += QString("Verkaufte Artikel: %1 (%L2 %)\n") content += QString("Verkaufte Artikel: %1 (%L2 %)\n")
.arg(numArticlesSold, 6) .arg(numArticlesSold, 6)
.arg(percentArticlesSold, 0, 'f', 2); .arg(percentArticlesSold, 0, 'f', 2);
content += QString("Anzahl Kunden: %1\n\n").arg(market_->getSales().size(), 6); content += QString("Anzahl Kunden: %1\n\n").arg(m_market->getSales().size(), 6);
content += QString("Gesamtumsatz: %1\n").arg(market_->getOverallSumAsString().c_str(), 10); content += QString("Gesamtumsatz: %1\n").arg(m_market->getOverallSumAsString().c_str(), 10);
content += content +=
QString("Ausgezahlt: %1\n") QString("Ausgezahlt: %1\n")
.arg(market_->getOverallPaymentAsString(feeInPercent, maxFeeInEuro * 100).c_str(), 10); .arg(m_market->getOverallPaymentAsString(feeInPercent, maxFeeInEuro * 100).c_str(), 10);
content += content +=
QString("Verbleibend: %1\n\n") QString("Verbleibend: %1\n\n")
.arg(market_->getOverallRevenueAsString(feeInPercent, maxFeeInEuro * 100).c_str(), 10); .arg(m_market->getOverallRevenueAsString(feeInPercent, maxFeeInEuro * 100).c_str(), 10);
content += QString("(Einbehaltener Prozentsatz: %1 %)\n").arg(feeInPercent, 3); content += QString("(Einbehaltener Prozentsatz: %1 %)\n").arg(feeInPercent, 3);
content += QString("(Maximal einbehaltener Betrag: %1 €)\n").arg(maxFeeInEuro, 3); content += QString("(Maximal einbehaltener Betrag: %1 €)\n").arg(maxFeeInEuro, 3);
@ -191,12 +196,12 @@ void ReportDialog::onPrintSellerReceiptButtonClicked()
QString posPrinterDevice = settings.value("global/posPrinterDevice", "").toString(); QString posPrinterDevice = settings.value("global/posPrinterDevice", "").toString();
QString posPrinterEndpoint = settings.value("global/posPrinterEndpoint", "").toString(); QString posPrinterEndpoint = settings.value("global/posPrinterEndpoint", "").toString();
auto selModel = ui_.reportView->selectionModel(); auto selModel = m_ui.reportView->selectionModel();
if (selModel->hasSelection() == false) if (selModel->hasSelection() == false)
return; return;
auto indexes = selModel->selectedRows(); auto indexes = selModel->selectedRows();
auto& seller = market_->getSellers().at(indexes[0].row()); auto &seller = m_market->getSellers().at(indexes[0].row());
auto printerDevice = auto printerDevice =
convertToPosPrinterDevice(posPrinterDevice.toStdString(), posPrinterEndpoint.toStdString()); convertToPosPrinterDevice(posPrinterDevice.toStdString(), posPrinterEndpoint.toStdString());
@ -215,12 +220,12 @@ void ReportDialog::onPrintSellerReceiptButtonClicked()
settings.value("global/commune", "Dettingen").toString().toStdString()); settings.value("global/commune", "Dettingen").toString().toStdString());
} }
void ReportDialog::onReportViewSelectionChanged(const QItemSelection& selected, void ReportDialog::onReportViewSelectionChanged(const QItemSelection &selected,
[[maybe_unused]] const QItemSelection& deselected) [[maybe_unused]] const QItemSelection &deselected)
{ {
if (selected.size() > 0) { if (selected.size() > 0) {
ui_.printSellerReceiptButton->setEnabled(true); m_ui.printSellerReceiptButton->setEnabled(true);
} else { } else {
ui_.printSellerReceiptButton->setEnabled(false); m_ui.printSellerReceiptButton->setEnabled(false);
} }
} }

View file

@ -5,7 +5,7 @@
#include "reportmodel.h" #include "reportmodel.h"
#include <marketplace.h> #include <core/marketplace.h>
#include <QDialog> #include <QDialog>
@ -24,9 +24,10 @@ class ReportDialog : public QDialog
void onReportViewSelectionChanged(const QItemSelection& selected, void onReportViewSelectionChanged(const QItemSelection& selected,
const QItemSelection& deselected); const QItemSelection& deselected);
private : Ui::ReportDialog ui_; private:
Marketplace* market_; Ui::ReportDialog m_ui;
std::unique_ptr<ReportModel> model_; Marketplace* m_market;
std::unique_ptr<ReportModel> m_model;
}; };
#endif #endif

View file

@ -5,16 +5,16 @@
#include <QSettings> #include <QSettings>
ReportModel::ReportModel(Marketplace* market, QObject* parent) ReportModel::ReportModel(Marketplace* market, QObject* parent)
: QAbstractTableModel(parent), market_(market) : QAbstractTableModel(parent), m_market(market)
{ {
QSettings settings; QSettings settings;
feeInPercent_ = settings.value("global/feeInPercent").toInt(); m_feeInPercent = settings.value("global/feeInPercent").toInt();
maxFeeInCent_ = settings.value("global/maxFeeInEuro").toInt() * 100; m_maxFeeInCent = settings.value("global/maxFeeInEuro").toInt() * 100;
} }
int ReportModel::rowCount([[maybe_unused]] const QModelIndex& parent) const int ReportModel::rowCount([[maybe_unused]] const QModelIndex& parent) const
{ {
return static_cast<int>(market_->getSellers().size()); return static_cast<int>(m_market->getSellers().size());
} }
int ReportModel::columnCount([[maybe_unused]] const QModelIndex& parent) const { return 7; } int ReportModel::columnCount([[maybe_unused]] const QModelIndex& parent) const { return 7; }
@ -52,14 +52,14 @@ QVariant ReportModel::data(const QModelIndex& index, int role) const
if (role != Qt::DisplayRole) if (role != Qt::DisplayRole)
return QVariant(); return QVariant();
if (market_->getSellers().size() == 0) if (m_market->getSellers().size() == 0)
return QVariant(); return QVariant();
Seller* seller = market_->getSellers().at(index.row()).get(); Seller* seller = m_market->getSellers().at(index.row()).get();
switch (index.column()) { switch (index.column()) {
case 0: case 0:
return seller->getUuidAsString().c_str(); return seller->getId();
case 1: case 1:
return seller->getSellerNo(); return seller->getSellerNo();
case 2: case 2:
@ -71,7 +71,7 @@ QVariant ReportModel::data(const QModelIndex& index, int role) const
case 5: case 5:
return seller->sumAsString().c_str(); return seller->sumAsString().c_str();
case 6: case 6:
return paymentAsString(seller->sumInCents(), feeInPercent_, maxFeeInCent_).c_str(); return paymentAsString(seller->sumInCents(), m_feeInPercent, m_maxFeeInCent).c_str();
default: default:
return "???"; return "???";
} }

View file

@ -1,7 +1,7 @@
#ifndef REPORT_MODEL_H #ifndef REPORT_MODEL_H
#define REPORT_MODEL_H #define REPORT_MODEL_H
#include <marketplace.h> #include <core/marketplace.h>
#include <QAbstractTableModel> #include <QAbstractTableModel>
@ -15,9 +15,9 @@ class ReportModel : public QAbstractTableModel
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
private: private:
Marketplace* market_; Marketplace* m_market;
int feeInPercent_{}; int m_feeInPercent{};
int maxFeeInCent_{}; int m_maxFeeInCent{};
}; };
#endif #endif

View file

@ -1,6 +1,6 @@
#include "salemodel.h" #include "salemodel.h"
#include <article.h> #include <core/article.h>
#include <algorithm> #include <algorithm>
@ -9,7 +9,7 @@
SaleModel::SaleModel(Marketplace* market, QObject* parent) : QAbstractItemModel(parent) SaleModel::SaleModel(Marketplace* market, QObject* parent) : QAbstractItemModel(parent)
{ {
marketplace_ = market; m_marketplace = market;
} }
QModelIndex SaleModel::index(int row, int column, const QModelIndex& parent) const QModelIndex SaleModel::index(int row, int column, const QModelIndex& parent) const
@ -18,7 +18,7 @@ QModelIndex SaleModel::index(int row, int column, const QModelIndex& parent) con
return QModelIndex(); return QModelIndex();
if (!parent.isValid()) { if (!parent.isValid()) {
Sale* sale = marketplace_->getSales().at(row).get(); Sale* sale = m_marketplace->getSales().at(row).get();
return createIndex(row, column, sale); return createIndex(row, column, sale);
} else if (!parent.parent().isValid()) { } else if (!parent.parent().isValid()) {
Sale* sale = static_cast<Sale*>(parent.internalPointer()); Sale* sale = static_cast<Sale*>(parent.internalPointer());
@ -44,15 +44,15 @@ QModelIndex SaleModel::parent(const QModelIndex& index) const
Sale* sale{}; Sale* sale{};
Article* article{}; Article* article{};
Entity* ent = static_cast<Entity*>(index.internalPointer()); EntityUuid* ent = static_cast<EntityUuid*>(index.internalPointer());
sale = dynamic_cast<Sale*>(ent); sale = dynamic_cast<Sale*>(ent);
if (sale) { if (sale) {
if (sale == rootItem.get()) if (sale == m_rootItem.get())
return QModelIndex(); return QModelIndex();
else { else {
return createIndex(-1, 0, rootItem.get()); return createIndex(-1, 0, m_rootItem.get());
} }
} else { } else {
article = dynamic_cast<Article*>(ent); article = dynamic_cast<Article*>(ent);
@ -80,7 +80,7 @@ QVariant SaleModel::data(const QModelIndex& index, int role) const
if (role == Qt::FontRole) { if (role == Qt::FontRole) {
QFont myFont; QFont myFont;
QFont myFixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); QFont myFixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont);
if (myFixedFont.fixedPitch() == false) { if (myFixedFont.fixedPitch() == false) {
myFixedFont.setFamily("monospace"); myFixedFont.setFamily("monospace");
} }
@ -113,7 +113,9 @@ QVariant SaleModel::data(const QModelIndex& index, int role) const
Article* article = static_cast<Article*>(index.internalPointer()); Article* article = static_cast<Article*>(index.internalPointer());
switch (index.column()) { switch (index.column()) {
case 0: case 0:
return (std::string("Verk. ") + article->getSeller()->getSellerNoAsString() + " (" + article->getCompleteArticleNo() + ")").c_str(); return (std::string("Verk. ") + article->getSeller()->getSellerNoAsString() + " (" +
article->getCompleteArticleNo() + ")")
.c_str();
case 1: case 1:
return article->getPriceAsString().c_str(); return article->getPriceAsString().c_str();
case 2: case 2:
@ -130,7 +132,7 @@ int SaleModel::rowCount(const QModelIndex& parent) const
return 0; return 0;
if (!parent.isValid()) { if (!parent.isValid()) {
return marketplace_->getSales().size(); return m_marketplace->getSales().size();
} else if (!parent.parent().isValid()) { } else if (!parent.parent().isValid()) {
Sale* sale = static_cast<Sale*>(parent.internalPointer()); Sale* sale = static_cast<Sale*>(parent.internalPointer());
return sale->getArticles().size(); return sale->getArticles().size();
@ -165,7 +167,7 @@ QVariant SaleModel::headerData(int section, Qt::Orientation orientation, int rol
void SaleModel::onBasketDataChanged() void SaleModel::onBasketDataChanged()
{ {
emit beginResetModel(); emit beginResetModel();
auto& sales = marketplace_->getSales(); auto& sales = m_marketplace->getSales();
std::sort(sales.begin(), sales.end(), [](const auto& lhs, const auto& rhs) { std::sort(sales.begin(), sales.end(), [](const auto& lhs, const auto& rhs) {
return lhs->getTimestamp() > rhs->getTimestamp(); return lhs->getTimestamp() > rhs->getTimestamp();
}); });
@ -177,11 +179,11 @@ bool SaleModel::removeRows(int row, int count, const QModelIndex& parent)
if (!parent.isValid()) { if (!parent.isValid()) {
// remove complete sale // remove complete sale
emit beginRemoveRows(parent, row, row + count - 1); emit beginRemoveRows(parent, row, row + count - 1);
auto& sale = marketplace_->getSales().at(row); auto& sale = m_marketplace->getSales().at(row);
sale->setState(Sale::State::DELETE); sale->setState(Sale::State::DELETE);
std::for_each(sale->getArticles().begin(), sale->getArticles().end(), std::for_each(sale->getArticles().begin(), sale->getArticles().end(),
[](auto& a) { a->setState(Article::State::DELETE); }); [](auto& a) { a->setState(Article::State::DELETE); });
marketplace_->storeToDb(); m_marketplace->storeToDb();
emit endRemoveRows(); emit endRemoveRows();
} else if (!parent.parent().isValid()) { } else if (!parent.parent().isValid()) {
@ -197,7 +199,7 @@ bool SaleModel::removeRows(int row, int count, const QModelIndex& parent)
sale->setState(Sale::State::DELETE); sale->setState(Sale::State::DELETE);
} }
emit beginRemoveRows(parent.parent(), 0, 0); emit beginRemoveRows(parent.parent(), 0, 0);
marketplace_->storeToDb(); m_marketplace->storeToDb();
emit endRemoveRows(); emit endRemoveRows();
} }

View file

@ -1,7 +1,7 @@
#ifndef SALEMODEL_H #ifndef SALEMODEL_H
#define SALEMODEL_H #define SALEMODEL_H
#include <marketplace.h> #include <core/marketplace.h>
#include <QAbstractItemModel> #include <QAbstractItemModel>
@ -12,7 +12,7 @@ class SaleModel : public QAbstractItemModel
public: public:
explicit SaleModel(Marketplace* market, QObject* parent = nullptr); explicit SaleModel(Marketplace* market, QObject* parent = nullptr);
virtual QModelIndex index(int row, int column, virtual QModelIndex index(int row, int column,
const QModelIndex& parent = QModelIndex()) const override; const QModelIndex& parent = QModelIndex()) const override;
virtual QModelIndex parent(const QModelIndex& index) const override; virtual QModelIndex parent(const QModelIndex& index) const override;
virtual QVariant data(const QModelIndex& index, int role) const override; virtual QVariant data(const QModelIndex& index, int role) const override;
virtual int rowCount(const QModelIndex& parent) const override; virtual int rowCount(const QModelIndex& parent) const override;
@ -24,8 +24,8 @@ class SaleModel : public QAbstractItemModel
void onBasketDataChanged(); void onBasketDataChanged();
private: private:
Marketplace* marketplace_; Marketplace* m_marketplace;
std::unique_ptr<Sale> rootItem{new Sale()}; std::unique_ptr<Sale> m_rootItem{new Sale()};
}; };
#endif #endif

View file

@ -7,25 +7,25 @@
SellerDialog::SellerDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f) SellerDialog::SellerDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f)
{ {
ui_.setupUi(this); m_ui.setupUi(this);
ui_.editButton->setVisible(false); m_ui.editButton->setVisible(false);
market_ = dynamic_cast<MainWindow*>(parent)->getMarketplace(); m_market = dynamic_cast<MainWindow*>(parent)->getMarketplace();
model_ = std::make_unique<SellerModel>(market_, ui_.tableView); m_model = std::make_unique<SellerModel>(m_market, m_ui.tableView);
ui_.tableView->setModel(model_.get()); m_ui.tableView->setModel(m_model.get());
ui_.tableView->setColumnHidden(0, true); // hide the uuid m_ui.tableView->setColumnHidden(0, true); // hide the uuid
ui_.tableView->setRowHidden(0, true); // hide the special "Sonderkonto" user m_ui.tableView->setRowHidden(0, true); // hide the special "Sonderkonto" user
connect(ui_.newButton, &QPushButton::clicked, this, &SellerDialog::on_newButton_clicked); connect(m_ui.newButton, &QPushButton::clicked, this, &SellerDialog::on_newButton_clicked);
connect(ui_.deleteButton, &QPushButton::clicked, this, &SellerDialog::on_deleteButton_clicked); connect(m_ui.deleteButton, &QPushButton::clicked, this, &SellerDialog::on_deleteButton_clicked);
connect(model_.get(), &SellerModel::duplicateSellerNo, this, connect(m_model.get(), &SellerModel::duplicateSellerNo, this,
&SellerDialog::on_model_duplicateSellerNo); &SellerDialog::on_model_duplicateSellerNo);
connect(ui_.tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this, connect(m_ui.tableView->selectionModel(), &QItemSelectionModel::selectionChanged, this,
&SellerDialog::onSellerViewSelectionChanged); &SellerDialog::onSellerViewSelectionChanged);
} }
void SellerDialog::on_newButton_clicked() void SellerDialog::on_newButton_clicked()
{ {
// Don't allow new seller if market has already started // Don't allow new seller if market has already started
if (market_->getSales().size() > 0) { if (m_market->getSales().size() > 0) {
QMessageBox(QMessageBox::Icon::Warning, "Hinweis", QMessageBox(QMessageBox::Icon::Warning, "Hinweis",
"Da die Verkaufsphase schon begonnen hat (Artikel wurden bereits verkauft) " "Da die Verkaufsphase schon begonnen hat (Artikel wurden bereits verkauft) "
"können Sie keine Verkäufer mehr hinzufügen.", "können Sie keine Verkäufer mehr hinzufügen.",
@ -34,22 +34,22 @@ void SellerDialog::on_newButton_clicked()
return; return;
} }
ui_.tableView->reset(); m_ui.tableView->reset();
ui_.tableView->model()->insertRows(ui_.tableView->model()->rowCount(), 1); m_ui.tableView->model()->insertRows(m_ui.tableView->model()->rowCount(), 1);
ui_.tableView->scrollToBottom(); m_ui.tableView->scrollToBottom();
ui_.tableView->selectRow(ui_.tableView->model()->rowCount() - 1); m_ui.tableView->selectRow(m_ui.tableView->model()->rowCount() - 1);
QModelIndex idx = ui_.tableView->model()->index(ui_.tableView->model()->rowCount() - 1, 2); QModelIndex idx = m_ui.tableView->model()->index(m_ui.tableView->model()->rowCount() - 1, 2);
ui_.tableView->setCurrentIndex(idx); m_ui.tableView->setCurrentIndex(idx);
ui_.tableView->edit(idx); m_ui.tableView->edit(idx);
} }
void SellerDialog::on_deleteButton_clicked() void SellerDialog::on_deleteButton_clicked()
{ {
auto selModel = ui_.tableView->selectionModel(); auto selModel = m_ui.tableView->selectionModel();
if (selModel->hasSelection() == false) if (selModel->hasSelection() == false)
return; return;
if (market_->getSales().size() > 0) { if (m_market->getSales().size() > 0) {
QMessageBox(QMessageBox::Icon::Warning, "Hinweis", QMessageBox(QMessageBox::Icon::Warning, "Hinweis",
"Da die Verkaufsphase schon begonnen hat (Artikel wurden bereits verkauft) " "Da die Verkaufsphase schon begonnen hat (Artikel wurden bereits verkauft) "
"können Sie keine Verkäufer mehr löschen.", "können Sie keine Verkäufer mehr löschen.",
@ -60,7 +60,8 @@ void SellerDialog::on_deleteButton_clicked()
auto dlgResult = auto dlgResult =
QMessageBox(QMessageBox::Icon::Warning, "Sind Sie sicher?", QMessageBox(QMessageBox::Icon::Warning, "Sind Sie sicher?",
"Löschen wirkt sich direkt auf die Datenbank aus. Möchten Sie fortfahren?", "Löschen wirkt sich <b>sofort</b> auf die Datenbank aus. Sie können den "
"Vorgang nicht rückgängig machen. Möchten Sie fortfahren?",
QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, this) QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, this)
.exec(); .exec();
if (dlgResult == QMessageBox::No) if (dlgResult == QMessageBox::No)
@ -71,7 +72,7 @@ void SellerDialog::on_deleteButton_clicked()
// Deleting the rows, beginning with the last one! // Deleting the rows, beginning with the last one!
for (auto iter = indexes.constEnd() - 1; iter >= indexes.constBegin(); --iter) { for (auto iter = indexes.constEnd() - 1; iter >= indexes.constBegin(); --iter) {
ui_.tableView->model()->removeRow(iter->row()); m_ui.tableView->model()->removeRow(iter->row());
} }
} }
@ -101,8 +102,8 @@ void SellerDialog::onSellerViewSelectionChanged(const QItemSelection& selected,
[[maybe_unused]] const QItemSelection& deselected) [[maybe_unused]] const QItemSelection& deselected)
{ {
if (selected.size() > 0) { if (selected.size() > 0) {
ui_.deleteButton->setEnabled(true); m_ui.deleteButton->setEnabled(true);
} else { } else {
ui_.deleteButton->setEnabled(false); m_ui.deleteButton->setEnabled(false);
} }
} }

View file

@ -26,9 +26,9 @@ class SellerDialog : public QDialog
void on_deleteButton_clicked(); void on_deleteButton_clicked();
void on_model_duplicateSellerNo(const QString& message); void on_model_duplicateSellerNo(const QString& message);
virtual void accept() override; virtual void accept() override;
Ui::SellerDialog ui_; Ui::SellerDialog m_ui;
Marketplace* market_; Marketplace* m_market;
std::unique_ptr<SellerModel> model_; std::unique_ptr<SellerModel> m_model;
}; };
#endif #endif

View file

@ -5,13 +5,13 @@
#include <QMessageBox> #include <QMessageBox>
SellerModel::SellerModel(Marketplace* market, QObject* parent) SellerModel::SellerModel(Marketplace* market, QObject* parent)
: QAbstractTableModel(parent), marketplace_(market) : QAbstractTableModel(parent), m_marketplace(market)
{ {
} }
int SellerModel::rowCount([[maybe_unused]] const QModelIndex& parent) const int SellerModel::rowCount([[maybe_unused]] const QModelIndex& parent) const
{ {
return marketplace_->getSellers().size(); return m_marketplace->getSellers().size();
} }
int SellerModel::columnCount([[maybe_unused]] const QModelIndex& parent) const { return 5; } int SellerModel::columnCount([[maybe_unused]] const QModelIndex& parent) const { return 5; }
@ -21,17 +21,14 @@ QVariant SellerModel::data(const QModelIndex& index, int role) const
if (role != Qt::DisplayRole) if (role != Qt::DisplayRole)
return QVariant(); return QVariant();
if (marketplace_->getSellers().size() == 0) if (m_marketplace->getSellers().size() == 0)
return QVariant(); return QVariant();
Seller* seller = marketplace_->getSellers().at(index.row()).get(); Seller* seller = m_marketplace->getSellers().at(index.row()).get();
/* if (seller->getState() == Seller::State::DELETE)
return QVariant();
*/
switch (index.column()) { switch (index.column()) {
case 0: case 0:
return seller->getUuidAsString().c_str(); return seller->getId();
case 1: case 1:
return seller->getSellerNo(); return seller->getSellerNo();
case 2: case 2:
@ -81,21 +78,21 @@ bool SellerModel::setData(const QModelIndex& index, const QVariant& value, int r
if (role != Qt::EditRole) if (role != Qt::EditRole)
return false; return false;
Seller* seller = marketplace_->getSellers().at(index.row()).get(); Seller* seller = m_marketplace->getSellers().at(index.row()).get();
switch (index.column()) { switch (index.column()) {
case 0: case 0:
seller->setUuidFromString(value.toString().toStdString()); seller->setId(value.toInt());
break; break;
case 1: { case 1: {
if (value.toInt() < 0) if (value.toInt() < 0)
return false; return false;
auto iter = auto iter =
std::find_if(marketplace_->getSellers().begin(), marketplace_->getSellers().end(), std::find_if(m_marketplace->getSellers().begin(), m_marketplace->getSellers().end(),
[&value](const std::unique_ptr<Seller>& s) { [&value](const std::unique_ptr<Seller>& s) {
return value.toInt() == s->getSellerNo(); return value.toInt() == s->getSellerNo();
}); });
if (iter != marketplace_->getSellers().end()) { if (iter != m_marketplace->getSellers().end()) {
emit duplicateSellerNo( emit duplicateSellerNo(
"Die Verkäufernummer muss eindeutig sein.\n(Möglicherweise wird die Nummer von " "Die Verkäufernummer muss eindeutig sein.\n(Möglicherweise wird die Nummer von "
"einem (ausgeblendeten) Eintrag, der zum Löschen vorgemerkt ist, verwendet.)"); "einem (ausgeblendeten) Eintrag, der zum Löschen vorgemerkt ist, verwendet.)");
@ -126,9 +123,8 @@ bool SellerModel::insertRows(int row, int count, const QModelIndex& parent)
{ {
emit beginInsertRows(parent, row, row + count - 1); emit beginInsertRows(parent, row, row + count - 1);
auto seller = std::make_unique<Seller>(); auto seller = std::make_unique<Seller>();
seller->createUuid(); seller->setSellerNo(m_marketplace->getNextSellerNo());
seller->setSellerNo(marketplace_->getNextSellerNo()); m_marketplace->getSellers().push_back(std::move(seller));
marketplace_->getSellers().push_back(std::move(seller));
emit endInsertRows(); emit endInsertRows();
return true; return true;
@ -136,21 +132,21 @@ bool SellerModel::insertRows(int row, int count, const QModelIndex& parent)
bool SellerModel::removeRows(int row, int count, const QModelIndex& parent) bool SellerModel::removeRows(int row, int count, const QModelIndex& parent)
{ {
auto seller = marketplace_->getSellers().at(row).get(); auto seller = m_marketplace->getSellers().at(row).get();
if (seller->getState() == Seller::State::NEW) { if (seller->getState() == Seller::State::NEW) {
emit beginRemoveRows(parent, row, row + count - 1); emit beginRemoveRows(parent, row, row + count - 1);
marketplace_->getSellers().erase( m_marketplace->getSellers().erase(
std::remove_if(marketplace_->getSellers().begin(), marketplace_->getSellers().end(), std::remove_if(m_marketplace->getSellers().begin(), m_marketplace->getSellers().end(),
[&seller](const std::unique_ptr<Seller>& a) { [&seller](const std::unique_ptr<Seller>& a) {
return a->getUuid() == seller->getUuid(); return a->getId() == seller->getId();
}), }),
marketplace_->getSellers().end()); m_marketplace->getSellers().end());
emit endRemoveRows(); emit endRemoveRows();
return true; return true;
} else { } else {
emit beginRemoveRows(parent, row, row + count - 1); emit beginRemoveRows(parent, row, row + count - 1);
seller->setState(Seller::State::DELETE); seller->setState(Seller::State::DELETE);
marketplace_->storeToDb(true); m_marketplace->storeToDb(true);
emit endRemoveRows(); emit endRemoveRows();
return true; return true;
} }

View file

@ -1,30 +1,31 @@
#ifndef SELLER_MODEL_H #ifndef SELLER_MODEL_H
#define SELLER_MODEL_H #define SELLER_MODEL_H
#include <marketplace.h> #include <core/marketplace.h>
#include <QAbstractTableModel> #include <QAbstractTableModel>
class SellerModel : public QAbstractTableModel class SellerModel : public QAbstractTableModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit SellerModel(Marketplace* market, QObject* parent = nullptr); explicit SellerModel(Marketplace *market, QObject *parent = nullptr);
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
virtual Qt::ItemFlags flags(const QModelIndex& index) const override; virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; virtual bool setData(const QModelIndex &index, const QVariant &value,
virtual bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; int role = Qt::EditRole) override;
virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()) override; virtual bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
signals: signals:
void duplicateSellerNo(const QString& message); void duplicateSellerNo(const QString &message);
private: private:
Marketplace* marketplace_; Marketplace *m_marketplace;
}; };
#endif #endif

View file

@ -2,9 +2,10 @@
#include "mainwindow.h" #include "mainwindow.h"
#include <database.h> #include <core/database.h>
#include <posprinter.h> #include <core/utils.h>
#include <utils.h> #include <printer/posprinter.h>
#include <printer/utils.h>
#include <exception> #include <exception>
#include <stdexcept> #include <stdexcept>
@ -12,9 +13,9 @@
#include <QMessageBox> #include <QMessageBox>
#include <QSettings> #include <QSettings>
SettingsDialog::SettingsDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(parent, f) SettingsDialog::SettingsDialog(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f)
{ {
ui_.setupUi(this); m_ui.setupUi(this);
QSettings settings{}; QSettings settings{};
int cashPointNo = settings.value("global/cashPointNo").toInt(); int cashPointNo = settings.value("global/cashPointNo").toInt();
@ -25,25 +26,25 @@ SettingsDialog::SettingsDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(par
int maxFeeInEuro = settings.value("global/maxFeeInEuro").toInt(); int maxFeeInEuro = settings.value("global/maxFeeInEuro").toInt();
if (parent) if (parent)
market_ = dynamic_cast<MainWindow*>(parent)->getMarketplace(); m_market = dynamic_cast<MainWindow *>(parent)->getMarketplace();
ui_.cashPointNoSpinBox->setValue(cashPointNo); m_ui.cashPointNoSpinBox->setValue(cashPointNo);
ui_.communeEdit->setText(commune); m_ui.communeEdit->setText(commune);
ui_.posPrinterDeviceEdit->setText(posPrinterDevice); m_ui.posPrinterDeviceEdit->setText(posPrinterDevice);
ui_.posPrinterEndpointEdit->setText(posPrinterEndpoint); m_ui.posPrinterEndpointEdit->setText(posPrinterEndpoint);
ui_.feePercentSpinBox->setValue(feeInPercent); m_ui.feePercentSpinBox->setValue(feeInPercent);
ui_.maxFeeSpinBox->setValue(maxFeeInEuro); m_ui.maxFeeSpinBox->setValue(maxFeeInEuro);
connect(ui_.testPosPrinterButton, &QPushButton::clicked, this, [=]() { connect(m_ui.testPosPrinterButton, &QPushButton::clicked, this, [this]() {
using namespace std::string_literals; using namespace std::string_literals;
try { try {
if (ui_.posPrinterDeviceEdit->text().isEmpty()) { if (m_ui.posPrinterDeviceEdit->text().isEmpty()) {
PosPrinter printer; PosPrinter printer;
printer.printTest(); printer.printTest();
} else { } else {
std::string posPrinterDeviceString = ui_.posPrinterDeviceEdit->text().toStdString(); std::string posPrinterDeviceString = m_ui.posPrinterDeviceEdit->text().toStdString();
std::string posPrinterEndpointString = std::string posPrinterEndpointString =
ui_.posPrinterEndpointEdit->text().toStdString(); m_ui.posPrinterEndpointEdit->text().toStdString();
try { try {
auto printerDevice = auto printerDevice =
@ -54,7 +55,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(par
printer.printTest(); printer.printTest();
} }
} catch (std::exception&) { } catch (std::exception &) {
QMessageBox(QMessageBox::Icon::Warning, "Falsche Eingabe", QMessageBox(QMessageBox::Icon::Warning, "Falsche Eingabe",
QString("Eingabeformat für Device (hexadezimale IDs): " QString("Eingabeformat für Device (hexadezimale IDs): "
"<VendorID>:<ProductID>\nBeispiel: 0416:5011\n " "<VendorID>:<ProductID>\nBeispiel: 0416:5011\n "
@ -64,7 +65,7 @@ SettingsDialog::SettingsDialog(QWidget* parent, Qt::WindowFlags f) : QDialog(par
return; return;
} }
} }
} catch (std::runtime_error& err) { } catch (std::runtime_error &err) {
QMessageBox(QMessageBox::Icon::Warning, "Bondrucker Fehler", QMessageBox(QMessageBox::Icon::Warning, "Bondrucker Fehler",
QString("Test schlug fehl: ") + err.what(), QMessageBox::StandardButton::Ok, QString("Test schlug fehl: ") + err.what(), QMessageBox::StandardButton::Ok,
this) this)
@ -79,17 +80,17 @@ void SettingsDialog::accept()
QSettings settings; QSettings settings;
int oldCashPointNo = settings.value("global/cashPointNo").toInt(); int oldCashPointNo = settings.value("global/cashPointNo").toInt();
int newCashPointNo = ui_.cashPointNoSpinBox->value(); int newCashPointNo = m_ui.cashPointNoSpinBox->value();
settings.setValue("global/commune", ui_.communeEdit->text().trimmed()); settings.setValue("global/commune", m_ui.communeEdit->text().trimmed());
settings.setValue("global/posPrinterDevice", ui_.posPrinterDeviceEdit->text().trimmed()); settings.setValue("global/posPrinterDevice", m_ui.posPrinterDeviceEdit->text().trimmed());
settings.setValue("global/posPrinterEndpoint", ui_.posPrinterEndpointEdit->text().trimmed()); settings.setValue("global/posPrinterEndpoint", m_ui.posPrinterEndpointEdit->text().trimmed());
settings.setValue("global/feeInPercent", ui_.feePercentSpinBox->value()); settings.setValue("global/feeInPercent", m_ui.feePercentSpinBox->value());
settings.setValue("global/maxFeeInEuro", ui_.maxFeeSpinBox->value()); settings.setValue("global/maxFeeInEuro", m_ui.maxFeeSpinBox->value());
if (oldCashPointNo != newCashPointNo) { if (oldCashPointNo != newCashPointNo) {
int result{0}; int result{0};
if (market_ && market_->getSales().size() > 0) { if (m_market && m_market->getSales().size() > 0) {
result = QMessageBox(QMessageBox::Icon::Question, "Sind Sie sicher?", result = QMessageBox(QMessageBox::Icon::Question, "Sind Sie sicher?",
"Möchten Sie die Kassen-Nr wirklich ändern? Diese muss über alle " "Möchten Sie die Kassen-Nr wirklich ändern? Diese muss über alle "
"Installationen hinweg eindeutig sein.", "Installationen hinweg eindeutig sein.",
@ -102,7 +103,7 @@ void SettingsDialog::accept()
if (result == QMessageBox::Yes) { if (result == QMessageBox::Yes) {
try { try {
Database().updateCashPointNo(oldCashPointNo, newCashPointNo); Database().updateCashPointNo(oldCashPointNo, newCashPointNo);
} catch (std::exception& ex) { } catch (std::exception &ex) {
std::string errMsg("Das Ändern der Kassen-Nr. ist fehlgeschlagen: "); std::string errMsg("Das Ändern der Kassen-Nr. ist fehlgeschlagen: ");
errMsg.append(ex.what()); errMsg.append(ex.what());
QMessageBox(QMessageBox::Icon::Critical, "Fehler", errMsg.c_str(), QMessageBox(QMessageBox::Icon::Critical, "Fehler", errMsg.c_str(),
@ -111,7 +112,7 @@ void SettingsDialog::accept()
QDialog::accept(); QDialog::accept();
return; return;
} }
settings.setValue("global/cashPointNo", ui_.cashPointNoSpinBox->value()); settings.setValue("global/cashPointNo", m_ui.cashPointNoSpinBox->value());
} }
} }

View file

@ -3,12 +3,14 @@
#include "ui_settingsdialog.h" #include "ui_settingsdialog.h"
#include <marketplace.h> #include <core/marketplace.h>
#include <QDialog> #include <QDialog>
class SettingsDialog : public QDialog class SettingsDialog : public QDialog
{ {
Q_OBJECT
public: public:
SettingsDialog(QWidget* parent = nullptr, SettingsDialog(QWidget* parent = nullptr,
Qt::WindowFlags f = Qt::WindowTitleHint | Qt::WindowSystemMenuHint); Qt::WindowFlags f = Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
@ -17,8 +19,8 @@ class SettingsDialog : public QDialog
void accept() override; void accept() override;
private: private:
Ui::SettingsDialog ui_; Ui::SettingsDialog m_ui;
Marketplace* market_{}; Marketplace* m_market{};
}; };
#endif #endif

3
src/meson.build Normal file
View file

@ -0,0 +1,3 @@
subdir('core')
subdir('printer')
subdir('gui')

View file

@ -1,6 +1,6 @@
set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_STATIC_LIBS ON)
find_package(Boost 1.62 REQUIRED) find_package(Boost 1.78 REQUIRED)
if(WIN32) if(WIN32)
find_package(LIBUSB REQUIRED) find_package(LIBUSB REQUIRED)
@ -11,6 +11,7 @@ endif()
set(PRINTER_SOURCES set(PRINTER_SOURCES
posprinter.cpp posprinter.cpp
utils.cpp
) )
add_library(printer STATIC ${PRINTER_SOURCES}) add_library(printer STATIC ${PRINTER_SOURCES})
@ -19,4 +20,4 @@ if(WIN32)
else() else()
target_link_libraries(printer core ${LibUSB_LIBRARIES}) target_link_libraries(printer core ${LibUSB_LIBRARIES})
endif() endif()
target_include_directories(printer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(printer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/.. ${Boost_INCLUDE_DIRS})

9
src/printer/meson.build Normal file
View file

@ -0,0 +1,9 @@
libusb = dependency('libusb-1.0')
src = ['posprinter.cpp', 'utils.cpp']
printer_inc = include_directories('..')
printer_lib = static_library('printer', src, dependencies: [libusb, core_dep])
printer_dep = declare_dependency(link_with : printer_lib, include_directories : printer_inc)

View file

@ -1,6 +1,6 @@
#include "posprinter.h" #include "posprinter.h"
#include <marketplace.h> #include <core/marketplace.h>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>

View file

@ -1,8 +1,8 @@
#ifndef POS_PRINTER_H #ifndef POS_PRINTER_H
#define POS_PRINTER_H #define POS_PRINTER_H
#include <sale.h> #include <core/sale.h>
#include <seller.h> #include <core/seller.h>
#include <memory> #include <memory>
@ -36,7 +36,8 @@ class PosPrinter
void printHeader(const std::string& commune = "Musterhausen"); void printHeader(const std::string& commune = "Musterhausen");
void printTest(); void printTest();
void printSaleReceipt(Sale* sale, const std::string& commune = "Dettingen"); void printSaleReceipt(Sale* sale, const std::string& commune = "Dettingen");
void printSellerReceipt(Seller* seller, const int percent, const int maxFeeInCent, const std::string& commune = "Dettingen"); void printSellerReceipt(Seller* seller, const int percent, const int maxFeeInCent,
const std::string& commune = "Dettingen");
bool isValid(); bool isValid();
struct Command { struct Command {

26
src/printer/utils.cpp Normal file
View file

@ -0,0 +1,26 @@
#include "utils.h"
std::optional<PrinterDevice> convertToPosPrinterDevice(const std::string& device,
const std::string& endpoint)
{
if (device.empty()) {
return std::nullopt;
}
PrinterDevice printerDevice;
std::string delimiter = ":";
try {
printerDevice.idVendor = std::stoi(device.substr(0, device.find(delimiter)), 0, 16);
printerDevice.idProduct = std::stoi(device.substr(device.find(delimiter) + 1), 0, 16);
if (endpoint.empty()) {
printerDevice.endpoint = 0x03;
} else {
printerDevice.endpoint = std::stoi(endpoint, 0, 16);
}
} catch (std::exception& ex) {
throw ex;
}
return printerDevice;
}

12
src/printer/utils.h Normal file
View file

@ -0,0 +1,12 @@
#ifndef PRINTER_UTILS_H
#define PRINTER_UTILS_H
#include "posprinter.h"
#include <optional>
#include <string>
std::optional<PrinterDevice> convertToPosPrinterDevice(const std::string& vendor,
const std::string& endpoint);
#endif

View file

@ -2,3 +2,4 @@ if(NOT KIMA2_USE_EXTERNAL_JSON)
set(JSON_BuildTests OFF CACHE INTERNAL "") set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json EXCLUDE_FROM_ALL) add_subdirectory(nlohmann_json EXCLUDE_FROM_ALL)
endif() endif()
#add_subdirectory(csv-parser)

@ -0,0 +1 @@
Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03

View file

@ -0,0 +1,27 @@
project('singleapplication')
qt5 = import('qt5')
dep_qt5 = dependency('qt5', modules: ['Core', 'Widgets', 'Network'])
singleapp_inc = include_directories('singleapplication.git')
singleapp_moc = qt5.preprocess(
moc_headers: ['singleapplication.git/singleapplication.h', 'singleapplication.git/singleapplication_p.h'],
moc_extra_arguments: ['-DQAPPLICATION_CLASS=QApplication'],
dependencies: dep_qt5
)
singleapp_lib = static_library('SingleApplication',
['singleapplication.git/singleapplication.cpp', 'singleapplication.git/singleapplication_p.cpp', singleapp_moc],
include_directories: singleapp_inc,
cpp_args : '-DQAPPLICATION_CLASS=QApplication',
dependencies: dep_qt5
)
singleapp_dep = declare_dependency(
include_directories: singleapp_inc,
link_with: singleapp_lib
)
# On windows, SingleApplication needs to be linked against advapi32. This is
# done by adding 'advapi32' to cpp_winlibs, where it should be by default.

@ -0,0 +1 @@
Subproject commit 8c48163c4d3fbba603cfe8a5b94046c9dad71825

View file

@ -9,14 +9,14 @@
BOOST_AUTO_TEST_CASE(create_uuid_nil) BOOST_AUTO_TEST_CASE(create_uuid_nil)
{ {
Seller seller{}; Seller seller{};
BOOST_TEST(seller.getUuid().is_nil() == true); BOOST_TEST(seller.getId().is_nil() == true);
} }
BOOST_AUTO_TEST_CASE(create_uuid) BOOST_AUTO_TEST_CASE(create_uuid)
{ {
Seller seller{}; Seller seller{};
seller.createUuid(); seller.createUuid();
BOOST_TEST(seller.getUuid().is_nil() == false); BOOST_TEST(seller.getId().is_nil() == false);
} }
BOOST_AUTO_TEST_CASE(create_many) BOOST_AUTO_TEST_CASE(create_many)
@ -25,7 +25,7 @@ BOOST_AUTO_TEST_CASE(create_many)
std::array<Seller, QUANTITY> sellers; std::array<Seller, QUANTITY> sellers;
for (unsigned i = 0; i < sellers.size(); i++) { for (unsigned i = 0; i < sellers.size(); i++) {
sellers[i] = Seller(); sellers[i] = Seller();
sellers[i].createUuid(); //sellers[i].createUuid();
} }
} }