Fummeljob hierum darum
This commit is contained in:
@@ -143,9 +143,7 @@ Frame
|
||||
var bd = birthday.text
|
||||
if (len === 2 || len === 5) birthday.text = bd + "."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Label
|
||||
|
||||
277
Gui/AddNewObject.qml
Normal file
277
Gui/AddNewObject.qml
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -41,9 +41,9 @@ ColumnLayout
|
||||
{
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
ObjectView
|
||||
AddNewObject
|
||||
{
|
||||
id: objectView
|
||||
id: newObject
|
||||
width: parent.width
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
174
Gui/CustomerContactDetails.qml
Normal file
174
Gui/CustomerContactDetails.qml
Normal file
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -2,26 +2,61 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
Item
|
||||
{
|
||||
property int selectedClient: -1
|
||||
id: clDet
|
||||
ColumnLayout
|
||||
{
|
||||
Label
|
||||
{
|
||||
text: qsTr("Ausgewählter Kunde " + selectedClient)
|
||||
}
|
||||
property int selectedClient: -1
|
||||
property var client: null
|
||||
property var contact: null
|
||||
id: clDet
|
||||
|
||||
Button
|
||||
{
|
||||
text: qsTr("Kunden zeigen")
|
||||
text: qsTr("Zurück")
|
||||
//Layout.columnSpan: 2
|
||||
onClicked: customersStack.pop()
|
||||
}
|
||||
|
||||
SplitView
|
||||
{
|
||||
id: clDetView
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
leftPadding: 9
|
||||
rightPadding: 9
|
||||
|
||||
CustomerDetailsView
|
||||
{
|
||||
id: customerDetails
|
||||
}
|
||||
|
||||
CustomerContactDetails
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
225
Gui/CustomerDetailsView.qml
Normal file
225
Gui/CustomerDetailsView.qml
Normal file
@@ -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']? '<a href="' + client['business']['website'] + '">' + client['business']['website'] + '</a>': ""
|
||||
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']? '<a href="mailto:' + client['business']['email'] + '">' + client['business']['email'] + '</a>': ""
|
||||
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
|
||||
// }
|
||||
}
|
||||
@@ -145,6 +145,7 @@ Item
|
||||
hoverEnabled: true
|
||||
onDoubleClicked:
|
||||
{
|
||||
business_model.onRowClicked(row)
|
||||
customersStack.push("CustomerDetails.qml", {selectedClient: row});
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
33
Gui/NoCustomerContact.qml
Normal file
33
Gui/NoCustomerContact.qml
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
134
Gui/PrinterDialog.qml
Normal file
134
Gui/PrinterDialog.qml
Normal file
@@ -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())
|
||||
}
|
||||
}
|
||||
47
Gui/ReadMe.qml
Normal file
47
Gui/ReadMe.qml
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
16
Gui/UsersPage.qml
Normal file
16
Gui/UsersPage.qml
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
10
Gui/main.qml
10
Gui/main.qml
@@ -26,6 +26,16 @@ ApplicationWindow
|
||||
visible: bad_config || !db_con ? false: true
|
||||
}
|
||||
|
||||
PrinterDialog
|
||||
{
|
||||
id: printerDialog
|
||||
}
|
||||
|
||||
ReadMe
|
||||
{
|
||||
id: readMeWin
|
||||
}
|
||||
|
||||
Item
|
||||
{
|
||||
id: mainView
|
||||
|
||||
54
README
Normal file
54
README
Normal file
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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("$")
|
||||
|
||||
23
lib/Printers.py
Normal file
23
lib/Printers.py
Normal file
@@ -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
|
||||
81
lib/PyqcrmPDF.py
Normal file
81
lib/PyqcrmPDF.py
Normal file
@@ -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()
|
||||
13
main.py
13
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)
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -18,5 +18,7 @@
|
||||
<file>fonts/LittleBirdsRegular.ttf</file>
|
||||
<file>fonts/ReginaldScript.ttf</file>
|
||||
<file>images/account.svg</file>
|
||||
<file>README</file>
|
||||
<file>LICENSE</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
7
qml.qrc
7
qml.qrc
@@ -35,5 +35,12 @@
|
||||
<file>Gui/EmployeesTable.qml</file>
|
||||
<file>Gui/EmployeeDetails.qml</file>
|
||||
<file>Gui/ObjectDetails.qml</file>
|
||||
<file>Gui/AddNewObject.qml</file>
|
||||
<file>Gui/PrinterDialog.qml</file>
|
||||
<file>Gui/CustomerContactDetails.qml</file>
|
||||
<file>Gui/NoCustomerContact.qml</file>
|
||||
<file>Gui/CustomerDetailsView.qml</file>
|
||||
<file>Gui/ReadMe.qml</file>
|
||||
<file>Gui/UsersPage.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
||||
@@ -4,6 +4,10 @@ pycryptodome
|
||||
psutil
|
||||
toml
|
||||
mariadb
|
||||
soundfile
|
||||
sounddevice
|
||||
reportlab
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user