Fixing ObjectAddOnContactPerson.qml conflict

This commit is contained in:
2025-02-27 14:20:15 +01:00
parent 0f253c518d
commit 6bf6ff3111
15 changed files with 22567 additions and 168 deletions

View File

@@ -145,8 +145,21 @@ ColumnLayout
}
}
}
Component.onCompleted:
{
employee_model.addedNewEmployee.connect(onAddNewEmployee)
}
// }
// } // ScrollView
function onAddNewEmployee(added)
{
if (added)
console.log('addedsuccesfully')
else
console.log('failedtoadd')
appLoader.source = 'EmployeeTable.qml'
}
function checkFields()
{

View File

@@ -11,20 +11,6 @@ GridLayout
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
@@ -73,6 +59,21 @@ GridLayout
property string name: "postcode"
id: postcode
Layout.fillWidth: true
editable: true
onCurrentTextChanged: checkFields()
onEditTextChanged: checkFields()
onActivated: currentValue
model: address_model
textRole: "display"
popup.height: 300
popup.y: postcode.y + 5 - (postcode.height * 2)
currentIndex: -1
onCurrentIndexChanged: city.currentIndex = postcode.currentIndex
validator: RegularExpressionValidator
{
regularExpression: /([0-9]{1,5})/
}
}
Label
@@ -106,7 +107,8 @@ GridLayout
SpinBox
{
id: parteien
property string name: "units"
id: partitions
Layout.fillWidth: true
from: 1
to: 100
@@ -121,6 +123,7 @@ GridLayout
SpinBox
{
property string name: "floors"
id: floors
Layout.fillWidth: true
from: 1
@@ -141,7 +144,7 @@ GridLayout
id: mezzanin
Layout.fillWidth: true
editable: false
model: [qsTr("Jööö"), qsTr("Nöööööööööööööööööööööööööö")]
model: [qsTr("Ja"), qsTr("Nein")]
}
Label
@@ -156,66 +159,25 @@ GridLayout
id: lift
Layout.fillWidth: true
editable: false
model: [qsTr("Jööö"), qsTr("Nöööööööööööööööööööööööööö")]
model: [qsTr("Ja"), qsTr("Nein")]
}
//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")
text: qsTr("Objekt-Nr.")
Layout.alignment: Qt.AlignRight
checked: false
onCheckStateChanged:
{
//checkFields()
}
}
TextField
{
property string name: "objectno"
id: objectno
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
placeholderText: qsTr("0 oder leer um eine Nummer automatisch zu generieren")
placeholderTextColor: "pink"
}
CheckBox
{
id: accessible
text: qsTr("Erreichbar")
Layout.alignment: Qt.AlignRight
checked: false
onCheckStateChanged:
{
//checkFields()
}
}
Label
{
@@ -232,39 +194,35 @@ GridLayout
}
//// 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?")
text: qsTr("Reinigungsmittel wo?")
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
}
TextField
{
property string name: "cleansing"
id: cleamsing
property string name: "cleaningproducts"
id: cleaningproducts
Layout.fillWidth: true
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
onTextChanged: checkFields()
}
Item
{
Layout.fillHeight: true
}
function checkObjectField()
{
return street.text.trim() && houseno.text.trim() &&
(postcode.editText.trim() || postcode.currentText.trim()) &&
(city.editText.trim() || city.currentText.trim()) &&
cleaningproducts.text.trim()
}
}

View File

@@ -20,19 +20,19 @@ ColumnLayout
}
CheckBox
{
id: checkAddObject
id: checkAddContact
text: qsTr("Ansprechpartner hinzufügen")
Layout.alignment: Qt.AlignRight
checked: false
onCheckStateChanged:
{
//checkFields()
checkFields()
}
}
RowLayout
{
id: addobject
id: addObject
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 45
@@ -51,7 +51,7 @@ ColumnLayout
ObjectAddOns
{
id: addObjectLayout
visible: checkAddObject.checked
visible: checkAddContact.checked
}
}
RowLayout
@@ -65,24 +65,26 @@ ColumnLayout
}
Button
{
property var new_object: null
id: saveBtn
text: qsTr("Speichern")
enabled: false
onClicked:
{
if (!checkAddObject.checked)
new_object = JsLib.parseForm(newObject)
new_object['lift'] = new_object['lift'] === 'Ja' ? 1 : 0
new_object['mezzanin'] = new_object['mezzanin'] === 'Ja' ? 1 : 0
if (!checkAddContact.checked)
{
new_object = JsLib.parseForm(objectView)
// object_model.addObject(new_object, 0)
// appLoader.source = "ObjectTable.qml"
console.log(new_object)
var list = []
object_model.addObject(new_object, list, false)
}
else
{
new_object = JsLib.parseForm(objectView)
var new_objecto = JsLib.parseForm(addObjectLayout)
console.log(new_objecto)
//objecto_model.addObject(new_objecto)
var new_objecto = addObjectLayout.getForm()
object_model.addObject(new_object, new_objecto, true)
}
}
}
@@ -93,7 +95,10 @@ ColumnLayout
Layout.fillHeight: true
}
Component.onCompleted:
{
object_model.objectAdded.connect(onObjectAdded)
}
// Connections
// {
@@ -107,18 +112,18 @@ ColumnLayout
// }
// }
// function checkFields()
// {
// if(checkAddObject.checked)
// {
// if(!objectView.checkObjectField() || !addObjectLayout.checkObjectField())
// saveBtn.enabled = false
// else
// saveBtn.enabled = true
// }
// else if (!objectView.checkObjectField())
// saveBtn.enabled = false
// else
// saveBtn.enabled = true
// }
function checkFields()
{
if(checkAddContact.checked)
{
if(!newObject.checkObjectField() || !addObjectLayout.contactPerson.contacts || !addObjectLayout.contactPerson.contacts.length)
saveBtn.enabled = false
else
saveBtn.enabled = true
}
else if (!newObject.checkObjectField())
saveBtn.enabled = false
else
saveBtn.enabled = true
}
}

View File

@@ -7,6 +7,21 @@ GridLayout
property var contacts: null
columns: 2
Label
{
text: qsTr("Kontaktdaten")
Layout.alignment: Qt.AlignRight | Qt.AlignTop
}
ComboBox
{
property string name: "contacttype"
id: contacttype
Layout.fillWidth: true
editable: false
model: [qsTr("Beirat"), qsTr("Hausmeister")]
}
Label
{
text: qsTr("Anrede")
@@ -15,7 +30,7 @@ GridLayout
ComboBox
{
id: title
model: [qsTr("Herr"),qsTr("Frau")]
model: [qsTr("Herr"), qsTr("Frau"), qsTr("keine Angabe")]
Layout.fillWidth: true
}
Label
@@ -29,6 +44,7 @@ GridLayout
Layout.fillWidth: true
placeholderText: "Pflichtfeld"
placeholderTextColor: "red"
// onTextChanged: checkContactFields()
}
Label
{
@@ -51,9 +67,26 @@ GridLayout
{
id: phonenumber
Layout.fillWidth: true
placeholderText: "Pflichtfeld"
placeholderText: mobile.text ? "" : "Pflichtfeld"
placeholderTextColor: "red"
}
Label
{
text: qsTr("Mobil")
Layout.alignment: Qt.AlignRight
}
TextField
{
id: mobile
Layout.fillWidth: true
placeholderText: phonenumber.text ? "" : "Pflichtfeld"
placeholderTextColor: "red"
}
Label
{
@@ -82,32 +115,59 @@ GridLayout
{
id: removeContact
text: qsTr("Entfernen")
enabled: false
onClicked:
{
if (contactView.highlightFollowsCurrentItem)
{
delete contacts[contactView.currentIndex]
contacts = contacts.filter(elm => elm)
contactModel.remove(contactView.currentIndex)
contactView.highlightFollowsCurrentItem = false
contactView.currentIndex = -1
if (Object.keys(contacts).length === 0)
{
enabled = false
console.log(contacts)
}
checkFields()
}
}
}
Button
{
id: addContact
text: qsTr("Hinzufügen")
enabled: firstname.text.trim() && lastname.text.trim() && (phonenumber.text.trim() || mobile.text.trim()) && posizion.text.trim() && (contacts === null || Object.keys(contacts).length < 3)
onClicked:
{
var num_contacts = 0
if (contacts !== null && contacts !== undefined) num_contacts = Object.keys(contacts).length
else contacts = {}
if (num_contacts < 3 && firstname.text.trim() !== "" && lastname.text.trim() !== "" && phonenumber.text.trim() !== "" && posizion.text.trim() !== "")
else contacts = []
if (num_contacts < 3 && firstname.text.trim() !== "" && lastname.text.trim() !== "" && (phonenumber.text.trim() !== "" || mobile.text.trim() !== "") && posizion.text.trim() !== "")
{
contacts[num_contacts] = {}
contacts[num_contacts]["title"] = title.currentText
contacts[num_contacts]["fname"] = firstname.text.trim()
contacts[num_contacts]["lname"] = lastname.text.trim()
contacts[num_contacts]["phone"] = phonenumber.text.trim()
contacts[num_contacts]["mobile"] = mobile.text.trim()
contacts[num_contacts]["position"] = posizion.text.trim()
contactModel.append({name: title.currentText + " " + firstname.text.trim() + " " + lastname.text.trim(), phone: phonenumber.text.trim(), posizion: posizion.text.trim()})
contactModel.append({name: title.currentText + " " + firstname.text.trim() + " " + lastname.text.trim(), phone: phonenumber.text.trim(), mobile: mobile.text.trim(), posizion: posizion.text.trim()})
if (checkFields())
{
saveBtn.enabled = true
}
firstname.text = ""
lastname.text = ""
phonenumber.text = ""
mobile.text = ""
posizion.text = ""
removeContact.enabled = true
checkFields()
}
}
}
@@ -136,7 +196,7 @@ GridLayout
width: 175
font.bold: true
horizontalAlignment: Text.AlignLeft
color: "black"
color: "white"
}
Text
@@ -146,7 +206,17 @@ GridLayout
width: 100
font.bold: true
horizontalAlignment: Text.AlignLeft
color: "black"
color: "white"
}
Text
{
id: cpmobile
text: qsTr("Mobil")
width: 100
font.bold: true
horizontalAlignment: Text.AlignLeft
color: "white"
}
Text
@@ -156,7 +226,7 @@ GridLayout
width: 150
font.bold: true
horizontalAlignment: Text.AlignLeft
color: "black"
color: "white"
}
}
}
@@ -182,30 +252,35 @@ GridLayout
Rectangle
{
id: mainRect
Layout.fillWidth: true
implicitHeight: 100
color: firstname.palette.base
border.color: firstname.activeFocus? firstname.palette.highlight: firstname.palette.base
ListView
{
id: contactView
implicitHeight: 500
implicitHeight: parent.height
implicitWidth: parent.width
model: contactModel
header: headline
highlight: highlight
highlightFollowsCurrentItem: true
focus: true
highlight: Rectangle { color: "grey"}
highlightFollowsCurrentItem: false
onActiveFocusChanged: if(!focus) currentIndex = -1
delegate: Item
{
//width: parent.width
width: contactView.width
height: 15
MouseArea
{
anchors.fill: parent
onClicked:
{
contactView.currentIndex = index
contactView.highlightFollowsCurrentItem = true
}
}
MouseArea
{
@@ -227,18 +302,28 @@ GridLayout
text: model.name
width: 175
horizontalAlignment: Text.AlignLeft
color: "white"
}
Text
{
text: model.phone
width: 100
horizontalAlignment: Text.AlignLeft
color: "white"
}
Text
{
text: model.mobile
width: 100
horizontalAlignment: Text.AlignLeft
color: "white"
}
Text
{
text: model.posizion
width: 150
horizontalAlignment: Text.AlignLeft
color: "white"
}
}
}

View File

@@ -1,9 +1,11 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import "../js/qmldict.js" as JsLib
Frame
{
property alias contactPerson: oaocontactperson
Layout.alignment: Qt.AlignTop
Layout.fillWidth: true
ColumnLayout
@@ -19,4 +21,8 @@ Frame
Layout.fillHeight: true
}
}
function getForm()
{
return oaocontactperson.contacts
}
}

View File

@@ -15,13 +15,13 @@ Item
Button
{
text: qsTr("Objekts zeigen")
onClicked: customersStack.pop()
text: qsTr("Zurück zu den Objekten")
onClicked: objectsStack.pop()
}
}
Component.onCompleted:
{
business_model.onRowClicked(selectedObject)
object_model.onRowClicked(selectedObject)
}
}

View File

@@ -39,7 +39,7 @@ Item
id: horizontalHeaderview
Layout.fillWidth: true
implicitHeight: 40
visible: false
//visible: false
movableColumns: true //@disable-check M16
syncView: objectTable
@@ -68,36 +68,22 @@ Item
id: objectTable
Layout.fillHeight: true
Layout.fillWidth: true
columnSpacing: 1
rowSpacing: 2
model: object_model
alternatingRows: true
resizableColumns: true // @disable-check M16
selectionBehavior: TableView.SelectRows
ScrollBar.vertical: ScrollBar
{
policy: objectTable.contentHeight > objectTable.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
}
columnSpacing: 1
rowSpacing: 2
//model: object_model
alternatingRows: true
resizableColumns: true // @disable-check M16
selectionBehavior: TableView.SelectRows
selectionModel: ItemSelectionModel
{
id: obmodel
model: objectTable.model
}
// Timer
// {
// id: redrawTable
// running: true
// interval: 1
// repeat: false
// onTriggered:
// {
// objectTable.forceLayout();
// }
// }
delegate:Rectangle
{
required property bool selected
@@ -112,7 +98,7 @@ Item
Text
{
text: model.display === null? "": model.display
text: (model.display === null || model.display === undefined)? "": model.display
elide: Text.ElideRight
width: parent.width
height: parent.height

File diff suppressed because it is too large Load Diff

View File

@@ -50,6 +50,19 @@ function parseForm(...form)
{
data_form[form[i].children[j].name] = form[i].children[j].checked
}
else if (form[i].children[j].toString().startsWith("SpinBox"))
{
data_form[form[i].children[j].name] = form[i].children[j].value
}
// else if (form[i].children[j].toString().startsWith("QQuickContentItem"))
// {
// console.log(form[i].children[j].children.children)
// for (var k = 0; k < form[i].children[j].length; k++)
// {
// console.log(form[i].children[j].name)
// }
// }
}
}
return data_form

View File

@@ -6,7 +6,7 @@ from ..PyqcrmFlags import PyqcrmAppliEmpyFlags
class EmployeeDAO(QObject):
newEmployeeAdded = Signal()
newEmployeeAdded = Signal(bool)
__cur = None
__all_cols = None
@@ -44,7 +44,8 @@ class EmployeeDAO(QObject):
if self.__cur:
self.__cur.callproc("addApplicant", (json.dumps(data), applicant, enc_key,))
self.__con.commit()
self.newEmployeeAdded.emit()
self.newEmployeeAdded.emit(True)
except mariadb.Error as e:
print(str(e))
self.newEmployeeAdded.emit(False)

View File

@@ -2,9 +2,11 @@ from PySide6.QtCore import QAbstractTableModel, QModelIndex, Qt, Slot, Signal
from .EmployeeDAO import EmployeeDAO
from ..PyqcrmFlags import PyqcrmFlags, PyqcrmAppliEmpyFlags
from ..ConfigLoader import ConfigLoader
import re
class EmployeeModel(QAbstractTableModel):
addedNewEmployee = Signal(bool)
__data = None
__employee_dao = None
__visible_index = None
@@ -20,21 +22,25 @@ class EmployeeModel(QAbstractTableModel):
self.__employee_dao.newEmployeeAdded.connect(self.__refreshView)
self.__conf = ConfigLoader().getConfig()
self.__key = self.__conf['pyqcrm']['ENCRYPTION_KEY']
print(self.__key)
self.__getData()
@Slot(dict, bool)
def addEmployee(self, new_employee, applicant = True):
new_employee['worklicense'] = int(new_employee['worklicense'])
new_employee['residencetype'] = int(new_employee['residencetype'])
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)
@Slot(str)
def viewCriterion(self, criterion, processed = False, fired = False):
self.__getData(criterion, processed, fired)
@Slot()
def __refreshView(self):
self.__getData()
@Slot(bool)
def __refreshView(self, added):
if added:
self.__getData()
self.addedNewEmployee.emit(added)
def __getData(self, criterion = "Alle", processed = False, fired = False, every_state = True):
self.beginResetModel()
@@ -64,6 +70,8 @@ class EmployeeModel(QAbstractTableModel):
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:
tr = re.sub("Keine Angabe ","", tr)
#print(f"Data: {tr}")
# return row[index.column() + 2]
return tr

37
lib/DB/ObjectDAO.py Normal file
View File

@@ -0,0 +1,37 @@
from .DbManager import DbManager
import json
import mariadb
from PySide6.QtCore import QObject, Signal
from ..PyqcrmFlags import PyqcrmAppliEmpyFlags
class ObjectDAO(QObject):
newObjectAdded = Signal(bool)
def __init__(self):
super().__init__()
#print(f"*** File: {__file__}, __init__()")
self.__con = DbManager().getConnection()
if self.__con:
self.__cur = self.__con.cursor()
def addObject(self, new_object, new_objcontact, enc_key):
try:
if self.__cur:
self.__cur.callproc("addObject", (json.dumps(new_object), json.dumps(new_objcontact), enc_key,))
self.__con.commit()
self.newObjectAdded.emit(True)
except mariadb.Error as e:
self.newObjectAdded.emit(False)
print(str(e))
def getObjects(self, criterion, enc_key = None):
try:
if self.__cur:
self.__cur.callproc("getObjects", (criterion, 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))

87
lib/DB/ObjectModel.py Normal file
View File

@@ -0,0 +1,87 @@
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
class ObjectModel(QAbstractTableModel):
objectAdded = Signal(bool)
__data = None
__object_dao = None
__visible_index = None
__visible_columns = None
__col_name = ""
__employee_dao = None
__col_skip = 2
__everyone = True
def __init__(self):
super().__init__()
self.__object_dao = ObjectDAO()
self.__object_dao.newObjectAdded.connect(self.__refreshView)
self.__conf = ConfigLoader().getConfig()
self.__key = self.__conf['pyqcrm']['ENCRYPTION_KEY']
self.__object_dao.newObjectAdded.connect(self.objectAdded)
self.__getData()
@Slot(dict, list, bool)
def addObject(self, new_object, new_objcontact = None, new_contact = False):
print(new_object)
print(new_objcontact)
self.__object_dao.addObject(new_object, new_objcontact, self.__key)
# @Slot(str)
# def viewCriterion(self, criterion, processed = False, fired = False):
# self.__getData(criterion, processed, fired)
@Slot()
def __refreshView(self):
self.__getData()
def __getData(self, criterion = "Alle"):
self.beginResetModel()
rows, self.__visible_columns = self.__object_dao.getObjects(criterion, self.__key)
self.__data = rows
self.endResetModel()
def rowCount(self, parent= QModelIndex()):
return len (self.__data)
def columnCount(self, parent= QModelIndex()):
return len(self.__visible_columns) - self.__col_skip
@Slot(str)
def viewCriterion(self, criterion):
self.__getData(criterion)
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")
#print(f"Data: {tr}")
# return row[index.column() + 2]
return tr
return None
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
return super().headerData(section, orientation, role)
@Slot(int)
def onRowClicked(self, row):
#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()
# self.__getContactInfo()
# print(self.__business_dict)

10
main.py
View File

@@ -15,6 +15,7 @@ from lib.DB.AddressModel import AddressModel
from lib.DB.BTypeModel import BTypeModel
from lib.DB.ContactModel import ContactModel
from lib.DB.EmployeeModel import EmployeeModel
from lib.DB.ObjectModel import ObjectModel
from lib.Printers import Printers
@@ -39,12 +40,13 @@ business_model = None
business_type = None
contact_model = None
employee_model = None
object_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, employee_model, db_con, printers
global address_model, bad_config, business_model, user, business_type, contact_model, employee_model, object_model, db_con, printers
if not bad_config:
dbconf = config.getConfig()['database']
DbManager(dbconf)
@@ -57,19 +59,21 @@ def initializeProgram():
business_type = BTypeModel()
contact_model = ContactModel()
employee_model = EmployeeModel()
object_model = ObjectModel()
publishContext()
bad_config = False
def publishContext():
# print(f"In {__file__} file, publishContext()")
global engine, address_model, bad_config, business_model, user, business_type, contact_model, employee_model, printers
global engine, address_model, bad_config, business_model, user, business_type, contact_model, object_model, employee_model, printers
engine.rootContext().setContextProperty("loggedin_user", user)
engine.rootContext().setContextProperty("business_model", business_model)
engine.rootContext().setContextProperty("address_model", address_model)
engine.rootContext().setContextProperty("business_type", business_type)
engine.rootContext().setContextProperty("contact_model", contact_model)
engine.rootContext().setContextProperty("employee_model", employee_model)
engine.rootContext().setContextProperty("object_model", object_model)
if __name__ == "__main__":
#QResource.registerResource("rc_qml.py")

View File

@@ -22,6 +22,8 @@
"lib/DB/EmployeeModel.py",
"lib/DB/EmployeeDAO.py",
"lib/Printers.py",
"lib/PyqcrmPDF.py"
"lib/PyqcrmPDF.py",
"lib/DB/ObjectDAO.py",
"lib/DB/ObjectModel.py"
]
}