diff --git a/Gui/AddContact.qml b/Gui/AddContact.qml
index c904cf1..78f78d7 100644
--- a/Gui/AddContact.qml
+++ b/Gui/AddContact.qml
@@ -143,9 +143,7 @@ Frame
var bd = birthday.text
if (len === 2 || len === 5) birthday.text = bd + "."
}
-
}
-
}
Label
diff --git a/Gui/AddNewObject.qml b/Gui/AddNewObject.qml
new file mode 100644
index 0000000..e48fbd8
--- /dev/null
+++ b/Gui/AddNewObject.qml
@@ -0,0 +1,277 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+GridLayout
+{
+ id: newObject
+
+ columns: 4
+ Layout.fillWidth: true
+ Layout.fillHeight: true
+ rowSpacing: 9
+
+ Label
+ {
+ text: qsTr("Firma")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+ ComboBox
+ {
+ property string name: "business"
+ id: business
+ editable: true
+ Layout.fillWidth: true
+ Layout.columnSpan: 3
+ }
+
+ //// New grid row
+
+ Label
+ {
+ text: qsTr("Straße")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ TextField
+ {
+ property string name: "street"
+ id: street
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
+ onTextChanged: checkFields()
+ placeholderText: "Pflichtfeld"
+ placeholderTextColor: "red"
+ }
+
+ Label
+ {
+ text: qsTr("Nr.")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ TextField
+ {
+ property string name: "houseno"
+ id: houseno
+ Layout.fillWidth: true
+ Layout.alignment: Qt.AlignVCenter
+ onTextChanged: checkFields()
+ placeholderText: "Pflichtfeld"
+ placeholderTextColor: "red"
+ }
+
+ // New grid row
+ Label
+ {
+ text: qsTr("PLZ")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ ComboBox
+ {
+ property string name: "postcode"
+ id: postcode
+ Layout.fillWidth: true
+ }
+
+ Label
+ {
+ text: qsTr("Ort")
+ Layout.alignment: Qt.AlignRight
+
+ }
+
+ ComboBox
+ {
+ property string name: "city"
+ id: city
+ Layout.fillWidth: true
+ editable: true
+ onEditTextChanged: checkFields()
+ onCurrentTextChanged: checkFields()
+ model: address_model
+ textRole: "city"
+ popup.height: 300
+ popup.y: postcode.y + 5 - (postcode.height * 2)
+ currentIndex: -1
+ }
+
+ // New grid row
+ Label
+ {
+ text: qsTr("Parteien")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ SpinBox
+ {
+ id: parteien
+ Layout.fillWidth: true
+ from: 1
+ to: 100
+ value: 1
+ }
+
+ Label
+ {
+ text: qsTr("Stockwerke")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ SpinBox
+ {
+ id: floors
+ Layout.fillWidth: true
+ from: 1
+ to: 100
+ value: 1
+ }
+
+ // New grid row
+ Label
+ {
+ text: qsTr("Zwischenetage")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ ComboBox
+ {
+ property string name: "mezzanin"
+ id: mezzanin
+ Layout.fillWidth: true
+ editable: false
+ model: [qsTr("Jööö"), qsTr("Nöööööööööööööööööööööööööö")]
+ }
+
+ Label
+ {
+ text: qsTr("Aufzug")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ ComboBox
+ {
+ property string name: "lift"
+ id: lift
+ Layout.fillWidth: true
+ editable: false
+ model: [qsTr("Jööö"), qsTr("Nöööööööööööööööööööööööööö")]
+ }
+
+ //New grid row
+
+ Label
+ {
+ text: qsTr("Fenster")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ ComboBox
+ {
+ property string name: "windows"
+ id: windows
+ Layout.fillWidth: true
+ editable: false
+ model: [qsTr("Jööö"), qsTr("Nöööööööööööööööööööööööööö")]
+ onCurrentIndexChanged: nrWindows.enabled = (windows.currentIndex === 0)? true: false
+ }
+
+ Label
+ {
+ text: qsTr("Anzahl")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ SpinBox
+ {
+ id: nrWindows
+ Layout.fillWidth: true
+ from: 0
+ to: 100
+ value: 0
+ }
+
+ // New grid row
+ CheckBox
+ {
+ id: ladder
+ text: qsTr("Leiter")
+ Layout.alignment: Qt.AlignRight
+ checked: false
+ onCheckStateChanged:
+ {
+ //checkFields()
+ }
+ }
+
+ CheckBox
+ {
+ id: accessible
+ text: qsTr("Erreichbar")
+ Layout.alignment: Qt.AlignRight
+ checked: false
+ onCheckStateChanged:
+ {
+ //checkFields()
+ }
+ }
+
+ Label
+ {
+ text: qsTr("Besonderheiten")
+ Layout.alignment: Qt.AlignRight
+ }
+ ComboBox
+ {
+ property string name: "remarks"
+ id: remarks
+ Layout.fillWidth: true
+ editable: false
+ textRole: "display"
+ }
+
+ //// New grid row
+ Label
+ {
+ text: qsTr("kontaktdaten")
+ Layout.alignment: Qt.AlignRight | Qt.AlignTop
+ }
+
+ ComboBox
+ {
+ property string name: "contact"
+ id: contact
+ Layout.fillWidth: true
+ editable: false
+ model: [qsTr("Beirat"), qsTr("Hausmeister")]
+ }
+
+ Label
+ {
+ text: qsTr("Reingunsmittel wo?")
+ Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
+ }
+
+ TextField
+ {
+ property string name: "cleansing"
+ id: cleamsing
+ Layout.fillWidth: true
+ placeholderText: "Pflichtfeld"
+ placeholderTextColor: "red"
+ }
+ Item
+ {
+ Layout.fillHeight: true
+ }
+}
+
+
+
+
+
+
+
+
+
diff --git a/Gui/AddObject.qml b/Gui/AddObject.qml
index d75845f..630b3b8 100644
--- a/Gui/AddObject.qml
+++ b/Gui/AddObject.qml
@@ -41,9 +41,9 @@ ColumnLayout
{
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
- ObjectView
+ AddNewObject
{
- id: objectView
+ id: newObject
width: parent.width
}
}
diff --git a/Gui/ApplicantPersonalData.qml b/Gui/ApplicantPersonalData.qml
index 6f54a56..df19d65 100644
--- a/Gui/ApplicantPersonalData.qml
+++ b/Gui/ApplicantPersonalData.qml
@@ -182,10 +182,18 @@ GridLayout
Layout.columnSpan: 3
visible: radio.children[1].checked
validator: RegularExpressionValidator
-
{
regularExpression: /((^|)(0[1-9]{1}|[1-2]{1}[0-9]{1}|3[0-1]))\.((^|)(0[1-9]{1}|1[0-2]{1}))\.((^|)(196[0-9]{1}|19[7-9]{1}[0-9]{1}|20[0-9]{2}))/
}
+ Keys.onPressed: (event)=>
+ {
+ if (event.key !== Qt.Key_Backspace)
+ {
+ var len = birthday.length
+ var bd = birthday.text
+ if (len === 2 || len === 5) birthday.text = bd + "."
+ }
+ }
}
Label
diff --git a/Gui/CustomerContactDetails.qml b/Gui/CustomerContactDetails.qml
new file mode 100644
index 0000000..6c66c9a
--- /dev/null
+++ b/Gui/CustomerContactDetails.qml
@@ -0,0 +1,174 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+GridLayout
+{
+ columns: 2
+ rowSpacing: 25
+ Layout.leftMargin: 7
+
+ // Grid row
+ ColumnLayout
+ {
+ Layout.columnSpan: 2
+ Label
+ {
+ id: contactLabel
+ color: "darksalmon"
+ font.bold: true
+ text: qsTr("Ansprechpartner")
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: contact? contact['contact']['salute'] + " " + contact['contact']['fname'] + " " + contact['contact']['lname']: ""
+ }
+ }
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Geburtsdatum")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: contact? contact['contact']['birthday']: ""
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("E-Mail")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: contact? contact['contact']['email']: ""
+ }
+ }
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Position")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: contact? contact['contact']['position']: ""
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Priorität")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: contact? contact['contact']['priority']: ""
+ }
+ }
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Telefon")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: contact? contact['contact']['phone']: ""
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Handy")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: contact? contact['contact']['cell']: ""
+ }
+ }
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Abrechnung")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: contact? contact['contact']['invoice']: ""
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Mahnung")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: contact? contact['contact']['reminder']: ""
+ }
+ }
+
+ // Grid row
+ Item
+ {
+ Layout.columnSpan: 2
+ Layout.fillHeight: true
+ }
+
+ Component.onCompleted:
+ {
+ if (contact && contact['contact']['salute'] === "Frau")
+ contactLabel.text = qsTr("Ansprechpartnerin")
+ }
+}
diff --git a/Gui/CustomerDetails.qml b/Gui/CustomerDetails.qml
index 9860d47..fbd4e54 100644
--- a/Gui/CustomerDetails.qml
+++ b/Gui/CustomerDetails.qml
@@ -2,26 +2,61 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-Item
+ColumnLayout
{
property int selectedClient: -1
+ property var client: null
+ property var contact: null
id: clDet
- ColumnLayout
+
+ Button
{
- Label
+ text: qsTr("Zurück")
+ //Layout.columnSpan: 2
+ onClicked: customersStack.pop()
+ }
+
+ SplitView
+ {
+ id: clDetView
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ leftPadding: 9
+ rightPadding: 9
+
+ CustomerDetailsView
{
- text: qsTr("Ausgewählter Kunde " + selectedClient)
+ id: customerDetails
}
- Button
+ CustomerContactDetails
{
- text: qsTr("Kunden zeigen")
- onClicked: customersStack.pop()
+ id: contactDetails
+ visible: false
}
+
+ NoCustomerContact
+ {
+ id: noCustomerContact
+ visible: false
+ }
+ }
+
+ Item
+ {
+ //Layout.columnSpan: 2
+ Layout.fillHeight: true
}
Component.onCompleted:
{
- business_model.onRowClicked(selectedClient)
+ //business_model.onRowClicked(selectedClient)
+ client = business_model.getClientDetails()
+ if (client['business']['contactid'] > 0)
+ {
+ contact = contact_model.getContactDetails(client['business']['contactid'])
+ contactDetails.visible = true
+ }
+ else noCustomerContact.visible = true
}
}
diff --git a/Gui/CustomerDetailsView.qml b/Gui/CustomerDetailsView.qml
new file mode 100644
index 0000000..c69ac2b
--- /dev/null
+++ b/Gui/CustomerDetailsView.qml
@@ -0,0 +1,225 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+GridLayout
+{
+ columns: 2
+ rowSpacing: 25
+ SplitView.preferredWidth: clDetView.width / 3 * 1.8
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Steuer-ID")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['tax']? client['business']['tax']: ""
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Anmerkungen")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['info']? client['business']['info']: ""
+ }
+ }
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Kundenname")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['company']
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("CEO")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['ceo']
+ }
+ }
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Telefon")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['phone']? client['business']['phone']: ""
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Handy")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['cell']? client['business']['cell']: ""
+ }
+ }
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Webseite")
+ font.bold: true
+ }
+
+ Label
+ {
+ id: clientWebsite
+ color: "goldenrod"
+ font.underline: false
+ text: client['business']['website']? '' + client['business']['website'] + '': ""
+ onLinkActivated:
+ {
+ var web_protocol = /^((http|https):\/\/)/;
+ var client_website = !web_protocol.test(client['business']['website'])? "https://" + client['business']['website']: client['business']['website'];
+ Qt.openUrlExternally(client_website)
+ }
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("E-Mail")
+ font.bold: true
+ }
+
+ Label
+ {
+ id: clientEmail
+ color: "goldenrod"
+ text: client['business']['email']? '' + client['business']['email'] + '': ""
+ onLinkActivated: Qt.openUrlExternally('mailto:' + client['business']['email'])
+ }
+ }
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Straße")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['street']? client['business']['tax']: ""
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Haus-Nr.")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['house']? client['business']['house']: ""
+ }
+ }
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("PLZ")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['zip']? client['business']['zip']: ""
+ }
+ }
+
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ text: qsTr("Stadt")
+ font.bold: true
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: client['business']['city']? client['business']['city']: ""
+ }
+ }
+
+ // Grid row
+ // Item
+ // {
+ // Layout.columnSpan: 2
+ // Layout.fillHeight: true
+ // }
+}
diff --git a/Gui/CustomersTable.qml b/Gui/CustomersTable.qml
index 70895e2..3746ced 100644
--- a/Gui/CustomersTable.qml
+++ b/Gui/CustomersTable.qml
@@ -145,6 +145,7 @@ Item
hoverEnabled: true
onDoubleClicked:
{
+ business_model.onRowClicked(row)
customersStack.push("CustomerDetails.qml", {selectedClient: row});
}
diff --git a/Gui/LoginScreen.qml b/Gui/LoginScreen.qml
index ff032b2..9cf01a6 100644
--- a/Gui/LoginScreen.qml
+++ b/Gui/LoginScreen.qml
@@ -83,6 +83,13 @@ Item
placeholderText: qsTr ("Benutzernamen eingeben")
implicitWidth: 300
font: hussarPrint.font
+ focus: true
+ onAccepted:
+ {
+ if (benutzerName.text.trim() && passwort.text.trim())
+ loggedin_user.login(benutzerName.text.trim(), passwort.text)
+ else if(benutzerName.text.trim()) passwort.forceActiveFocus()
+ }
}
}
@@ -110,6 +117,12 @@ Item
implicitWidth: 300
font: hussarPrint.font
echoMode: TextInput.Password
+ onAccepted:
+ {
+ if (benutzerName.text.trim() && passwort.text.trim())
+ loggedin_user.login(benutzerName.text.trim(), passwort.text)
+ else if(passwort.text.trim()) benutzerName.forceActiveFocus()
+ }
}
}
@@ -193,6 +206,7 @@ Item
config.invalidEncryptionKey.connect(getEncryptionKey)
config.checkEncryptionKey()
loggedin_user.noDbConnection.connect(dbConnectionFailed)
+ benutzerName.forceActiveFocus()
}
function loggedin()
diff --git a/Gui/NoCustomerContact.qml b/Gui/NoCustomerContact.qml
new file mode 100644
index 0000000..eb0434c
--- /dev/null
+++ b/Gui/NoCustomerContact.qml
@@ -0,0 +1,33 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+GridLayout
+{
+ columns: 2
+ rowSpacing: 25
+
+ // Grid row
+ ColumnLayout
+ {
+ Label
+ {
+ color: "darksalmon"
+ font.bold: true
+ text: qsTr("Kein Ansprechpartner gefunden")
+ }
+
+ Label
+ {
+ color: "goldenrod"
+ text: qsTr("Was willst du tun?")
+ }
+ }
+
+ // Grid row
+ Item
+ {
+ Layout.columnSpan: 2
+ Layout.fillHeight: true
+ }
+}
diff --git a/Gui/PrinterDialog.qml b/Gui/PrinterDialog.qml
new file mode 100644
index 0000000..d0ba6a7
--- /dev/null
+++ b/Gui/PrinterDialog.qml
@@ -0,0 +1,134 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Window
+{
+ property alias printerDialog: printDialog
+ property var printers: null
+
+ id: printDialog
+ title: qsTr("PYQCRM - Drucker")
+ color: palette.base
+ minimumWidth: 300
+ maximumWidth: 600
+ minimumHeight: 150
+ maximumHeight: 150
+ ColumnLayout
+ {
+ spacing: 9
+ y: 15
+ implicitWidth: parent.width
+ RowLayout
+ {
+ Layout.fillWidth: true
+ Layout.leftMargin: 5
+ Layout.rightMargin: 5
+ Label
+ {
+ id: printersLabel
+ Layout.alignment: Qt.AlignRight
+ text: qsTr("Drucker")
+ }
+
+ ComboBox
+ {
+ id: allPrinters
+ model: printers
+ Layout.fillWidth: true
+ }
+ }
+
+ RowLayout
+ {
+ Layout.leftMargin: 5
+ Layout.rightMargin: 5
+ Layout.fillWidth: true
+
+ Label
+ {
+ Layout.minimumWidth: printersLabel.width
+ Layout.alignment: Qt.AlignRight
+ text: qsTr("Kopie")
+ }
+
+ SpinBox
+ {
+ id: copiesSpinBox
+ from: 1
+ to: 10
+ value: 1
+ }
+
+ Item
+ {
+ Layout.fillWidth: true
+ }
+ }
+
+ RowLayout
+ {
+ Layout.leftMargin: 5
+ Layout.rightMargin: 5
+ Layout.fillWidth: true
+ CheckBox
+ {
+ id: colorPrint
+ text: qsTr("Farbe")
+ Layout.minimumWidth: printersLabel.width
+ }
+
+ Item
+ {
+ Layout.fillWidth: true
+ }
+ }
+
+ RowLayout
+ {
+ Layout.leftMargin: 5
+ Layout.rightMargin: 5
+ Layout.fillWidth: true
+ Item
+ {
+ Layout.fillWidth: true
+ }
+
+ Button
+ {
+ id: printButton
+ text: qsTr("Drucken")
+ onClicked:
+ {
+ var copies = copiesSpinBox.value > 1? copiesSpinBox.value + " copies": "one copy"
+ console.log("Printing ", copies, " using ", allPrinters.currentText);
+ printDialog.close();
+ }
+ }
+
+ Button
+ {
+ text: qsTr("Ablehnen")
+ onClicked: printDialog.close();
+ }
+ }
+
+ Item
+ {
+ Layout.fillHeight: true
+ }
+ }
+
+ onVisibleChanged:
+ {
+ copiesSpinBox.value = 1
+ colorPrint.checked = true
+ }
+
+ Component.onCompleted:
+ {
+ printers = sys_printers.getPrinters()
+ if (sys_printers.getDefaultPrinter())
+ allPrinters.currentIndex = allPrinters.indexOfValue(sys_printers.getDefaultPrinter())
+ }
+}
diff --git a/Gui/ReadMe.qml b/Gui/ReadMe.qml
new file mode 100644
index 0000000..cd9d1eb
--- /dev/null
+++ b/Gui/ReadMe.qml
@@ -0,0 +1,47 @@
+import QtQuick
+import QtQuick.Controls
+import QtQuick.Layouts
+
+Window
+{
+ property alias readMeWin: readMeWin
+ id: readMeWin
+ width: 400
+ height: 300
+ title: "PYQCRM - README"
+ color: palette.base
+
+ ScrollView
+ {
+ anchors.fill: parent
+ TextArea
+ {
+ id: readMe
+ anchors.fill: parent
+ readOnly: true
+ wrapMode: TextArea.Wrap
+ color: "darksalmon"
+
+ Component.onCompleted:
+ {
+ var filePath = "qrc:/README";
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", filePath, true);
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState === XMLHttpRequest.DONE)
+ {
+ if (xhr.status === 200)
+ {
+ readMe.text = xhr.responseText;
+ }
+ else
+ {
+ readMe.text = qsTr("Datei nicht gefunden!");
+ }
+ }
+ };
+ xhr.send();
+ }
+ }
+ }
+}
diff --git a/Gui/TopBar.qml b/Gui/TopBar.qml
index 0ec4e88..758dfb0 100644
--- a/Gui/TopBar.qml
+++ b/Gui/TopBar.qml
@@ -172,7 +172,32 @@ RowLayout
icon.color: "red"
flat: true
Layout.rightMargin: 9
- }
+ onClicked: mainMenu.open()
+ Menu {
+ id: mainMenu
+ MenuItem
+ {
+ text: qsTr("Benutzer-Verwaltung")
+ onTriggered: appLoader.source = "UsersPage.qml"
+ }
+ MenuSeparator {}
+ MenuItem { text: qsTr("Als PDF exportieren") }
+ MenuSeparator {}
+ MenuItem { text: qsTr("Drucken") }
+ MenuItem
+ {
+ text: qsTr("Erweiterter Druck")
+ onTriggered: printerDialog.show()
+ }
+ MenuSeparator {}
+ MenuItem
+ {
+ text: qsTr("Über PYQCRM")
+ onTriggered: readMeWin.show()
+ }
+ }
+ }
}
+
diff --git a/Gui/UsersPage.qml b/Gui/UsersPage.qml
new file mode 100644
index 0000000..31d8c59
--- /dev/null
+++ b/Gui/UsersPage.qml
@@ -0,0 +1,16 @@
+import QtQuick
+import QtQuick.Layouts
+import QtQuick.Controls
+
+Item
+{
+ anchors.fill: parent
+
+ Label
+ {
+ text: qsTr("Benutzer-Verwaltung")
+ anchors.centerIn: parent
+ font.pixelSize: 57
+ font.bold: true
+ }
+}
diff --git a/Gui/main.qml b/Gui/main.qml
index 1ae8060..bd3f55e 100644
--- a/Gui/main.qml
+++ b/Gui/main.qml
@@ -26,6 +26,16 @@ ApplicationWindow
visible: bad_config || !db_con ? false: true
}
+ PrinterDialog
+ {
+ id: printerDialog
+ }
+
+ ReadMe
+ {
+ id: readMeWin
+ }
+
Item
{
id: mainView
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..6562a0e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+Nicht zu verwenden ohne Zahlung!
diff --git a/README b/README
new file mode 100644
index 0000000..fe19178
--- /dev/null
+++ b/README
@@ -0,0 +1,54 @@
+ PYQCRM - Python QtQuick CRM System
+
+Einleitung:
+----------------
+
+PYQCRM entstand als Teil einer Abschlusspraxis und dem Bedarf der TERO GmbH an einem Managementprogramm für den internen Gebrauch.
+
+
+Erstellung:
+----------------
+
+Das Program wurde mit den folgenen Techologien entwickelt:
+
+- IDE: Qt-Creator 6.8.x
+- Framework: QtQuick - QML 6.8.x
+- Sprache: Python 3.13.x
+- Datenbank: MariaDB 10.11.x
+- Sonstiges: JavaScript
+
+
+Installation:
+------------------
+
+Möglicherweise ist für deine Plattform ein Installationspaket verfügbar. Prüfe es also zuerst.
+
+Es gibt zwei möglichkeiten das Program zu verwenden:
+
+Möglichkeit I:
+
+1. Datenbank einrichten (MaraiDB SQL-Schema verfügbar)
+2. Python installiert und eingerichtet
+3. Das Program in einen bevorzugten Pfad kopieren.
+4. Erstelle einen Link zum main.py-Skript und stelle ihn so ein, dass er mit deinem Python-Interpreter ausgeführt wird
+
+Möglichkeit II:
+
+1. Datenbank einrichten (MaraiDB SQL-Schema verfügbar)
+2. Die ausführbare Version des Programs in einen bevorzugten Pfad kopieren
+3. Erstelle einen Link zur ausführbaren Datei, die für dein System erstellt wurde (pyqcrm/pyqcrm.exe)
+
+
+Kudos:
+----------
+
+Wenn dir dieses Kunstwerk gefällt, kannst du uns deine Wertschätzung einfach dadurch zeigen, dass du jedem von uns einen Bungalow, einen 760IL BMW, lebenslange Flugtickets inklusive Begleitung kaufst und als Geschenk ein paar Millionen Euro auf ein Schweizer Bankkonto überweist.
+
+
+Team:
+---------
+
+Marcopolo
+Schnacke
+Renegade
+
diff --git a/doc/Tero_CRM_Reinigungsunternehmen.txt b/doc/Tero_CRM_Reinigungsunternehmen.txt
index edb6049..b33e0fe 100644
--- a/doc/Tero_CRM_Reinigungsunternehmen.txt
+++ b/doc/Tero_CRM_Reinigungsunternehmen.txt
@@ -1,29 +1,88 @@
-CRM für Objektbetreuung Reinigungsservice
-
-Aktueller Stand CRM Sebastian
-
-Umsetzung mit Python + QML
-
-- Kunden anlegen
-- Abrufen und anzeigen vorhandender Daten aus der Datenbank
-- GUI vorhanden
-
-Minimal Voraussetzung:
-
-- Kunden anlegen (Hausverwaltung Krefeld)
-- Objekt anlegen
-- Ansprechpartner anlegen
-- Arbeitnehmer anlegen
-- Arbeitsauftrag anlegen
-- Rechnung schreiben (Zugferd Modus)
-- Dokumenten Managment System (DMS)
-- Arbeitszeitdokumentation
-- Urlaubsplaner
-
-
-Nice To Have:
-- WhatsApp
-- Telefon
-- Social Media Uploader
-
-
+CRM für Objektbetreuung Reinigungsservice
+
+
+Minimal Voraussetzung:
+
+
+Mitarbeiter
+- Dokumenten Management System (DMS)
+- Arbeitszeitdokumentation
+- Urlaubsplaner
+- Arbeitnehmer anlegen
+
+Nice To Have:
+- WhatsApp
+- Telefon
+- Social Media Uploader
+Dokumentenvorlagen
+
+
+
+Kunden
+- Kunden anlegen (Hausverwaltung Krefeld)
+- Dokumenten Management System (DMS)
+- Ansprechpartner anlegen
+RG auch in CC Funktion
+
+Nice To Have:
+Dokumentenvorlagen
+
+
+
+
+Dashboard
+
+
+
+Abrechnung
+- Rechnung schreiben (Zugferd Modus)
+- Fixkostenerfassung
+Rechnungsarchiv
+Mahnfunktion
+
+
+Objekt
+- Objekt anlegen
+- Ansprechpartner anlegen
+- Dokumenten Management System (DMS)
+Str. (Pflicht)
+Hausnummer (Pflicht)
+PLZ
+Ort (Pflicht)
+Parteien (Anzahl)
+Stockwerke (Anzahl)
+Zwischenetage (ja/nein)
+Aufzug (ja/nein)
+Fenster (ja/nein) anschließend Anzahl + ohne Leiter erreichbar (ja/nein)
+Besonderheiten (Infofeld)
+Kontaktdaten Hausmeister / Beirat
+Reinigungsmittel zu finden? ( Wo ist es im Kellerraum, Dachgeschoss, 2 Tür von links?)
+Foto Upload wäre toll ;-)
+
+Leistungen
+Treppenhausreinigung
+Garten
+siehe Homepage !!!
+
+
+
+
+Angebot
+
+
+Auftrag
+- Arbeitsauftrag anlegen
+Pflegehinweise zum Objekt
+BG Sicherheitsdatenblätter
+Reinigungsmittel erhalten am ?
+Preis ab Datum
+Objektkontrolle (Wann wurde das Objekt das letzte Mal kontrolliert?) Ähnlich wie Preis mit Datum
+Preise
+Schlüssel
+
+
+Auswertung
+Stundenkalender
+
+
+
diff --git a/lib/ConfigLoader.py b/lib/ConfigLoader.py
index f26ed9a..a3bda34 100644
--- a/lib/ConfigLoader.py
+++ b/lib/ConfigLoader.py
@@ -41,7 +41,7 @@ class ConfigLoader(QObject):
else:
config_dir.mkdir(0o750, True, True)
- @Slot(dict, result= bool)
+ @Slot(dict, result = bool)
def setConfig(self, app_config):
# print(f"In {__file__} file, setConfig()")
if not self.__config:
diff --git a/lib/DB/BusinessDAO.py b/lib/DB/BusinessDAO.py
index 62fa609..5dbf439 100644
--- a/lib/DB/BusinessDAO.py
+++ b/lib/DB/BusinessDAO.py
@@ -27,6 +27,17 @@ class BusinessDAO(QObject):
except mariadb.Error as e:
print(str(e))
+ def getOneBusiness(self, business_id, enc_key = None):
+ try:
+ if self.__cur:
+ self.__cur.callproc("getCustomer", (business_id, enc_key,))
+ #self.__all_cols = [desc[0] for desc in self.__cur.description]
+ return self.__cur.fetchall() #, self.__all_cols
+ else:
+ return None #, None
+ except mariadb.Error as e:
+ print(str(e))
+
def addBusiness(self, data, contact_id):
try:
if self.__cur:
diff --git a/lib/DB/BusinessModel.py b/lib/DB/BusinessModel.py
index 120fd70..da793b0 100644
--- a/lib/DB/BusinessModel.py
+++ b/lib/DB/BusinessModel.py
@@ -65,6 +65,8 @@ class BusinessModel(QAbstractTableModel):
__visible_index = {}
__col_name = ""
__business_dao = None
+ __business = None
+ __business_dict = {'business':{}} #,'contact':{}}
def __init__(self):
super().__init__()
@@ -80,6 +82,22 @@ class BusinessModel(QAbstractTableModel):
self.__data = rows
self.endResetModel()
+ def __getBusinessInfo(self):
+ self.__business_dict['business']['id'] = self.__business[0][0]
+ self.__business_dict['business']['contactid'] = self.__business[0][1]
+ self.__business_dict['business']['company'] = self.__business[0][2]
+ self.__business_dict['business']['phone'] = self.__business[0][3]
+ self.__business_dict['business']['cell'] = self.__business[0][4]
+ self.__business_dict['business']['email'] = self.__business[0][5]
+ self.__business_dict['business']['website'] = self.__business[0][6]
+ self.__business_dict['business']['ceo'] = self.__business[0][7]
+ self.__business_dict['business']['info'] = self.__business[0][8]
+ self.__business_dict['business']['tax'] = self.__business[0][9]
+ self.__business_dict['business']['street'] = self.__business[0][10]
+ self.__business_dict['business']['house'] = self.__business[0][11]
+ self.__business_dict['business']['zip'] = self.__business[0][12]
+ self.__business_dict['business']['city'] = self.__business[0][13]
+
def rowCount(self, parent= QModelIndex()):
return len (self.__data)
@@ -113,7 +131,17 @@ class BusinessModel(QAbstractTableModel):
@Slot(int)
def onRowClicked(self, row):
- print(f"Selected table row: {row}, corresponding DB ID: {self.__data[row][0]}")
+ #print(f"Selected table row: {row}, corresponding DB ID: {self.__data[row][0]}")
+ if not self.__business_dict['business'] or self.__data[row][0] != self.__business_dict['business']['id']:
+ self.__business = self.__business_dao.getOneBusiness(self.__data[row][0], self.__key)
+ #print(self.__business)
+ self.__getBusinessInfo()
+ # self.__getContactInfo()
+ # print(self.__business_dict)
+
+ @Slot(result = dict)
+ def getClientDetails(self):
+ return self.__business_dict
@Slot(str)
def viewCriterion(self, criterion):
@@ -134,5 +162,3 @@ class BusinessModel(QAbstractTableModel):
def updateTable(self):
pass
-
-
diff --git a/lib/DB/ContactDAO.py b/lib/DB/ContactDAO.py
index 71ffecf..a5ba4df 100644
--- a/lib/DB/ContactDAO.py
+++ b/lib/DB/ContactDAO.py
@@ -30,4 +30,15 @@ class ContactDAO:
except Exception as e:
print("PYT: " + str(e))
+ def getContact(self, contact_id, enc_key = None):
+ try:
+ if self.__cur:
+ self.__cur.callproc("getCustomerContact", (contact_id, enc_key,))
+ #self.__all_cols = [desc[0] for desc in self.__cur.description]
+ return self.__cur.fetchall() #, self.__all_cols
+ else:
+ return None #, None
+ except mariadb.Error as e:
+ print(str(e))
+
diff --git a/lib/DB/ContactModel.py b/lib/DB/ContactModel.py
index 0cc833c..513b3b3 100644
--- a/lib/DB/ContactModel.py
+++ b/lib/DB/ContactModel.py
@@ -5,6 +5,10 @@ import logging
class ContactModel(QObject):
contactIdReady = Signal(int)
+
+ __contact = None
+ __contact_dict = {'contact':{}}
+
def __init__(self):
super().__init__()
# print(f"*** File: {__file__}, __init__()")
@@ -27,5 +31,29 @@ class ContactModel(QObject):
i = ContactDAO().addContact(contact, self.__key)
self.contactIdReady.emit(i)
+ def __getContact(self, contact):
+ self.__contact = ContactDAO().getContact(contact, self.__key)
+ self.__getContactInfo()
+
+ @Slot(int, result = dict)
+ def getContactDetails(self, contact):
+ self.__getContact(contact)
+ #print(self.__contact_dict)
+ return self.__contact_dict
+
+ def __getContactInfo(self):
+ self.__contact_dict['contact']['id'] = self.__contact[0][0]
+ self.__contact_dict['contact']['salute'] = self.__contact[0][1]
+ self.__contact_dict['contact']['fname'] = self.__contact[0][2].decode("utf-8")
+ self.__contact_dict['contact']['lname'] = self.__contact[0][3].decode("utf-8")
+ self.__contact_dict['contact']['phone'] = self.__contact[0][4].decode("utf-8")
+ self.__contact_dict['contact']['cell'] = self.__contact[0][5].decode("utf-8")
+ self.__contact_dict['contact']['position'] = self.__contact[0][6]
+ self.__contact_dict['contact']['email'] = self.__contact[0][7].decode("utf-8")
+ self.__contact_dict['contact']['birthday'] = self.__contact[0][8].decode("utf-8")
+ self.__contact_dict['contact']['priority'] = "Ja" if self.__contact[0][9] else "Nein"
+ self.__contact_dict['contact']['invoice'] = "Ja" if self.__contact[0][10] else "Nein"
+ self.__contact_dict['contact']['reminder'] = "Ja" if self.__contact[0][11] else "Nein"
+
diff --git a/lib/DB/UserManager.py b/lib/DB/UserManager.py
index 83da450..dd8b5f6 100644
--- a/lib/DB/UserManager.py
+++ b/lib/DB/UserManager.py
@@ -1,9 +1,13 @@
from .DbManager import DbManager
from ..PyqcrmFlags import PyqcrmFlags
from ..Vermasseln import Vermasseln
-from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput
+#from PySide6.QtMultimedia import QMediaPlayer, QAudioOutput : Not working well with Nuitka
+import soundfile as sf
+import sounddevice as sd
from .UserDAO import UserDAO
-from PySide6.QtCore import Slot, QObject, Signal, QUrl
+from PySide6.QtCore import Slot, QObject, Signal, QUrl, QFile
+import tempfile
+
class UserManager(QObject):
@@ -61,12 +65,24 @@ class UserManager(QObject):
if user:
self.__checkPassword(password, user[2])
else:
- player = QMediaPlayer(self)
- audioOutput = QAudioOutput(self)
- player.setAudioOutput(audioOutput)
- player.setSource(QUrl("qrc:/sounds/fail2c.ogg"))
- audioOutput.setVolume(150)
- player.play()
+ fail_src = ":/sounds/fail2c.ogg"
+ with tempfile.NamedTemporaryFile(suffix='.ogg') as ogg_file:
+ failure_sound = QFile(fail_src)
+ if not failure_sound.open(QFile.ReadOnly):
+ print(f"Failed to open resource file: {fail_src}")
+ else:
+ ogg_file.write(failure_sound.readAll())
+ ogg_path = ogg_file.name
+ fail, samplerate = sf.read(ogg_path)
+ sd.play(fail, samplerate)
+
+ ### Not working with Nuitka
+ # player = QMediaPlayer(self)
+ # audioOutput = QAudioOutput(self)
+ # player.setAudioOutput(audioOutput)
+ # player.setSource(QUrl("qrc:/sounds/fail2c.ogg"))
+ # audioOutput.setVolume(150)
+ # player.play()
def __checkPassword(self, password, hash_password):
pw_list = hash_password.split("$")
diff --git a/lib/Printers.py b/lib/Printers.py
new file mode 100644
index 0000000..c93c7f4
--- /dev/null
+++ b/lib/Printers.py
@@ -0,0 +1,23 @@
+from PySide6.QtCore import QObject, Slot
+from PySide6.QtPrintSupport import QPrinterInfo
+
+class Printers(QObject):
+ __printers = None
+ __default_printer = None
+ __default_printer_name = None
+ __available_printers = []
+
+ def __init__(self):
+ super().__init__()
+ self.__printers = QPrinterInfo.availablePrinters()
+ self.__available_printers = QPrinterInfo.availablePrinterNames()
+ self.__default_printer = QPrinterInfo.defaultPrinter()
+ self.__default_printer_name = QPrinterInfo.defaultPrinterName()
+
+ @Slot(result = list)
+ def getPrinters(self):
+ return self.__available_printers
+
+ @Slot(result = str)
+ def getDefaultPrinter(self):
+ return self.__default_printer_name
diff --git a/lib/PyqcrmPDF.py b/lib/PyqcrmPDF.py
new file mode 100644
index 0000000..c9286e6
--- /dev/null
+++ b/lib/PyqcrmPDF.py
@@ -0,0 +1,81 @@
+from reportlab.lib.pagesizes import A4 #, letter...etc
+from reportlab.pdfgen import canvas
+from reportlab.pdfbase import pdfmetrics
+from reportlab.pdfbase.ttfonts import TTFont
+
+
+'''
+ Need to check for sender and receiver sections
+ Other alternatives can be pdf-gen, pdf-generator, pdf-play, pdf34, abdul-987-pdf
+'''
+
+
+class PyqcrmPDF:
+ __pyq_doc = None
+ __pyq_pdf = None
+ __pyq_usable_width = 0
+ __pyq_usable_height = 0
+ __pyq_lr_margin = 20
+ __pyq_tb_margin = 25
+ __line = 0
+ __line_height = 14
+ __doc_title = "PYQCRM PDF Document"
+ __doc_author = "The PYQCRM Team"
+ __doc_subject = "PYQCRM Document"
+
+ def __init__(self, doc_title, doc_subject, pdf_file):
+ self.__pyq_usable_width = A4[0] - self.__pyq_lr_margin - self.__pyq_lr_margin
+ self.__pyq_usable_height = A4[1] - self.__pyq_tb_margin - self.__pyq_tb_margin
+ self.__line = self.__pyq_usable_height + self.__pyq_tb_margin
+ self.__pyq_pdf = canvas.Canvas(pdf_file, pagesize=A4)
+ self.__pyq_pdf.setAuthor(self.__doc_author)
+ self.__pyq_pdf.setTitle(doc_title if doc_title else self.__doc_title)
+ self.__pyq_pdf.setSubject(doc_subject if doc_subject else self.__doc_subject)
+ self.__pyq_doc = self.__pyq_pdf.beginText(self.__pyq_lr_margin, self.__line)
+ self.__pyq_doc.setFont("Helvetica", 12)
+
+ def addLine(self, line):
+ #self.__pyq_pdf.drawString(self.__pyq_lr_margin, self.__line, line) : Need to check if it does the trick!
+ # print(f"Line No.: {self.__line}")
+ # print(f"Line: {line}")
+ if self.__pyq_doc.getY() < self.__pyq_tb_margin:
+ # print("creating a new page...")
+ self.__pyq_pdf.drawText(self.__pyq_doc)
+ self.__pyq_pdf.showPage()
+ self.__line = self.__pyq_usable_height + self.__pyq_tb_margin
+ # print(f"Line No.: {self.__line}")
+ self.__pyq_doc = self.__pyq_pdf.beginText(self.__pyq_lr_margin, self.__line)
+ self.__pyq_doc.setFont("Helvetica", 12)
+
+ if pdfmetrics.stringWidth(line, "Helvetica", 12) > self.__pyq_usable_width:
+ # print(f"Line width: {pdfmetrics.stringWidth(line, 'Helvetica', 12)}, Available width: {self.__pyq_usable_width}")
+ words = line.split(' ')
+ line = ''
+ for word in words:
+ # print(f"Line: {line}")
+ if self.__pyq_doc.getY() < self.__pyq_tb_margin:
+ # print("creating a new page...")
+ self.__pyq_pdf.drawText(self.__pyq_doc)
+ self.__pyq_pdf.showPage()
+ self.__line = self.__pyq_usable_height + self.__pyq_tb_margin
+ # print(f"Line No.: {self.__line}")
+ self.__pyq_doc = self.__pyq_pdf.beginText(self.__pyq_lr_margin, self.__line)
+ self.__pyq_doc.setFont("Helvetica", 12)
+
+ if pdfmetrics.stringWidth(line + word + ' ', "Helvetica", 12) <= self.__pyq_usable_width:
+ line = line + word + ' '
+ else:
+ self.__pyq_doc.textLine(line)
+ line = word + ' '
+ # print(f"Last line: {line}")
+
+ if line:
+ # print(f"Available line: {line}")
+ self.__pyq_doc.textLine(line)
+
+ self.__line = self.__line + self.__line_height
+
+ def saveDoc(self):
+ self.__pyq_pdf.drawText(self.__pyq_doc)
+ self.__pyq_pdf.showPage()
+ self.__pyq_pdf.save()
diff --git a/main.py b/main.py
index f146afa..d4f396f 100644
--- a/main.py
+++ b/main.py
@@ -1,4 +1,5 @@
# # !/home/linuxero/proj/tero/pyqcrm/.qtcreator/venv-3.13.1/bin/python3
+import os
import sys
import logging
from PySide6.QtGui import QGuiApplication
@@ -13,9 +14,10 @@ from lib.DB.UserManager import UserManager
from lib.DB.AddressModel import AddressModel
from lib.DB.BTypeModel import BTypeModel
from lib.DB.ContactModel import ContactModel
+from lib.Printers import Printers
-
+os.environ['QML_XHR_ALLOW_FILE_READ'] = '1'
# [pyqcrm]
# program-name=""
@@ -35,14 +37,16 @@ address_model = None
business_model = None
business_type = None
contact_model = None
+printers = None
user = None
def initializeProgram():
#print(f"In {__file__} file, initializeProgram()")
- global address_model, bad_config, business_model, user, business_type, contact_model, db_con
+ global address_model, bad_config, business_model, user, business_type, contact_model, db_con, printers
if not bad_config:
dbconf = config.getConfig()['database']
DbManager(dbconf)
+ printers = Printers()
if DbManager().getConnection():
db_con = True
user = UserManager()
@@ -55,7 +59,7 @@ def initializeProgram():
def publishContext():
# print(f"In {__file__} file, publishContext()")
- global engine, address_model, bad_config, business_model, user, business_type, contact_model
+ global engine, address_model, bad_config, business_model, user, business_type, contact_model, printers
engine.rootContext().setContextProperty("loggedin_user", user)
engine.rootContext().setContextProperty("business_model", business_model)
engine.rootContext().setContextProperty("address_model", address_model)
@@ -76,14 +80,13 @@ if __name__ == "__main__":
config = ConfigLoader()
-
if not config.getConfig():
bad_config = True
config.configurationReady.connect(initializeProgram)
else:
initializeProgram()
-
+ engine.rootContext().setContextProperty("sys_printers", printers)
engine.rootContext().setContextProperty("bad_config", bad_config) # print(f"Fehler: {i}")
engine.rootContext().setContextProperty("db_con", db_con)
engine.rootContext().setContextProperty("config", config)
diff --git a/pyqcrm.pyproject b/pyqcrm.pyproject
index 1d5ad15..aea1a65 100644
--- a/pyqcrm.pyproject
+++ b/pyqcrm.pyproject
@@ -20,6 +20,8 @@
"lib/DB/ContactDAO.py",
"lib/DB/ContactModel.py",
"lib/DB/EmployeeModel.py",
- "lib/DB/EmployeeDAO.py"
+ "lib/DB/EmployeeDAO.py",
+ "lib/Printers.py",
+ "lib/PyqcrmPDF.py"
]
}
diff --git a/pyqcrm.qrc b/pyqcrm.qrc
index ce334d8..6fd41f3 100644
--- a/pyqcrm.qrc
+++ b/pyqcrm.qrc
@@ -18,5 +18,7 @@
fonts/LittleBirdsRegular.ttf
fonts/ReginaldScript.ttf
images/account.svg
+ README
+ LICENSE
diff --git a/qml.qrc b/qml.qrc
index fd7dfe1..1e6ca28 100644
--- a/qml.qrc
+++ b/qml.qrc
@@ -35,5 +35,12 @@
Gui/EmployeesTable.qml
Gui/EmployeeDetails.qml
Gui/ObjectDetails.qml
+ Gui/AddNewObject.qml
+ Gui/PrinterDialog.qml
+ Gui/CustomerContactDetails.qml
+ Gui/NoCustomerContact.qml
+ Gui/CustomerDetailsView.qml
+ Gui/ReadMe.qml
+ Gui/UsersPage.qml
diff --git a/requirements.txt b/requirements.txt
index b95e0fa..1a4ee44 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,6 +4,10 @@ pycryptodome
psutil
toml
mariadb
+soundfile
+sounddevice
+reportlab
+