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