Validate applicant and call database procedure

This commit is contained in:
Yuri Becker
2025-04-15 15:24:31 +02:00
parent a720dfebeb
commit f0382a960e
15 changed files with 149 additions and 74 deletions

View File

@@ -6,23 +6,38 @@ ColumnLayout {
anchors.fill: parent
spacing: Dimensions.l
Component.onCompleted: {
employee_model.addedNewEmployee.connect(successful => {
if (successful)
contentStack.pop();
});
}
ApplicantForm {
id: applicantForm
Layout.alignment: Qt.AlignTop
Layout.fillHeight: true
Layout.verticalStretchFactor: 1
}
RowLayout {
spacing: Dimensions.l
Layout.alignment: Qt.AlignRight
spacing: Dimensions.l
Button {
icon.source: "qrc:/images/ArrowLeftCircle-Outline.svg"
text: qsTr("Verwerfen")
}
onClicked: contentStack.pop()
}
Button {
enabled: applicantForm.valid
icon.source: "qrc:/images/CheckCircle.svg"
text: qsTr("Speichern")
onClicked: {
employee_model.addApplicant(applicantForm.value);
}
}
}
}

View File

@@ -7,15 +7,10 @@ ColumnLayout {
id: colPar
function checkFields() {
if (radio.children[1].checked) {
if (!personalData.checkPersonalField())
saveBtn.enabled = false;
else
saveBtn.enabled = true;
} else if (!personalData.checkPersonalField())
saveBtn.enabled = false;
else
saveBtn.enabled = true;
}
function onAddNewEmployee(added) {
if (added)
@@ -49,20 +44,6 @@ ColumnLayout {
personalData.requiredField();
}
}
Row {
id: radio
Layout.fillWidth: true
//Layout.columnSpan: 2
RadioButton {
checked: true
text: qsTr("Bewerber")
}
RadioButton {
text: qsTr("Mitarbeiter")
}
}
RowLayout {
Layout.fillWidth: true
spacing: 50
@@ -121,22 +102,8 @@ ColumnLayout {
text: qsTr("Speichern")
onClicked: {
var new_applicant;
if (radio.children[0].checked) {
// Ein Bewerber
new_applicant = JsLib.parseForm(personalData);
employee_model.addEmployee(new_applicant, true);
// appLoader.source = "EmployeeTable.qml"
// console.log(JSON.stringify (new_applicant))
} else {
// Ein Mitarbeiter
// console.log(personalData, bankAccount, nationalInsurance, applicantVarious)
new_applicant = JsLib.parseForm(personalData, bankAccount, nationalInsurance, applicantVarious);
const new_applicant = JsLib.parseForm(personalData, bankAccount, nationalInsurance, applicantVarious);
employee_model.addEmployee(new_applicant, false);
// var new_contact = JsLib.addApplicant(addContactLayout)
// contact_model.addContact(new_contact)
// console.log(JSON.stringify (new_applicant))
}
}
}
}

View File

@@ -6,6 +6,20 @@ import TeroStyle
ColumnLayout {
readonly property int fieldM: 235
readonly property int fieldS: 110
readonly property bool valid: city.acceptableInput && email.acceptableInput && firstname.acceptableInput && lastname.acceptableInput && mobile.acceptableInput && phone.acceptableInput && postcode.acceptableInput && formofaddress.acceptableInput && title.acceptableInput
readonly property var value: QtObject {
readonly property string city: (city.editText ? city.editText : city.currentText) ?? ""
readonly property string email: email.text
readonly property string firstname: firstname.text
readonly property string formofaddress: formofaddress.currentText ?? ""
readonly property string houseno: houseno.text ?? ""
readonly property string lastname: lastname.text
readonly property string mobile: mobile.text
readonly property string phone: phone.text
readonly property string postcode: (postcode.editText ? postcode.editText : postcode.currentText) ?? ""
readonly property string street: (street.editText ? street.editText : street.currentText) ?? ""
readonly property string title: title.currentText
}
spacing: Dimensions.l
@@ -34,31 +48,43 @@ ColumnLayout {
onCurrentTextChanged: {
switch (title.currentIndex) {
case 1:
salutation.text = "Sehr geehrter Herr ";
formofaddress.text = "Sehr geehrter Herr ";
break;
case 2:
salutation.text = "Sehr geehrte Frau ";
formofaddress.text = "Sehr geehrte Frau ";
break;
default:
salutation.text = "Guten Tag ";
formofaddress.text = "Guten Tag ";
}
}
}
}
Field {
label: qsTr("Vorname*")
label: qsTr("Vorname")
mandatory: true
TextField {
id: firstname
implicitWidth: fieldM
placeholderText: "Max"
placeholderText: qsTr("Max")
validator: NotEmptyValidator {
}
}
}
Field {
label: qsTr("Nachname*")
label: qsTr("Nachname")
mandatory: true
TextField {
id: lastname
implicitWidth: fieldM
placeholderText: qsTr("Mustermann")
validator: NotEmptyValidator {
}
}
}
}
@@ -66,6 +92,8 @@ ColumnLayout {
spacing: Dimensions.m
Field {
id: street
label: qsTr("Straße")
TextField {
@@ -74,6 +102,8 @@ ColumnLayout {
}
}
Field {
id: houseno
label: qsTr("Hausnummer")
TextField {
@@ -86,6 +116,8 @@ ColumnLayout {
ComboBox {
id: postcode
currentIndex: -1
editable: true
implicitWidth: fieldS
model: address_model
@@ -97,14 +129,19 @@ ColumnLayout {
}
Field {
label: qsTr("Ort")
mandatory: true
ComboBox {
id: city
currentIndex: -1
editable: true
implicitWidth: fieldM
model: address_model
textRole: "city"
validator: NotEmptyValidator {
}
}
}
}
@@ -125,10 +162,12 @@ ColumnLayout {
label: qsTr("Telefonnummer")
TextField {
id: phone
implicitWidth: fieldM
placeholderText: "+49 1234 567890"
validator: PhoneNumberValidator {
validator: OptionalPhoneNumberValidator {
}
}
}
@@ -136,10 +175,12 @@ ColumnLayout {
label: qsTr("Mobil")
TextField {
id: mobile
implicitWidth: fieldM
placeholderText: "+49 123 4567891011"
validator: PhoneNumberValidator {
validator: OptionalPhoneNumberValidator {
}
}
}
@@ -147,10 +188,12 @@ ColumnLayout {
label: qsTr("E-Mail Adresse")
TextField {
id: email
implicitWidth: fieldM
placeholderText: "tero@example.org"
validator: EmailAddressValidator {
validator: OptionalEmailAddressValidator {
}
}
}
@@ -158,7 +201,7 @@ ColumnLayout {
label: qsTr("Briefanrede")
TextField {
id: salutation
id: formofaddress
implicitWidth: fieldM
}

View File

@@ -44,12 +44,12 @@ T.Button {
border.color: Colors.interactive
border.width: isFieldButton ? 1 : 0
bottomLeftRadius: topLeftRadius
color: !control.hovered ? Colors.primary : Colors.primaryLighter
color: !control.enabled ? Colors.disabled : !control.hovered ? Colors.primary : Colors.primaryLighter
radius: Dimensions.radius
topLeftRadius: isFieldButton ? 0 : radius
}
contentItem: I.IconLabel {
color: Colors.primaryContrast
color: !control.enabled ? Colors.disabledForeground : Colors.primaryContrast
display: control.display
font: control.font
icon: control.icon

View File

@@ -17,6 +17,8 @@ QtObject {
readonly property color mantle: theme === dark ? "#1E1E23" : "#e7e9ef"
readonly property color interactive: theme === dark ? "#878b97" : "#d9d9da"
readonly property color error: theme === dark ? "#ff2264" : "#ff004b"
readonly property color disabled: theme === dark ? Qt.darker(interactive, 1.9) : Qt.darker(interactive, 1.3)
readonly property color disabledForeground: theme === dark ? Qt.darker(foreground, 1.4) : Qt.lighter(foreground, 1.9)
readonly property color transparent: "transparent"
readonly property double highlightOpacity: .3

View File

@@ -5,11 +5,16 @@ import QtQuick.Layouts
ColumnLayout
{
required property string label
/**
* Adds an asterisk after the label, informing the user that this field
* is mandatory.
*/
property bool mandatory: false
spacing: Dimensions.s
Label
{
text: label
text: label + (mandatory ? "*" : "")
font: Typography.body
}
}

View File

@@ -0,0 +1,5 @@
import QtQuick
RegularExpressionValidator {
regularExpression: /^\S+.*\S+$/
}

View File

@@ -0,0 +1,5 @@
import QtQuick
RegularExpressionValidator {
regularExpression: /^$|([\+!#$%&\*\\/\=?\^_`\.{|}\~\-\_0-9A-Za-z]{1,185})@([0-9A-Za-z\.\-\_]{1,64})\.([a-zA-z]{2,5})/
}

View File

@@ -0,0 +1,5 @@
import QtQuick
RegularExpressionValidator {
regularExpression: /^$|([+0-9])([0-9\s]{1,17})/
}

View File

@@ -4,6 +4,7 @@ import QtQuick.Templates as T
T.TextField
{
id: control
background: Rectangle
{
id: background

View File

@@ -10,7 +10,10 @@ Field Field.qml
H1 H1.qml
H2 H2.qml
Label Label.qml
NotEmptyValidator NotEmptyValidator.qml
OptionalEmailAddressValidator OptionalEmailAddressValidator.qml
PhoneNumberValidator PhoneNumberValidator.qml
OptionalPhoneNumberValidator OptionalPhoneNumberValidator.qml
PostcodeValidator PostcodeValidator.qml
QuickFilter QuickFilter.qml
SearchBar SearchBar.qml

View File

@@ -2,7 +2,6 @@ from .DbManager import DbManager
import json
import mariadb
from PySide6.QtCore import QObject, Signal
# from ..PyqcrmFlags import PyqcrmAppliEmpyFlags
class EmployeeDAO(QObject):
@@ -35,7 +34,7 @@ class EmployeeDAO(QObject):
#self.__all_cols = [desc[0] for desc in self.__cur.description]
return self.__cur.fetchall() #, self.__all_cols
else:
return None #, None
return None
except mariadb.Error as e:
print(str(e))

View File

@@ -1,4 +1,8 @@
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt, Slot, Signal
import json
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt, Slot, Signal, QJsonDocument
from PySide6.QtQml import QJSValue
from .EmployeeDAO import EmployeeDAO
# from ..PyqcrmFlags import PyqcrmFlags, PyqcrmAppliEmpyFlags
from ..ConfigLoader import ConfigLoader
@@ -24,11 +28,27 @@ class EmployeeModel(QAbstractTableModel):
self.__getData()
@Slot(dict, bool)
def addEmployee(self, new_employee, applicant = True):
def addEmployee(self, new_employee):
if 'worklicense' in new_employee:
new_employee['worklicense'] = int(new_employee['worklicense'])
new_employee['residencetype'] = int(new_employee['residencetype'])
self.__employee_dao.addEmployee(new_employee, self.__key, applicant)
self.__employee_dao.addEmployee(new_employee, self.__key, False)
@Slot(QJSValue)
def addApplicant(self, applicant: QJSValue):
self.__employee_dao.addEmployee({
"city": applicant.property("city").toString(),
"email": applicant.property("email").toString(),
"firstname": applicant.property("firstname").toString(),
"formofaddress": applicant.property("formofaddress").toString(),
"houseno": applicant.property("houseno").toString(),
"lastname": applicant.property("lastname").toString(),
"mobile": applicant.property("mobile").toString(),
"phone": applicant.property("phone").toString(),
"postcode": applicant.property("postcode").toInt(),
"street": applicant.property("street").toString(),
"title": applicant.property("title").toString(),
}, self.__key, True)
@Slot(bool)
def __refreshView(self, added):
@@ -36,16 +56,17 @@ class EmployeeModel(QAbstractTableModel):
self.__getData()
self.addedNewEmployee.emit(added)
def __getData(self, criterion = "Alle", processed = False, fired = False, every_state = True):
def __getData(self, criterion="Alle", processed=False, fired=False, every_state=True):
self.beginResetModel()
rows, self.__visible_columns = self.__employee_dao.getEmployees(self.__key, criterion, processed, fired, every_state)
rows, self.__visible_columns = self.__employee_dao.getEmployees(self.__key, criterion, processed, fired,
every_state)
self.__data = rows
self.endResetModel()
def rowCount(self, parent= QModelIndex()):
return len (self.__data)
def rowCount(self, parent=QModelIndex()):
return len(self.__data)
def columnCount(self, parent= QModelIndex()):
def columnCount(self, parent=QModelIndex()):
return len(self.__visible_columns) - self.__col_skip
@Slot(str)
@@ -54,22 +75,23 @@ class EmployeeModel(QAbstractTableModel):
self.__col_skip = 2
self.__getData(criterion, criterion == 'Erledigt', False, criterion == 'Alle')
def data(self, index, role= Qt.DisplayRole):
def data(self, index, role=Qt.DisplayRole):
if role == Qt.DisplayRole:
row = self.__data[index.row()]
applicant_col = index.column() + self.__col_skip
tr = row[applicant_col] #if type(row[index.column() + 2]) is str else str(row[index.column() + 2], "utf-8")
tr = row[
applicant_col] # if type(row[index.column() + 2]) is str else str(row[index.column() + 2], "utf-8")
if applicant_col == 2 and self.__everyone:
tr = 'Ja' if tr == 1 else 'Nein'
else:
if tr:
tr = re.sub("Keine Angabe ","", tr)
#print(f"Data: {tr}")
tr = re.sub("Keine Angabe ", "", tr)
# print(f"Data: {tr}")
# return row[index.column() + 2]
return tr
return None
def headerData(self, section, orientation, role = Qt.DisplayRole):
def headerData(self, section, orientation, role=Qt.DisplayRole):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
self.__col_name = self.__visible_columns[section + self.__col_skip]
return self.__col_name
@@ -77,11 +99,11 @@ class EmployeeModel(QAbstractTableModel):
@Slot(int)
def onRowClicked(self, row):
#print(self.__data)
# print(self.__data)
print(f"Selected table row: {row}, corresponding DB ID: {self.__data[row][0]}")
#if not self.__employee_dict['employee'] or self.__data[row][0] != self.__employee_dict['employee']['id']:
#self.__employee = self.__employee_dao.getEmployee(self.__data[row][0], self.__key)
#print(self.__business)
#self.__getEmployeeInfo()
# if not self.__employee_dict['employee'] or self.__data[row][0] != self.__employee_dict['employee']['id']:
# self.__employee = self.__employee_dao.getEmployee(self.__data[row][0], self.__key)
# print(self.__business)
# self.__getEmployeeInfo()
# self.__getContactInfo()
# print(self.__business_dict)

View File

@@ -1,6 +1,5 @@
from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt, Slot, Signal
from .ObjectDAO import ObjectDAO
# from ..PyqcrmFlags import PyqcrmFlags, PyqcrmAppliEmpyFlags
from ..ConfigLoader import ConfigLoader
import re
import json

View File

@@ -57,7 +57,11 @@
<file>TeroStyle/H1.qml</file>
<file>TeroStyle/H2.qml</file>
<file>TeroStyle/Label.qml</file>
<file>TeroStyle/NotEmptyValidator.qml</file>
<file>TeroStyle/OptionalEmailAddressValidator.qml</file>
<file>TeroStyle/OptionalPhoneNumberValidator.qml</file>
<file>TeroStyle/PhoneNumberValidator.qml</file>
<file>TeroStyle/PostcodeValidator.qml</file>
<file>TeroStyle/qmldir</file>
<file>TeroStyle/QuickFilter.qml</file>
<file>TeroStyle/SearchBar.qml</file>