Beispiele¶
Im Verzeichnis examples
finden sich Python Dateien, die die Verwendung von PyAPplus64 demonstrieren.
Config-Dateien¶
Viele Scripte teilen sich Einstellungen. Beispielsweise greifen fast alle Scripte irgendwie auf APplus zu und benötigen Informationen,
mit welchem APP-Server, welchem Web-Server und welcher Datenbank sie sich verbinden sollen. Solche Informationen, insbesondere die Passwörter, werden nicht in
jedem Script gespeichert, sondern nur in den Config-Dateien. Es bietet sich wohl meist an, 3 Konfigdateien zu erstellen, je eine für
das Deploy-, das Test- und das Prod-System. Ein Beispiel ist im Unterverzeichnis examples/applus-server.yaml
zu finden.
1# Einstellung für die Verbindung mit dem APP-, Web- und DB-Server.
2# Viele der Einstellungen sind im APplus Manager zu finden
3
4appserver : {
5 server : "some-server",
6 port : 2037,
7 user : "asol.projects",
8 env : "default-umgebung" # hier wirklich Umgebung, nicht Mandant verwenden
9}
10webserver : {
11 baseurl : "http://some-server/APplusProd6/"
12}
13dbserver : {
14 server : "some-server",
15 db : "APplusProd6",
16 user : "SA",
17 password : "your-db-password"
18}
Damit nicht in jedem Script immer wieder neu die Konfig-Dateien ausgewählt werden müssen, werden die Konfigs für
das Prod-, Test- und Deploy-System in examples/applus_configs.py
hinterlegt. Diese wird in allen Scripten importiert,
so dass das Config-Verzeichnis und die darin enthaltenen Configs einfach zur Verfügung stehen.
1import pathlib
2
3basedir = pathlib.Path(__file__)
4configdir = basedir.joinpath("config")
5
6serverConfYamlDeploy = configdir.joinpath("applus-server-deploy.yaml")
7serverConfYamlTest = configdir.joinpath("applus-server-test.yaml")
8serverConfYamlProd = configdir.joinpath("applus-server-prod.yaml")
9
check_dokumente.py
¶
Einfaches Beispiel für lesenden und schreibenden Zugriff auf APplus Datenbank.
1import pathlib
2import PyAPplus64
3import applus_configs
4
5def main(confFile : pathlib.Path, docDir:str, updateDB:bool) -> None:
6 server = PyAPplus64.applus.applusFromConfigFile(confFile)
7
8 sql = PyAPplus64.sql_utils.SqlStatementSelect("ARTIKEL");
9 sql.addFields("ID", "ARTIKEL", "DOCUMENTS");
10 sql.where.addConditionFieldStringNotEmpty("DOCUMENTS");
11
12 for row in server.dbQueryAll(sql):
13 doc = pathlib.Path(docDir + row.DOCUMENTS);
14 if not doc.exists():
15 print("Bild '{}' für Artikel '{}' nicht gefunden".format(doc, row.ARTIKEL))
16
17 if updateDB:
18 upd = server.mkUseXMLRowUpdate("ARTIKEL", row.ID);
19 upd.addField("DOCUMENTS", None);
20 upd.update();
21
22if __name__ == "__main__":
23 main(applus_configs.serverConfYamlTest, "somedir\\WebServer\\DocLib", False)
adhoc_report.py
¶
Sehr einfaches Beispiel zur Erstellung einer Excel-Tabelle aus einer SQL-Abfrage.
1import PyAPplus64
2import applus_configs
3import pathlib
4
5def main(confFile : pathlib.Path, outfile : str) -> None:
6 server = PyAPplus64.applus.applusFromConfigFile(confFile)
7
8 # Einfache SQL-Anfrage
9 sql1 = ("select Material, count(*) as Anzahl from ARTIKEL "
10 "group by MATERIAL having MATERIAL is not null "
11 "order by Anzahl desc")
12 df1 = PyAPplus64.pandas.pandasReadSql(server, sql1)
13
14 # Sql Select-Statements können auch über SqlStatementSelect zusammengebaut
15 # werden. Die ist bei vielen, komplizierten Bedingungen teilweise hilfreich.
16 sql2 = PyAPplus64.SqlStatementSelect("ARTIKEL")
17 sql2.addFields("Material", "count(*) as Anzahl")
18 sql2.addGroupBy("MATERIAL")
19 sql2.having.addConditionFieldIsNotNull("MATERIAL")
20 sql2.order = "Anzahl desc"
21 df2 = PyAPplus64.pandas.pandasReadSql(server, sql2)
22
23 # Ausgabe als Excel mit 2 Blättern
24 PyAPplus64.pandas.exportToExcel(outfile, [(df1, "Materialien"), (df2, "Materialien 2")], addTable=True)
25
26
27if __name__ == "__main__":
28 main(applus_configs.serverConfYamlTest, "myout.xlsx")
mengenabweichung.py
¶
Etwas komplizierteres Beispiel zur Erstellung einer Excel-Datei aus SQL-Abfragen.
1import datetime
2import PyAPplus64
3import applus_configs
4import pandas as pd # type: ignore
5import pathlib
6from typing import *
7
8def ladeAlleWerkstattauftragMengenabweichungen(
9 server:PyAPplus64.APplusServer,
10 cond:PyAPplus64.SqlCondition|str|None=None) -> pd.DataFrame:
11 sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAG w");
12 sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL")
13
14 sql.addFieldsTable("w", "ID", "BAUFTRAG", "POSITION")
15 sql.addFields("(w.MENGE-w.MENGE_IST) as MENGENABWEICHUNG")
16 sql.addFieldsTable("w", "MENGE", "MENGE_IST",
17 "APLAN as ARTIKEL", "NAME as ARTIKELNAME")
18 sql.addFields("w.UPDDATE", "p.NAME as UPDNAME")
19
20 sql.where.addConditionFieldGe("w.STATUS", 5)
21 sql.where.addCondition("abs(w.MENGE-w.MENGE_IST) > 0.001")
22 sql.where.addCondition(cond)
23 sql.order="w.UPDDATE"
24 dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql);
25
26 # Add Links
27 df = dfOrg.copy();
28 df = df.drop(columns=["ID"]);
29 # df = df[['POSITION', 'BAUFTRAG', 'MENGE']] # reorder / filter columns
30
31 df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg,
32 lambda r: r.POSITION,
33 lambda r: server.makeWebLinkWauftrag(
34 bauftrag=r.BAUFTRAG, accessid=r.ID))
35 df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg,
36 lambda r: r.BAUFTRAG,
37 lambda r: server.makeWebLinkBauftrag(bauftrag=r.BAUFTRAG))
38
39 colNames = {
40 "BAUFTRAG" : "Betriebsauftrag",
41 "POSITION" : "Pos",
42 "MENGENABWEICHUNG" : "Mengenabweichung",
43 "MENGE" : "Menge",
44 "MENGE_IST" : "Menge-Ist",
45 "ARTIKEL" : "Artikel",
46 "ARTIKELNAME" : "Artikel-Name",
47 "UPDDATE" : "geändert am",
48 "UPDNAME" : "geändert von"
49 }
50 df.rename(columns=colNames, inplace=True);
51
52 return df
53
54
55def ladeAlleWerkstattauftragPosMengenabweichungen(
56 server : PyAPplus64.APplusServer,
57 cond:PyAPplus64.SqlCondition|str|None=None) -> pd.DataFrame:
58 sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAGPOS w");
59 sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL")
60
61 sql.addFieldsTable("w", "ID", "BAUFTRAG", "POSITION", "AG")
62 sql.addFields("(w.MENGE-w.MENGE_IST) as MENGENABWEICHUNG")
63 sql.addFieldsTable("w", "MENGE", "MENGE_IST", "APLAN as ARTIKEL")
64 sql.addFields("w.UPDDATE", "p.NAME as UPDNAME")
65
66 sql.where.addConditionFieldEq("w.STATUS", 4)
67 sql.where.addCondition("abs(w.MENGE-w.MENGE_IST) > 0.001")
68 sql.where.addCondition(cond)
69 sql.order="w.UPDDATE"
70
71 dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql);
72
73 # Add Links
74 df = dfOrg.copy();
75 df = df.drop(columns=["ID"]);
76 df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg,
77 lambda r: r.POSITION,
78 lambda r: server.makeWebLinkWauftrag(
79 bauftrag=r.BAUFTRAG, accessid=r.ID))
80 df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg,
81 lambda r: r.BAUFTRAG,
82 lambda r: server.makeWebLinkBauftrag(bauftrag=r.BAUFTRAG))
83 df['AG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg,
84 lambda r: r.AG,
85 lambda r: server.makeWebLinkWauftragPos(
86 bauftrag=r.BAUFTRAG, position=r.POSITION, accessid=r.ID))
87
88 # Demo zum Hinzufügen einer berechneten Spalte
89 # df['BAUFPOSAG'] = PyAPplus64.pandas.mkDataframeColumn(dfOrg,
90 # lambda r: "{}.{} AG {}".format(r.BAUFTRAG, r.POSITION, r.AG))
91
92 # Rename Columns
93 colNames = {
94 "BAUFTRAG" : "Betriebsauftrag",
95 "POSITION" : "Pos",
96 "AG" : "AG",
97 "MENGENABWEICHUNG" : "Mengenabweichung",
98 "MENGE" : "Menge",
99 "MENGE_IST" : "Menge-Ist",
100 "ARTIKEL" : "Artikel",
101 "UPDDATE" : "geändert am",
102 "UPDNAME" : "geändert von"
103 }
104 df.rename(columns=colNames, inplace=True);
105 return df
106
107def computeInYearMonthCond(field : str, year:int|None=None,
108 month:int|None=None) -> PyAPplus64.SqlCondition | None:
109 if not (year is None):
110 if month is None:
111 return PyAPplus64.sql_utils.SqlConditionDateTimeFieldInYear(field, year)
112 else:
113 return PyAPplus64.sql_utils.SqlConditionDateTimeFieldInMonth(field, year, month)
114 else:
115 return None
116
117def computeFileName(year:int|None=None, month:int|None=None) -> str:
118 if year is None:
119 return 'mengenabweichungen-all.xlsx';
120 else:
121 if month is None:
122 return 'mengenabweichungen-{:04d}.xlsx'.format(year);
123 else:
124 return 'mengenabweichungen-{:04d}-{:02d}.xlsx'.format(year, month);
125
126def _exportInternal(server: PyAPplus64.APplusServer, fn:str,
127 cond:Union[PyAPplus64.SqlCondition, str, None]) -> int:
128 df1 = ladeAlleWerkstattauftragMengenabweichungen(server, cond)
129 df2 = ladeAlleWerkstattauftragPosMengenabweichungen(server, cond)
130 print ("erzeuge " + fn);
131 PyAPplus64.pandas.exportToExcel(fn, [(df1, "WAuftrag"), (df2, "WAuftrag-Pos")], addTable=True)
132 return len(df1.index) + len(df2.index)
133
134def exportVonBis(server: PyAPplus64.APplusServer, fn:str,
135 von:datetime.datetime|None, bis:datetime.datetime|None) -> int:
136 cond = PyAPplus64.sql_utils.SqlConditionDateTimeFieldInRange("w.UPDDATE", von, bis)
137 return _exportInternal(server, fn, cond)
138
139def exportYearMonth(server: PyAPplus64.APplusServer,
140 year:int|None=None, month:int|None=None) -> int:
141 cond=computeInYearMonthCond("w.UPDDATE", year=year, month=month)
142 fn = computeFileName(year=year, month=month)
143 return _exportInternal(server, fn, cond)
144
145def computePreviousMonthYear(cyear : int, cmonth :int) -> Tuple[int, int]:
146 if cmonth == 1:
147 return (cyear-1, 12)
148 else:
149 return (cyear, cmonth-1);
150
151def computeNextMonthYear(cyear : int, cmonth :int) -> Tuple[int, int]:
152 if cmonth == 12:
153 return (cyear+1, 1)
154 else:
155 return (cyear, cmonth+1);
156
157def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) -> None:
158 server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)
159
160 now = datetime.date.today()
161 (cmonth, cyear) = (now.month, now.year)
162 (pyear, pmonth) = computePreviousMonthYear(cyear, cmonth);
163
164 # Ausgaben
165 exportYearMonth(server, cyear, cmonth) # Aktueller Monat
166 exportYearMonth(server, pyear, pmonth) # Vorheriger Monat
167 # export(cyear) # aktuelles Jahr
168 # export(cyear-1) # letztes Jahr
169 # export() # alles
170
171if __name__ == "__main__":
172 main(applus_configs.serverConfYamlTest)
mengenabweichung_gui.py
¶
Beispiel für eine sehr einfache GUI, die die Eingabe einfacher Parameter erlaubt. Die GUI wird um die Erzeugung von Excel-Dateien mit Mengenabweichungen gebaut.
1import PySimpleGUI as sg # type: ignore
2import mengenabweichung
3import datetime
4import PyAPplus64
5import applus_configs
6import pathlib
7from typing import *
8
9def parseDate (dateS:str) -> Tuple[datetime.datetime|None, bool]:
10 if dateS is None or dateS == '':
11 return (None, True)
12 else:
13 try:
14 return (datetime.datetime.strptime(dateS, '%d.%m.%Y'), True)
15 except:
16 sg.popup_error("Fehler beim Parsen des Datums '{}'".format(dateS))
17 return (None, False)
18
19def createFile(server:PyAPplus64.APplusServer, fileS:str, vonS:str, bisS:str)->None:
20 (von, vonOK) = parseDate(vonS)
21 if not vonOK: return
22
23 (bis, bisOK) = parseDate(bisS)
24 if not bisOK: return
25
26 if (fileS is None) or fileS == '':
27 sg.popup_error("Es wurde keine Ausgabedatei ausgewählt.")
28 return
29 else:
30 file = pathlib.Path(fileS)
31
32 c = mengenabweichung.exportVonBis(server, file.as_posix(), von, bis)
33 sg.popup_ok("{} Datensätze erfolgreich in Datei '{}' geschrieben.".format(c, file))
34
35
36def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) -> None:
37 server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)
38
39 layout = [
40 [sg.Text(('Bitte geben Sie an, für welchen Zeitraum die '
41 'Mengenabweichungen ausgegeben werden sollen:'))],
42 [sg.Text('Von (einschließlich)', size=(15,1)), sg.InputText(key='Von'),
43 sg.CalendarButton("Kalender", close_when_date_chosen=True,
44 target="Von", format='%d.%m.%Y')],
45 [sg.Text('Bis (ausschließlich)', size=(15,1)), sg.InputText(key='Bis'),
46 sg.CalendarButton("Kalender", close_when_date_chosen=True,
47 target="Bis", format='%d.%m.%Y')],
48 [sg.Text('Ausgabedatei', size=(15,1)), sg.InputText(key='File'),
49 sg.FileSaveAs(button_text="wählen", target="File",
50 file_types = (('Excel Files', '*.xlsx'),),
51 default_extension = ".xlsx")],
52 [sg.Button("Aktueller Monat"), sg.Button("Letzter Monat"),
53 sg.Button("Aktuelles Jahr"), sg.Button("Letztes Jahr")],
54 [sg.Button("Speichern"), sg.Button("Beenden")]
55 ]
56
57 systemName = server.scripttool.getSystemName() + "/" + server.scripttool.getMandant()
58 window = sg.Window("Mengenabweichung " + systemName, layout)
59 now = datetime.date.today()
60 (cmonth, cyear) = (now.month, now.year)
61 (pyear, pmonth) = mengenabweichung.computePreviousMonthYear(cyear, cmonth);
62 (nyear, nmonth) = mengenabweichung.computeNextMonthYear(cyear, cmonth);
63
64 while True:
65 event, values = window.read()
66 if event == sg.WIN_CLOSED or event == 'Beenden':
67 break
68 if event == 'Aktueller Monat':
69 window['Von'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear));
70 window['Bis'].update(value="01.{:02d}.{:04d}".format(nmonth, nyear));
71 if event == 'Letzter Monat':
72 window['Von'].update(value="01.{:02d}.{:04d}".format(pmonth, pyear));
73 window['Bis'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear));
74 if event == 'Aktuelles Jahr':
75 window['Von'].update(value="01.01.{:04d}".format(cyear));
76 window['Bis'].update(value="01.01.{:04d}".format(cyear+1));
77 if event == 'Letztes Jahr':
78 window['Von'].update(value="01.01.{:04d}".format(cyear-1));
79 window['Bis'].update(value="01.01.{:04d}".format(cyear));
80 if event == 'Speichern':
81 try:
82 createFile(server, values.get('File', None),
83 values.get('Von', None), values.get('Bis', None))
84 except Exception as e:
85 sg.popup_error_with_traceback("Beim Erzeugen der Excel-Datei trat ein Fehler auf:", e);
86
87 window.close()
88
89if __name__ == "__main__":
90 main(applus_configs.serverConfYamlProd)
copy_artikel.py
¶
Beispiel, wie Artikel inklusive Arbeitsplan und Stückliste dupliziert werden kann.
1import PyAPplus64
2import applus_configs
3import logging
4import yaml
5
6
7def main(confFile:pathlib.Path, artikel:str, artikelNeu:str|None=None) -> None:
8 # Server verbinden
9 server = PyAPplus64.applus.applusFromConfigFile(confFile)
10
11 # DuplicateBusinessObject für Artikel erstellen
12 dArt = PyAPplus64.duplicate.loadDBDuplicateArtikel(server, artikel);
13
14 # DuplicateBusinessObject zur Demonstration in YAML konvertieren und zurück
15 dArtYaml = yaml.dump(dArt);
16 print(dArtYaml);
17 dArt2 = yaml.load(dArtYaml, Loader=yaml.UnsafeLoader)
18
19 # Neue Artikel-Nummer bestimmen und DuplicateBusinessObject in DB schreiben
20 # Man könnte hier genauso gut einen anderen Server verwenden
21 if (artikelNeu is None):
22 artikelNeu = server.nextNumber("Artikel")
23
24 if not (dArt is None):
25 dArt.setFields({"artikel" : artikelNeu})
26 res = dArt.insert(server);
27 print(res);
28
29
30if __name__ == "__main__":
31 # Logger Einrichten
32 logging.basicConfig(level=logging.INFO)
33 # logger = logging.getLogger("PyAPplus64.applus_db");
34 # logger.setLevel(logging.ERROR)
35
36 main(applus_configs.serverConfYamlTest, "my-artikel", artikelNeu="my-artikel-copy")
37