Initial commit
This commit is contained in:
commit
4721e6c034
28 changed files with 5945 additions and 0 deletions
20
harbour-expenditure.desktop
Normal file
20
harbour-expenditure.desktop
Normal file
|
@ -0,0 +1,20 @@
|
|||
[Desktop Entry]
|
||||
Type=Application
|
||||
X-Nemo-Application-Type=silica-qt5
|
||||
Icon=harbour-expenditure
|
||||
Exec=harbour-expenditure
|
||||
Name=Expenditure
|
||||
# translation example:
|
||||
# your app name in German locale (de)
|
||||
#
|
||||
# Remember to comment out the following line, if you do not want to use
|
||||
# a different app name in German locale (de).
|
||||
#Name[de]=Expenditure
|
||||
|
||||
[X-Sailjail]
|
||||
# Replace with your organization as a reverse domain name
|
||||
OrganizationName=org.tplabs
|
||||
# ApplicationName does not have to be identical to Name
|
||||
ApplicationName=expenditure
|
||||
# Add the required permissions here
|
||||
Permissions=PublicDir;UserDirs;RemovableMedia
|
48
harbour-expenditure.pro
Normal file
48
harbour-expenditure.pro
Normal file
|
@ -0,0 +1,48 @@
|
|||
# NOTICE:
|
||||
#
|
||||
# Application name defined in TARGET has a corresponding QML filename.
|
||||
# If name defined in TARGET is changed, the following needs to be done
|
||||
# to match new name:
|
||||
# - corresponding QML filename must be changed
|
||||
# - desktop icon filename must be changed
|
||||
# - desktop filename must be changed
|
||||
# - icon definition filename in desktop file must be changed
|
||||
# - translation filenames have to be changed
|
||||
|
||||
# The name of your application
|
||||
TARGET = harbour-expenditure
|
||||
|
||||
CONFIG += sailfishapp
|
||||
|
||||
SOURCES += src/harbour-expenditure.cpp \
|
||||
src/File.cpp
|
||||
|
||||
DISTFILES += qml/harbour-expenditure.qml \
|
||||
qml/cover/CoverPage.qml \
|
||||
qml/pages/AboutPage.qml \
|
||||
qml/pages/Banner2ButtonsChoice.qml \
|
||||
qml/pages/BannerAddProject.qml \
|
||||
qml/pages/CalcPage.qml \
|
||||
qml/pages/FirstPage.qml \
|
||||
qml/pages/SettingsPage.qml \
|
||||
rpm/harbour-expenditure.changes.in \
|
||||
rpm/harbour-expenditure.changes.run.in \
|
||||
rpm/harbour-expenditure.spec \
|
||||
rpm/harbour-expenditure.yaml \
|
||||
translations/*.ts \
|
||||
harbour-expenditure.desktop
|
||||
|
||||
SAILFISHAPP_ICONS = 86x86 108x108 128x128 172x172
|
||||
|
||||
# to disable building translations every time, comment out the
|
||||
# following CONFIG line
|
||||
CONFIG += sailfishapp_i18n
|
||||
|
||||
# German translation is enabled as an example. If you aren't
|
||||
# planning to localize your app, remember to comment out the
|
||||
# following TRANSLATIONS line. And also do not forget to
|
||||
# modify the localized app name in the the .desktop file.
|
||||
TRANSLATIONS += translations/harbour-expenditure-de.ts
|
||||
|
||||
HEADERS += \
|
||||
src/File.h
|
1117
harbour-expenditure.pro.user
Normal file
1117
harbour-expenditure.pro.user
Normal file
File diff suppressed because it is too large
Load diff
BIN
icons/108x108/harbour-expenditure.png
Normal file
BIN
icons/108x108/harbour-expenditure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
BIN
icons/128x128/harbour-expenditure.png
Normal file
BIN
icons/128x128/harbour-expenditure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.8 KiB |
BIN
icons/172x172/harbour-expenditure.png
Normal file
BIN
icons/172x172/harbour-expenditure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
icons/86x86/harbour-expenditure.png
Normal file
BIN
icons/86x86/harbour-expenditure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5 KiB |
34
qml/cover/CoverPage.qml
Normal file
34
qml/cover/CoverPage.qml
Normal file
|
@ -0,0 +1,34 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
CoverBackground {
|
||||
/*
|
||||
Label {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
text: qsTr("Expenditure")
|
||||
}
|
||||
*/
|
||||
Image {
|
||||
id: name
|
||||
anchors.centerIn: parent
|
||||
source: "harbour-expenditure.png"
|
||||
width: Theme.iconSizeLarge
|
||||
height: Theme.iconSizeLarge
|
||||
sourceSize.width: width
|
||||
sourceSize.height: height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
/*
|
||||
CoverActionList {
|
||||
id: coverAction
|
||||
|
||||
CoverAction {
|
||||
iconSource: "image://theme/icon-cover-next"
|
||||
}
|
||||
CoverAction {
|
||||
iconSource: "image://theme/icon-cover-pause"
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
BIN
qml/cover/harbour-expenditure.png
Normal file
BIN
qml/cover/harbour-expenditure.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
277
qml/cover/harbour-expenditure.svg
Executable file
277
qml/cover/harbour-expenditure.svg
Executable file
|
@ -0,0 +1,277 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 17.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="86px"
|
||||
height="86px"
|
||||
viewBox="0 0 86 86"
|
||||
enable-background="new 0 0 86 86"
|
||||
xml:space="preserve"
|
||||
id="svg25"
|
||||
sodipodi:docname="harbour-expenditure.svg"
|
||||
inkscape:version="1.2 (1:1.2+202206011327+fc4e4096c5)"
|
||||
inkscape:export-filename="harbour-expenditure.png"
|
||||
inkscape:export-xdpi="295.81396"
|
||||
inkscape:export-ydpi="295.81396"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs29"><linearGradient
|
||||
id="linearGradient6854"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="12.8064"
|
||||
y1="12.8066"
|
||||
x2="73.1935"
|
||||
y2="73.1937"
|
||||
gradientTransform="translate(-0.29999871,-0.29999764)">
|
||||
<stop
|
||||
offset="0.0054"
|
||||
style="stop-color:#336f00;stop-opacity:1;"
|
||||
id="stop6850" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#1cae40;stop-opacity:1;"
|
||||
id="stop6852" />
|
||||
</linearGradient>
|
||||
|
||||
<linearGradient
|
||||
id="linearGradient360"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="12.8064"
|
||||
y1="12.8066"
|
||||
x2="73.193497"
|
||||
y2="73.193703"
|
||||
gradientTransform="translate(-0.29999871,-0.29999764)">
|
||||
<stop
|
||||
offset="0.0054"
|
||||
style="stop-color:#483737;stop-opacity:1"
|
||||
id="stop3-4" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#6c5353;stop-opacity:1"
|
||||
id="stop5-9" />
|
||||
</linearGradient><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#SVGID_1_"
|
||||
id="linearGradient1977"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-8.4350098e-6)"
|
||||
x1="12.8064"
|
||||
y1="12.8066"
|
||||
x2="73.1935"
|
||||
y2="73.1937" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#SVGID_1_"
|
||||
id="linearGradient5185"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-61.2709,-105.56315)"
|
||||
x1="12.8064"
|
||||
y1="12.8066"
|
||||
x2="73.1935"
|
||||
y2="73.1937" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#SVGID_1_"
|
||||
id="linearGradient5876"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(-122.40401,-2.5764892)"
|
||||
x1="12.8064"
|
||||
y1="12.8066"
|
||||
x2="73.1935"
|
||||
y2="73.1937" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#SVGID_1_"
|
||||
id="linearGradient6600"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(119.58762,-0.27337463)"
|
||||
x1="12.8064"
|
||||
y1="12.8066"
|
||||
x2="73.1935"
|
||||
y2="73.1937" /><linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient6854"
|
||||
id="linearGradient6651"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(235.36607,-0.27268137)"
|
||||
x1="12.8064"
|
||||
y1="12.8066"
|
||||
x2="73.1935"
|
||||
y2="73.1937" /></defs><sodipodi:namedview
|
||||
id="namedview27"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:snap-bbox-midpoints="true"
|
||||
inkscape:snap-object-midpoints="true"
|
||||
inkscape:snap-center="true"
|
||||
inkscape:bbox-paths="true"
|
||||
inkscape:bbox-nodes="true"
|
||||
inkscape:snap-bbox-edge-midpoints="true"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:snap-smooth-nodes="true"
|
||||
inkscape:snap-midpoints="true"
|
||||
inkscape:zoom="4.0000002"
|
||||
inkscape:cx="58.249997"
|
||||
inkscape:cy="62.624997"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1049"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1"
|
||||
inkscape:snap-page="true"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<g
|
||||
id="Layer_2"
|
||||
display="none">
|
||||
</g>
|
||||
<g
|
||||
id="Layer_1">
|
||||
<linearGradient
|
||||
id="SVGID_1_"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="12.8064"
|
||||
y1="12.8066"
|
||||
x2="73.1935"
|
||||
y2="73.1937"
|
||||
gradientTransform="translate(-0.29999871,-0.29999764)">
|
||||
<stop
|
||||
offset="0.0054"
|
||||
style="stop-color:#483737;stop-opacity:1"
|
||||
id="stop3" />
|
||||
<stop
|
||||
offset="1"
|
||||
style="stop-color:#6c5353;stop-opacity:1"
|
||||
id="stop5" />
|
||||
</linearGradient>
|
||||
|
||||
|
||||
|
||||
<circle
|
||||
fill="url(#SVGID_1_)"
|
||||
cx="43"
|
||||
cy="43"
|
||||
id="circle8-2-1"
|
||||
r="42.700001"
|
||||
style="fill:url(#linearGradient1977);stroke:none"
|
||||
inkscape:export-filename="harbour-expenditure.png"
|
||||
inkscape:export-xdpi="297.89001"
|
||||
inkscape:export-ydpi="297.89001" /><circle
|
||||
fill="url(#SVGID_1_)"
|
||||
cx="-18.270916"
|
||||
cy="-62.563152"
|
||||
id="circle8-2-3"
|
||||
r="42.700001"
|
||||
style="fill:url(#linearGradient5185)"
|
||||
inkscape:export-filename="/home/tobias/Dokumente/Sailfish/harbour-expenditure/icons/108x108/harbour-expenditure.png"
|
||||
inkscape:export-xdpi="121.40515"
|
||||
inkscape:export-ydpi="121.40515" /><g
|
||||
id="g1596-6"
|
||||
transform="translate(-109.6625)"><path
|
||||
id="path4805-0-6-0-5"
|
||||
style="font-variation-settings:normal;fill:#ff7f2a;fill-opacity:1;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 59.438474,-60.537276 a 32.075813,32.075813 0 0 0 15.88477,25.753904 32.075813,32.075813 0 0 0 32.076176,0 32.075813,32.075813 0 0 0 15.88476,-25.753904 z"
|
||||
inkscape:export-filename="/home/tobias/Dokumente/Sailfish/harbour-expenditure/icons/108x108/harbour-expenditure.png"
|
||||
inkscape:export-xdpi="121.40515"
|
||||
inkscape:export-ydpi="121.40515" /><path
|
||||
id="path4805-2-4-1-6-63"
|
||||
style="font-variation-settings:normal;fill:#00a1a9;fill-opacity:1;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 91.827145,-94.636885 c -4.286601,-0.06328 -8.620161,0.732953 -12.740231,2.439453 -10.80486,5.056629 -19.112,15.691881 -19.64844,27.609375 H 90.522465 L 112.56153,-86.62712 c -5.82161,-5.131559 -13.203344,-7.898584 -20.734385,-8.009765 z"
|
||||
inkscape:export-filename="/home/tobias/Dokumente/Sailfish/harbour-expenditure/icons/108x108/harbour-expenditure.png"
|
||||
inkscape:export-xdpi="121.40515"
|
||||
inkscape:export-ydpi="121.40515" /><path
|
||||
id="path4805-2-6-8-5-8-9"
|
||||
style="font-variation-settings:normal;fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 115.42676,-83.765792 -19.177735,19.177735 h 27.095705 a 32.075813,32.075813 0 0 0 -7.91797,-19.177735 z"
|
||||
inkscape:export-filename="/home/tobias/Dokumente/Sailfish/harbour-expenditure/icons/108x108/harbour-expenditure.png"
|
||||
inkscape:export-xdpi="121.40515"
|
||||
inkscape:export-ydpi="121.40515" /></g><circle
|
||||
fill="url(#SVGID_1_)"
|
||||
cx="-79.403999"
|
||||
cy="40.423508"
|
||||
id="circle8-2-1-8"
|
||||
r="42.700001"
|
||||
style="fill:url(#linearGradient5876)"
|
||||
inkscape:export-filename="harbour-expenditure.png"
|
||||
inkscape:export-xdpi="297.89001"
|
||||
inkscape:export-ydpi="297.89001" /><path
|
||||
id="path4805-0-5-0-8-2-0-8"
|
||||
style="font-variation-settings:normal;vector-effect:none;fill:#f18500;fill-opacity:1;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000"
|
||||
d="m -111.48017,40.424196 a 32.075813,32.075813 0 0 0 16.03711,27.77735 32.075813,32.075813 0 0 0 32.07617,0 32.075813,32.075813 0 0 0 16.03906,-27.77735 h -23.03125 c 0.0565,0.0484 0.11882,0.0932 0.17383,0.14258 1.875,1.68403 2.8125,3.8635 2.8125,6.53711 0,2.67361 -1.01562,4.90322 -3.04688,6.69141 -2.01388,1.78819 -4.65213,2.76172 -7.91601,2.91797 l -0.0274,7.89062 h -2.60351 l -0.0254,-7.83984 c -1.77083,-0.0868 -3.53365,-0.32921 -5.28711,-0.72852 -1.73611,-0.3993 -3.45487,-0.95421 -5.15625,-1.66601 v -4.6875 c 1.73611,1.07638 3.48091,1.89974 5.23437,2.47265 1.77084,0.57292 3.51563,0.87739 5.23438,0.91211 v -11.92773 c -1.15561,-0.17912 -2.20665,-0.42034 -3.17188,-0.71485 z m 33.11718,1.23633 v 11.30274 c 1.90972,-0.0521 3.40409,-0.58291 4.48047,-1.58985 1.0764,-1.00694 1.61328,-2.37717 1.61328,-4.11328 0,-1.61458 -0.4848,-2.86458 -1.45703,-3.75 -0.97222,-0.90278 -2.51866,-1.51975 -4.63672,-1.84961 z" /><path
|
||||
id="path4805-2-4-7-6-7-3-6-5"
|
||||
style="font-variation-settings:normal;vector-effect:none;fill:#00a1a9;fill-opacity:1;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000"
|
||||
d="m -91.6794,10.789435 a 32.075813,32.075813 0 0 0 -19.80078,29.634761 h 27.33984 c -1.93468,-0.59036 -3.49625,-1.40699 -4.66602,-2.46093 -1.75346,-1.57986 -2.6289,-3.67123 -2.6289,-6.275394 0,-2.72569 0.91146,-4.897138 2.73437,-6.511719 1.84028,-1.631942 4.41841,-2.569444 7.73438,-2.8125 v -6.11914 h 2.60351 l 0.0254,6.11914 c 1.37152,0.08681 2.76172,0.261069 4.16796,0.521485 1.40625,0.260416 2.83855,0.616971 4.29688,1.068359 v 4.503906 c -1.47569,-0.746526 -2.91797,-1.319445 -4.32422,-1.71875 -1.38889,-0.416666 -2.77713,-0.659071 -4.16602,-0.728515 v 11.224608 c 0.63124,0.095 1.22646,0.21306 1.80469,0.34375 L -56.72432,17.74256 A 32.075813,32.075813 0 0 0 -91.6794,10.789435 Z m 10.71289,15.167968 c -1.80555,0.06945 -3.22179,0.59028 -4.24609,1.5625 -1.00695,0.972222 -1.50977,2.274309 -1.50977,3.90625 0,1.493054 0.46875,2.674913 1.40625,3.542973 0.95486,0.86805 2.40516,1.449 4.34961,1.74414 z" /><path
|
||||
id="path4805-2-6-8-1-8-0-7-3-0"
|
||||
style="font-variation-settings:normal;vector-effect:none;fill:#75ae2c;fill-opacity:1;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;-inkscape-stroke:none;stop-color:#000000"
|
||||
d="m -56.72431,17.742555 -19.83426,19.835671 c 2.60112,0.58792 4.66872,1.53545 6.19727,2.8457 h 23.0332 a 32.075813,32.075813 0 0 0 -9.39648,-22.68164 z" /><g
|
||||
aria-label="$ "
|
||||
id="text427-0-6-7-5-2-9"
|
||||
style="font-size:53.3333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';fill:#6c5d53"
|
||||
transform="translate(-312.29297,107.58858)" /><g
|
||||
aria-label="$ "
|
||||
id="text427-0-6-7-5-2-9-4"
|
||||
style="font-size:53.3333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';fill:#6c5d53"
|
||||
transform="translate(-365.15599,103.03036)" /><g
|
||||
aria-label="$ "
|
||||
id="text427-0-6-7-5-2-9-3"
|
||||
style="font-size:53.3333px;line-height:1.25;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';fill:#6c5d53"
|
||||
transform="translate(-383.55449,91.55018)" /><circle
|
||||
fill="url(#SVGID_1_)"
|
||||
cx="162.58763"
|
||||
cy="42.726624"
|
||||
id="circle8-2-1-8-3"
|
||||
r="42.700001"
|
||||
style="fill:url(#linearGradient6600)"
|
||||
inkscape:export-filename="harbour-expenditure.png"
|
||||
inkscape:export-xdpi="297.89001"
|
||||
inkscape:export-ydpi="297.89001" /><g
|
||||
id="g7584"><path
|
||||
id="path4805-0-5-0-8-2-0-8-1"
|
||||
style="font-variation-settings:normal;fill:#ffffff;fill-opacity:0.603438;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 130.51146,42.727315 a 32.075813,32.075813 0 0 0 16.03711,27.77735 32.075813,32.075813 0 0 0 32.07617,0 32.075813,32.075813 0 0 0 16.03906,-27.77735 h -23.03125 c 0.0565,0.0484 0.11882,0.0932 0.17383,0.14258 1.875,1.68403 2.8125,3.8635 2.8125,6.53711 0,2.67361 -1.01562,4.90322 -3.04688,6.69141 -2.01388,1.78819 -4.65213,2.76172 -7.91601,2.91797 l -0.0274,7.89062 h -2.60351 l -0.0254,-7.83984 c -1.77083,-0.0868 -3.53365,-0.32921 -5.28711,-0.72852 -1.73611,-0.3993 -3.45487,-0.95421 -5.15625,-1.66601 v -4.6875 c 1.73611,1.07638 3.48091,1.89974 5.23437,2.47265 1.77084,0.57292 3.51563,0.87739 5.23438,0.91211 v -11.92773 c -1.15561,-0.17912 -2.20665,-0.42034 -3.17188,-0.71485 z m 33.11718,1.23633 v 11.30274 c 1.90972,-0.0521 3.40409,-0.58291 4.48047,-1.58985 1.0764,-1.00694 1.61328,-2.37717 1.61328,-4.11328 0,-1.61458 -0.4848,-2.86458 -1.45703,-3.75 -0.97222,-0.90278 -2.51866,-1.51975 -4.63672,-1.84961 z" /><path
|
||||
id="path4805-2-4-7-6-7-3-6-5-7"
|
||||
style="font-variation-settings:normal;fill:#999999;fill-opacity:0.595851;stroke:none;stroke-width:0.431;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 150.31223,13.092555 a 32.075813,32.075813 0 0 0 -19.80078,29.63476 h 27.33984 c -1.93468,-0.59036 -3.49625,-1.40699 -4.66602,-2.46093 -1.75346,-1.57986 -2.6289,-3.67123 -2.6289,-6.2754 0,-2.72569 0.91146,-4.89714 2.73437,-6.51172 1.84028,-1.63194 4.41841,-2.56944 7.73438,-2.8125 v -6.11914 h 2.60351 l 0.0254,6.11914 c 1.37152,0.0868 2.76172,0.26107 4.16796,0.52149 1.40625,0.26041 2.83855,0.61697 4.29688,1.06836 v 4.5039 c -1.47569,-0.74652 -2.91797,-1.31944 -4.32422,-1.71875 -1.38889,-0.41666 -2.77713,-0.65907 -4.16602,-0.72851 v 11.22461 c 0.63124,0.095 1.22646,0.21306 1.80469,0.34375 l 19.83399,-19.83594 a 32.075813,32.075813 0 0 0 -34.95508,-6.95312 z m 10.71289,15.16796 c -1.80555,0.0694 -3.22179,0.59028 -4.24609,1.5625 -1.00695,0.97222 -1.50977,2.27431 -1.50977,3.90625 0,1.49306 0.46875,2.67492 1.40625,3.54298 0.95486,0.86805 2.40516,1.449 4.34961,1.74414 z" /><path
|
||||
id="path4805-2-6-8-1-8-0-7-3-0-5"
|
||||
style="font-variation-settings:normal;fill:#cccccc;fill-opacity:0.601781;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 185.26732,20.045675 -19.83426,19.83567 c 2.60112,0.58792 4.66872,1.53545 6.19727,2.8457 h 23.0332 a 32.075813,32.075813 0 0 0 -9.39648,-22.68164 z" /></g><g
|
||||
id="g7584-5"
|
||||
transform="translate(-119.58762,0.27336443)"><path
|
||||
id="path4805-0-5-0-8-2-0-8-1-7"
|
||||
style="font-variation-settings:normal;fill:#ffffff;fill-opacity:0.69999999;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 130.51146,42.727315 a 32.075813,32.075813 0 0 0 16.03711,27.77735 32.075813,32.075813 0 0 0 32.07617,0 32.075813,32.075813 0 0 0 16.03906,-27.77735 h -23.03125 c 0.0565,0.0484 0.11882,0.0932 0.17383,0.14258 1.875,1.68403 2.8125,3.8635 2.8125,6.53711 0,2.67361 -1.01562,4.90322 -3.04688,6.69141 -2.01388,1.78819 -4.65213,2.76172 -7.91601,2.91797 l -0.0274,7.89062 h -2.60351 l -0.0254,-7.83984 c -1.77083,-0.0868 -3.53365,-0.32921 -5.28711,-0.72852 -1.73611,-0.3993 -3.45487,-0.95421 -5.15625,-1.66601 v -4.6875 c 1.73611,1.07638 3.48091,1.89974 5.23437,2.47265 1.77084,0.57292 3.51563,0.87739 5.23438,0.91211 v -11.92773 c -1.15561,-0.17912 -2.20665,-0.42034 -3.17188,-0.71485 z m 33.11718,1.23633 v 11.30274 c 1.90972,-0.0521 3.40409,-0.58291 4.48047,-1.58985 1.0764,-1.00694 1.61328,-2.37717 1.61328,-4.11328 0,-1.61458 -0.4848,-2.86458 -1.45703,-3.75 -0.97222,-0.90278 -2.51866,-1.51975 -4.63672,-1.84961 z" /><path
|
||||
id="path4805-2-4-7-6-7-3-6-5-7-4"
|
||||
style="font-variation-settings:normal;fill:#999999;fill-opacity:0.69999999;stroke:none;stroke-width:0.431;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 150.31223,13.092555 a 32.075813,32.075813 0 0 0 -19.80078,29.63476 h 27.33984 c -1.93468,-0.59036 -3.49625,-1.40699 -4.66602,-2.46093 -1.75346,-1.57986 -2.6289,-3.67123 -2.6289,-6.2754 0,-2.72569 0.91146,-4.89714 2.73437,-6.51172 1.84028,-1.63194 4.41841,-2.56944 7.73438,-2.8125 v -6.11914 h 2.60351 l 0.0254,6.11914 c 1.37152,0.0868 2.76172,0.26107 4.16796,0.52149 1.40625,0.26041 2.83855,0.61697 4.29688,1.06836 v 4.5039 c -1.47569,-0.74652 -2.91797,-1.31944 -4.32422,-1.71875 -1.38889,-0.41666 -2.77713,-0.65907 -4.16602,-0.72851 v 11.22461 c 0.63124,0.095 1.22646,0.21306 1.80469,0.34375 l 19.83399,-19.83594 a 32.075813,32.075813 0 0 0 -34.95508,-6.95312 z m 10.71289,15.16796 c -1.80555,0.0694 -3.22179,0.59028 -4.24609,1.5625 -1.00695,0.97222 -1.50977,2.27431 -1.50977,3.90625 0,1.49306 0.46875,2.67492 1.40625,3.54298 0.95486,0.86805 2.40516,1.449 4.34961,1.74414 z" /><path
|
||||
id="path4805-2-6-8-1-8-0-7-3-0-5-1"
|
||||
style="font-variation-settings:normal;fill:#cccccc;fill-opacity:0.69999999;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 185.26732,20.045675 -19.83426,19.83567 c 2.60112,0.58792 4.66872,1.53545 6.19727,2.8457 h 23.0332 a 32.075813,32.075813 0 0 0 -9.39648,-22.68164 z" /></g><circle
|
||||
fill="url(#SVGID_1_)"
|
||||
cx="278.36606"
|
||||
cy="42.727314"
|
||||
id="circle8-2-1-8-3-2"
|
||||
r="42.700001"
|
||||
style="fill:url(#linearGradient6651)"
|
||||
inkscape:export-filename="harbour-expenditure.png"
|
||||
inkscape:export-xdpi="297.89001"
|
||||
inkscape:export-ydpi="297.89001" /><path
|
||||
id="path4805-0-5-0-8-2-0-8-1-1"
|
||||
style="font-variation-settings:normal;fill:#ffffff;fill-opacity:0.60415018;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 246.28991,42.728013 a 32.075813,32.075813 0 0 0 16.03711,27.77735 32.075813,32.075813 0 0 0 32.07617,0 32.075813,32.075813 0 0 0 16.03906,-27.77735 H 287.411 c 0.0565,0.0484 0.11882,0.0932 0.17383,0.14258 1.875,1.68403 2.8125,3.8635 2.8125,6.53711 0,2.67361 -1.01562,4.90322 -3.04688,6.69141 -2.01388,1.78819 -4.65213,2.76172 -7.91601,2.91797 l -0.0274,7.89062 h -2.60351 l -0.0254,-7.83984 c -1.77083,-0.0868 -3.53365,-0.32921 -5.28711,-0.72852 -1.73611,-0.3993 -3.45487,-0.95421 -5.15625,-1.66601 v -4.6875 c 1.73611,1.07638 3.48091,1.89974 5.23437,2.47265 1.77084,0.57292 3.51563,0.87739 5.23438,0.91211 v -11.92773 c -1.15561,-0.17912 -2.20665,-0.42034 -3.17188,-0.71485 z m 33.11718,1.23633 v 11.30274 c 1.90972,-0.0521 3.40409,-0.58291 4.48047,-1.58985 1.0764,-1.00694 1.61328,-2.37717 1.61328,-4.11328 0,-1.61458 -0.4848,-2.86458 -1.45703,-3.75 -0.97222,-0.90278 -2.51866,-1.51975 -4.63672,-1.84961 z" /><path
|
||||
id="path4805-2-4-7-6-7-3-6-5-7-7"
|
||||
style="font-variation-settings:normal;fill:#999999;fill-opacity:0.59676164;stroke:none;stroke-width:0.431;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="M 266.09068,13.093249 A 32.075813,32.075813 0 0 0 246.2899,42.728013 h 27.33984 c -1.93468,-0.59036 -3.49625,-1.40699 -4.66602,-2.46093 -1.75346,-1.57986 -2.6289,-3.67123 -2.6289,-6.2754 0,-2.72569 0.91146,-4.89714 2.73437,-6.51172 1.84028,-1.631944 4.41841,-2.569444 7.73438,-2.812504 v -6.11914 h 2.60351 l 0.0254,6.11914 c 1.37152,0.0868 2.76172,0.26107 4.16796,0.52149 1.40625,0.26041 2.83855,0.61697 4.29688,1.06836 v 4.503904 c -1.47569,-0.74652 -2.91797,-1.31944 -4.32422,-1.71875 -1.38889,-0.41666 -2.77713,-0.65907 -4.16602,-0.72851 v 11.22461 c 0.63124,0.095 1.22646,0.21306 1.80469,0.34375 l 19.83399,-19.835944 a 32.075813,32.075813 0 0 0 -34.95508,-6.95312 z m 10.71289,15.167964 c -1.80555,0.0694 -3.22179,0.59028 -4.24609,1.5625 -1.00695,0.97222 -1.50977,2.27431 -1.50977,3.90625 0,1.49306 0.46875,2.67492 1.40625,3.54298 0.95486,0.86805 2.40516,1.449 4.34961,1.74414 z" /><path
|
||||
id="path4805-2-6-8-1-8-0-7-3-0-5-8"
|
||||
style="font-variation-settings:normal;fill:#cccccc;fill-opacity:0.6033051;stroke:none;stroke-width:0.430866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;stop-color:#000000"
|
||||
d="m 301.04577,20.046369 -19.83426,19.835674 c 2.60112,0.58792 4.66872,1.53545 6.19727,2.8457 h 23.0332 A 32.075813,32.075813 0 0 0 301.0455,20.046099 Z" /></g>
|
||||
</svg>
|
After Width: | Height: | Size: 20 KiB |
409
qml/harbour-expenditure.qml
Normal file
409
qml/harbour-expenditure.qml
Normal file
|
@ -0,0 +1,409 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import QtQuick.LocalStorage 2.0
|
||||
import "pages"
|
||||
|
||||
ApplicationWindow {
|
||||
initialPage: Component { FirstPage { } }
|
||||
cover: Qt.resolvedUrl("cover/CoverPage.qml")
|
||||
allowedOrientations: defaultAllowedOrientations
|
||||
|
||||
Item {
|
||||
id: storageItem
|
||||
|
||||
// general functions
|
||||
function getDatabase() {
|
||||
return storageItem.LocalStorage.openDatabaseSync("Bible_DB", "0.1", "BibleDatabaseComplete", 5000000); // 5 MB estimated size
|
||||
}
|
||||
function removeFullTable (tableName) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) { tx.executeSql('DROP TABLE IF EXISTS ' + tableName) });
|
||||
}
|
||||
function getTableCount (tableName, default_value) {
|
||||
var db = getDatabase();
|
||||
var res="";
|
||||
try {
|
||||
db.transaction(function(tx) {
|
||||
var rs = tx.executeSql('SELECT count(*) AS some_info FROM ' + tableName + ';');
|
||||
if (rs.rows.length > 0) {
|
||||
res = rs.rows.item(0).some_info;
|
||||
|
||||
} else {
|
||||
res = default_value;
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
//console.log("Database " + err);
|
||||
res = default_value;
|
||||
};
|
||||
return res
|
||||
}
|
||||
|
||||
// settings
|
||||
function setSettings( setting, value ) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS ' + 'settings_table' + '(setting TEXT UNIQUE, value TEXT)');
|
||||
var rs = tx.executeSql('INSERT OR REPLACE INTO ' + 'settings_table' + ' VALUES (?,?);', [setting,value]);
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function getSettings( setting, default_value ) {
|
||||
var db = getDatabase();
|
||||
var res="";
|
||||
try {
|
||||
db.transaction(function(tx) {
|
||||
var rs = tx.executeSql('SELECT value FROM '+ 'settings_table' +' WHERE setting=?;', [setting]);
|
||||
if (rs.rows.length > 0) {
|
||||
res = rs.rows.item(0).value;
|
||||
} else {
|
||||
res = default_value;
|
||||
}
|
||||
if (res === null) {
|
||||
res = default_value
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
//console.log("Database " + err);
|
||||
res = default_value;
|
||||
};
|
||||
//console.log(setting + " = " + res)
|
||||
return res
|
||||
}
|
||||
|
||||
// all projects available
|
||||
function setProject( project_id_timestamp, project_name, project_members, project_recent_payer_boolarray, project_recent_beneficiaries_boolarray, project_base_currency ) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS ' + 'projects_table' + ' (project_id_timestamp TEXT,
|
||||
project_name TEXT,
|
||||
project_members TEXT,
|
||||
project_recent_payer_boolarray TEXT,
|
||||
project_recent_beneficiaries_boolarray TEXT,
|
||||
project_base_currency TEXT)' );
|
||||
var rs = tx.executeSql('INSERT OR REPLACE INTO ' + 'projects_table' + ' VALUES (?,?,?,?,?,?);', [project_id_timestamp,
|
||||
project_name,
|
||||
project_members,
|
||||
project_recent_payer_boolarray,
|
||||
project_recent_beneficiaries_boolarray,
|
||||
project_base_currency ]);
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function updateProject ( project_id_timestamp, project_name, project_members, project_recent_payer_boolarray, project_recent_beneficiaries_boolarray, project_base_currency ) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS ' + 'projects_table' + ' (project_id_timestamp TEXT,
|
||||
project_name TEXT,
|
||||
project_members TEXT,
|
||||
project_recent_payer_boolarray TEXT,
|
||||
project_recent_beneficiaries_boolarray TEXT,
|
||||
project_base_currency TEXT)' );
|
||||
var rs = tx.executeSql('UPDATE projects_table'
|
||||
+ ' SET project_name="' + project_name
|
||||
+ '", project_members="' + project_members
|
||||
+ '", project_recent_payer_boolarray="' + project_recent_payer_boolarray
|
||||
+ '", project_recent_beneficiaries_boolarray="' + project_recent_beneficiaries_boolarray
|
||||
+ '", project_base_currency="' + project_base_currency
|
||||
+ '" WHERE project_id_timestamp=' + project_id_timestamp + ';');
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function updateField_Project ( project_id_timestamp, field_name, new_value ) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS ' + 'projects_table' + ' (project_id_timestamp TEXT,
|
||||
project_name TEXT,
|
||||
project_members TEXT,
|
||||
project_recent_payer_boolarray TEXT,
|
||||
project_recent_beneficiaries_boolarray TEXT,
|
||||
project_base_currency TEXT)' );
|
||||
var rs = tx.executeSql('UPDATE projects_table SET ' + field_name + '="' + new_value + '" WHERE project_id_timestamp=' + project_id_timestamp + ';');
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function deleteProject (project_id_timestamp) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS ' + 'projects_table' + ' (project_id_timestamp TEXT,
|
||||
project_name TEXT,
|
||||
project_members TEXT,
|
||||
project_recent_payer_boolarray TEXT,
|
||||
project_recent_beneficiaries_boolarray TEXT,
|
||||
project_base_currency TEXT)' );
|
||||
var rs = tx.executeSql('DELETE FROM ' + 'projects_table WHERE project_id_timestamp=' + project_id_timestamp + ';');
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
removeFullTable("table_" + project_id_timestamp)
|
||||
return res;
|
||||
}
|
||||
function getAllProjects( default_value ) {
|
||||
var db = getDatabase();
|
||||
var res=[];
|
||||
try {
|
||||
db.transaction(function(tx) {
|
||||
var rs = tx.executeSql('SELECT * FROM '+ 'projects_table;')
|
||||
if (rs.rows.length > 0) {
|
||||
for (var i = 0; i < rs.rows.length; i++) {
|
||||
res.push([rs.rows.item(i).project_id_timestamp,
|
||||
rs.rows.item(i).project_name,
|
||||
rs.rows.item(i).project_members,
|
||||
rs.rows.item(i).project_recent_payer_boolarray,
|
||||
rs.rows.item(i).project_recent_beneficiaries_boolarray,
|
||||
rs.rows.item(i).project_base_currency,
|
||||
])
|
||||
}
|
||||
} else {
|
||||
res = default_value;
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
//console.log("Database " + err);
|
||||
res = default_value;
|
||||
};
|
||||
return res
|
||||
}
|
||||
|
||||
// all exchange rates used
|
||||
function countExchangeRateOccurances (exchange_rate_currency, default_value) {
|
||||
var db = getDatabase();
|
||||
var res="";
|
||||
try {
|
||||
db.transaction(function(tx) {
|
||||
var rs = tx.executeSql('SELECT count(*) AS some_info FROM exchange_rates_table WHERE exchange_rate_currency=?;', [exchange_rate_currency]);
|
||||
if (rs.rows.length > 0) {
|
||||
res = rs.rows.item(0).some_info;
|
||||
} else {
|
||||
res = default_value;
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
//console.log("Database " + err);
|
||||
res = default_value;
|
||||
};
|
||||
return res
|
||||
}
|
||||
function setExchangeRate( exchange_rate_currency, exchange_rate_value ) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS ' + 'exchange_rates_table' + ' (exchange_rate_currency TEXT, exchange_rate_value TEXT)' );
|
||||
var rs = tx.executeSql('INSERT OR REPLACE INTO ' + 'exchange_rates_table' + ' VALUES (?,?);', [exchange_rate_currency, exchange_rate_value ]);
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function updateExchangeRate( exchange_rate_currency, exchange_rate_value ) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS exchange_rates_table (exchange_rate_currency TEXT, exchange_rate_value TEXT)');
|
||||
var rs = tx.executeSql('UPDATE exchange_rates_table SET exchange_rate_value="' + exchange_rate_value + '" WHERE exchange_rate_currency="' + exchange_rate_currency + '";');
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function getExchangeRate(exchange_rate_currency, default_value) {
|
||||
var db = getDatabase();
|
||||
var res=[];
|
||||
try {
|
||||
db.transaction(function(tx) {
|
||||
var rs = tx.executeSql('SELECT * FROM '+ 'exchange_rates_table' +' WHERE exchange_rate_currency=?;', [exchange_rate_currency]);
|
||||
if (rs.rows.length > 0) {
|
||||
for (var i = 0; i < rs.rows.length; i++) {
|
||||
res.push(rs.rows.item(i).exchange_rate_value)
|
||||
}
|
||||
} else {
|
||||
res = default_value;
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
//console.log("Database " + err);
|
||||
res = default_value;
|
||||
};
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
// all expenes in current project
|
||||
function setExpense( project_name_table, id_unixtime_created, date_time, expense_name, expense_sum, expense_currency, expense_info, expense_payer, expense_members ) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS table_' + project_name_table + ' (id_unixtime_created TEXT,
|
||||
date_time TEXT,
|
||||
expense_name TEXT,
|
||||
expense_sum TEXT,
|
||||
expense_currency TEXT,
|
||||
expense_info TEXT,
|
||||
expense_payer TEXT,
|
||||
expense_members TEXT)' );
|
||||
var rs = tx.executeSql('INSERT OR REPLACE INTO table_' + project_name_table + ' VALUES (?,?,?,?,?,?,?,?);', [ id_unixtime_created,
|
||||
date_time,
|
||||
expense_name,
|
||||
expense_sum,
|
||||
expense_currency,
|
||||
expense_info,
|
||||
expense_payer,
|
||||
expense_members ]);
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
//console.log("project info found and updated")
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function updateExpense ( project_name_table, id_unixtime_created, date_time, expense_name, expense_sum, expense_currency, expense_info, expense_payer, expense_members ) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS table_' + project_name_table + ' (id_unixtime_created TEXT,
|
||||
date_time TEXT,
|
||||
expense_name TEXT,
|
||||
expense_sum TEXT,
|
||||
expense_currency TEXT,
|
||||
expense_info TEXT,
|
||||
expense_payer TEXT,
|
||||
expense_members TEXT)' );
|
||||
var rs = tx.executeSql('UPDATE table_' + project_name_table
|
||||
+ ' SET date_time="' + date_time
|
||||
+ '", expense_name="' + expense_name
|
||||
+ '", expense_sum="' + expense_sum
|
||||
+ '", expense_currency="' + expense_currency
|
||||
+ '", expense_info="' + expense_info
|
||||
+ '", expense_payer="' + expense_payer
|
||||
+ '", expense_members="' + expense_members
|
||||
+ '" WHERE id_unixtime_created=' + id_unixtime_created + ';');
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function deleteExpense (project_id_timestamp, id_unixtime_created) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS table_' + project_id_timestamp + ' (id_unixtime_created TEXT,
|
||||
date_time TEXT,
|
||||
expense_name TEXT,
|
||||
expense_sum TEXT,
|
||||
expense_currency TEXT,
|
||||
expense_info TEXT,
|
||||
expense_payer TEXT,
|
||||
expense_members TEXT)' );
|
||||
//var rs = tx.executeSql('DELETE FROM table_' + project_id_timestamp + ';');
|
||||
var rs = tx.executeSql('DELETE FROM table_' + project_id_timestamp + ' WHERE id_unixtime_created=' + id_unixtime_created + ';');
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function deleteAllExpenses (project_id_timestamp) {
|
||||
var db = getDatabase();
|
||||
var res = "";
|
||||
db.transaction(function(tx) {
|
||||
tx.executeSql('CREATE TABLE IF NOT EXISTS table_' + project_id_timestamp + ' (id_unixtime_created TEXT,
|
||||
date_time TEXT,
|
||||
expense_name TEXT,
|
||||
expense_sum TEXT,
|
||||
expense_currency TEXT,
|
||||
expense_info TEXT,
|
||||
expense_payer TEXT,
|
||||
expense_members TEXT)' );
|
||||
var rs = tx.executeSql('DELETE FROM table_' + project_id_timestamp + ';');
|
||||
if (rs.rowsAffected > 0) {
|
||||
res = "OK";
|
||||
} else {
|
||||
res = "Error";
|
||||
}
|
||||
}
|
||||
);
|
||||
return res;
|
||||
}
|
||||
function getAllExpenses( project_name_table, default_value ) {
|
||||
var db = getDatabase();
|
||||
var res=[];
|
||||
try {
|
||||
db.transaction(function(tx) {
|
||||
var rs = tx.executeSql('SELECT * FROM table_'+ project_name_table + ';');
|
||||
if (rs.rows.length > 0) {
|
||||
for (var i = 0; i < rs.rows.length; i++) {
|
||||
res.push([rs.rows.item(i).id_unixtime_created,
|
||||
rs.rows.item(i).date_time,
|
||||
rs.rows.item(i).expense_name,
|
||||
rs.rows.item(i).expense_sum,
|
||||
rs.rows.item(i).expense_currency,
|
||||
rs.rows.item(i).expense_info,
|
||||
rs.rows.item(i).expense_payer,
|
||||
rs.rows.item(i).expense_members,
|
||||
])
|
||||
}
|
||||
} else {
|
||||
res = default_value;
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
//console.log("Database " + err);
|
||||
res = default_value;
|
||||
};
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
}
|
91
qml/pages/AboutPage.qml
Normal file
91
qml/pages/AboutPage.qml
Normal file
|
@ -0,0 +1,91 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.Portrait //All
|
||||
|
||||
SilicaFlickable {
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
contentHeight: idColumn.height // Tell SilicaFlickable the height of its content.
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
|
||||
Column {
|
||||
id: idColumn
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
height: Theme.itemSizeLarge
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.primaryColor
|
||||
text: qsTr("Expenditure")
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.paddingLarge
|
||||
}
|
||||
Image {
|
||||
width: parent.width
|
||||
height: Theme.itemSizeHuge
|
||||
source: "../cover/harbour-expenditure.png"
|
||||
sourceSize.width: height
|
||||
sourceSize.height: height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.paddingLarge * 2.5
|
||||
}
|
||||
Label {
|
||||
x: Theme.paddingMedium
|
||||
width: parent.width - 2*x
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
wrapMode: Text.Wrap
|
||||
text: qsTr("Expenditure is a tool to track and split bills, project or trip expenses in multiple currencies among groups.")
|
||||
|
||||
}
|
||||
Label {
|
||||
x: Theme.paddingMedium
|
||||
width: parent.width - 2*x
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
wrapMode: Text.Wrap
|
||||
text: qsTr("Thanksgiving, feedback and support is always welcome.")
|
||||
bottomPadding: Theme.paddingLarge * 2
|
||||
}
|
||||
Label {
|
||||
x: Theme.paddingMedium
|
||||
width: parent.width - 2*x
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
wrapMode: Text.Wrap
|
||||
text: qsTr("Troubleshooting:")
|
||||
+ "\n" + qsTr("In case of any database error tap 10x on the word 'Settings' for cleanup options.")
|
||||
bottomPadding: Theme.paddingLarge * 2
|
||||
}
|
||||
Label {
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
wrapMode: Text.Wrap
|
||||
text: qsTr("Contact:")
|
||||
+ "\n" + qsTr("Copyright © 2022 Tobias Planitzer")
|
||||
+ "\n" + ("tp.labs@protonmail.com")
|
||||
+ "\n" + qsTr("License: GPL v3")
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.paddingLarge * 2.5
|
||||
}
|
||||
}
|
||||
} // end Silica Flickable
|
||||
}
|
153
qml/pages/Banner2ButtonsChoice.qml
Normal file
153
qml/pages/Banner2ButtonsChoice.qml
Normal file
|
@ -0,0 +1,153 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
|
||||
MouseArea {
|
||||
id: popup
|
||||
z: 10
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
visible: opacity > 0
|
||||
opacity: 0.0
|
||||
onClicked: {
|
||||
hide()
|
||||
}
|
||||
|
||||
// UI variables
|
||||
property var hideBackColor : Theme.rgba(Theme.overlayBackgroundColor, 0.9)
|
||||
property string headlineInfoText : ""
|
||||
property string detailedInfoText : ""
|
||||
property string otherInfoText : ""
|
||||
property string filePath_Action : ""
|
||||
|
||||
Behavior on opacity {
|
||||
FadeAnimator {}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: hideBackColor
|
||||
onColorChanged: opacity = 4
|
||||
|
||||
Rectangle {
|
||||
id: idBackgroundRectProject
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
width: parent.width
|
||||
height: parent.height - anchors.topMargin - Theme.paddingLarge
|
||||
radius: Theme.paddingLarge
|
||||
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: addExpenseColumn.height
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
id: addExpenseColumn
|
||||
width: parent.width
|
||||
topPadding: Theme.paddingLarge
|
||||
bottomPadding: Theme.paddingLarge
|
||||
|
||||
Label {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
wrapMode: Text.WordWrap
|
||||
text: headlineInfoText
|
||||
bottomPadding: Theme.paddingLarge
|
||||
}
|
||||
Label {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.fontSizeTiny
|
||||
text: detailedInfoText
|
||||
bottomPadding: Theme.paddingLarge
|
||||
}
|
||||
Row {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
topPadding: Theme.paddingLarge
|
||||
bottomPadding: Theme.paddingLarge
|
||||
|
||||
Button {
|
||||
id: idButton1
|
||||
width: parent.width /2 - Theme.paddingLarge /2
|
||||
onClicked: {
|
||||
restoreProjectExpenses(filePath_Action, "replace")
|
||||
//backNavigationBlocked_deletedAddedProject = true
|
||||
hide()
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingLarge
|
||||
height: 1
|
||||
}
|
||||
Button {
|
||||
id: idButton2
|
||||
width: parent.width /2 - Theme.paddingLarge /2
|
||||
height: idButton1.height
|
||||
onClicked: {
|
||||
restoreProjectExpenses(filePath_Action, "merge")
|
||||
//backNavigationBlocked_deletedAddedProject = true
|
||||
hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.fontSizeTiny
|
||||
color: Theme.secondaryColor
|
||||
text: otherInfoText
|
||||
topPadding: Theme.paddingLarge
|
||||
bottomPadding: Theme.paddingLarge
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Icon {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: idBackgroundRectProject.anchors.topMargin / 2 - height/2
|
||||
source: "image://theme/icon-splus-cancel?"
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
|
||||
function notify( color, upperMargin, headText, bodyText, otherText, choiceText_1, choiceText_2, filePath ) {
|
||||
// color settings
|
||||
if (color && (typeof(color) != "undefined")) {
|
||||
idBackgroundRectProject.color = color
|
||||
} else {
|
||||
idBackgroundRectProject.color = Theme.rgba(Theme.highlightBackgroundColor, 0.9)
|
||||
}
|
||||
|
||||
// position settings
|
||||
if (upperMargin && (typeof(upperMargin) != "undefined")) {
|
||||
idBackgroundRectProject.anchors.topMargin = upperMargin
|
||||
} else {
|
||||
idBackgroundRectProject.anchors.topMargin = 0
|
||||
}
|
||||
|
||||
// set texts
|
||||
headlineInfoText = headText
|
||||
detailedInfoText = bodyText
|
||||
otherInfoText = otherText
|
||||
idButton1.text = choiceText_1
|
||||
idButton2.text = choiceText_2
|
||||
filePath_Action = filePath
|
||||
|
||||
// show banner overlay
|
||||
popup.opacity = 1.0
|
||||
}
|
||||
|
||||
function hide() {
|
||||
popup.opacity = 0.0
|
||||
}
|
||||
|
||||
}
|
||||
|
601
qml/pages/BannerAddExpense.qml
Normal file
601
qml/pages/BannerAddExpense.qml
Normal file
|
@ -0,0 +1,601 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
|
||||
MouseArea {
|
||||
id: popup
|
||||
z: 10
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
visible: opacity > 0
|
||||
opacity: 0.0
|
||||
onClicked: {
|
||||
hide()
|
||||
}
|
||||
onOpacityChanged: {
|
||||
//if needed
|
||||
}
|
||||
|
||||
|
||||
|
||||
// UI variables
|
||||
property var activeProjectID
|
||||
property var hideBackColor : Theme.rgba(Theme.overlayBackgroundColor, 0.9)
|
||||
property int amountBeneficiaries : 0
|
||||
property string modeEdit : "new"
|
||||
property date currentDate
|
||||
property date currentTime
|
||||
property double editedTimeStamp // unixtime, can be edited, is NOT entry creation timestamp
|
||||
property double createdTimeStamp
|
||||
property bool dateTimeManuallyChanged : false
|
||||
|
||||
|
||||
// suppress blend to main window on this overlay, e.g. for context menu ...
|
||||
// BUG: creates problems with _selectOrientation for context menus larger than 5 entries
|
||||
property alias __silica_applicationwindow_instance: fakeApplicationWindow
|
||||
Item {
|
||||
id: fakeApplicationWindow
|
||||
// suppresses warnings by context menu
|
||||
property var _dimScreen
|
||||
property var _undim
|
||||
function _undim() {}
|
||||
function _dimScreen() {}
|
||||
}
|
||||
Behavior on opacity {
|
||||
FadeAnimator {}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: hideBackColor
|
||||
onColorChanged: opacity = 4
|
||||
|
||||
Rectangle {
|
||||
id: idBackgroundRectExpenses
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
width: parent.width
|
||||
height: parent.height - anchors.topMargin - Theme.paddingLarge
|
||||
radius: Theme.paddingLarge
|
||||
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: addExpenseColumn.height
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
id: addExpenseColumn
|
||||
width: parent.width
|
||||
|
||||
Row {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
topPadding: Theme.paddingLarge
|
||||
bottomPadding: Theme.paddingLarge * 2
|
||||
|
||||
Column {
|
||||
id: idColumnAddDate
|
||||
width: parent.width /3*2 - Theme.paddingLarge /2
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: currentDate.toLocaleDateString(Qt.locale(), "dd. MMMM yyyy")
|
||||
color: Theme.highlightColor
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
unFocusTextFields()
|
||||
var dialog = pageStack.push(datePickerComponent, {
|
||||
date: currentDate // preset picker to todays date
|
||||
} )
|
||||
dialog.accepted.connect( function () {
|
||||
currentDate = (dialog.date)
|
||||
editedTimeStamp = Number((combineDateAndTime(currentDate, currentTime)).getTime())
|
||||
dateTimeManuallyChanged = true
|
||||
} )
|
||||
}
|
||||
}
|
||||
}
|
||||
Label {
|
||||
width: parent.width
|
||||
text: currentTime.toLocaleTimeString(Qt.locale(), Locale.ShortFormat)
|
||||
color: Theme.highlightColor
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
unFocusTextFields()
|
||||
var dialog = pageStack.push(timePickerComponent, {
|
||||
hour: currentTime.getHours(), // preset picker to current time
|
||||
minute: currentTime.getMinutes(),
|
||||
hourMode: 1
|
||||
} )
|
||||
dialog.accepted.connect( function () {
|
||||
currentTime = new Date ( dialog.time)
|
||||
editedTimeStamp = Number((combineDateAndTime(currentDate, currentTime)).getTime())
|
||||
dateTimeManuallyChanged = true
|
||||
} )
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Label {
|
||||
width: parent.width
|
||||
font.pixelSize: Theme.fontSizeTiny
|
||||
text: "create_" + createdTimeStamp
|
||||
}
|
||||
Label {
|
||||
width: parent.width
|
||||
font.pixelSize: Theme.fontSizeTiny
|
||||
text: "edit_" + editedTimeStamp
|
||||
}
|
||||
*/
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingLarge
|
||||
height: 1
|
||||
}
|
||||
Button {
|
||||
id: idLabelHeaderAdd2
|
||||
enabled: (idTextfieldItem.length > 0) && (amountBeneficiaries > 0)
|
||||
width: parent.width /3 - Theme.paddingLarge /2
|
||||
height: idColumnAddDate.height
|
||||
text: (modeEdit === "new") ? qsTr("Add") : qsTr("Save")
|
||||
onClicked: {
|
||||
addEditExpense()
|
||||
}
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
id: idTextfieldItem
|
||||
width: page.width
|
||||
acceptableInput: text.length < 255
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
EnterKey.onClicked: {
|
||||
focus = false
|
||||
}
|
||||
Label {
|
||||
anchors.top: parent.bottom
|
||||
anchors.topMargin: Theme.paddingSmall
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("expense")
|
||||
}
|
||||
}
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
TextField {
|
||||
id: idTextfieldPrice
|
||||
width: parent.width /3 + parent.width /6
|
||||
textRightMargin: 0
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly //use "Qt.ImhDigitsOnly" for INT
|
||||
text: Number("0").toFixed(2)
|
||||
EnterKey.onClicked: {
|
||||
focus = false
|
||||
}
|
||||
onFocusChanged: {
|
||||
text = text.replace(",", ".")
|
||||
text = Number(text).toFixed(2)
|
||||
if (focus) {
|
||||
selectAll()
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.top: parent.bottom
|
||||
anchors.topMargin: Theme.paddingSmall
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("price")
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: parent.width / 6
|
||||
height: 1
|
||||
}
|
||||
TextField {
|
||||
id: idTextfieldCurrency
|
||||
width: parent.width /3
|
||||
textLeftMargin: 0
|
||||
horizontalAlignment: TextInput.AlignRight
|
||||
acceptableInput: text.length > 0
|
||||
EnterKey.enabled: text.length >= 0
|
||||
EnterKey.onClicked: {
|
||||
focus = false
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (text.length === 0) {
|
||||
text = recentlyUsedCurrency
|
||||
}
|
||||
if (focus) {
|
||||
selectAll()
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.bottom
|
||||
anchors.topMargin: Theme.paddingSmall
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("currency")
|
||||
}
|
||||
}
|
||||
}
|
||||
TextField {
|
||||
id: idTextfieldInfo
|
||||
width: page.width
|
||||
//acceptableInput: text.length < 255
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
EnterKey.onClicked: {
|
||||
focus = false
|
||||
}
|
||||
Label {
|
||||
anchors.top: parent.bottom
|
||||
anchors.topMargin: Theme.paddingSmall
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("info")
|
||||
}
|
||||
}
|
||||
Row {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
topPadding: Theme.paddingLarge * 2
|
||||
bottomPadding: Theme.paddingMedium
|
||||
|
||||
Label {
|
||||
width: parent.width / 2 - Theme.paddingLarge/2
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("payment by")
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingLarge
|
||||
height: 1
|
||||
}
|
||||
Label {
|
||||
width: parent.width / 2 - Theme.paddingLarge/2
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("beneficiary")
|
||||
}
|
||||
}
|
||||
Column {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
|
||||
Repeater {
|
||||
model: listModel_activeProjectMembers
|
||||
delegate: Row {
|
||||
id: idtestcolumn
|
||||
width: parent.width
|
||||
|
||||
Label {
|
||||
id: idLabelPayerName
|
||||
width: parent.width / 3*2 - Theme.paddingLarge/2
|
||||
height: Theme.iconSizeSmallPlus
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.bold: member_isPayer === "true"
|
||||
color: (member_isPayer === "true") ? Theme.primaryColor : Theme.highlightColor
|
||||
text: member_name
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
for (var i = 0; i < listModel_activeProjectMembers.count ; i++) {
|
||||
listModel_activeProjectMembers.setProperty(i, "member_isPayer", "false")
|
||||
}
|
||||
member_isPayer="true"
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingLarge
|
||||
height: 1
|
||||
}
|
||||
Item {
|
||||
width: parent.width / 3 - Theme.paddingLarge/2
|
||||
height: parent.height
|
||||
|
||||
Icon {
|
||||
anchors.right: parent.right
|
||||
height: parent.height
|
||||
width: height
|
||||
color: (member_isPayer==="true") ? Theme.primaryColor : Theme.highlightColor
|
||||
source: (member_isBeneficiary==="true") ? "image://theme/icon-m-accept?" : ""
|
||||
|
||||
Rectangle {
|
||||
z: -1
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - Theme.paddingSmall
|
||||
height: width
|
||||
color: "transparent"
|
||||
border.width: (member_isPayer==="true") ? 2 : 1
|
||||
border.color: Theme.secondaryColor
|
||||
radius: width/4
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: -Theme.paddingLarge
|
||||
anchors.rightMargin: -Theme.paddingLarge
|
||||
onClicked: {
|
||||
(member_isBeneficiary==="true") ? (member_isBeneficiary="false") : (member_isBeneficiary="true")
|
||||
amountBeneficiaries = 0
|
||||
for (var i = 0; i < listModel_activeProjectMembers.count ; i++) {
|
||||
if (listModel_activeProjectMembers.get(i).member_isBeneficiary === "true") {
|
||||
amountBeneficiaries += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.itemSizeSmall / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Icon {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: idBackgroundRectExpenses.anchors.topMargin / 2 - height/2
|
||||
source: "image://theme/icon-splus-cancel?"
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
|
||||
|
||||
function notify( color, upperMargin, modeEditNew, activeProjectID_unixtime, expense_ID_created ) {
|
||||
// color settings
|
||||
if (color && (typeof(color) != "undefined")) {
|
||||
idBackgroundRectExpenses.color = color
|
||||
}
|
||||
else {
|
||||
idBackgroundRectExpenses.color = Theme.rgba(Theme.highlightBackgroundColor, 0.9)
|
||||
}
|
||||
|
||||
// position settings
|
||||
if (upperMargin && (typeof(upperMargin) != "undefined")) {
|
||||
idBackgroundRectExpenses.anchors.topMargin = upperMargin
|
||||
}
|
||||
else {
|
||||
idBackgroundRectExpenses.anchors.topMargin = 0
|
||||
}
|
||||
|
||||
// project settings
|
||||
activeProjectID = activeProjectID_unixtime
|
||||
modeEdit = modeEditNew
|
||||
|
||||
// reset time and date to current time and date
|
||||
if (modeEditNew === "new") {
|
||||
idTextfieldItem.text = ""
|
||||
idTextfieldPrice.text = "0"
|
||||
idTextfieldCurrency.text = recentlyUsedCurrency
|
||||
idTextfieldInfo.text = ""
|
||||
currentDate = new Date()
|
||||
currentTime = new Date()
|
||||
createdTimeStamp = Number(new Date().getTime())
|
||||
editedTimeStamp = Number(new Date().getTime())
|
||||
} else { // modeEditNew === "edit"
|
||||
//console.log("editing " + expense_ID_created)
|
||||
for (var i = 0; i < listModel_activeProjectExpenses.count ; i++) {
|
||||
if (Number(expense_ID_created) === Number(listModel_activeProjectExpenses.get(i).id_unixtime_created)) {
|
||||
idTextfieldItem.text = listModel_activeProjectExpenses.get(i).expense_name
|
||||
idTextfieldPrice.text = listModel_activeProjectExpenses.get(i).expense_sum
|
||||
idTextfieldCurrency.text = listModel_activeProjectExpenses.get(i).expense_currency
|
||||
idTextfieldInfo.text = listModel_activeProjectExpenses.get(i).expense_info
|
||||
var olderDateTime = Number(listModel_activeProjectExpenses.get(i).date_time)
|
||||
currentDate = new Date(olderDateTime)
|
||||
currentTime = new Date (olderDateTime)
|
||||
createdTimeStamp = Number(listModel_activeProjectExpenses.get(i).id_unixtime_created)
|
||||
editedTimeStamp = Number(listModel_activeProjectExpenses.get(i).date_time)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// remember last beneficiaries and payer in "new" mode, load expense beneficiaries and payer in "edit" mode
|
||||
amountBeneficiaries = 0
|
||||
if (modeEditNew === "new") {
|
||||
|
||||
// count beneficiaries
|
||||
for (i = 0; i < listModel_activeProjectMembers.count ; i++) {
|
||||
if (listModel_activeProjectMembers.get(i).member_isBeneficiary === "true") {
|
||||
amountBeneficiaries += 1
|
||||
}
|
||||
}
|
||||
|
||||
// use last used project specific beneficiaries and payers settings
|
||||
for (var j = 0; j < listModel_allProjects.count ; j++) {
|
||||
if ( Number(listModel_allProjects.get(j).project_id_timestamp) === Number(activeProjectID_unixtime) ) {
|
||||
var activeProjectMembersArray = (listModel_allProjects.get(j).project_members).split(" ||| ")
|
||||
var activeProjectRecentPayerArray = (listModel_allProjects.get(j).project_recent_payer_boolarray).split(" ||| ")
|
||||
var activeProjectRecentBeneficiariesArray = (listModel_allProjects.get(j).project_recent_beneficiaries_boolarray).split(" ||| ")
|
||||
for (var s = 0; s < activeProjectMembersArray.length ; s++) {
|
||||
listModel_activeProjectMembers.set( s, {
|
||||
"member_isBeneficiary" : activeProjectRecentBeneficiariesArray[s],
|
||||
"member_isPayer" : activeProjectRecentPayerArray[s],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // "edit" mode
|
||||
|
||||
// find item in expenses list for editing
|
||||
for (var k = 0; k < listModel_activeProjectExpenses.count ; k++) {
|
||||
if (Number(expense_ID_created) === Number(listModel_activeProjectExpenses.get(k).id_unixtime_created)) {
|
||||
var editedBeneficiariesList = (listModel_activeProjectExpenses.get(k).expense_members).split(" ||| ")
|
||||
var editPayersList =(listModel_activeProjectExpenses.get(k).expense_payer).split(" ||| ")
|
||||
|
||||
for (var l = 0; l < listModel_activeProjectMembers.count; l++) {
|
||||
listModel_activeProjectMembers.setProperty(l, "member_isBeneficiary", "false")
|
||||
listModel_activeProjectMembers.setProperty(l, "member_isPayer", "false")
|
||||
|
||||
// mark beneficiaries and get amount
|
||||
for (var m = 0; m < editedBeneficiariesList.length; m++) {
|
||||
if (listModel_activeProjectMembers.get(l).member_name === editedBeneficiariesList[m]) {
|
||||
listModel_activeProjectMembers.setProperty(l, "member_isBeneficiary", "true")
|
||||
amountBeneficiaries += 1
|
||||
}
|
||||
}
|
||||
// mark payer
|
||||
for (m = 0; m < editPayersList.length; m++) {
|
||||
if (listModel_activeProjectMembers.get(l).member_name === editPayersList[m]) {
|
||||
listModel_activeProjectMembers.setProperty(l, "member_isPayer", "true")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//console.log(amountBeneficiaries)
|
||||
|
||||
// show banner overlay
|
||||
popup.opacity = 1.0
|
||||
|
||||
// focus on expense text searchField
|
||||
if (modeEditNew === "new") {
|
||||
idTextfieldItem.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
|
||||
function hide() {
|
||||
unFocusTextFields()
|
||||
popup.opacity = 0.0 // make invisible
|
||||
|
||||
// clear all fields
|
||||
idTextfieldItem.text = ""
|
||||
idTextfieldPrice.text = "0"
|
||||
idTextfieldCurrency.text = recentlyUsedCurrency
|
||||
idTextfieldInfo.text = ""
|
||||
//idButtonAddExpense.visible = true
|
||||
}
|
||||
|
||||
function unFocusTextFields() {
|
||||
idTextfieldItem.focus = false
|
||||
idTextfieldPrice.focus = false
|
||||
idTextfieldCurrency.focus = false
|
||||
idTextfieldInfo.focus = false
|
||||
}
|
||||
|
||||
function combineDateAndTime(date, time) {
|
||||
// warning: slice necessary to avoid singele digit outputs which can not be used in combined call
|
||||
var year = date.getFullYear();
|
||||
var month = ('0' + (date.getMonth() + 1)).slice(-2); // Jan is 0, dec is 11
|
||||
var day = ('0' + date.getDate()).slice(-2)
|
||||
//var month = date.getMonth() // only give one digit outputs <10, which causes errors later
|
||||
//var day = date.getDate(); // only gives one digit outputs <10, which causes errors later
|
||||
var dateString = year + '-' + month + '-' + day;
|
||||
var hours = ('0' + time.getHours()).slice(-2)
|
||||
var minutes = ('0' + time.getMinutes()).slice(-2)
|
||||
var timeString = hours + ':' + minutes + ':00';
|
||||
//var timeString = time.getHours() + ':' + time.getMinutes() + ':00';
|
||||
//console.log(dateString)
|
||||
//console.log(timeString)
|
||||
var combined = Date.fromLocaleString(Qt.locale(), dateString + ' ' + timeString, "yyyy-MM-dd hh:mm:ss")
|
||||
//console.log(combined)
|
||||
return combined;
|
||||
}
|
||||
|
||||
function addEditExpense() {
|
||||
var project_name_table = activeProjectID.toString()
|
||||
var id_unixtime_created = createdTimeStamp // time of entry creation, does not change, serves as unique expense_ID
|
||||
var date_time = editedTimeStamp // new or edited time of expense
|
||||
var expense_name = idTextfieldItem.text
|
||||
var expense_sum = idTextfieldPrice.text
|
||||
var expense_currency = idTextfieldCurrency.text
|
||||
var expense_info = idTextfieldInfo.text
|
||||
var expense_members = ""
|
||||
var project_recent_payer_boolarray = ""
|
||||
var project_recent_beneficiaries_boolarray = ""
|
||||
for (var i = 0; i < listModel_activeProjectMembers.count ; i++) {
|
||||
project_recent_payer_boolarray += " ||| " + listModel_activeProjectMembers.get(i).member_isPayer
|
||||
project_recent_beneficiaries_boolarray += " ||| " + listModel_activeProjectMembers.get(i).member_isBeneficiary
|
||||
if (listModel_activeProjectMembers.get(i).member_isPayer === "true") {
|
||||
var expense_payer = listModel_activeProjectMembers.get(i).member_name
|
||||
|
||||
}
|
||||
if (listModel_activeProjectMembers.get(i).member_isBeneficiary === "true") {
|
||||
expense_members += " ||| " + listModel_activeProjectMembers.get(i).member_name
|
||||
}
|
||||
}
|
||||
project_recent_payer_boolarray = project_recent_payer_boolarray.replace(" ||| ", "")
|
||||
project_recent_beneficiaries_boolarray = project_recent_beneficiaries_boolarray.replace(" ||| ", "")
|
||||
expense_members = expense_members.replace(" ||| ", "")
|
||||
//console.log("table_name= " + project_name_table)
|
||||
//console.log(id_unixtime_created + ", " + date_time + ", " + expense_name + ", " + expense_sum + ", " + expense_currency + ", " + expense_info + ", " + expense_payer + ", " + expense_members)
|
||||
|
||||
// update listmodel expenses and store in DB
|
||||
if (modeEdit === "new") {
|
||||
listModel_activeProjectExpenses.append({
|
||||
id_unixtime_created : Number(id_unixtime_created).toFixed(0),
|
||||
date_time : Number(date_time).toFixed(0),
|
||||
expense_name : expense_name,
|
||||
expense_sum : Number(expense_sum).toFixed(2),
|
||||
expense_currency : expense_currency,
|
||||
expense_info : expense_info,
|
||||
expense_payer : expense_payer,
|
||||
expense_members : expense_members,
|
||||
})
|
||||
storageItem.setExpense(project_name_table, id_unixtime_created.toString(), date_time.toString(), expense_name, expense_sum, expense_currency, expense_info, expense_payer, expense_members)
|
||||
} else { //modeEdit === "edit"
|
||||
for (var j = 0; j < listModel_activeProjectExpenses.count ; j++) {
|
||||
if (id_unixtime_created === Number(listModel_activeProjectExpenses.get(j).id_unixtime_created)) {
|
||||
listModel_activeProjectExpenses.set(j, {
|
||||
id_unixtime_created : Number(id_unixtime_created).toFixed(0),
|
||||
date_time : Number(date_time).toFixed(0),
|
||||
expense_name : expense_name,
|
||||
expense_sum : Number(expense_sum).toFixed(2),
|
||||
expense_currency : expense_currency,
|
||||
expense_info : expense_info,
|
||||
expense_payer : expense_payer,
|
||||
expense_members : expense_members,
|
||||
})
|
||||
}
|
||||
}
|
||||
storageItem.updateExpense(project_name_table, id_unixtime_created.toString(), date_time.toString(), expense_name, expense_sum, expense_currency, expense_info, expense_payer, expense_members)
|
||||
// if dates got changed: also sort expenses list
|
||||
if (dateTimeManuallyChanged) {
|
||||
listModel_activeProjectExpenses.quick_sort()
|
||||
dateTimeManuallyChanged = false
|
||||
}
|
||||
}
|
||||
|
||||
//remember recently used currency
|
||||
recentlyUsedCurrency = expense_currency
|
||||
storageItem.setSettings("recentlyUsedCurrency", recentlyUsedCurrency)
|
||||
|
||||
// update allProject_Listmodel and DB for recent_beneficiaries and recent_payer in case of "new" entry
|
||||
if (modeEdit === "new") {
|
||||
for (var k = 0; k < listModel_allProjects.count ; k++) {
|
||||
if ( Number(listModel_allProjects.get(k).project_id_timestamp) === Number(activeProjectID_unixtime) ) {
|
||||
listModel_allProjects.set(k, {
|
||||
"project_recent_payer_boolarray" : project_recent_payer_boolarray ,
|
||||
"project_recent_beneficiaries_boolarray" : project_recent_beneficiaries_boolarray,
|
||||
})
|
||||
}
|
||||
}
|
||||
storageItem.updateField_Project(activeProjectID_unixtime, "project_recent_payer_boolarray", project_recent_payer_boolarray)
|
||||
storageItem.updateField_Project(activeProjectID_unixtime, "project_recent_beneficiaries_boolarray", project_recent_beneficiaries_boolarray)
|
||||
}
|
||||
|
||||
// finally hide popup banner
|
||||
hide()
|
||||
}
|
||||
}
|
||||
|
502
qml/pages/BannerAddProject.qml
Normal file
502
qml/pages/BannerAddProject.qml
Normal file
|
@ -0,0 +1,502 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
|
||||
MouseArea {
|
||||
id: popup
|
||||
z: 10
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
visible: opacity > 0
|
||||
opacity: 0.0
|
||||
onClicked: {
|
||||
hide()
|
||||
}
|
||||
onOpacityChanged: {
|
||||
//if needed
|
||||
}
|
||||
|
||||
// UI variables
|
||||
property var hideBackColor : Theme.rgba(Theme.overlayBackgroundColor, 0.9)
|
||||
property int amountBeneficiaries : listModel_activeProjectMembersTEMP.count
|
||||
property string modeEdit : "new"
|
||||
property real editItemIndex : -1 // -1=new, otherwise gives index of list
|
||||
|
||||
property int tempProjectListIndex
|
||||
property bool showTextfieldMemberName : false
|
||||
property string timeStamp : ((new Date).getTime()).toString() // creates unix timestamp
|
||||
|
||||
property string backupFilePath : ""
|
||||
|
||||
// suppress blend to main window on this overlay, e.g. for context menu ...
|
||||
// BUG: creates problems with _selectOrientation for context menus larger than 5 entries
|
||||
property alias __silica_applicationwindow_instance: fakeApplicationWindow
|
||||
Item {
|
||||
id: fakeApplicationWindow
|
||||
// suppresses warnings by context menu
|
||||
property var _dimScreen
|
||||
property var _undim
|
||||
function _undim() {}
|
||||
function _dimScreen() {}
|
||||
}
|
||||
Behavior on opacity {
|
||||
FadeAnimator {}
|
||||
}
|
||||
ListModel {
|
||||
id: listModel_activeProjectMembersTEMP
|
||||
}
|
||||
RemorsePopup {
|
||||
z: 10
|
||||
id: remorse_deleteProject
|
||||
}
|
||||
|
||||
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: hideBackColor
|
||||
onColorChanged: opacity = 4
|
||||
|
||||
Rectangle {
|
||||
id: idBackgroundRectProject
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
width: parent.width
|
||||
height: parent.height - anchors.topMargin - Theme.paddingLarge
|
||||
radius: Theme.paddingLarge
|
||||
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: addExpenseColumn.height
|
||||
clip: true
|
||||
|
||||
Column {
|
||||
id: addExpenseColumn
|
||||
width: parent.width
|
||||
|
||||
Row {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
topPadding: Theme.paddingLarge
|
||||
bottomPadding: Theme.paddingLarge
|
||||
|
||||
Column {
|
||||
id: idColumnAddProject
|
||||
width: parent.width /3*2 - Theme.paddingLarge /2
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: qsTr("Project")
|
||||
}
|
||||
Label {
|
||||
width: parent.width
|
||||
font.pixelSize: Theme.fontSizeTiny
|
||||
text: (modeEdit === "new") ? (qsTr("create")) : (qsTr("edit"))
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingLarge
|
||||
height: 1
|
||||
}
|
||||
Button {
|
||||
id: idLabelHeaderAdd2
|
||||
enabled: (idTextfieldProjectname.length > 0) && (amountBeneficiaries > 0)
|
||||
width: parent.width /3 - Theme.paddingLarge /2
|
||||
height: idColumnAddProject.height
|
||||
text: (modeEdit === "new") ? qsTr("Add") : qsTr("Save")
|
||||
onClicked: {
|
||||
addProjectDB()
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
width: parent.width
|
||||
topPadding: Theme.paddingLarge
|
||||
bottomPadding: Theme.paddingLarge
|
||||
|
||||
TextField {
|
||||
id: idTextfieldProjectname
|
||||
width: parent.width /3 * 2 - Theme.paddingLarge
|
||||
acceptableInput: text.length < 255
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
EnterKey.onClicked: {
|
||||
focus = false
|
||||
}
|
||||
Label {
|
||||
anchors.top: parent.bottom
|
||||
anchors.topMargin: Theme.paddingSmall
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("name")
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingLarge
|
||||
height: 1
|
||||
}
|
||||
TextField {
|
||||
id: idTextfieldCurrencyProject
|
||||
width: parent.width /3
|
||||
textLeftMargin: 0
|
||||
horizontalAlignment: TextInput.AlignRight
|
||||
acceptableInput: text.length > 0
|
||||
EnterKey.enabled: text.length >= 0
|
||||
EnterKey.onClicked: {
|
||||
focus = false
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (text.length === 0) {
|
||||
text = recentlyUsedCurrency
|
||||
}
|
||||
if (focus) {
|
||||
selectAll()
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.bottom
|
||||
anchors.topMargin: Theme.paddingSmall
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("base currency")
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
topPadding: Theme.paddingLarge
|
||||
|
||||
Label {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width/ 3*2 - Theme.paddingLarge / 2
|
||||
height: idAddMemberButton.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
text: qsTr("Members")
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingLarge
|
||||
height: 1
|
||||
}
|
||||
Item {
|
||||
id: idAddMemberButton
|
||||
width: parent.width / 3 - Theme.paddingLarge/2
|
||||
height: Theme.iconSizeMedium
|
||||
|
||||
Icon {
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: parent.height
|
||||
width: height
|
||||
source: !showTextfieldMemberName ? "image://theme/icon-m-add?" : "image://theme/icon-m-clear?"
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
showTextfieldMemberName ? showTextfieldMemberName=false : showTextfieldMemberName=true
|
||||
if (showTextfieldMemberName) {
|
||||
// clear field and set for new member
|
||||
editItemIndex = -1 // -1=add new member
|
||||
idTextfieldAddMember.text = ""
|
||||
idTextfieldAddMember.forceActiveFocus()
|
||||
} else {
|
||||
idTextfieldAddMember.text = ""
|
||||
idTextfieldAddMember.focus = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Column {
|
||||
visible: !showTextfieldMemberName
|
||||
width: parent.width
|
||||
|
||||
Repeater {
|
||||
model: listModel_activeProjectMembersTEMP
|
||||
delegate: ListItem {
|
||||
contentHeight: Theme.itemSizeExtraSmall
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
text: qsTr("rename")
|
||||
onClicked: {
|
||||
showTextfieldMemberName ? showTextfieldMemberName=false : showTextfieldMemberName=true
|
||||
editItemIndex = index
|
||||
idTextfieldAddMember.text = member_name
|
||||
idTextfieldAddMember.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("remove")
|
||||
onClicked: {
|
||||
listModel_activeProjectMembersTEMP.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
height: parent.height
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.highlightColor
|
||||
text: member_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
width: parent.width
|
||||
visible: showTextfieldMemberName
|
||||
topPadding: Theme.paddingMedium
|
||||
|
||||
TextField {
|
||||
id: idTextfieldAddMember
|
||||
width: parent.width // 3*2 - Theme.paddingLarge
|
||||
acceptableInput: text.length < 255
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
onActiveFocusChanged: {
|
||||
if (!focus) {
|
||||
showTextfieldMemberName = false
|
||||
idTextfieldAddMember.text = ""
|
||||
}
|
||||
}
|
||||
EnterKey.onClicked: {
|
||||
if (text.length > 0) {
|
||||
if (editItemIndex === -1) { // -1=add new member
|
||||
listModel_activeProjectMembersTEMP.append({ member_name : idTextfieldAddMember.text,
|
||||
member_isBeneficiary : true,
|
||||
member_isPayer : false,
|
||||
})
|
||||
} else { // "edit" existing member name by index in listmodel
|
||||
listModel_activeProjectMembersTEMP.setProperty(editItemIndex, "member_name", idTextfieldAddMember.text)
|
||||
}
|
||||
}
|
||||
focus = false
|
||||
showTextfieldMemberName = false
|
||||
}
|
||||
Label {
|
||||
anchors.top: parent.bottom
|
||||
anchors.topMargin: Theme.paddingSmall
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
text: qsTr("name")
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.itemSizeSmall
|
||||
}
|
||||
Row {
|
||||
width: parent.width
|
||||
visible: (modeEdit === "edit") && (listModel_allProjects.count > 0) // make sure there is always one project left once created
|
||||
spacing: Theme.paddingLarge
|
||||
leftPadding: Theme.paddingLarge
|
||||
|
||||
Button {
|
||||
id: idLabelDeleteProject
|
||||
width: parent.width /3 - parent.spacing * 1.5
|
||||
height: idColumnAddProject.height
|
||||
color: Theme.errorColor
|
||||
text: (Number(activeProjectID_unixtime) != Number(timeStamp)) ? qsTr("Delete") : qsTr("Reset")
|
||||
onClicked: {
|
||||
if (Number(activeProjectID_unixtime) != Number(timeStamp)) { // if it is not the active project, delete it
|
||||
remorse_deleteProject.execute(qsTr("Delete this project?"), function() {
|
||||
deleteProject()
|
||||
})
|
||||
} else { // if active project
|
||||
remorse_deleteProject.execute(qsTr("Clear all transactions?"), function() {
|
||||
clearProject()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Button {
|
||||
id: idLabelBackupProject
|
||||
width: parent.width /3 - parent.spacing * 1.5
|
||||
height: idColumnAddProject.height
|
||||
text: qsTr("Backup")
|
||||
onClicked: {
|
||||
// hide() // ToDo: maybe close this popup?
|
||||
pageStack.push(idFolderPickerPage)
|
||||
}
|
||||
}
|
||||
Button {
|
||||
id: idLabelRestoreProject
|
||||
width: parent.width /3 - parent.spacing
|
||||
height: idColumnAddProject.height
|
||||
text: qsTr("Restore")
|
||||
onClicked: {
|
||||
// hide() // ToDo: maybe close this popup?
|
||||
pageStack.push(idFilePickerPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.itemSizeSmall / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Icon {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: idBackgroundRectProject.anchors.topMargin / 2 - height/2
|
||||
source: "image://theme/icon-splus-cancel?"
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
|
||||
function notify( color, upperMargin, modeEditNew, indexCurrentProject ) {
|
||||
// color settings
|
||||
if (color && (typeof(color) != "undefined")) {
|
||||
idBackgroundRectProject.color = color
|
||||
}
|
||||
else {
|
||||
idBackgroundRectProject.color = Theme.rgba(Theme.highlightBackgroundColor, 0.9)
|
||||
}
|
||||
|
||||
// position settings
|
||||
if (upperMargin && (typeof(upperMargin) != "undefined")) {
|
||||
idBackgroundRectProject.anchors.topMargin = upperMargin
|
||||
}
|
||||
else {
|
||||
idBackgroundRectProject.anchors.topMargin = 0
|
||||
}
|
||||
|
||||
// adjust input fields
|
||||
tempProjectListIndex = indexCurrentProject
|
||||
listModel_activeProjectMembersTEMP.clear()
|
||||
if (modeEditNew === "new") {
|
||||
modeEdit = "new"
|
||||
timeStamp = ((new Date).getTime()).toString()
|
||||
idTextfieldCurrencyProject.text = recentlyUsedCurrency
|
||||
idTextfieldProjectname.text = ""
|
||||
idTextfieldProjectname.forceActiveFocus()
|
||||
} else { // edit project mode
|
||||
modeEdit = "edit"
|
||||
var tempProjectMembersArray = []
|
||||
tempProjectMembersArray = (listModel_allProjects.get(idComboboxProject.currentIndex).project_members).split(" ||| ")
|
||||
for (var i = 0; i < tempProjectMembersArray.length ; i++) {
|
||||
listModel_activeProjectMembersTEMP.append({ member_name : tempProjectMembersArray[i],
|
||||
})
|
||||
}
|
||||
timeStamp = (listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp).toString()
|
||||
idTextfieldProjectname.text = listModel_allProjects.get(idComboboxProject.currentIndex).project_name
|
||||
idTextfieldCurrencyProject.text = listModel_allProjects.get(idComboboxProject.currentIndex).project_base_currency
|
||||
}
|
||||
|
||||
// all members are beneficiaries
|
||||
|
||||
|
||||
// show banner overlay
|
||||
popup.opacity = 1.0
|
||||
}
|
||||
|
||||
function hide() {
|
||||
idTextfieldProjectname.focus = false
|
||||
idTextfieldCurrencyProject.focus = false
|
||||
|
||||
// clear all fields
|
||||
idTextfieldProjectname.text = ""
|
||||
idTextfieldCurrencyProject.text = recentlyUsedCurrency
|
||||
|
||||
// make invisible
|
||||
popup.opacity = 0.0
|
||||
}
|
||||
|
||||
function addProjectDB () {
|
||||
var project_id_timestamp = timeStamp
|
||||
var project_name = idTextfieldProjectname.text
|
||||
var project_members = ""
|
||||
var project_recent_payer_boolarray = ""
|
||||
var project_recent_beneficiaries_boolarray = ""
|
||||
var project_base_currency = idTextfieldCurrencyProject.text
|
||||
|
||||
for (var i = 0; i < listModel_activeProjectMembersTEMP.count ; i++) {
|
||||
project_members += " ||| " + listModel_activeProjectMembersTEMP.get(i).member_name
|
||||
project_recent_beneficiaries_boolarray += " ||| " + "true"
|
||||
// recent_payer can only be one person, initially this will be the first entry of the list
|
||||
if (i===0) {
|
||||
project_recent_payer_boolarray += " ||| " + "true"
|
||||
} else {
|
||||
project_recent_payer_boolarray += " ||| " + "false"
|
||||
}
|
||||
}
|
||||
// remove first occurance of " ||| " to later be able to split that string
|
||||
project_members = project_members.replace(" ||| ", "")
|
||||
project_recent_payer_boolarray = project_recent_payer_boolarray.replace(" ||| ", "")
|
||||
project_recent_beneficiaries_boolarray = project_recent_beneficiaries_boolarray.replace(" ||| ", "")
|
||||
|
||||
if (modeEdit === "new") {
|
||||
// store in DB and list for new project
|
||||
storageItem.setProject( project_id_timestamp, project_name, project_members, project_recent_payer_boolarray, project_recent_beneficiaries_boolarray, project_base_currency )
|
||||
listModel_allProjects.append({ project_id_timestamp : Number(project_id_timestamp),
|
||||
project_name : project_name,
|
||||
project_members : project_members,
|
||||
project_recent_payer_boolarray : project_recent_payer_boolarray,
|
||||
project_recent_beneficiaries_boolarray : project_recent_beneficiaries_boolarray,
|
||||
project_base_currency : project_base_currency,
|
||||
})
|
||||
|
||||
// if this is the first project, auto set it as active project
|
||||
if (listModel_allProjects.count === 1) { // auto-sets as currently active project, if this project is very first one
|
||||
storageItem.setSettings("activeProjectID_unixtime", Number(project_id_timestamp) )
|
||||
activeProjectID_unixtime = Number(project_id_timestamp)
|
||||
loadActiveProjectInfos_FromDB( Number(project_id_timestamp) )
|
||||
//console.log("auto set as active project ID = " + activeProjectID_unixtime)
|
||||
}
|
||||
|
||||
} else { // modeEdit === "edit
|
||||
// update DB and list for existing project
|
||||
storageItem.updateProject( project_id_timestamp, project_name, project_members, project_recent_payer_boolarray, project_recent_beneficiaries_boolarray, project_base_currency )
|
||||
for (var j = 0; j < listModel_allProjects.count ; j++) {
|
||||
if (listModel_allProjects.get(j).project_id_timestamp === Number(project_id_timestamp)) {
|
||||
//console.log("updated entry at: id_" + project_id_timestamp)
|
||||
listModel_allProjects.set(j, { "project_name" : project_name,
|
||||
"project_members" : project_members,
|
||||
"project_recent_payer_boolarray" : project_recent_payer_boolarray,
|
||||
"project_recent_beneficiaries_boolarray" : project_recent_beneficiaries_boolarray,
|
||||
"project_base_currency" : project_base_currency
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
updateEvenWhenCanceled = true
|
||||
hide()
|
||||
}
|
||||
|
||||
function deleteProject() {
|
||||
updateEvenWhenCanceled = true
|
||||
storageItem.deleteProject(timeStamp)
|
||||
listModel_allProjects.remove(tempProjectListIndex)
|
||||
// set active project to reasonable one
|
||||
if (idComboboxProject.currentIndex != 0) {
|
||||
idComboboxProject.currentIndex = idComboboxProject.currentIndex -1
|
||||
}
|
||||
//console.log("auto set after deleting ID = " + Number(listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp))
|
||||
loadActiveProjectInfos_FromDB(Number(listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp)) // needed to make sure there is no expense or member list still active
|
||||
hide()
|
||||
}
|
||||
|
||||
function clearProject() {
|
||||
updateEvenWhenCanceled = true
|
||||
listModel_activeProjectExpenses.clear()
|
||||
storageItem.removeFullTable( "table_" + activeProjectID_unixtime.toString() )
|
||||
loadActiveProjectInfos_FromDB(Number(listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp)) // needed to make sure there is no expense or member list still active
|
||||
hide()
|
||||
}
|
||||
|
||||
}
|
||||
|
556
qml/pages/CalcPage.qml
Normal file
556
qml/pages/CalcPage.qml
Normal file
|
@ -0,0 +1,556 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
|
||||
Page {
|
||||
id: pageResults
|
||||
allowedOrientations: Orientation.All
|
||||
onVisibleChanged: {
|
||||
if (visible === true) { // entered page
|
||||
listModel_activeProjectResults.clear()
|
||||
listModel_activeProjectResultsSettlement.clear()
|
||||
generateExchangeRateListFromExpenses()
|
||||
addExchangeRates_listmodelActiveProjectExpenses()
|
||||
}
|
||||
}
|
||||
|
||||
property real totalSpendingAmount_baseCurrency : 0
|
||||
property int counterShownExchangeRates : 0
|
||||
property string toShareString : ""
|
||||
|
||||
ListModel {
|
||||
id: listModel_activeProjectResultsSettlement
|
||||
}
|
||||
SilicaFlickable{
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
contentHeight: resultsColumn.height // tell overall height
|
||||
|
||||
PullDownMenu {
|
||||
MenuItem {
|
||||
text: qsTr("Share detailed")
|
||||
onClicked: {
|
||||
createShareString("detailed")
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Share compact")
|
||||
onClicked: {
|
||||
createShareString("compact")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: resultsColumn
|
||||
x: Theme.paddingLarge
|
||||
width: parent.width - 2*x
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
topPadding: Theme.paddingLarge
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignRight
|
||||
color: Theme.highlightColor
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
text: qsTr("Results")
|
||||
}
|
||||
Label {
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.highlightColor
|
||||
text: activeProjectName + " [" + activeProjectCurrency + "]"
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.paddingLarge + Theme.paddingSmall
|
||||
}
|
||||
Label {
|
||||
visible: listModel_exchangeRates.count > 1 // main exchange rate is also counted in
|
||||
width: parent.width
|
||||
bottomPadding: Theme.paddingMedium
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("EXCHANGE RATES")
|
||||
}
|
||||
Repeater {
|
||||
id: idRepeaterExchangeRates
|
||||
width: parent.width
|
||||
model: listModel_exchangeRates
|
||||
delegate: Column {
|
||||
id: idColumnContent
|
||||
visible: expense_currency !== activeProjectCurrency
|
||||
width: parent.width
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Label {
|
||||
width: parent.width / 3
|
||||
height: parent.height
|
||||
topPadding: Theme.paddingSmall
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
wrapMode: Text.WordWrap
|
||||
text: (exchangeRateMode === 1)
|
||||
? (new Date(Number(date_time)).toLocaleString(Qt.locale(), "dd.MM.yy" + " - " + "hh:mm")) //"ddd dd.MM.yyyy - hh:mm"
|
||||
: (qsTr("constant"))
|
||||
}
|
||||
Label {
|
||||
width: parent.width / 3
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
wrapMode: Text.WordWrap
|
||||
topPadding: Theme.paddingSmall
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: Number(1).toFixed(2) + " " + "<b>" +expense_currency + "</b>" + " = "
|
||||
}
|
||||
TextField {
|
||||
id: idTextfieldExchangeRate
|
||||
width: parent.width / 3
|
||||
textRightMargin: idLabelProjectCurrency.width
|
||||
textLeftMargin: 0
|
||||
inputMethodHints: Qt.ImhFormattedNumbersOnly //use "Qt.ImhDigitsOnly" for INT
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: Number(exchange_rate) //.toFixed(decimalPlacesCurrencyRate)
|
||||
EnterKey.onClicked: {
|
||||
focus = false
|
||||
}
|
||||
onFocusChanged: {
|
||||
if (text.length < 1) {
|
||||
text = Number(exchange_rate) //.toFixed(decimalPlacesCurrencyRate)
|
||||
} else {
|
||||
text = text.replace(",", ".")
|
||||
text = Number(text) //.toFixed(decimalPlacesCurrencyRate)
|
||||
}
|
||||
if (focus) {
|
||||
selectAll()
|
||||
} else { // unfocus
|
||||
exchange_rate = Number(text)
|
||||
storeExchangeRate_DB(expense_currency, exchange_rate)
|
||||
addExchangeRates_listmodelActiveProjectExpenses()
|
||||
}
|
||||
}
|
||||
Label {
|
||||
id: idLabelProjectCurrency
|
||||
anchors.left: parent.right
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
//color: Theme.highlightColor
|
||||
color: Theme.secondaryColor
|
||||
text: "<b>" + base_currency + "</b>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.paddingLarge
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
bottomPadding: Theme.paddingMedium
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("SPENDING OVERVIEW")
|
||||
}
|
||||
Row {
|
||||
visible: listModel_activeProjectResults.count > 0
|
||||
width: parent.width
|
||||
|
||||
Label {
|
||||
width: parent.width / 4
|
||||
color: Theme.secondaryColor
|
||||
font.italic: true
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: qsTr("name")
|
||||
}
|
||||
Label {
|
||||
width: parent.width / 4
|
||||
color: Theme.secondaryColor
|
||||
font.italic: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: qsTr("payments")
|
||||
}
|
||||
Label {
|
||||
width: parent.width / 4
|
||||
color: Theme.secondaryColor
|
||||
font.italic: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: qsTr("benefits")
|
||||
}
|
||||
Label {
|
||||
width: parent.width / 4
|
||||
color: Theme.secondaryColor
|
||||
font.italic: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: qsTr("saldo")
|
||||
}
|
||||
}
|
||||
Repeater {
|
||||
id: idRepeaterExpensesOverview
|
||||
model: listModel_activeProjectResults
|
||||
delegate: Row {
|
||||
width: parent.width
|
||||
|
||||
Label {
|
||||
width: parent.width / 4
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: beneficiary_name
|
||||
}
|
||||
Label {
|
||||
width: parent.width / 4
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: expense_sum.toFixed(2)
|
||||
}
|
||||
Label {
|
||||
width: parent.width / 4
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: beneficiary_sum.toFixed(2)
|
||||
}
|
||||
Label {
|
||||
width: parent.width / 4
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
horizontalAlignment: Text.AlignRight
|
||||
color: (( Number(expense_sum) - Number(beneficiary_sum) ).toFixed(2) < 0) ? Theme.errorColor : "green"
|
||||
text: ( Number(expense_sum) - Number(beneficiary_sum) ).toFixed(2)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
visible: listModel_activeProjectResults.count > 0
|
||||
width: parent.width
|
||||
topPadding: Theme.paddingMedium
|
||||
|
||||
Label {
|
||||
width: parent.width / 4 * 2
|
||||
color: Theme.secondaryColor
|
||||
font.bold: true
|
||||
font.overline: true
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: totalSpendingAmount_baseCurrency.toFixed(2)
|
||||
//text: activeProjectCurrency + " " + totalSpendingAmount_baseCurrency.toFixed(2)
|
||||
}
|
||||
Item {
|
||||
width: parent.width / 4
|
||||
height: 1
|
||||
}
|
||||
Label {
|
||||
width: parent.width / 4
|
||||
color: Theme.secondaryColor
|
||||
font.bold: true
|
||||
font.overline: true
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: activeProjectCurrency
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.paddingLarge * 2.5
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
bottomPadding: Theme.paddingMedium
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("SETTLEMENT SUGGESTION")
|
||||
}
|
||||
Repeater {
|
||||
id: idRepeaterSettlementSuggestions
|
||||
visible: model.count > 0
|
||||
model: listModel_activeProjectResultsSettlement
|
||||
delegate: Row {
|
||||
width: parent.width
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
wrapMode: Text.WordWrap
|
||||
text: (settling_sum.toFixed(2) >= 0) ?
|
||||
("<b>" + from_name + "</b> " + qsTr("owes") + " <b>" + to_name + "</b> " + qsTr("the sum of") + " <b>" + settling_sum.toFixed(2) + "</b> <i>" + activeProjectCurrency + "</i>.")
|
||||
: ("<b>" + to_name + "</b> " + qsTr("owes") + " <b>" + from_name + "</b> " + qsTr("the sum of") + " <b>" + (-settling_sum).toFixed(2) + "</b> <i>" + activeProjectCurrency + "</i>.")
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.itemSizeSmall
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function generateExchangeRateListFromExpenses() {
|
||||
listModel_exchangeRates.clear()
|
||||
|
||||
for (var i = 0; i < listModel_activeProjectExpenses.count; i++) {
|
||||
if (exchangeRateMode === 0 ) { // collective currency
|
||||
// check if available and where occuring
|
||||
var currencyAlreadySet = false
|
||||
var addCurrencyIndex = 0
|
||||
for (var j = 0; j < listModel_exchangeRates.count; j++) {
|
||||
if ( listModel_activeProjectExpenses.get(i).expense_currency === listModel_exchangeRates.get(j).expense_currency ) {
|
||||
currencyAlreadySet = true
|
||||
addCurrencyIndex = j
|
||||
}
|
||||
}
|
||||
// if it has not been set yet, add to exchange rate list
|
||||
if (currencyAlreadySet === false) {
|
||||
var tempStoredExchangeRateDB = Number(storageItem.getExchangeRate(listModel_activeProjectExpenses.get(i).expense_currency, 1))
|
||||
listModel_exchangeRates.append({ expense_currency : listModel_activeProjectExpenses.get(i).expense_currency,
|
||||
base_currency : activeProjectCurrency,
|
||||
exchange_rate : tempStoredExchangeRateDB,
|
||||
date_time : Number(0)
|
||||
})
|
||||
}
|
||||
} else { // exchangeRateMode === 1 ... individual transactions
|
||||
var tempExpenseCurrency = listModel_activeProjectExpenses.get(i).expense_currency
|
||||
if (tempExpenseCurrency !== activeProjectCurrency) {
|
||||
tempStoredExchangeRateDB = Number(storageItem.getExchangeRate(listModel_activeProjectExpenses.get(i).expense_currency, 1))
|
||||
listModel_exchangeRates.append({ expense_currency : listModel_activeProjectExpenses.get(i).expense_currency,
|
||||
base_currency : activeProjectCurrency,
|
||||
exchange_rate : tempStoredExchangeRateDB,
|
||||
date_time : Number(listModel_activeProjectExpenses.get(i).date_time)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function storeExchangeRate_DB (exchange_rate_currency, exchange_rate_value) {
|
||||
var tempOccurences = Number(storageItem.countExchangeRateOccurances(exchange_rate_currency, 0))
|
||||
if (tempOccurences === 0) { // add new entry
|
||||
storageItem.setExchangeRate(exchange_rate_currency, exchange_rate_value)
|
||||
} else { // update existing entry
|
||||
storageItem.updateExchangeRate(exchange_rate_currency, exchange_rate_value)
|
||||
}
|
||||
}
|
||||
|
||||
function addExchangeRates_listmodelActiveProjectExpenses() {
|
||||
for (var i = 0; i < listModel_activeProjectExpenses.count; i++) {
|
||||
var tempExpenseCurrency = listModel_activeProjectExpenses.get(i).expense_currency
|
||||
var tempExpenseDateTime = Number(listModel_activeProjectExpenses.get(i).date_time)
|
||||
|
||||
if (tempExpenseCurrency === activeProjectCurrency) { // paid in project specific currency
|
||||
listModel_activeProjectExpenses.set(i, {"exchange_rate": Number(1)})
|
||||
} else { // if paid in different currency
|
||||
|
||||
if ( exchangeRateMode === 0 ) { // ... collective currency
|
||||
for (var k = 0; k < listModel_exchangeRates.count; k++) {
|
||||
if (listModel_exchangeRates.get(k).expense_currency === tempExpenseCurrency) {
|
||||
listModel_activeProjectExpenses.set(i, {"exchange_rate": Number(listModel_exchangeRates.get(k).exchange_rate)})
|
||||
//console.log(Number(listModel_exchangeRates.get(k).exchange_rate))
|
||||
}
|
||||
}
|
||||
} else { // ... individual transactions
|
||||
for (k = 0; k < listModel_exchangeRates.count; k++) {
|
||||
if ((listModel_exchangeRates.get(k).expense_currency === tempExpenseCurrency) && (Number(listModel_exchangeRates.get(k).date_time) === tempExpenseDateTime)) {
|
||||
listModel_activeProjectExpenses.set(i, {"exchange_rate": Number(listModel_exchangeRates.get(k).exchange_rate)})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
calculateResults_members()
|
||||
}
|
||||
|
||||
function calculateResults_members () {
|
||||
listModel_activeProjectResults.clear()
|
||||
listModel_activeProjectResultsSettlement.clear()
|
||||
totalSpendingAmount_baseCurrency = 0
|
||||
|
||||
// sum up benefits per member
|
||||
for (var i = 0; i < listModel_activeProjectExpenses.count; i++) {
|
||||
var tempBeneficiariesArray = (listModel_activeProjectExpenses.get(i).expense_members).split(" ||| ")
|
||||
for (var j = 0; j < tempBeneficiariesArray.length; j++) {
|
||||
var tempExpense_inBaseCurrency_perBeneficiariy = ((Number(listModel_activeProjectExpenses.get(i).expense_sum) / tempBeneficiariesArray.length) * Number(listModel_activeProjectExpenses.get(i).exchange_rate))
|
||||
|
||||
// cycle through results list, check if beneficiary is already available and where
|
||||
var tempBeneficiaryAvailable = false
|
||||
var tempBeneficiaryIndex = 0
|
||||
for (var k = 0; k < listModel_activeProjectResults.count; k++ ) {
|
||||
if (listModel_activeProjectResults.get(k).beneficiary_name === tempBeneficiariesArray[j]) {
|
||||
tempBeneficiaryAvailable = true
|
||||
tempBeneficiaryIndex = k
|
||||
}
|
||||
}
|
||||
// if not available, add him to results list (only on first occurance)
|
||||
if (tempBeneficiaryAvailable === false) {
|
||||
//console.log("first entry for: " + tempBeneficiariesArray[j])
|
||||
//console.log(tempExpense_inBaseCurrency_perBeneficiariy + " " + activeProjectCurrency)
|
||||
listModel_activeProjectResults.append({ beneficiary_name : tempBeneficiariesArray[j],
|
||||
beneficiary_sum : Number(tempExpense_inBaseCurrency_perBeneficiariy),
|
||||
base_currency : activeProjectCurrency,
|
||||
expense_sum : 0 // this info gets added later
|
||||
})
|
||||
} else { // otherwise add his share to the existing list
|
||||
var tempBeneficiarySum = Number(listModel_activeProjectResults.get(tempBeneficiaryIndex).beneficiary_sum) + Number(tempExpense_inBaseCurrency_perBeneficiariy)
|
||||
listModel_activeProjectResults.set(tempBeneficiaryIndex, { "beneficiary_sum": Number(tempBeneficiarySum) })
|
||||
//console.log("latest benefitSum for " + tempBeneficiariesArray[j] + " is: " + tempBeneficiarySum)
|
||||
}
|
||||
//console.log(tempBeneficiariesArray[j])
|
||||
//console.log(tempExpense_inBaseCurrency_perBeneficiariy)
|
||||
}
|
||||
}
|
||||
|
||||
// once listModel_activeProjectResults is created, go over expenses again and add payments
|
||||
for ( i = 0; i < listModel_activeProjectExpenses.count; i++) {
|
||||
var tempExpensePayer = listModel_activeProjectExpenses.get(i).expense_payer
|
||||
var tempExpense_inBaseCurrency = Number(listModel_activeProjectExpenses.get(i).expense_sum) * Number(listModel_activeProjectExpenses.get(i).exchange_rate)
|
||||
totalSpendingAmount_baseCurrency += tempExpense_inBaseCurrency
|
||||
//console.log(tempExpensePayer)
|
||||
//console.log(tempExpense_inBaseCurrency)
|
||||
|
||||
// check if payer is already in results list as benefitter
|
||||
var tempPayerAvailable = false
|
||||
var tempPayerIndex = 0
|
||||
for (var l = 0; l < listModel_activeProjectResults.count; l++ ) {
|
||||
if (listModel_activeProjectResults.get(l).beneficiary_name === tempExpensePayer) {
|
||||
tempPayerAvailable = true
|
||||
tempPayerIndex = l
|
||||
}
|
||||
}
|
||||
|
||||
// if not available, add him to results list (only on first occurance)
|
||||
if (tempPayerAvailable === false) {
|
||||
//console.log("first entry for payer: " + tempExpensePayer + " = " + tempExpense_inBaseCurrency + " " + activeProjectCurrency)
|
||||
//console.log("seems he paid but never benefits from anything")
|
||||
listModel_activeProjectResults.append({ beneficiary_name : tempExpensePayer,
|
||||
beneficiary_sum : 0,
|
||||
base_currency : activeProjectCurrency,
|
||||
expense_sum : Number(tempExpense_inBaseCurrency),
|
||||
})
|
||||
} else { // otherwise add his share to the existing list
|
||||
var tempPayerSum = Number(listModel_activeProjectResults.get(tempPayerIndex).expense_sum) + Number(tempExpense_inBaseCurrency)
|
||||
listModel_activeProjectResults.set(tempPayerIndex, { "expense_sum": Number(tempPayerSum) })
|
||||
//console.log("latest payerSum for " + tempExpensePayer + " is: " + tempPayerSum)
|
||||
}
|
||||
}
|
||||
|
||||
// sort results list according
|
||||
listModel_activeProjectResults.quick_sort("desc") // payer with highest expense on top
|
||||
|
||||
|
||||
// apply a (n-1) algorithm to settle expenses (how much each person ows to whom)
|
||||
var outstandingNamesArray = []
|
||||
var outstandingSumsArray = []
|
||||
var totalSum = 0
|
||||
// iter backwards to get smallest amounts first, since listModel_activeProjectResults starts with highest expenses
|
||||
for (var m = listModel_activeProjectResults.count-1; m >= 0; m--) {
|
||||
outstandingNamesArray.push(listModel_activeProjectResults.get(m).beneficiary_name)
|
||||
outstandingSumsArray.push( Number(listModel_activeProjectResults.get(m).expense_sum) - Number(listModel_activeProjectResults.get(m).beneficiary_sum) )
|
||||
totalSum += ( Number(listModel_activeProjectResults.get(m).expense_sum) - Number(listModel_activeProjectResults.get(m).beneficiary_sum) )
|
||||
}
|
||||
//console.log(outstandingNamesArray)
|
||||
//console.log(outstandingSumsArray)
|
||||
//console.log(totalSum)
|
||||
|
||||
function splitPayments() {
|
||||
const mean = totalSum / outstandingNamesArray.length
|
||||
var sortedValuesPaid = []
|
||||
for (var n = 0; n < outstandingSumsArray.length; n++) {
|
||||
sortedValuesPaid.push(outstandingSumsArray[n] - mean)
|
||||
}
|
||||
var i = 0
|
||||
var j = outstandingNamesArray.length - 1
|
||||
var debt
|
||||
while (i < j) {
|
||||
debt = Math.min(-(sortedValuesPaid[i]), sortedValuesPaid[j])
|
||||
sortedValuesPaid[i] += debt
|
||||
sortedValuesPaid[j] -= debt
|
||||
listModel_activeProjectResultsSettlement.append({ from_name : outstandingNamesArray[i],
|
||||
to_name : outstandingNamesArray[j],
|
||||
settling_sum : Number(debt),
|
||||
})
|
||||
//console.log(outstandingNamesArray[i] + " owes " + outstandingNamesArray[j] + " the sum of " + debt )
|
||||
if (sortedValuesPaid[i] === 0) { i++ }
|
||||
if (sortedValuesPaid[j] === 0) { j-- }
|
||||
}
|
||||
}
|
||||
splitPayments()
|
||||
}
|
||||
|
||||
function createShareString (detailGrade) {
|
||||
|
||||
// create a shareable string
|
||||
toShareString = "\n" + qsTr("Project:") + " " + activeProjectName
|
||||
+ "\n" + qsTr("Total expenses") + " = "
|
||||
+ totalSpendingAmount_baseCurrency.toFixed(2) + " " + activeProjectCurrency
|
||||
+ "\n" + "\n"
|
||||
for (var i = 0; i < listModel_activeProjectResults.count; i++) {
|
||||
toShareString += listModel_activeProjectResults.get(i).beneficiary_name + ":"
|
||||
+ "\n" + qsTr("payed") + " " + listModel_activeProjectResults.get(i).expense_sum.toFixed(2) + " " + activeProjectCurrency
|
||||
+ "\n" + qsTr("received") + " " + listModel_activeProjectResults.get(i).beneficiary_sum.toFixed(2) + " " + activeProjectCurrency
|
||||
+ "\n" + qsTr("saldo") + " " + (Number(listModel_activeProjectResults.get(i).expense_sum) - Number(listModel_activeProjectResults.get(i).beneficiary_sum)).toFixed(2) + " " + activeProjectCurrency
|
||||
+ "\n" + "\n"
|
||||
}
|
||||
toShareString += qsTr("Settlement suggestion:")
|
||||
+ "\n"
|
||||
for (i = 0; i < listModel_activeProjectResultsSettlement.count; i++) {
|
||||
if ( Number(listModel_activeProjectResultsSettlement.get(i).settling_sum).toFixed(2) >= 0 ) {
|
||||
toShareString += listModel_activeProjectResultsSettlement.get(i).from_name
|
||||
+ " " + qsTr("owes") + " "
|
||||
+ listModel_activeProjectResultsSettlement.get(i).to_name
|
||||
+ " " + qsTr("the sum of") + " " + Number(listModel_activeProjectResultsSettlement.get(i).settling_sum).toFixed(2) + " " + activeProjectCurrency
|
||||
+ "." + "\n"
|
||||
} else { // amount < 0
|
||||
toShareString += listModel_activeProjectResultsSettlement.get(i).to_name
|
||||
+ " " + qsTr("owes") + " "
|
||||
+ listModel_activeProjectResultsSettlement.get(i).from_name
|
||||
+ " " + qsTr("the sum of") + " " + (-1*Number(listModel_activeProjectResultsSettlement.get(i).settling_sum).toFixed(2)) + " " + activeProjectCurrency
|
||||
+ "." + "\n"
|
||||
}
|
||||
}
|
||||
//console.log(toShareString)
|
||||
|
||||
|
||||
// add details if necessary
|
||||
if (detailGrade === "detailed") {
|
||||
toShareString += "\n" + "\n" + "\n"
|
||||
+ qsTr("Detailed Spendings:")
|
||||
+ "\n" + "\n"
|
||||
for ( i = 0; i < listModel_activeProjectExpenses.count; i++) {
|
||||
if (sortOrderExpenses === 0) { // 0=descending, 1=ascending
|
||||
var tmpEntryNumber = (listModel_activeProjectExpenses.count - i)
|
||||
} else {
|
||||
tmpEntryNumber = (i+1)
|
||||
}
|
||||
toShareString += qsTr("Expense #") + tmpEntryNumber
|
||||
+ "\n" + qsTr("date:") + " " + new Date(Number(listModel_activeProjectExpenses.get(i).date_time)).toLocaleString(Qt.locale(), "ddd dd.MM.yyyy - hh:mm")
|
||||
+ "\n" + qsTr("payer:") + " " + listModel_activeProjectExpenses.get(i).expense_payer
|
||||
+ "\n" + qsTr("item:") + " " + listModel_activeProjectExpenses.get(i).expense_name
|
||||
+ "\n" + qsTr("price:") + " " + Number(listModel_activeProjectExpenses.get(i).expense_sum).toFixed(2) + " " + listModel_activeProjectExpenses.get(i).expense_currency
|
||||
+ "\n" + qsTr("beneficiaries:") + " " + listModel_activeProjectExpenses.get(i).expense_members.split(" ||| ").join(", ") // .replace(" ||| ", ", ")
|
||||
+ "\n" + qsTr("info:") + " " + listModel_activeProjectExpenses.get(i).expense_info
|
||||
+ "\n" + "\n"
|
||||
}
|
||||
}
|
||||
console.log(toShareString)
|
||||
|
||||
// send this string
|
||||
Clipboard.text = toShareString
|
||||
shareActionText.mimeType = "text/plain" // "text/*" or "application/text"
|
||||
shareActionText.resources = [{
|
||||
"data": toShareString,
|
||||
"name": activeProjectName,
|
||||
} ]
|
||||
shareActionText.trigger()
|
||||
}
|
||||
|
||||
}
|
445
qml/pages/FirstPage.qml
Normal file
445
qml/pages/FirstPage.qml
Normal file
|
@ -0,0 +1,445 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import Sailfish.Share 1.0 // ToDo: instead of copy to clipboard, send to app
|
||||
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
|
||||
// project specific global variables, loaded when activating a new project
|
||||
property double activeProjectID_unixtime : Number(storageItem.getSettings("activeProjectID_unixtime", 0))
|
||||
property int activeProjectID_listIndex
|
||||
property string activeProjectName
|
||||
property string activeProjectCurrency : "EUR"
|
||||
|
||||
|
||||
// program specific global variables
|
||||
property int sortOrderExpenses : Number(storageItem.getSettings("sortOrderExpensesIndex", 0)) // 0=descending, 1=ascending
|
||||
property int exchangeRateMode : Number(storageItem.getSettings("exchangeRateModeIndex", 0)) // 0=collective, 1=individual
|
||||
property int interativeScrollbarMode : Number(storageItem.getSettings("interativeScrollbarMode", 0)) // 0=standard, 1=interactive
|
||||
property string recentlyUsedCurrency : storageItem.getSettings("recentlyUsedCurrency", activeProjectCurrency)
|
||||
|
||||
// navigation specific blocking
|
||||
property bool updateEvenWhenCanceled : false
|
||||
property bool delegateMenuOpen : false
|
||||
|
||||
|
||||
// autostart
|
||||
Component.onCompleted: {
|
||||
generateAllProjectsList_FromDB()
|
||||
loadActiveProjectInfos_FromDB(activeProjectID_unixtime)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// other items, components and pages
|
||||
ListModel {
|
||||
id: listModel_allProjects
|
||||
}
|
||||
ListModel {
|
||||
id: listModel_activeProjectMembers
|
||||
}
|
||||
ListModel {
|
||||
id: listModel_activeProjectExpenses
|
||||
property string sortColumnName: "date_time" //"id_unixtime_created"
|
||||
function swap(a,b) {
|
||||
if (a<b) {
|
||||
move(a,b,1);
|
||||
move (b-1,a,1);
|
||||
}
|
||||
else if (a>b) {
|
||||
move(b,a,1);
|
||||
move (a-1,b,1);
|
||||
}
|
||||
}
|
||||
function partition(begin, end, pivot) {
|
||||
var piv=get(pivot)[sortColumnName];
|
||||
swap(pivot, end-1);
|
||||
var store=begin;
|
||||
var ix;
|
||||
for(ix=begin; ix<end-1; ++ix) {
|
||||
if (sortOrderExpenses === 1){
|
||||
if(get(ix)[sortColumnName] < piv) {
|
||||
swap(store,ix);
|
||||
++store;
|
||||
}
|
||||
} else { // (sortOrderExpenses === 0)
|
||||
if(get(ix)[sortColumnName] > piv) {
|
||||
swap(store,ix);
|
||||
++store;
|
||||
}
|
||||
}
|
||||
}
|
||||
swap(end-1, store);
|
||||
return store;
|
||||
}
|
||||
function qsort(begin, end) {
|
||||
if(end-1>begin) {
|
||||
var pivot=begin+Math.floor(Math.random()*(end-begin));
|
||||
|
||||
pivot=partition( begin, end, pivot);
|
||||
|
||||
qsort(begin, pivot);
|
||||
qsort(pivot+1, end);
|
||||
}
|
||||
}
|
||||
function quick_sort() {
|
||||
qsort(0,count)
|
||||
}
|
||||
|
||||
onCountChanged: {
|
||||
quick_sort()
|
||||
}
|
||||
}
|
||||
ListModel {
|
||||
id: listModel_exchangeRates
|
||||
}
|
||||
ListModel {
|
||||
id: listModel_activeProjectResults
|
||||
property string sortColumnName : "expense_sum"
|
||||
property string sortOrderResults : "desc" //"asc"
|
||||
function swap(a,b) {
|
||||
if (a<b) {
|
||||
move(a,b,1);
|
||||
move (b-1,a,1);
|
||||
}
|
||||
else if (a>b) {
|
||||
move(b,a,1);
|
||||
move (a-1,b,1);
|
||||
}
|
||||
}
|
||||
function partition(begin, end, pivot) {
|
||||
var piv=get(pivot)[sortColumnName];
|
||||
swap(pivot, end-1);
|
||||
var store=begin;
|
||||
var ix;
|
||||
for(ix=begin; ix<end-1; ++ix) {
|
||||
if (sortOrderResults === "asc"){
|
||||
if(get(ix)[sortColumnName] < piv) {
|
||||
swap(store,ix);
|
||||
++store;
|
||||
}
|
||||
} else { // (sortOrderResults === "desc")
|
||||
if(get(ix)[sortColumnName] > piv) {
|
||||
swap(store,ix);
|
||||
++store;
|
||||
}
|
||||
}
|
||||
}
|
||||
swap(end-1, store);
|
||||
return store;
|
||||
}
|
||||
function qsort(begin, end) {
|
||||
if(end-1>begin) {
|
||||
var pivot=begin+Math.floor(Math.random()*(end-begin));
|
||||
|
||||
pivot=partition( begin, end, pivot);
|
||||
|
||||
qsort(begin, pivot);
|
||||
qsort(pivot+1, end);
|
||||
}
|
||||
}
|
||||
function quick_sort(orderDirection) {
|
||||
sortOrderResults = orderDirection
|
||||
qsort(0,count)
|
||||
}
|
||||
}
|
||||
SettingsPage {
|
||||
id: settingsPage
|
||||
}
|
||||
CalcPage {
|
||||
id: calcPage
|
||||
}
|
||||
BannerAddExpense {
|
||||
id: bannerAddExpense
|
||||
}
|
||||
Component {
|
||||
id: datePickerComponent
|
||||
DatePickerDialog {}
|
||||
}
|
||||
Component {
|
||||
id: timePickerComponent
|
||||
TimePickerDialog {}
|
||||
}
|
||||
ShareAction {
|
||||
id: shareActionText
|
||||
}
|
||||
|
||||
|
||||
|
||||
// main page, current project
|
||||
SilicaListView {
|
||||
id: idSilicaListView
|
||||
anchors.fill: parent
|
||||
header: Row {
|
||||
width: (interativeScrollbarMode === 0) ? (parent.width) : ((isPortrait) ? (parent.width) : (parent.width - Theme.paddingLarge*2))
|
||||
visible: activeProjectID_unixtime !== 0
|
||||
topPadding: Theme.paddingLarge
|
||||
bottomPadding: Theme.paddingLarge
|
||||
|
||||
|
||||
Item {
|
||||
id: idLeftSpacer
|
||||
width: Theme.paddingSmall + Theme.paddingMedium
|
||||
height: 1
|
||||
}
|
||||
Column {
|
||||
id: idHeaderInfoColumn
|
||||
width: parent.width - idLeftSpacer.width
|
||||
bottomPadding: Theme.paddingSmall
|
||||
|
||||
Label {
|
||||
x: Theme.paddingMedium
|
||||
width: parent.width - 2*x
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.highlightColor
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Expenses")
|
||||
}
|
||||
Label {
|
||||
x: Theme.paddingMedium
|
||||
width: parent.width - 2*x
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.highlightColor
|
||||
wrapMode: Text.WordWrap
|
||||
//text: "ID_" + activeProjectID_unixtime + " [" + activeProjectCurrency + "]"
|
||||
text: activeProjectName + " [" + activeProjectCurrency + "]"
|
||||
}
|
||||
}
|
||||
}
|
||||
footer: Item {
|
||||
width: parent.width
|
||||
height: Theme.itemSizeSmall
|
||||
}
|
||||
spacing: Theme.paddingMedium
|
||||
quickScroll: (interativeScrollbarMode === 0) ? (true) : (false)
|
||||
|
||||
VerticalScrollDecorator {
|
||||
enabled: (interativeScrollbarMode === 0) ? (true) : (false)
|
||||
visible: enabled
|
||||
}
|
||||
ScrollBar {
|
||||
id: idScrollBarDate
|
||||
enabled: (interativeScrollbarMode === 0) ? false : true
|
||||
labelVisible: true
|
||||
topPadding: (isPortrait) ? (Theme.itemSizeLarge + Theme.paddingLarge) : (0)
|
||||
bottomPadding: (isPortrait) ? Theme.itemSizeSmall : 0
|
||||
labelModelTag: "date_time"
|
||||
visible: (interativeScrollbarMode === 0) ? (false) : (idPulldownMenu.active === false) && (delegateMenuOpen === false)
|
||||
}
|
||||
PullDownMenu {
|
||||
id: idPulldownMenu
|
||||
quickSelect: true
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Settings")
|
||||
onClicked: pageStack.push(settingsPage)
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Calculate")
|
||||
enabled: activeProjectID_unixtime !== 0
|
||||
onClicked: pageStack.animatorPush(calcPage)
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Add")
|
||||
enabled: activeProjectID_unixtime !== 0
|
||||
onClicked: bannerAddExpense.notify( Theme.rgba(Theme.highlightDimmerColor, 1), Theme.itemSizeLarge, "new", activeProjectID_unixtime, 0 )
|
||||
}
|
||||
}
|
||||
ViewPlaceholder {
|
||||
enabled: activeProjectID_unixtime === 0 // listModel_allProjects.count === 0
|
||||
text: qsTr("Create new project.")
|
||||
hintText: qsTr("Nothing loaded yet.")
|
||||
}
|
||||
|
||||
model: listModel_activeProjectExpenses
|
||||
delegate: ListItem {
|
||||
id: idListItem
|
||||
contentHeight: idListLabelsQML.height
|
||||
contentWidth: (interativeScrollbarMode === 0) ? (parent.width) : (parent.width - idScrollBarDate.width)
|
||||
//contentWidth: (idSilicaListView.visibleArea.heightRatio < 1.0 && idPulldownMenu.active === false) ? (parent.width - idScrollBarDate.width) : (parent.width)
|
||||
menu: ContextMenu {
|
||||
id: idContextMenu
|
||||
|
||||
MenuItem {
|
||||
text: qsTr("Edit")
|
||||
onClicked: {
|
||||
bannerAddExpense.notify( Theme.rgba(Theme.highlightDimmerColor, 1), Theme.itemSizeLarge, "edit", activeProjectID_unixtime, id_unixtime_created )
|
||||
}
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("Remove")
|
||||
onClicked: {
|
||||
idRemorseDelete.execute(idListItem, qsTr("Remove entry?"), function() {
|
||||
storageItem.deleteExpense(activeProjectID_unixtime, id_unixtime_created )
|
||||
listModel_activeProjectExpenses.remove(index)
|
||||
} )
|
||||
}
|
||||
}
|
||||
}
|
||||
onMenuOpenChanged: {
|
||||
// set variable to disable scrollBar visibility
|
||||
if (menuOpen === true) {
|
||||
delegateMenuOpen = true
|
||||
} else {
|
||||
delegateMenuOpen = false
|
||||
}
|
||||
}
|
||||
|
||||
RemorseItem {
|
||||
id: idRemorseDelete
|
||||
}
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Rectangle {
|
||||
width: Theme.paddingSmall
|
||||
height: idListLabelsQML.height
|
||||
color: Theme.rgba(Theme.highlightBackgroundColor, 0.4)
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingMedium
|
||||
height: 1
|
||||
}
|
||||
Column {
|
||||
id: idListLabelsQML
|
||||
width: parent.width - Theme.paddingSmall - 2*Theme.paddingMedium
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Label {
|
||||
width: parent.width/3*2- Theme.paddingLarge/2
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: new Date(Number(date_time)).toLocaleString(Qt.locale(), "ddd dd.MM.yyyy - hh:mm")
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingLarge
|
||||
height: 1
|
||||
}
|
||||
Label {
|
||||
width: parent.width/3 - Theme.paddingLarge/2
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: expense_payer
|
||||
}
|
||||
}
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
Label {
|
||||
width: parent.width/3*2 - Theme.paddingLarge/2
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: expense_name
|
||||
}
|
||||
Item {
|
||||
width: Theme.paddingLarge
|
||||
height: 1
|
||||
}
|
||||
Label {
|
||||
width: parent.width/3 - Theme.paddingLarge/2
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignRight
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
text: Number(expense_sum).toFixed(2) + " " + expense_currency.toString()
|
||||
}
|
||||
}
|
||||
Label {
|
||||
id: idLabelExpenseInfo
|
||||
visible: idLabelExpenseInfo.text.length > 0
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.fontSizeTiny
|
||||
color: Theme.secondaryColor
|
||||
text: expense_info
|
||||
}
|
||||
Label {
|
||||
id: idLabelBeneficiaries
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
font.pixelSize: Theme.fontSizeTiny
|
||||
color: Theme.secondaryColor
|
||||
text: qsTr("group:") + " " + expense_members.split(" ||| ").join(", ")
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.paddingSmall
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // end SilicaListView
|
||||
|
||||
|
||||
|
||||
|
||||
function generateAllProjectsList_FromDB() {
|
||||
listModel_allProjects.clear()
|
||||
var allProjectsOverview = storageItem.getAllProjects("none")
|
||||
//console.log(allProjectsOverview)
|
||||
if (allProjectsOverview !== "none") {
|
||||
for (var i = 0; i < allProjectsOverview.length ; i++) {
|
||||
|
||||
listModel_allProjects.append({
|
||||
project_id_timestamp : Number(allProjectsOverview[i][0]),
|
||||
project_name : allProjectsOverview[i][1],
|
||||
project_members : allProjectsOverview[i][2],
|
||||
project_recent_payer_boolarray : allProjectsOverview[i][3],
|
||||
project_recent_beneficiaries_boolarray : allProjectsOverview[i][4],
|
||||
project_base_currency : allProjectsOverview[i][5],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
function loadActiveProjectInfos_FromDB(activeProjectID_unixtime) {
|
||||
//console.log( "loading project: " + Number(activeProjectID_unixtime) )
|
||||
listModel_activeProjectMembers.clear()
|
||||
listModel_activeProjectExpenses.clear()
|
||||
for (var j = 0; j < listModel_allProjects.count ; j++) {
|
||||
//console.log("in listmodel: " + Number(listModel_allProjects.get(j).project_id_timestamp) )
|
||||
// only use active project infos
|
||||
if ( Number(listModel_allProjects.get(j).project_id_timestamp) === Number(activeProjectID_unixtime) ) {
|
||||
// find active project name and currency
|
||||
activeProjectName = listModel_allProjects.get(j).project_name
|
||||
activeProjectID_listIndex = j
|
||||
activeProjectCurrency = listModel_allProjects.get(j).project_base_currency
|
||||
|
||||
// generate active project members list
|
||||
var activeProjectMembersArray = (listModel_allProjects.get(j).project_members).split(" ||| ")
|
||||
var activeProjectRecentPayerBoolArray = (listModel_allProjects.get(j).project_recent_payer_boolarray).split(" ||| ")
|
||||
var activeProjectRecentBeneficiariesBoolArray = (listModel_allProjects.get(j).project_recent_beneficiaries_boolarray).split(" ||| ")
|
||||
for (var i = 0; i < activeProjectMembersArray.length ; i++) {
|
||||
listModel_activeProjectMembers.append({
|
||||
member_name : activeProjectMembersArray[i],
|
||||
member_isBeneficiary : activeProjectRecentBeneficiariesBoolArray[i],
|
||||
member_isPayer : activeProjectRecentPayerBoolArray[i],
|
||||
})
|
||||
}
|
||||
|
||||
// generate active project expenses list
|
||||
var currentProjectEntries = storageItem.getAllExpenses( activeProjectID_unixtime, "none")
|
||||
if (currentProjectEntries !== "none") {
|
||||
for (i = 0; i < currentProjectEntries.length ; i++) {
|
||||
listModel_activeProjectExpenses.append({
|
||||
id_unixtime_created : Number(currentProjectEntries[i][0]).toFixed(0),
|
||||
date_time : Number(currentProjectEntries[i][1]).toFixed(0),
|
||||
expense_name : currentProjectEntries[i][2],
|
||||
expense_sum : Number(currentProjectEntries[i][3]).toFixed(2),
|
||||
expense_currency : currentProjectEntries[i][4],
|
||||
expense_info : currentProjectEntries[i][5],
|
||||
expense_payer : currentProjectEntries[i][6],
|
||||
expense_members : currentProjectEntries[i][7],
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
106
qml/pages/ScrollBar.qml
Executable file
106
qml/pages/ScrollBar.qml
Executable file
|
@ -0,0 +1,106 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
|
||||
|
||||
Rectangle {
|
||||
// inherited values
|
||||
property var flickable: parent
|
||||
property bool labelVisible : true
|
||||
property string labelModelTag : ""
|
||||
property real topPadding : 0
|
||||
property real bottomPadding : 0
|
||||
property color handleColor : Theme.highlightBackgroundColor
|
||||
|
||||
// own values but overwritable
|
||||
property int scrollToNumber : 0
|
||||
property string showLabel : ""
|
||||
property int listcountMax : flickable.count // (flickable.count !== undefined) ? flickable.count : 0 // dirty bugfix for scrollBar wron position on empty lists
|
||||
property real roundCornersRadius : Theme.paddingLarge
|
||||
|
||||
id: scrollbar
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: topPadding
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: bottomPadding
|
||||
anchors.right: parent.right
|
||||
width: roundCornersRadius * 2
|
||||
radius: width / 2
|
||||
color: Theme.rgba(Theme.primaryColor, 0.075)
|
||||
visible: flickable.visibleArea.heightRatio < 1.0
|
||||
|
||||
Rectangle {
|
||||
id: handle
|
||||
width: parent.width
|
||||
height: Math.max(roundCornersRadius*2, flickable.visibleArea.heightRatio * scrollbar.height)
|
||||
color: handleColor
|
||||
opacity: (clicker.drag.active ) ? 1 : 0.4
|
||||
radius: width / 2
|
||||
}
|
||||
Rectangle {
|
||||
id: scrollLabelBackground
|
||||
visible: labelVisible
|
||||
anchors.verticalCenter: handle.verticalCenter
|
||||
anchors.right: handle.left
|
||||
anchors.rightMargin: Theme.paddingLarge * 1.5
|
||||
width: scrollLabel.width + height*1.2
|
||||
height: roundCornersRadius * 2
|
||||
radius: roundCornersRadius
|
||||
color: handleColor
|
||||
opacity: (clicker.drag.active ) ? 1 : 0
|
||||
|
||||
Label {
|
||||
id: scrollLabel
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
//text: showLabel
|
||||
text: new Date(Number(showLabel)).toLocaleString(Qt.locale(), "dd.MM.yyyy - hh:mm")
|
||||
}
|
||||
Rectangle {
|
||||
anchors.left: parent.right
|
||||
anchors.leftMargin: -roundCornersRadius
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.anchors.rightMargin + 2*roundCornersRadius
|
||||
height: Theme.paddingSmall/3*2
|
||||
color: parent.color
|
||||
}
|
||||
}
|
||||
Binding {
|
||||
// jump handle to where the currently visible part of flickable ist
|
||||
target: handle
|
||||
property: "y"
|
||||
value: (flickable.visibleArea.yPosition) * (scrollbar.height - ( handle.height - (flickable.visibleArea.heightRatio * scrollbar.height)))
|
||||
when: !clicker.pressed
|
||||
}
|
||||
MouseArea {
|
||||
id: clicker
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: -Theme.paddingSmall
|
||||
anchors.leftMargin: -Theme.paddingSmall
|
||||
preventStealing: true
|
||||
drag {
|
||||
target: handle
|
||||
minimumY: 0
|
||||
maximumY: scrollbar.height - handle.height
|
||||
axis: Drag.YAxis
|
||||
}
|
||||
onMouseYChanged: {
|
||||
//flickable.contentY = handle.y / drag.maximumY * (flickable.contentHeight - flickable.height)
|
||||
scrollToNumber = Math.ceil(handle.y / drag.maximumY * listcountMax)
|
||||
if (scrollToNumber < 0) {
|
||||
scrollToNumber = 0
|
||||
} else if (scrollToNumber >= listcountMax) {
|
||||
scrollToNumber = listcountMax -1 // because index starts at 0, while count() starts at 1
|
||||
}
|
||||
showLabel = (flickable.model.get(scrollToNumber)[labelModelTag] !== undefined) ? flickable.model.get(scrollToNumber)[labelModelTag] : "" //flickable.model.get(scrollToNumber)[labelModelTag]
|
||||
flickable.positionViewAtIndex( scrollToNumber, ListView.Center)
|
||||
//flickable.contentY = handle.y / drag.maximumY * (flickable.contentHeight - flickable.height)
|
||||
}
|
||||
onClicked: {
|
||||
//flickable.contentY = mouse.y / scrollbar.height * (flickable.contentHeight - flickable.height)
|
||||
scrollToNumber = Math.ceil(mouse.y / scrollbar.height * listcountMax)
|
||||
showLabel = (flickable.model.get(scrollToNumber)[labelModelTag] !== undefined) ? flickable.model.get(scrollToNumber)[labelModelTag] : "" //flickable.model.get(scrollToNumber)[labelModelTag]
|
||||
flickable.positionViewAtIndex( scrollToNumber, ListView.Center )
|
||||
}
|
||||
//onReleased: console.log("released")
|
||||
}
|
||||
}
|
456
qml/pages/SettingsPage.qml
Normal file
456
qml/pages/SettingsPage.qml
Normal file
|
@ -0,0 +1,456 @@
|
|||
import QtQuick 2.6
|
||||
import Sailfish.Silica 1.0
|
||||
import Sailfish.Pickers 1.0
|
||||
import FileIO 1.0
|
||||
import Nemo.Notifications 1.0
|
||||
|
||||
|
||||
|
||||
Dialog {
|
||||
id: pageSettings
|
||||
|
||||
property int showMaintenanceButtonsCounter : 0
|
||||
property string notificationString : ""
|
||||
//property int oldProjectIndex
|
||||
|
||||
allowedOrientations: Orientation.Portrait
|
||||
backNavigation: (bannerAddProject.opacity < 1) // && (updateEvenWhenCanceled === false)
|
||||
forwardNavigation: (bannerAddProject.opacity < 1)
|
||||
onOpened: {
|
||||
//console.log("old project index = " + activeProjectID_unixtime)
|
||||
showMaintenanceButtonsCounter = 0
|
||||
updateEvenWhenCanceled = false
|
||||
idComboboxProject.currentIndex = 0
|
||||
for (var j = 0; j < listModel_allProjects.count ; j++) {
|
||||
if (Number(listModel_allProjects.get(j).project_id_timestamp) === Number(storageItem.getSettings("activeProjectID_unixtime", 0))) {
|
||||
idComboboxProject.currentIndex = j
|
||||
}
|
||||
}
|
||||
idComboboxSortingExpenses.currentIndex = Number(storageItem.getSettings("sortOrderExpensesIndex", 0)) // 0=descending, 1=ascending
|
||||
idComboboxExchangeRateMode.currentIndex = Number(storageItem.getSettings("exchangeRateModeIndex", 0)) // 0=collective, 1=individual
|
||||
idComboboxShowInteractiveScrollbar.currentIndex = Number(storageItem.getSettings("interativeScrollbarMode", 0)) //0=standard, 1=interactive
|
||||
notificationString = ""
|
||||
}
|
||||
onDone: {
|
||||
if (result == DialogResult.Accepted) {
|
||||
writeDB_Settings()
|
||||
}
|
||||
}
|
||||
onRejected: {
|
||||
// in certain cases reload list even on cancel: if project was cleared, delted or created
|
||||
// then use previous activeProjectID_unixtime instead of the new one from dropdown menu
|
||||
if (updateEvenWhenCanceled === true) {
|
||||
loadActiveProjectInfos_FromDB(activeProjectID_unixtime)
|
||||
updateEvenWhenCanceled = false
|
||||
}
|
||||
}
|
||||
|
||||
BannerAddProject {
|
||||
id: bannerAddProject
|
||||
}
|
||||
Banner2ButtonsChoice {
|
||||
id: banner2ButtonsChoice
|
||||
}
|
||||
Notification {
|
||||
id: notificationBackup
|
||||
expireTimeout: 4000
|
||||
//appName: qsTr("Expenditure")
|
||||
//icon: "image://theme/icon-lock-warning"
|
||||
|
||||
function showSmall(message) {
|
||||
replacesId = 0
|
||||
previewSummary = ""
|
||||
previewBody = message
|
||||
publish()
|
||||
}
|
||||
function showBig(title, message) {
|
||||
replacesId = 0
|
||||
previewSummary = title
|
||||
previewBody = message
|
||||
publish()
|
||||
}
|
||||
}
|
||||
RemorsePopup {
|
||||
z: 10
|
||||
id: remorse_clearExchangeRatesDB
|
||||
}
|
||||
RemorsePopup {
|
||||
z: 10
|
||||
id: remorse_restoreBackupFile
|
||||
}
|
||||
ListModel {
|
||||
id: listModel_tempProjectExpenses
|
||||
}
|
||||
Component {
|
||||
id: idFolderPickerPage
|
||||
|
||||
FolderPickerPage {
|
||||
dialogTitle: qsTr("Backup to")
|
||||
onSelectedPathChanged: {
|
||||
backupProjectExpenses( selectedPath )
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: idFilePickerPage
|
||||
|
||||
FilePickerPage {
|
||||
title: qsTr("Restore backup file")
|
||||
nameFilters: [ '*.csv' ]
|
||||
onSelectedContentPropertiesChanged: {
|
||||
var selectedPath = selectedContentProperties.filePath
|
||||
idTextFileBackup.source = selectedPath
|
||||
var tempProjectIndex_File = ((idTextFileBackup.text).split("\n"))[0]
|
||||
var tempProjectIndex = listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp
|
||||
var headlineText = qsTr("Restore backup - choose action")
|
||||
var otherText = qsTr("Replace deletes all former project-expenses and uses those from backup-file instead.") + " "
|
||||
+ qsTr("Merge keeps former project-expenses and adds those from backup-file which are not yet on the list.")
|
||||
if (parseInt(tempProjectIndex) !== parseInt(tempProjectIndex_File)) {
|
||||
var detailText = qsTr("File info: This backup was created by a different project.")
|
||||
} else {
|
||||
detailText = qsTr("File info: This backup was created by the original project.")
|
||||
}
|
||||
var choiceText_1 = qsTr("Replace")
|
||||
var choiceText_2 = qsTr("Merge")
|
||||
banner2ButtonsChoice.notify( Theme.rgba(Theme.highlightDimmerColor, 1), Theme.itemSizeLarge, headlineText, detailText, otherText, choiceText_1, choiceText_2, selectedPath )
|
||||
}
|
||||
}
|
||||
}
|
||||
TextFileIO {
|
||||
id: idTextFileBackup
|
||||
}
|
||||
|
||||
|
||||
|
||||
SilicaFlickable{
|
||||
anchors.fill: parent
|
||||
contentHeight: column.height // tell overall height
|
||||
|
||||
Column {
|
||||
id: column
|
||||
width: pageSettings.width
|
||||
|
||||
DialogHeader {
|
||||
//title: qsTr("Settings")
|
||||
}
|
||||
Row {
|
||||
width: parent.width
|
||||
bottomPadding: Theme.paddingLarge
|
||||
|
||||
Label {
|
||||
id: idLabelSettingsHeader
|
||||
width: parent.width / 6 * 5
|
||||
leftPadding: Theme.paddingLarge
|
||||
font.pixelSize: Theme.fontSizeExtraLarge
|
||||
color: Theme.highlightColor
|
||||
text: qsTr("Settings")
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: showMaintenanceButtonsCounter = showMaintenanceButtonsCounter + 1
|
||||
}
|
||||
}
|
||||
IconButton {
|
||||
width: parent.width / 6
|
||||
anchors.verticalCenter: idLabelSettingsHeader.verticalCenter
|
||||
icon.color: Theme.highlightColor
|
||||
icon.scale: 1.1
|
||||
icon.source: "image://theme/icon-m-about?"
|
||||
onClicked: {
|
||||
pageStack.animatorPush(Qt.resolvedUrl("AboutPage.qml"), {})
|
||||
}
|
||||
}
|
||||
}
|
||||
Row {
|
||||
width: parent.width
|
||||
|
||||
ComboBox {
|
||||
id: idComboboxProject
|
||||
width: (listModel_allProjects.count === 0) ? (parent.width / 6*5) : (parent.width / 6*4)
|
||||
label: qsTr("Project")
|
||||
menu: ContextMenu {
|
||||
|
||||
Repeater {
|
||||
enabled: listModel_allProjects.count > 0
|
||||
model: listModel_allProjects
|
||||
|
||||
MenuItem {
|
||||
text: project_name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
enabled: listModel_allProjects.count === 0
|
||||
anchors.fill: parent
|
||||
preventStealing: true
|
||||
onClicked: bannerAddProject.notify( Theme.rgba(Theme.highlightDimmerColor, 1), Theme.itemSizeLarge, "new", idComboboxProject.currentIndex )
|
||||
}
|
||||
}
|
||||
IconButton {
|
||||
visible: listModel_allProjects.count > 0
|
||||
width: parent.width / 6
|
||||
icon.source: "image://theme/icon-m-edit?"
|
||||
onClicked: {
|
||||
bannerAddProject.notify( Theme.rgba(Theme.highlightDimmerColor, 1), Theme.itemSizeLarge, "edit", idComboboxProject.currentIndex )
|
||||
}
|
||||
}
|
||||
IconButton {
|
||||
width: parent.width / 6
|
||||
icon.source: "image://theme/icon-m-add?"
|
||||
onClicked: {
|
||||
bannerAddProject.notify( Theme.rgba(Theme.highlightDimmerColor, 1), Theme.itemSizeLarge, "new", idComboboxProject.currentIndex )
|
||||
}
|
||||
}
|
||||
}
|
||||
ComboBox {
|
||||
id: idComboboxSortingExpenses
|
||||
width: parent.width
|
||||
label: qsTr("Sorting")
|
||||
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
text: qsTr("descending")
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("ascending")
|
||||
}
|
||||
}
|
||||
}
|
||||
ComboBox {
|
||||
id: idComboboxExchangeRateMode
|
||||
width: parent.width
|
||||
label: qsTr("Exchange rate")
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
text: qsTr("per currency (constant)")
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("per transaction (dates)")
|
||||
}
|
||||
}
|
||||
}
|
||||
ComboBox {
|
||||
id: idComboboxShowInteractiveScrollbar
|
||||
width: parent.width
|
||||
label: qsTr("Scrollbar")
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
text: qsTr("normal")
|
||||
}
|
||||
MenuItem {
|
||||
text: qsTr("interactive (beta)")
|
||||
}
|
||||
}
|
||||
}
|
||||
Column {
|
||||
visible: showMaintenanceButtonsCounter > 9
|
||||
width: parent.width
|
||||
topPadding: Theme.paddingLarge * 3
|
||||
spacing: Theme.paddingLarge
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: Theme.errorColor
|
||||
text: qsTr("Database cleanup - requires restart:")
|
||||
leftPadding: Theme.paddingLarge + Theme.paddingSmall
|
||||
rightPadding: leftPadding
|
||||
}
|
||||
Button {
|
||||
text: qsTr("settings")
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onClicked: {
|
||||
remorse_clearExchangeRatesDB.execute(qsTr("Delete stored settings?"), function() {
|
||||
storageItem.removeFullTable( "settings_table" )
|
||||
})
|
||||
}
|
||||
}
|
||||
Button {
|
||||
text: qsTr("exchange rates")
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onClicked: {
|
||||
remorse_clearExchangeRatesDB.execute(qsTr("Delete stored exchange rates?"), function() {
|
||||
storageItem.removeFullTable( "exchange_rates_table" )
|
||||
})
|
||||
}
|
||||
}
|
||||
Button {
|
||||
text: qsTr("projects")
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onClicked: {
|
||||
remorse_clearExchangeRatesDB.execute(qsTr("Delete stored projects?"), function() {
|
||||
// remove all individual project expense tables
|
||||
for (var j = 0; j < listModel_allProjects.count ; j++) {
|
||||
var tempTablename = "table_" + listModel_allProjects.get(j).project_id_timestamp
|
||||
storageItem.removeFullTable(tempTablename)
|
||||
}
|
||||
// remove all projects overview table
|
||||
storageItem.removeFullTable( "projects_table" )
|
||||
// set latest setting_id to zero
|
||||
activeProjectID_unixtime = 0
|
||||
storageItem.setSettings("activeProjectID_unixtime", activeProjectID_unixtime)
|
||||
// update front page
|
||||
updateEvenWhenCanceled = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Item {
|
||||
width: parent.width
|
||||
height: Theme.paddingLarge
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ******************************************** important functions ******************************************** //
|
||||
|
||||
function writeDB_Settings() {
|
||||
storageItem.setSettings("sortOrderExpensesIndex", idComboboxSortingExpenses.currentIndex)
|
||||
sortOrderExpenses = idComboboxSortingExpenses.currentIndex
|
||||
listModel_activeProjectExpenses.quick_sort()
|
||||
|
||||
storageItem.setSettings("exchangeRateModeIndex", idComboboxExchangeRateMode.currentIndex)
|
||||
exchangeRateMode = idComboboxExchangeRateMode.currentIndex
|
||||
|
||||
storageItem.setSettings("interativeScrollbarMode", idComboboxShowInteractiveScrollbar.currentIndex)
|
||||
interativeScrollbarMode = idComboboxShowInteractiveScrollbar.currentIndex
|
||||
|
||||
if (listModel_allProjects.count > 0) { // only works if a project is actually created and loaded, otherwise this gets triggered directly in BannerAddProject.qml
|
||||
storageItem.setSettings("activeProjectID_unixtime", Number(listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp))
|
||||
activeProjectID_unixtime = Number(listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp)
|
||||
loadActiveProjectInfos_FromDB(Number(listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp))
|
||||
}
|
||||
|
||||
// ToDo: update base currency, if it was changed
|
||||
}
|
||||
|
||||
function backupProjectExpenses(selectedPath) {
|
||||
listModel_tempProjectExpenses.clear()
|
||||
var tempProjectIndex = listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp
|
||||
var backupFileName = encodeURIComponent(listModel_allProjects.get(idComboboxProject.currentIndex).project_name) + "_backup.csv" //replaces misleading special characters with %-symbols
|
||||
var backupFilePath = selectedPath + "/" + backupFileName //StandardPaths.documents
|
||||
|
||||
// check if project exists
|
||||
var currentProjectEntries = storageItem.getAllExpenses(listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp, "none")
|
||||
if (currentProjectEntries !== "none") {
|
||||
// generate temp project expenses list from chosen list
|
||||
for (var i = 0; i < currentProjectEntries.length ; i++) {
|
||||
listModel_tempProjectExpenses.append({
|
||||
id_unixtime_created : Number(currentProjectEntries[i][0]).toFixed(0),
|
||||
date_time : Number(currentProjectEntries[i][1]).toFixed(0),
|
||||
expense_name : currentProjectEntries[i][2],
|
||||
expense_sum : Number(currentProjectEntries[i][3]).toFixed(2),
|
||||
expense_currency : currentProjectEntries[i][4],
|
||||
expense_info : currentProjectEntries[i][5],
|
||||
expense_payer : currentProjectEntries[i][6],
|
||||
expense_members : currentProjectEntries[i][7],
|
||||
})
|
||||
}
|
||||
// create string from these temp info
|
||||
var toBackupString = tempProjectIndex + "\n"
|
||||
+ listModel_allProjects.get(idComboboxProject.currentIndex).project_name + "\n"
|
||||
+ "id_unixtime_created;*;date_time;*;expense_payer;*;expense_name;*;expense_sum;*;expense_currency;*;expense_members;*;expense_info" + "\n"
|
||||
for (var j = 0; j < listModel_tempProjectExpenses.count; j++) {
|
||||
toBackupString += listModel_tempProjectExpenses.get(j).id_unixtime_created
|
||||
+ ";*;" + listModel_tempProjectExpenses.get(j).date_time
|
||||
+ ";*;" + listModel_tempProjectExpenses.get(j).expense_payer
|
||||
+ ";*;" + listModel_tempProjectExpenses.get(j).expense_name
|
||||
+ ";*;" + listModel_tempProjectExpenses.get(j).expense_sum
|
||||
+ ";*;" + listModel_tempProjectExpenses.get(j).expense_currency
|
||||
+ ";*;" + listModel_tempProjectExpenses.get(j).expense_members
|
||||
+ ";*;" + listModel_tempProjectExpenses.get(j).expense_info
|
||||
+ "\n"
|
||||
}
|
||||
//console.log(toBackupString)
|
||||
|
||||
// store in file and give notification
|
||||
idTextFileBackup.source = backupFilePath
|
||||
idTextFileBackup.text = toBackupString
|
||||
notificationString = backupFilePath
|
||||
|
||||
//show notification somehow on top
|
||||
var headlineText = qsTr("Backup successful")
|
||||
var detailText = qsTr("File saved to:") + backupFilePath
|
||||
var triggerHiding = false
|
||||
notificationBackup.showBig(headlineText, detailText)
|
||||
}
|
||||
}
|
||||
|
||||
function restoreProjectExpenses(selectedPath, selectedAction) {
|
||||
idTextFileBackup.source = selectedPath
|
||||
var loadTextString = (idTextFileBackup.text)
|
||||
var pos = loadTextString.lastIndexOf("\n") // ToDo: remove last occurance of "\n"
|
||||
var loadTextLinesArray = (loadTextString.substring(0,pos) + loadTextString.substring(pos+1)).split("\n")
|
||||
var tempStringExistingId_unixtime = ""
|
||||
//var tempProjectIndex = listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp
|
||||
|
||||
// plausibility check on lines 0 to 2
|
||||
if (loadTextLinesArray[2] === "id_unixtime_created;*;date_time;*;expense_payer;*;expense_name;*;expense_sum;*;expense_currency;*;expense_members;*;expense_info") {
|
||||
var tempProjectIndex = listModel_allProjects.get(idComboboxProject.currentIndex).project_id_timestamp
|
||||
|
||||
// if REPLACE: delete old entries in project-specific expense table in database, otherwise just MERGE
|
||||
if (selectedAction === "replace") {
|
||||
storageItem.deleteAllExpenses(tempProjectIndex)
|
||||
} else { //"merge"
|
||||
// generate expenses list for this project
|
||||
var currentProjectEntries = storageItem.getAllExpenses( activeProjectID_unixtime, "none")
|
||||
if (currentProjectEntries !== "none") {
|
||||
for (var i = 0; i < currentProjectEntries.length ; i++) {
|
||||
tempStringExistingId_unixtime += (currentProjectEntries[i][0]).toString() + ";"
|
||||
}
|
||||
}
|
||||
//console.log(tempStringExistingId_unixtime)
|
||||
}
|
||||
|
||||
// fill with backup entries
|
||||
for (i = 3; i < loadTextLinesArray.length; i++) {
|
||||
var tempExpenseLineArray = (loadTextLinesArray[i]).split(";*;")
|
||||
var project_name_table = tempProjectIndex
|
||||
var id_unixtime_created = tempExpenseLineArray[0]
|
||||
var date_time = tempExpenseLineArray[1]
|
||||
var expense_payer = tempExpenseLineArray[2]
|
||||
var expense_name = tempExpenseLineArray[3]
|
||||
var expense_sum = tempExpenseLineArray[4]
|
||||
var expense_currency = tempExpenseLineArray[5]
|
||||
var expense_members = tempExpenseLineArray[6]
|
||||
var expense_info = tempExpenseLineArray[7]
|
||||
|
||||
if (selectedAction === "replace") {
|
||||
storageItem.setExpense(project_name_table, id_unixtime_created.toString(), date_time.toString(), expense_name, expense_sum, expense_currency, expense_info, expense_payer, expense_members)
|
||||
//console.log("entering DB -> " + tempExpenseLineArray)
|
||||
} else {
|
||||
// check for double entries, only add if not existent
|
||||
if (tempStringExistingId_unixtime.indexOf(id_unixtime_created.toString()) === -1) {
|
||||
storageItem.setExpense(project_name_table, id_unixtime_created.toString(), date_time.toString(), expense_name, expense_sum, expense_currency, expense_info, expense_payer, expense_members)
|
||||
//console.log("entering DB -> " + tempExpenseLineArray)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//show notification somehow on top
|
||||
var headlineText = qsTr("Backup successfully restored.")
|
||||
if (selectedAction === "replace") {
|
||||
var detailText = qsTr("Project expenses have been overwritten by backup-file expenses.")
|
||||
} else {
|
||||
detailText = qsTr("Project expenses have been merged with backup-file expenses.")
|
||||
}
|
||||
var triggerHiding = false
|
||||
notificationBackup.showBig(headlineText, detailText)
|
||||
|
||||
// set this flag when the current project gets merged or replaced with a backup
|
||||
if ( Number(tempProjectIndex) === Number(activeProjectID_unixtime) ) {
|
||||
updateEvenWhenCanceled = true
|
||||
}
|
||||
} else {
|
||||
//show notification somehow on top
|
||||
headlineText = qsTr("Validity check failed.")
|
||||
detailText = qsTr("This backup file does not seem to be created by Expenditure:") + " " + backupFilePath
|
||||
triggerHiding = false
|
||||
notificationBackup.showBig(headlineText, detailText)
|
||||
}
|
||||
}
|
||||
}
|
18
rpm/harbour-expenditure.changes.in
Normal file
18
rpm/harbour-expenditure.changes.in
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Rename this file as harbour-expenditure.changes to include changelog
|
||||
# entries in your RPM file.
|
||||
#
|
||||
# Add new changelog entries following the format below.
|
||||
# Add newest entries to the top of the list.
|
||||
# Separate entries from eachother with a blank line.
|
||||
#
|
||||
# Alternatively, if your changelog is automatically generated (e.g. with
|
||||
# the git-change-log command provided with Sailfish OS SDK), create a
|
||||
# harbour-expenditure.changes.run script to let mb2 run the required commands for you.
|
||||
|
||||
# * date Author's Name <author's email> version-release
|
||||
# - Summary of changes
|
||||
|
||||
* Sun Apr 13 2014 Jack Tar <jack.tar@example.com> 0.0.1-1
|
||||
- Scrubbed the deck
|
||||
- Hoisted the sails
|
||||
|
25
rpm/harbour-expenditure.changes.run.in
Normal file
25
rpm/harbour-expenditure.changes.run.in
Normal file
|
@ -0,0 +1,25 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Rename this file as harbour-expenditure.changes.run to let mb2 automatically
|
||||
# generate changelog from well formatted Git commit messages and tag
|
||||
# annotations.
|
||||
|
||||
git-change-log
|
||||
|
||||
# Here are some basic examples how to change from the default behavior. Run
|
||||
# git-change-log --help inside the Sailfish OS SDK chroot or build engine to
|
||||
# learn all the options git-change-log accepts.
|
||||
|
||||
# Use a subset of tags
|
||||
#git-change-log --tags refs/tags/my-prefix/*
|
||||
|
||||
# Group entries by minor revision, suppress headlines for patch-level revisions
|
||||
#git-change-log --dense '/[0-9]+.[0-9+$'
|
||||
|
||||
# Trim very old changes
|
||||
#git-change-log --since 2014-04-01
|
||||
#echo '[ Some changelog entries trimmed for brevity ]'
|
||||
|
||||
# Use the subjects (first lines) of tag annotations when no entry would be
|
||||
# included for a revision otherwise
|
||||
#git-change-log --auto-add-annotations
|
67
rpm/harbour-expenditure.spec
Normal file
67
rpm/harbour-expenditure.spec
Normal file
|
@ -0,0 +1,67 @@
|
|||
#
|
||||
# Do NOT Edit the Auto-generated Part!
|
||||
# Generated by: spectacle version 0.32
|
||||
#
|
||||
|
||||
Name: harbour-expenditure
|
||||
|
||||
# >> macros
|
||||
# << macros
|
||||
|
||||
Summary: Expenditure
|
||||
Version: 0.2
|
||||
Release: 1
|
||||
Group: Qt/Qt
|
||||
License: LICENSE
|
||||
URL: http://example.org/
|
||||
Source0: %{name}-%{version}.tar.bz2
|
||||
Source100: harbour-expenditure.yaml
|
||||
Requires: sailfishsilica-qt5 >= 0.10.9
|
||||
BuildRequires: pkgconfig(sailfishapp) >= 1.0.2
|
||||
BuildRequires: pkgconfig(Qt5Core)
|
||||
BuildRequires: pkgconfig(Qt5Qml)
|
||||
BuildRequires: pkgconfig(Qt5Quick)
|
||||
BuildRequires: desktop-file-utils
|
||||
|
||||
%description
|
||||
Short description of my Sailfish OS Application
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}-%{version}
|
||||
|
||||
# >> setup
|
||||
# << setup
|
||||
|
||||
%build
|
||||
# >> build pre
|
||||
# << build pre
|
||||
|
||||
%qmake5
|
||||
|
||||
make %{?_smp_mflags}
|
||||
|
||||
# >> build post
|
||||
# << build post
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
# >> install pre
|
||||
# << install pre
|
||||
%qmake5_install
|
||||
|
||||
# >> install post
|
||||
# << install post
|
||||
|
||||
desktop-file-install --delete-original \
|
||||
--dir %{buildroot}%{_datadir}/applications \
|
||||
%{buildroot}%{_datadir}/applications/*.desktop
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%{_bindir}/%{name}
|
||||
%{_datadir}/%{name}
|
||||
%{_datadir}/applications/%{name}.desktop
|
||||
%{_datadir}/icons/hicolor/*/apps/%{name}.png
|
||||
# >> files
|
||||
# << files
|
42
rpm/harbour-expenditure.yaml
Normal file
42
rpm/harbour-expenditure.yaml
Normal file
|
@ -0,0 +1,42 @@
|
|||
Name: harbour-expenditure
|
||||
Summary: Expenditure
|
||||
Version: 0.2
|
||||
Release: 1
|
||||
# The contents of the Group field should be one of the groups listed here:
|
||||
# https://github.com/mer-tools/spectacle/blob/master/data/GROUPS
|
||||
Group: Qt/Qt
|
||||
URL: http://example.org/
|
||||
License: LICENSE
|
||||
# This must be generated before uploading a package to a remote build service.
|
||||
# Usually this line does not need to be modified.
|
||||
Sources:
|
||||
- '%{name}-%{version}.tar.bz2'
|
||||
Description: |
|
||||
Short description of my Sailfish OS Application
|
||||
Builder: qmake5
|
||||
|
||||
# This section specifies build dependencies that are resolved using pkgconfig.
|
||||
# This is the preferred way of specifying build dependencies for your package.
|
||||
PkgConfigBR:
|
||||
- sailfishapp >= 1.0.2
|
||||
- Qt5Core
|
||||
- Qt5Qml
|
||||
- Qt5Quick
|
||||
|
||||
# Build dependencies without a pkgconfig setup can be listed here
|
||||
# PkgBR:
|
||||
# - package-needed-to-build
|
||||
|
||||
# Runtime dependencies which are not automatically detected
|
||||
Requires:
|
||||
- sailfishsilica-qt5 >= 0.10.9
|
||||
|
||||
# All installed files
|
||||
Files:
|
||||
- '%{_bindir}/%{name}'
|
||||
- '%{_datadir}/%{name}'
|
||||
- '%{_datadir}/applications/%{name}.desktop'
|
||||
- '%{_datadir}/icons/hicolor/*/apps/%{name}.png'
|
||||
|
||||
# For more information about yaml and what's supported in Sailfish OS
|
||||
# build system, please see https://wiki.merproject.org/wiki/Spectacle
|
60
src/File.cpp
Normal file
60
src/File.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include "File.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
File::File()
|
||||
{
|
||||
connect(this, SIGNAL(sourceChanged()), this, SLOT(readFile()));
|
||||
}
|
||||
|
||||
void File::setSource(const QString &source)
|
||||
{
|
||||
m_source = source;
|
||||
emit sourceChanged();
|
||||
}
|
||||
|
||||
QString File::source() const
|
||||
{
|
||||
return m_source;
|
||||
}
|
||||
|
||||
void File::setText(const QString &text)
|
||||
{
|
||||
QFile file(m_source);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
m_text = "";
|
||||
qDebug() << "Error:" << m_source << "open failed! File not yet created.";
|
||||
}
|
||||
else {
|
||||
qint64 byte = file.write(text.toUtf8());
|
||||
if (byte != text.toUtf8().size()) {
|
||||
m_text = text.toUtf8().left(byte);
|
||||
qDebug() << "Error:" << m_source << "open failed!";
|
||||
}
|
||||
else {
|
||||
m_text = text;
|
||||
}
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
emit textChanged();
|
||||
}
|
||||
|
||||
void File::readFile()
|
||||
{
|
||||
QFile file(m_source);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
m_text = "";
|
||||
qDebug() << "Error:" << m_source << "open failed!";
|
||||
}
|
||||
|
||||
m_text = file.readAll();
|
||||
emit textChanged();
|
||||
}
|
||||
|
||||
QString File::text() const
|
||||
{
|
||||
return m_text;
|
||||
}
|
33
src/File.h
Normal file
33
src/File.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
#ifndef QT_HUB_FILE_H
|
||||
#define QT_HUB_FILE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class File : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
File();
|
||||
|
||||
Q_PROPERTY(QString source READ source WRITE setSource NOTIFY sourceChanged)
|
||||
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
|
||||
|
||||
QString source() const;
|
||||
void setSource(const QString &source);
|
||||
|
||||
QString text() const;
|
||||
void setText(const QString &text);
|
||||
|
||||
signals:
|
||||
void sourceChanged();
|
||||
void textChanged();
|
||||
|
||||
private slots:
|
||||
void readFile();
|
||||
|
||||
private:
|
||||
QString m_source;
|
||||
QString m_text;
|
||||
};
|
||||
|
||||
#endif //FILE_H
|
25
src/harbour-expenditure.cpp
Normal file
25
src/harbour-expenditure.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifdef QT_QML_DEBUG
|
||||
#include <QtQuick>
|
||||
#endif
|
||||
|
||||
#include <sailfishapp.h>
|
||||
|
||||
#include <QtQuick> // FileIO
|
||||
#include "File.h" // FileIO
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// SailfishApp::main() will display "qml/harbour-expenditure.qml", if you need more
|
||||
// control over initialization, you can use:
|
||||
//
|
||||
// - SailfishApp::application(int, char *[]) to get the QGuiApplication *
|
||||
// - SailfishApp::createView() to get a new QQuickView * instance
|
||||
// - SailfishApp::pathTo(QString) to get a QUrl to a resource file
|
||||
// - SailfishApp::pathToMainQml() to get a QUrl to the main QML file
|
||||
//
|
||||
// To display the view, call "show()" (will show fullscreen on device).
|
||||
|
||||
qmlRegisterType<File>("FileIO", 1, 0, "TextFileIO"); // FileIO
|
||||
|
||||
return SailfishApp::main(argc, argv);
|
||||
}
|
430
translations/harbour-expenditure-de.ts
Normal file
430
translations/harbour-expenditure-de.ts
Normal file
|
@ -0,0 +1,430 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>AboutPage</name>
|
||||
<message>
|
||||
<source>Expenditure</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copyright © 2022 Tobias Planitzer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>License: GPL v3</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expenditure is a tool to track and split bills, project or trip expenses in multiple currencies among groups.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thanksgiving, feedback and support is always welcome.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Troubleshooting:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>In case of any database error tap 10x on the word 'Settings' for cleanup options.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Contact:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BannerAddExpense</name>
|
||||
<message>
|
||||
<source>expense</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>info</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>payment by</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>price</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>currency</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>beneficiary</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BannerAddProject</name>
|
||||
<message>
|
||||
<source>Add</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>base currency</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Members</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>rename</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>remove</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete this project?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear all transactions?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>create</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>edit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Backup</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restore</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CalcPage</name>
|
||||
<message>
|
||||
<source>payments</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>saldo</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>owes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>EXCHANGE RATES</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>SPENDING OVERVIEW</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>SETTLEMENT SUGGESTION</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>the sum of</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Total expenses</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>payed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>received</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settlement suggestion:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>item:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>payer:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>price:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>info:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>beneficiaries:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Detailed Spendings:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expense #</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>date:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Share detailed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Share compact</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>benefits</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Results</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>constant</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirstPage</name>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Create new project.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Nothing loaded yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Calculate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>group:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expenses</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove entry?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsPage</name>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sorting</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>descending</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ascending</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exchange rate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete stored exchange rates?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete stored projects?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete stored settings?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>per currency (constant)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>per transaction (dates)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restore backup file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Backup to</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scrollbar</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>interactive (beta)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>normal</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Backup successful</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Replace</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Merge</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File saved to:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Validity check failed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This backup file does not seem to be created by Expenditure:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Backup successfully restored.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project expenses have been overwritten by backup-file expenses.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project expenses have been merged with backup-file expenses.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restore backup - choose action</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Replace deletes all former project-expenses and uses those from backup-file instead.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Merge keeps former project-expenses and adds those from backup-file which are not yet on the list.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File info: This backup was created by a different project.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File info: This backup was created by the original project.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>exchange rates</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>projects</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database cleanup - requires restart:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
430
translations/harbour-expenditure.ts
Normal file
430
translations/harbour-expenditure.ts
Normal file
|
@ -0,0 +1,430 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>AboutPage</name>
|
||||
<message>
|
||||
<source>Expenditure</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Copyright © 2022 Tobias Planitzer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>License: GPL v3</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expenditure is a tool to track and split bills, project or trip expenses in multiple currencies among groups.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Thanksgiving, feedback and support is always welcome.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Troubleshooting:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>In case of any database error tap 10x on the word 'Settings' for cleanup options.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Contact:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BannerAddExpense</name>
|
||||
<message>
|
||||
<source>expense</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>info</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>payment by</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>price</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>currency</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>beneficiary</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>BannerAddProject</name>
|
||||
<message>
|
||||
<source>Add</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>base currency</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Members</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>rename</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>remove</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete this project?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear all transactions?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>create</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>edit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Backup</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Reset</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restore</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CalcPage</name>
|
||||
<message>
|
||||
<source>payments</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>saldo</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>owes</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>EXCHANGE RATES</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>SPENDING OVERVIEW</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>SETTLEMENT SUGGESTION</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>the sum of</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Total expenses</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>payed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>received</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Settlement suggestion:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>item:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>payer:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>price:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>info:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>beneficiaries:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Detailed Spendings:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expense #</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>date:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Share detailed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Share compact</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>benefits</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Results</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>constant</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>FirstPage</name>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Create new project.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Nothing loaded yet.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Edit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Calculate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>group:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Expenses</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Add</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove entry?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SettingsPage</name>
|
||||
<message>
|
||||
<source>Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Sorting</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>descending</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>ascending</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exchange rate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete stored exchange rates?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete stored projects?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Delete stored settings?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>per currency (constant)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>per transaction (dates)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restore backup file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Backup to</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Scrollbar</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>interactive (beta)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>normal</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Backup successful</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Replace</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Merge</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File saved to:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Validity check failed.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>This backup file does not seem to be created by Expenditure:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Backup successfully restored.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project expenses have been overwritten by backup-file expenses.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Project expenses have been merged with backup-file expenses.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Restore backup - choose action</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Replace deletes all former project-expenses and uses those from backup-file instead.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Merge keeps former project-expenses and adds those from backup-file which are not yet on the list.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File info: This backup was created by a different project.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File info: This backup was created by the original project.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>exchange rates</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>projects</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Database cleanup - requires restart:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
Loading…
Reference in a new issue