From: Michael Wagner Date: Thu, 19 Mar 2026 21:44:16 +0000 (+0100) Subject: Erste Version X-Git-Tag: eigenheimer-util_0.1-0~1 X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/SVBaL.git/commitdiff_plain/06cedd05523f072d850e26d82d6394db66632cbc Erste Version --- diff --git a/COBOL/.dummy b/COBOL/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/COBOL/vereincheck.cbl b/COBOL/vereincheck.cbl new file mode 100644 index 0000000..84f81cd --- /dev/null +++ b/COBOL/vereincheck.cbl @@ -0,0 +1,17 @@ + IDENTIFICATION DIVISION. + PROGRAM-ID. vereincheck. + *********************************************************** + * Comilieren: + * cobc -x vereincheck.cbl ../src/fileutil.o + *********************************************************** + DATA DIVISION. + WORKING-STORAGE SECTION. + 01 action-par PIC X VALUE "A". + 01 data-val PIC X(9). + 01 ret-val PIC 99. + + PROCEDURE DIVISION. + + CALL "fileutil" USING action-par, data-val, ret-val + DISPLAY "ret-val: "ret-val + . diff --git a/Mitgliederverwaltung/.dummy b/Mitgliederverwaltung/.dummy deleted file mode 100644 index e69de29..0000000 diff --git a/Mitgliederverwaltung/AWK/.dummy b/Mitgliederverwaltung/AWK/.dummy deleted file mode 100644 index e69de29..0000000 diff --git a/Mitgliederverwaltung/AWK/DataTypes/.dummy b/Mitgliederverwaltung/AWK/DataTypes/.dummy deleted file mode 100644 index e69de29..0000000 diff --git a/Mitgliederverwaltung/AWK/DataTypes/Zahlweise.php b/Mitgliederverwaltung/AWK/DataTypes/Zahlweise.php deleted file mode 100644 index 3bed534..0000000 --- a/Mitgliederverwaltung/AWK/DataTypes/Zahlweise.php +++ /dev/null @@ -1,23 +0,0 @@ - 'Barzahler', - self::ZW_SELBST => 'Selbstüberweise', - self::ZW_EINZUG => 'Bankeinzug', - ); - - public function __construct($value) { - $this->initInstance('Zahlweise', self::$defd_vals, $value); - } -} diff --git a/Mitgliederverwaltung/AWK/MVAdmin.php b/Mitgliederverwaltung/AWK/MVAdmin.php deleted file mode 100644 index d1b89c0..0000000 --- a/Mitgliederverwaltung/AWK/MVAdmin.php +++ /dev/null @@ -1,14 +0,0 @@ - array ( - 'intern' => 'Stamm', - 'extern' => 'Personenstamm'), - 2 => array ( - 'intern' => 'Grundstuck', - 'extern' => 'Grundstück'), - 3 => array ( - 'intern' => 'Beitrage', - 'extern' => 'Beiträge'), -// 4 => array ( -// 'intern' => 'Werkzeug', -// 'extern' => 'Werkzeug') - ); - - $atdc_tab = array ( - - // Personenstamm - array ( - 'intern' => 'nachname', - 'extern' => 'Nachname', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'vorname', - 'extern' => 'Vorname', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'stasse', - 'extern' => 'Straße', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'plz', - 'extern' => 'PLZ', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'ort', - 'extern' => 'Ort', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'zahlweise', - 'extern' => 'Zahlweise', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET), - 'typ_cl' => 'Zahlweise'), - array ( - 'intern' => 'iban', - 'extern' => 'IBAN', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'bic', - 'extern' => 'BIC', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'kommentar', - 'extern' => 'Kommentar', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'Grundstuck', - 'extern' => 'Versicherte_Grundstücke', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET, AttrProperty::AP_LIST, AttrProperty::AP_REL), - 'rel_moc' => 2), //Grundstücke - array ( - 'intern' => 'Beitrag', - 'extern' => 'Zahlungen', - 'moc' => 'Stamm', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET, AttrProperty::AP_LIST, AttrProperty::AP_REL), - 'rel_moc' => 3), //Zahlungen - - // Grundstück - array ( - 'intern' => 'wie_anschrift', - 'extern' => 'wie_Anschrift', - 'moc' => 'Grundstuck', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET, AttrProperty::AP_BOOL)), - array ( - 'intern' => 'strasse', - 'extern' => 'Straße', - 'moc' => 'Grundstuck', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'plz', - 'extern' => 'PLZ', - 'moc' => 'Grundstuck', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'ort', - 'extern' => 'Ort', - 'moc' => 'Grundstuck', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - array ( - 'intern' => 'kommentar', - 'extern' => 'Kommentar', - 'moc' => 'Grundstuck', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - - // Beiträge - array ( - 'intern' => 'jahr', - 'extern' => 'Jahr', - 'moc' => 'Beitrage', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_INT)), - array ( - 'intern' => 'zahlweise', - 'extern' => 'Zahlweise', - 'moc' => 'Beitrage', - 'props' => array (AttrProperty::AP_GET), - 'typ_cl' => 'Zahlweise'), - array ( - 'intern' => 'kommentar', - 'extern' => 'Kommentar', - 'moc' => 'Beitrage', - 'props' => array (AttrProperty::AP_GET, AttrProperty::AP_SET)), - ); - -?> diff --git a/Mitgliederverwaltung/AWK/impl/data/.dummy b/Mitgliederverwaltung/AWK/impl/data/.dummy deleted file mode 100644 index e69de29..0000000 diff --git a/Mitgliederverwaltung/AWK/impl/data/build.properties b/Mitgliederverwaltung/AWK/impl/data/build.properties deleted file mode 100644 index 82cc9ed..0000000 --- a/Mitgliederverwaltung/AWK/impl/data/build.properties +++ /dev/null @@ -1,5 +0,0 @@ -# Database driver -propel.database = mysql - -# Project name -propel.project = propel diff --git a/Mitgliederverwaltung/AWK/impl/data/runtime-conf.xml b/Mitgliederverwaltung/AWK/impl/data/runtime-conf.xml deleted file mode 100644 index c8ef64a..0000000 --- a/Mitgliederverwaltung/AWK/impl/data/runtime-conf.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - mysql - - mysql:host=localhost;dbname=svbal - SVBaL - SVBaL - - utf8 - - - - - - diff --git a/Mitgliederverwaltung/AWK/impl/data/schema.xml b/Mitgliederverwaltung/AWK/impl/data/schema.xml deleted file mode 100644 index 62162b7..0000000 --- a/Mitgliederverwaltung/AWK/impl/data/schema.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - -
- - - - - - - - -
- - - - - - - - - - - - -
- - - - - - - - - - -
-
diff --git a/Mitgliederverwaltung/etc/.dummy b/Mitgliederverwaltung/etc/.dummy deleted file mode 100644 index e69de29..0000000 diff --git a/Mitgliederverwaltung/gui/.dummy b/Mitgliederverwaltung/gui/.dummy deleted file mode 100644 index e69de29..0000000 diff --git a/Mitgliederverwaltung/gui/ConfigurationData.php b/Mitgliederverwaltung/gui/ConfigurationData.php deleted file mode 100644 index bb3ef9d..0000000 --- a/Mitgliederverwaltung/gui/ConfigurationData.php +++ /dev/null @@ -1,8 +0,0 @@ - array ("Mitgliederverwaltung"), - "autoloader" => array ("ClassLoader", "autoload"), - "Mitgliederverwaltung::InterfaceClass" => "MVAdmin", - "PropelClassPath" => "AWK/impl/data/build" - ); diff --git a/Mitgliederverwaltung/gui/index.php b/Mitgliederverwaltung/gui/index.php deleted file mode 100644 index bc674a8..0000000 --- a/Mitgliederverwaltung/gui/index.php +++ /dev/null @@ -1,41 +0,0 @@ -log(__FILE__, $requestMethod); -$logger->log(__FILE__, "GET-Variabeles:"); -while ( list($key, $value) = each($_GET) ) { - $logger->log(__FILE__, $key . " : " . $value);; -} -$logger->log(__FILE__, "POST-Variabeles:"); -while ( list($key, $value) = each($_POST) ) { - $logger->log(__FILE__, $key . " : " . $value);; -} - -// call implementation -try { - $control = new GenadControl(); - $control->doIt(); -} -catch (Exception $e) { - $logger->logException(__FILE__, $e); - echo "Error occurred!"; -} - diff --git a/Mitgliederverwaltung/gui/name_map.php b/Mitgliederverwaltung/gui/name_map.php deleted file mode 100644 index 54afac8..0000000 --- a/Mitgliederverwaltung/gui/name_map.php +++ /dev/null @@ -1,10 +0,0 @@ -nameMap = array ( - "Back" => "Zurück", - "Class Selection" => "Typ", - "Create" => "Anlegen", - "List" => "Liste", - "Select" => "Auswählen", - "Submit changes" => "Änderungen speichern", - "zur_Zeit_bei" => "zur Zeit bei" -); diff --git a/Mitgliederverwaltung/gui/view/.dummy b/Mitgliederverwaltung/gui/view/.dummy deleted file mode 100644 index e69de29..0000000 diff --git a/Mitgliederverwaltung/gui/view/admin.css b/Mitgliederverwaltung/gui/view/admin.css deleted file mode 120000 index e0f1667..0000000 --- a/Mitgliederverwaltung/gui/view/admin.css +++ /dev/null @@ -1 +0,0 @@ -/usr/share/php/GenericAdmin/gui/view/admin.css \ No newline at end of file diff --git a/Mitgliederverwaltung/gui/view/gfx b/Mitgliederverwaltung/gui/view/gfx deleted file mode 120000 index ac9994b..0000000 --- a/Mitgliederverwaltung/gui/view/gfx +++ /dev/null @@ -1 +0,0 @@ -/usr/share/php/mLohn/gui/view/gfx/ \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..174cd92 --- /dev/null +++ b/README @@ -0,0 +1,6 @@ +SVBaL-Repository +================ +- Erste Versuche mit GenericAdmin, am 10.1.2024 gelöscht. +10.2.2024 +- Beginn mit Python-Werkzeug für den S-Verein-Deltaabgleich + diff --git a/Test/.dummy b/Test/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/Test/FitnessePages/.dummy b/Test/FitnessePages/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/Test/FitnessePages/DjangoTest/.dummy b/Test/FitnessePages/DjangoTest/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/Test/FitnessePages/DjangoTest/content.txt b/Test/FitnessePages/DjangoTest/content.txt new file mode 100644 index 0000000..5897464 --- /dev/null +++ b/Test/FitnessePages/DjangoTest/content.txt @@ -0,0 +1,6 @@ +!|test.util.CallScript| +|call script|!-rm -r ~/test |true-!| +|call script|mkdir -p ~/test/python/eh_util/eh_app| +|call script|cp ../python/eh_util/eh_app/tests.py ~/test/python/eh_util/eh_app/| +|call script|cp ../python/eh_util/manage.py ~/test/python/eh_util/| +|call script|cd ~/test/python/eh_util/; ./manage.py test| diff --git a/Test/FitnessePages/DjangoTest/properties.xml b/Test/FitnessePages/DjangoTest/properties.xml new file mode 100644 index 0000000..e13fdf4 --- /dev/null +++ b/Test/FitnessePages/DjangoTest/properties.xml @@ -0,0 +1,12 @@ + + + true + true + true + true + true + true + true + true + true + diff --git a/Test/FitnessePages/content.txt b/Test/FitnessePages/content.txt new file mode 100644 index 0000000..990fb6f --- /dev/null +++ b/Test/FitnessePages/content.txt @@ -0,0 +1 @@ +!contents -R2 -g -p -f -h \ No newline at end of file diff --git a/Test/FitnessePages/properties.xml b/Test/FitnessePages/properties.xml new file mode 100644 index 0000000..72aa2b6 --- /dev/null +++ b/Test/FitnessePages/properties.xml @@ -0,0 +1,11 @@ + + + true + true + true + true + true + true + true + true + diff --git a/Test/bin/.dummy b/Test/bin/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/Test/bin/create-test-export b/Test/bin/create-test-export new file mode 100755 index 0000000..05ef991 --- /dev/null +++ b/Test/bin/create-test-export @@ -0,0 +1,19 @@ +#!/bin/bash + +if [ $# -lt 1 ]; then + echo "usage: create-test-export INPUT" + exit 1 +fi +head -1 $1 > TestExport.csv +grep 14962 $1 >> TestExport.csv +grep 19603 $1 >> TestExport.csv +grep 76491 $1 >> TestExport.csv +grep 76441 $1 >> TestExport.csv +grep 75987 $1 >> TestExport.csv +grep 75723 $1 >> TestExport.csv +grep 14965 $1 >> TestExport.csv +grep 14964 $1 >> TestExport.csv +grep 76736 $1 >> TestExport.csv + +# remove BOM +sed -i $'1s/^\uFEFF//' TestExport.csv diff --git a/Test/etc/.dummy b/Test/etc/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/Test/etc/Export.csv b/Test/etc/Export.csv new file mode 100644 index 0000000..5819ab7 --- /dev/null +++ b/Test/etc/Export.csv @@ -0,0 +1,10 @@ +"Anrede";"Vorname";"Nachname";"Straße";"Zusatzadresse";"PLZ";"Ort";"Land";"Titel";"Geschlecht";"Familienstand";"Mitglieds-Nr";"Geburtsdatum";"Eintrittsdatum";"Austrittsdatum";"Austrittsgrund";"Zahlungsart";"IBAN";"BIC";"Kontonummer";"Bankleitzahl";"Kreditinstitut";"Kontoinhaber";"Mandatsreferenz";"Debitorenkonto-Nr";"Status";"Branche";"Notfallnummer";"Notfallkontakt";"KommE-Mail_P1";"KommFax_P1";"KommMobil_P1";"KommWeb_P1";"KommTelefon_P1";"KommSkype_P1";"Abteilung_1";"Abteilungseintritt_1";"Abteilungsaustritt_1";"Abteilungsstatus_1";"Abteilungsstatus DFB_1";"Abteilungsaustrittsgrund_1";"Abteilung_2";"Abteilungseintritt_2";"Abteilungsaustritt_2";"Abteilungsstatus_2";"Abteilungsstatus DFB_2";"Abteilungsaustrittsgrund_2";"Beitragsbezeichnung_1_1";"Beitragsstart_1_1";"Beitragsende_1_1";"BeitragBerechnetBis_1_1";"BeitragZahlweise_1_1";"BeitragFälligkeitsdatum_1_1";"BeitragVariabel_1_1";"BeitragFormel_1_1";"BeitragGrundbetrag1_1_1";"BeitragGB1Gesperrt_1_1";"BeitragGrundbetrag2_1_1";"BeitragGB2Gesperrt_1_1";"BeitragGrundbetrag3_1_1";"BeitragGB3Gesperrt_1_1";"BeitragGrundbetrag4_1_1";"BeitragGB4Gesperrt_1_1";"BeitragGrundbetrag5_1_1";"BeitragGB5Gesperrt_1_1";"BeitragZahlweiseGesperrt_1_1";"Freifeldname_1";"Freifeldwert_1";"Freifeldname_2";"Freifeldwert_2";"Freifeldname_3";"Freifeldwert_3";"NotizArt_1";"NotizPrio_1";"NotizBetreff_1";"NotizInhalt_1";"NotizDatum_1" +Herr;Horsti;Acktu;"Blombergstr. 30";"";81825;München;Deutschland;;;;"14962";25.04.1948;01.01.1998;31.12.2050;;;DE24700202701780047001;HYVEDEMMXXX;"1780047000";"70020270";"UniCredit Bank - HypoVereinsbank";Acktun Horst;"10058";"10253";Aktiv;;;;;"";"";;"4317535";;Standard;01.01.1998;;Aktiv;;;;;;;;;Standard;01.01.1998;;;Jahr;;nein;;;;;;;;;;;;;;"";;"";Zeitung;"Ja";;;;; +Herr;Horsti;Acktu;"Blombergstr. 30";"";81825;München;Deutschland;;;;"14963";25.04.1948;01.01.1998;31.12.2023;;;DE24700202701780047001;HYVEDEMMXXX;"1780047000";"70020270";"UniCredit Bank - HypoVereinsbank";Acktun Horst;"10058";"10253";Aktiv;;;;;"";"";;"4317535";;Standard;01.01.1998;;Aktiv;;;;;;;;;Standard;01.01.1998;;;Jahr;;nein;;;;;;;;;;;;;;"";;"";Zeitung;"Ja";;;;; +Herr und Frau;Dr. Michael und Ingrid;Wagner;"Turfstr. 18 a";"";81929;München;Deutschland;;Jur. Person oder sächlich;;"14964";;01.04.2024;;;Lastschrift;DE54700905000004428285;GENODEF1S04;"4428285";"70090500";"Sparda-Bank München";Wagner Dr. Michael und Ingrid;"10255";"10255";Aktiv;;;;;;;;;;;;;;;;Zusatzgrundstück;01.04.2024;;Aktiv;;;;;;;;;;;;;;;;;;;;;;Partnernummer;"76736";VersichertesObjekt;"Gleiwitzer Str. 26, 81929 München";Zeitung;"Nein";;;;; +Herr und Frau;Dr. Michael und Ingrid;Wagner;"Turfstr. 18 a";"";81929;München;Deutschland;;;;"14965";;01.04.2024;;;Lastschrift;DE54700905000004428285;GENODEF1S04;"4428285";"70090500";"Sparda-Bank München";Wagner Dr. Michael und Ingrid;"10256";"10256";Aktiv;;;;;;;;;;;;;;;;Zusatzgrundstück;01.04.2024;;Aktiv;;;;;;;;;;;;;;;;;;;;;;Partnernummer;"76736";VersichertesObjekt;"Finkenweg 5, 83556 Griesstätt";Zeitung;"Nein";;;;; +Herr und Frau;Dr. Michael und Ingrid;Wagner;"Turfstr. 18 a";"";81929;München;Deutschland;;;;"76736";25.05.1965;01.12.2012;;;Lastschrift;DE54700905000004428285;GENODEF1S04;"4428285";"70090500";"Sparda-Bank München";Wagner Dr. Michael und Ingrid;"10218";"10218";Aktiv;;;;bonzius@vindelicia.de;"";"";;"99 75 94 94";;Standard;01.12.2012;;Aktiv;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"";VersichertesObjekt;"81929 München, Gleiwitzerstr. 28";Zeitung;"Ja";;;;; +Frau;Elke;Söller;"Argula-von-Grumbach-Sr. 5";"";92345;Dietfurt;Deutschland;;;;"77912";08.05.1979;01.07.2008;;;;;;;;;;"10193";"10193";Aktiv;;;;;"";"";;"084 64 / 64 21 79";;Standard;01.07.2008;;Aktiv;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"";VersichertesObjekt;"92363 Breitenbrunn, Breitenegg 1 a";Zeitung;"Ja";;;;; +Frau;Marianne;Söller;"Böhmerbrunnenstr. 27";"";92345;Dietfurt;Deutschland;;;;"76441";;01.01.1995;;;;DE98760520800570713611;BYLADEM1NMA;"570713610";"76052080";"Sparkasse Neumarkt i d OPf-Parsberg";Söllner2 Marianne;"10249";"10249";Aktiv;;;;;;;;;;;;;;;;Zusatzgrundstück;01.01.1995;;Aktiv;;;;;;;;;;;;;;;;;;;;;;Partnernummer;"75987";;"";Zeitung;"Nein";;;;; +Frau;Marianne;Söller;"Böhmerbrunnenstr. 27";"";92354;Dietfurt;Deutschland;;;;"75987";;01.01.1985;;;;DE98760520800570713611;BYLADEM1NMA;"570713610";"76052080";"Sparkasse Neumarkt i d OPf-Parsberg";Söllner Marianne;"10194";"10194";Aktiv;;;;;"";"";;"084 64 / 15 42";;Standard;01.01.1985;;Aktiv;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"";VersichertesObjekt;"92345 Dietfurt 2, Böhmerbrunnenstr. 21";Zeitung;"Ja";;;;; +Frau;Marianne;Söller;"Böhmerbrunnenstr. 27";"";92345;Dietfurt;Deutschland;;;;"76491";;01.09.1992;;;;DE98760520800570713611;BYLADEM1NMA;"570713610";"76052080";"Sparkasse Neumarkt i d OPf-Parsberg";Söllner3 Marianne;"10250";"10250";Aktiv;;;;;;;;;;;;;;;;Zusatzgrundstück;01.09.1992;;Aktiv;;;;;;;;;;;;;;;;;;;;;;Partnernummer;"75987";VersichertesObjekt;"92363 Breitbrunn, Tannenweg 6";Zeitung;"Nein";;;;; diff --git a/Test/etc/TestExport.csv b/Test/etc/TestExport.csv new file mode 100644 index 0000000..3948d46 --- /dev/null +++ b/Test/etc/TestExport.csv @@ -0,0 +1,10 @@ +"Mitglieds-Nr";Nachname;Vorname;Anrede;"Straße";PLZ;Ort;Geburtsdatum;Eintrittsdatum;Austrittsdatum;Abteilungen;Zahlungsart;IBAN;BIC;Kontoinhaber;"Mandatsreferenz";Abweichender Zahler Mitglied;Abweichender Zahler IBAN;E-Mail;Postinfo;VersichertesObjekt;Zeitung +"14962";Acktun;Horst;Herr;"Blombergstr. 30";81825;München;25.04.1948;01.01.1998;;Standard (01.01.1998 bis -);Lastschrift;DE24 7002 0270 1780 0470 00;HYVEDEMMXXX;Acktun Horst;"10058";;;;Nein;;Ja +"* 19603";Bachmann;Sonja und Horst;Herr und Frau;"Kampenwandstr. 1a";81671;München;04.12.1954;01.04.2010;31.12.2024;Zusatzgrundstück (01.04.2010 bis 31.12.2024);Lastschrift (AZ: Bachmann, Horst und Sonja);;;;"";Bachmann, Horst und Sonja;DE47 7015 0000 0080 1337 39;;Nein;81671 München, Hachinger Bach-Str. 4 c;Nein +"76491";Söllner;Marianne;Frau;"Böhmerbrunnenstr. 27";92345;Dietfurt;;01.09.1992;;Zusatzgrundstück (01.09.1992 bis -);Lastschrift (AZ: Söllner, Marianne);;;;"";Söllner, Marianne;DE98 7605 2080 0570 7136 10;;Nein;92363 Breitbrunn, Tannenweg 6;Nein +"76441";Söllner;Marianne;Frau;"Böhmerbrunnenstr. 27";92345;Dietfurt;;01.01.1995;;Zusatzgrundstück (01.01.1995 bis -);Lastschrift (AZ: Söllner, Marianne);;;;"";Söllner, Marianne;DE98 7605 2080 0570 7136 10;;Nein;;Nein +"75987";Söllner;Marianne;Frau;"Böhmerbrunnenstr. 27";92354;Dietfurt;;01.01.1985;;Standard (01.01.1985 bis -);Lastschrift;DE98 7605 2080 0570 7136 10;BYLADEM1NMA;Söllner Marianne;"10194";;;;Nein;92345 Dietfurt 2, Böhmerbrunnenstr. 21;Ja +"75723";Tungl;Inge;Frau;"Sonnwendjochstr. 79";81825;München;;01.01.1960;;Standard (01.01.1960 bis -);Überweisung;;;Tungl Inge;"";;;;Nein;;Ja +"14965";Wagner;Dr. Michael und Ingrid;;"Turfstr. 18 a";81929;München;;01.04.2024;;Zusatzgrundstück (01.04.2024 bis -);Lastschrift (AZ: Wagner, Dr. Michael und Ingrid);;;;"";Wagner, Dr. Michael und Ingrid;DE54 7009 0500 0004 4282 85;;Nein;Finkenweg 5, 83556 Griesstätt;Nein +"14964";Wagner;Dr. Michael und Ingrid;Herr und Frau;"Turfstr. 18 a";81929;München;;01.04.2024;;Zusatzgrundstück (01.04.2024 bis -);Lastschrift (AZ: Wagner, Dr. Michael und Ingrid);;;;"";Wagner, Dr. Michael und Ingrid;DE54 7009 0500 0004 4282 85;;Nein;Gleiwitzer Str. 26, 81929 München;Nein +"76736";Wagner;Dr. Michael und Ingrid;Herr und Frau;"Turfstr. 18 a";81929;München;25.05.1965;01.12.2012;;Standard (01.12.2012 bis -);Lastschrift;DE54 7009 0500 0004 4282 85;GENODEF1S04;Wagner Dr. Michael und Ingrid;"10218";;;bonzius@vindelicia.de;Nein;81929 München, Gleiwitzerstr. 28;Ja diff --git a/bin/.dummy b/bin/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/bin/delete_vorlage.sh b/bin/delete_vorlage.sh new file mode 100755 index 0000000..1b42d8b --- /dev/null +++ b/bin/delete_vorlage.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e + +if [ $# -ne 3 ] +then + echo "invalid call: delete_vorlage.sh VEREIN VORLAGE" + exit 7 +fi + +verein=$1 +vorlage=$2 + +echo "Delete $vorlage for $verein" +rm eh_app/templates/$verein/$vorlage diff --git a/bin/ehapp-verein-check.sh b/bin/ehapp-verein-check.sh new file mode 100755 index 0000000..4ff238c --- /dev/null +++ b/bin/ehapp-verein-check.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +usage="ehapp-verein-check DATA_PATH" + +if [ -z "$1" ]; then + echo $usage + exit 7 +fi +data_path=$1 + +mkdir -p $data_path/quellen +mkdir -p $data_path/upload +mkdir -p $data_path/build diff --git a/bin/install_vorlage.sh b/bin/install_vorlage.sh new file mode 100755 index 0000000..9e5ca56 --- /dev/null +++ b/bin/install_vorlage.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -e + +if [ $# -ne 4 ] +then + echo "invalid call: install.sh VEREIN TGZDATEI WORKDIR" + exit 7 +fi + +verein=$1 +tgz=$2 +workdir=$3 +vorlage=${2%%.tgz} + +echo "Install $vorlage for $verein" + +mkdir -p $workdir +mkdir -p eh_app/templates/$verein + +pushd $workdir >/dev/null + tar xf $workdir/upload/$2 +popd >/dev/null +ln -sf $workdir/$vorlage/$vorlage.tex eh_app/templates/$verein/ diff --git a/bin/pdfersteller.sh b/bin/pdfersteller.sh new file mode 100755 index 0000000..f471c5a --- /dev/null +++ b/bin/pdfersteller.sh @@ -0,0 +1,18 @@ +#/bin/bash +set -e + +usage="pdferasteller DIR FILE" +if [ $# -ne 2 ]; then + echo $usage + exit 7 +fi +dir=$1 +file=$2 +ergebnis_dir="ergebnis/" + +cd $dir +pdflatex $file + +# Schiebe Ergebnis in das Ergebnisverzeichnis +erg_datei=${file%.tex}.pdf +mv $erg_datei $ergebnis_dir diff --git a/debian/.dummy b/debian/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/debian/eigenheimer-util.changelog b/debian/eigenheimer-util.changelog new file mode 100644 index 0000000..b27984b --- /dev/null +++ b/debian/eigenheimer-util.changelog @@ -0,0 +1,4 @@ +svbal (0.1-%BUILD%) unstable; urgency=medium + * initial version: Ausweiserstellung, Kassenbrief + -- Michael Wagner Sat, 06 Jun 2020 20:03:04 +0100 + diff --git a/debian/eigenheimer-util.conf b/debian/eigenheimer-util.conf new file mode 100644 index 0000000..4f91cd7 --- /dev/null +++ b/debian/eigenheimer-util.conf @@ -0,0 +1,2 @@ +compile_type=NONE +target_type=DEB diff --git a/debian/eigenheimer-util.control b/debian/eigenheimer-util.control new file mode 100644 index 0000000..138b18e --- /dev/null +++ b/debian/eigenheimer-util.control @@ -0,0 +1,12 @@ +Source: svbal +Section: main +Priority: optional +Maintainer: Michael Wagner +Build-Depends: git, mbuild + +Package: eigenheimer-util +Architecture: all +Depends: texlive-latex-base, texlive-latex-recommended, texlive-lang-german, texlive-latex-extra, python3, apache2, libapache2-mod-wsgi-py3, python3-django +Description: Mitgliederverwaltung Eigenheimerverband + . + diff --git a/debian/eigenheimer-util.cp b/debian/eigenheimer-util.cp new file mode 100755 index 0000000..adcff7b --- /dev/null +++ b/debian/eigenheimer-util.cp @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +mkdir -p $1/opt +cp -r python/eh_app $1/opt/ +rm -r $1/opt/eh_app/templates/svbal 2>/dev/null |true + +mkdir -p $1/opt/eh_app/bin +cp bin/* $1/opt/eh_app/bin/ diff --git a/debian/eigenheimer-util.postinst b/debian/eigenheimer-util.postinst new file mode 100755 index 0000000..33b88b4 --- /dev/null +++ b/debian/eigenheimer-util.postinst @@ -0,0 +1,40 @@ +#!/bin/bash +set -e + +ln -sf /opt/eh_app /opt/mysite/ + +# add application to settings.py +if ! grep "### EHUTIL ###" /opt/mysite/mysite/settings.py >/dev/null +then + echo "add ehutil settings to settings.py" + cat << EOF >> /opt/mysite/mysite/settings.py +### EHUTIL ### + +INSTALLED_APPS.append('eh_app.apps.EhAppConfig') + +### EHUTIL-END ### +EOF +fi + +# DB anlegen/migrieren +/opt/mysite/manage.py migrate + +# add url routing +if ! grep "### EHUTIL ###" /opt/mysite/mysite/urls.py >/dev/null +then + echo "add url routing" + cat << EOF >> /opt/mysite/mysite/urls.py +### EHUTIL ### + +from django.urls import include +urlpatterns.append(path('eh-app/', include('eh_app.urls'))) + +### EHUTIL-END ### +EOF +fi + +# prepare template dir +chown www-data:www-data /opt/eh_app/templates + +# restart apache +systemctl restart apache2 diff --git a/doc/.dummy b/doc/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/doc/README.Django b/doc/README.Django new file mode 100644 index 0000000..7044dea --- /dev/null +++ b/doc/README.Django @@ -0,0 +1,16 @@ +Django +====== + +- Version +python3 -m django --version + +- Projekt/Anwendung +django-admin startproject mysite + +- Server starten +manage.py runserver + +- Model-View-Control + x Model -> awk.py + x View -> templates + forms.py + x Control -> views.py diff --git a/etc/SVBaL.conf b/etc/SVBaL.conf deleted file mode 100644 index b9909e2..0000000 --- a/etc/SVBaL.conf +++ /dev/null @@ -1,9 +0,0 @@ -# Configuration for mKrimi Verleihnix Gui - Alias /SVBaL "/usr/share/php/SVBaL/gui" - - Options +FollowSymlinks - RewriteEngine On - RewriteBase /SVBaL/ - RewriteRule ^.*php$ index.php - - diff --git a/etc/eh_app.conf b/etc/eh_app.conf new file mode 100644 index 0000000..4b89735 --- /dev/null +++ b/etc/eh_app.conf @@ -0,0 +1 @@ +WSGIScriptAlias /eh-app /opt/mysite/mysite/wsgi.py diff --git a/latex/.dummy b/latex/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/latex/ausweis.tex b/latex/ausweis.tex new file mode 100644 index 0000000..90295fe --- /dev/null +++ b/latex/ausweis.tex @@ -0,0 +1,17 @@ +\documentclass{scrlttr2} +\LoadLetterOption{ausweis} +\setkomavar{date}{\vspace*{-6\baselineskip}\today} +\begin{document} +\begin{letter}{ + %\today \par \vspace*{2\baselineskip} + \vspace*{4\baselineskip}[[Testdruck]]\\Michael Wagner\\Turfstr. 18a\\81929 München +} +\opening{\par \vspace*{3\baselineskip}Sehr geehrter Herr Wagner,} +Hier steht der Text + +in mehreren Absätzen SDFG SDF SDFG SDFGHSDFGH SDFGH SDFGH SDF SDFG SDF SDSGH SDFG HGFTHs dfgsdf sdfg sdfg sdf sdfg dfsg sdfg s. + +Und ein dritter. +\closing{Mit freundlichen Grüßen} +\end{letter} +\end{document} diff --git a/latex/brief.lco b/latex/brief.lco new file mode 100644 index 0000000..91817ba --- /dev/null +++ b/latex/brief.lco @@ -0,0 +1,55 @@ +\ProvidesFile{testlco.lco}[2022/01/08 letter class option] +\RequirePackage{ngerman} % Deutsche Sprache +\RequirePackage{fix-cm} +\RequirePackage[T1]{fontenc} % Ligaturschriften +\RequirePackage[utf8]{inputenc} % Eingabezeichensatz +\RequirePackage{graphicx} % Grafikpaket +\RequirePackage{hyperref} +\usepackage{wallpaper} % Hintergrundbilder +\hypersetup{pdfpagemode=None} +\KOMAoptions{ + paper=a4, + pagenumber=false, +% fromalign=right, +% fromrule=afteraddress, +% fromphone, +% fromfax, + fromlogo, +% fromurl,fromemail, +% backaddress, + foldmarks, +% headsepline,footsepline, + headsepline=true, % Linie unter dem Header der Folgeseite + enlargefirstpage, + parskip=half +} +\setkomavar{fromname}{SV Berg am Laim} +\setkomavar{fromaddress}{Turfstr. 18a\\81929~München} +\setkomavar{firsthead}{} +%\setkomavar{fromphone}{+49\,(0)\,555\,12345} +%\setkomavar{fromfax}{+49\,(0)\,555\,12345} +%\setkomavar{fromemail}{joachim@example.org} +\setkomavar*{emailseparator}{mailto} +\setkomavar{emailseparator}{:} +%\setkomavar{fromurl}{\url{//www.schlosser.info}} +\setkomavar*{urlseparator}{} +\setkomavar{urlseparator}{} +\setkomavar{frombank}{Eine Bank\\IBAN~123\,45\,678} +\setkomavar{place}{München} +\setkomavar{signature}{Michael Wagner\\1.Vorsitzender} +%\setkomavar{fromlogo}{\parbox[b]{8cm}{\usekomafont{fromaddress}% +% {\mbox{\LARGE \bfseries Dr.\ rer.\ nat.\ Joachim~Schlosser}} +% \smallskip} +%} +\setkomafont{backaddress}{\sffamily} +\setkomafont{fromaddress}{\sffamily} +%\setkomavar{fromlogo}{\parbox[b]{8cm}{\includegraphics[width=2cm]{logo.jpeg}}} +\setkomavar{fromlogo}{\includegraphics[width=2cm]{logo.jpeg}} +\setkomavar{nexthead}{\mbox{\textbf{Siedlervereinigung Berg am Laim}} \usekomavar{fromlogo}} +\headheight=2cm +\setkomafont{fromname}{\sffamily} +\addtokomafont{fromaddress}{\scriptsize} +\pagestyle{headings} +%\CenterWallPaper{1}{MitgliederausweisSVBaLVorlage_leer_v2.pdf} +\CenterWallPaper{1}{../Aktuell/BriefpapierSVBaLVorlage.pdf} + diff --git a/latex/brief.tex b/latex/brief.tex new file mode 120000 index 0000000..6794f2f --- /dev/null +++ b/latex/brief.tex @@ -0,0 +1 @@ +brief_202507.tex \ No newline at end of file diff --git a/python/eh_app/.dummy b/python/eh_app/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_app/AWK/.dummy b/python/eh_app/AWK/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_app/AWK/config.py b/python/eh_app/AWK/config.py new file mode 100644 index 0000000..9ebd559 --- /dev/null +++ b/python/eh_app/AWK/config.py @@ -0,0 +1,54 @@ +''' +Created on 02.08.2024 + +@author: sparky2021 +''' +from eh_app.models import ConfigData + +the_instance = None + +class Config: + ''' + Singleton Klasse für Konfiguration + ''' + + def __init__(self, verein): + self.verein = verein + + def getConfig(self, key, default=None): + data = ConfigData.objects.filter(verein=self.verein, key=key) + if data: + return data[0].value + return default + + def requireConfig(self, key): + data = self.getConfig(key) + if not data: + raise RuntimeError(f"Kein Eintag für: {self.verein}/{key}") + return data + + def setConfig(self, key, value=None): + # No value supplied: delete entry + # check, if entry already exists + data = ConfigData.objects.filter(verein=self.verein, key=key) + if data: + # check data set + if len(data) != 1: + raise RuntimeError("config entry not unique") + if value: + if data[0].value != value: + data[0].value = value + data[0].save() + else: + data[0].delete() + elif value: + data = ConfigData(verein=self.verein, key=key, value=value) + data.save() + +def getInstance(verein=None): + global the_instance + if not the_instance: + if not verein: + raise RuntimeError("Bei der ersten Instanzierung muss der Verein mitgegeben werden.") + the_instance = Config(verein) + return the_instance diff --git a/python/eh_app/AWK/pdf_ersteller.py b/python/eh_app/AWK/pdf_ersteller.py new file mode 100644 index 0000000..9f673e9 --- /dev/null +++ b/python/eh_app/AWK/pdf_ersteller.py @@ -0,0 +1,68 @@ +import os + +class PdfErsteller(object): + ''' + Klasse zur Erstellung von PDFs + Diese Klasse verwaltet Resourcen. Mit "with" verwenden! + ''' + + def __init__(self, request, template, basisbtr: int, zusatzbtr: int, pfad, pdfersteller): + self.request = request + self.template = template + self.pfad = pfad + self.pdfersteller = pdfersteller + self.basisbtr = basisbtr + self.zusatzbtr = zusatzbtr + self.gesamtbetrag = 0 + self.anzahl = 0 + self.csvfile = open(os.path.join(pfad, "ergebnis", "rechnungen.csv"), "w") + print("Name", "Vorname", "Straße", "PLZ", "Ort", "Anz. Grundstück", "Betrag", sep=";", file=self.csvfile ) + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, traceback): + print ("Gesamt", self.gesamtbetrag, sep=";", file=self.csvfile ) + self.csvfile.close() + + def erstellepdf(self, m): + gg = m.VersicherteGrundstucke() + g1 = gg[0] + gn = gg[1:] + beitrag = self.basisbtr + self.zusatzbtr*len(gn) + konto = None + if len(m.iban) > 0: + konto = m.iban[0:4]+" xxxx xxxx xxxx "+m.iban[-2:] + + # Hinweise zur Templateerstellung: + # https://docs.djangoproject.com/en/2.2/ref/templates/builtins/#ref-templates-builtins-tags + + tex_bearbeitet = self.template.render( + {"vorname" : m.vorname, + "nachname" : m.nachname, + "strasse" : m.strasse, + "plz" : m.plz, + "ort" : m.ort, + "anrede" : m.anrede, + "grundstuck1" : g1, + "basisbeitrag" : self.basisbtr, + "g_weitere" : gn, + "zusatzbeitrag": self.zusatzbtr, + "beitrag" : beitrag, + "konto" : konto, + "m_nr" : m.mitgliedsnr + }, self.request) + + with open(f'{self.pfad}/{m.nachname}_{m.mitgliedsnr}_brief.tex', 'w') as f: + f.write(tex_bearbeitet) # schreibt in dieses neue Dokument + + # AUfruf PDF latex tex -> pdf + import subprocess + rc = subprocess.call(["bash", self.pdfersteller, self.pfad, f'{m.nachname}_{m.mitgliedsnr}_brief.tex']) + if rc != 0: + raise RuntimeError(f"pdfersteller.sh failed: {rc}") + + # schreibe Eintrag in Ergebnisdatei und erhöhe Gesamzbetrag + print(m.nachname, m.vorname, m.strasse, m.plz, m.ort, len(gg), beitrag, sep=";", file=self.csvfile ) + self.gesamtbetrag += beitrag + self.anzahl += 1 diff --git a/python/eh_app/AWK/routines.py b/python/eh_app/AWK/routines.py new file mode 100644 index 0000000..6f76b2b --- /dev/null +++ b/python/eh_app/AWK/routines.py @@ -0,0 +1,291 @@ +import os +import time + +from eh_app.AWK import config + +''' +def handle_uploaded_file(csv_file): + csv_file_name = str(csv_file) + data_path = config.getInstance().requireConfig("data_path") + path = os.path.join(data_path, csv_file_name) + with open(path, 'wb+') as destination: + for chunk in csv_file.chunks(): + destination.write(chunk) +''' +from django.utils.datetime_safe import strftime +def aktualisiere_config(config, vorlage, beitrag_data): + ''' + if file: + uploaded_file = file.name + data_path = config.requireConfig("data_path") + # copy briefpapier into data_path + with open(os.path.join(data_path, uploaded_file), 'wb+') as destination: + for chunk in file.chunks(): + destination.write(chunk) + config.setConfig("briefpapier", uploaded_file) + ''' + if vorlage: + config.setConfig("vorlage", vorlage) + if beitrag_data["basisbeitrag"]: + config.setConfig("beitrag_basis", beitrag_data["basisbeitrag"]) + + if beitrag_data["zusatzbeitrag"]: + config.setConfig("beitrag_zusatz", beitrag_data["zusatzbeitrag"]) + +def erstelle_ehmeldung(data): + from PyPDF2 import PdfFileWriter, PdfFileReader + from datetime import date + import io + from reportlab.pdfgen import canvas + from reportlab.lib.pagesizes import letter + + packet = io.BytesIO() + can = canvas.Canvas(packet, pagesize=letter) + + # Adressfeld + can.setFont("Helvetica", 14) + can.drawString(80, 770, "Siedlervereinigung Berg am Laim") + can.setFont("Helvetica", 12) + can.drawString(205, 618, data["VorZuname"]) + can.drawString(205, 600, data["VorZunamePartner"]) + can.drawString(205, 583, data["Wohnanschrift"]) + can.drawString(205, 566, data["Telefon"]) + can.drawString(365, 566, data["Email"]) + can.drawString(205, 549, data["Geburtsdatum"]) + versichertes_object = data["VersichertesObjekt"] + if versichertes_object == "": + versichertes_object = data["Wohnanschrift"] + can.drawString(205, 500, versichertes_object) + can.drawString(193, 466, str(data["AnzahlWohnungen"])) + if data["Selbstgenutzt"]: + can.drawString(360, 466, "X") + if data["Eigentumswohnung"]: + can.drawString(192, 449, "X") + if data["Gewerblich"]: + can.drawString(360, 449, "X") + can.drawString(140, 376, date.today().strftime('%d.%m.%Y')) + can.drawString(350, 376, "Maschinell erstellt.") + + can.save() + + #move to the beginning of the StringIO buffer + packet.seek(0) + + # create a new PDF with Reportlab + new_pdf = PdfFileReader(packet) + # read your existing PDF + existing_pdf = PdfFileReader(open("/home/sparky2021/SVBaL/Aktuell/BeitrittserklarungSVBaL.pdf", "rb")) + output = PdfFileWriter() + # add the "watermark" (which is the new pdf) on the existing page + page = existing_pdf.pages[0] + page.mergePage(new_pdf.pages[0]) + output.addPage(page) + # finally, write "output" to a real file + output_stream = open("meldung.pdf", "wb") + output.write(output_stream) + output_stream.close() + +def erstelle_ausweis(data): + from PyPDF2 import PdfFileWriter, PdfFileReader + from datetime import date + import io + from reportlab.pdfgen import canvas + from reportlab.lib.pagesizes import letter + + packet = io.BytesIO() + can = canvas.Canvas(packet, pagesize=letter) + + # Adressfeld + can.setFont("Helvetica", 12) + can.drawString(70, 640, "Herr/Frau") + can.drawString(70, 625, data["VorZuname"]) + can.drawString(70, 610, data["Wohnanschrift"]) + can.drawString(70, 590, "PLZ/Ort") + + # Datum + can.setFont("Helvetica", 9) + can.drawString(350, 520, "München, "+date.today().strftime('%d.%m.%Y')) + + # Anschreiben + can.drawString(70, 500, "Sehr geehrte/r Herr/Frau"+data["VorZuname"]+",") + text = can.beginText(70, 480) + text.textLines("Hier Ihr Ausweis") + can.drawText(text) + + + can.line(20, 180, 560, 180) + + # Ausweis + can.setFont("Helvetica", 12) + can.drawString(460, 100, "#0817654") + can.drawString(330, 80, data["VorZuname"]) + can.setFont("Helvetica", 9) + can.drawString(400, 56, data["Geburtsdatum"]) + can.drawString(400, 42, date.today().strftime('%d.%m.%Y')) + versichertes_object = data["VersichertesObjekt"] + if versichertes_object == "": + versichertes_object = data["Wohnanschrift"] + can.drawString(400, 20, versichertes_object) + + can.save() + + #move to the beginning of the StringIO buffer + packet.seek(0) + + # create a new PDF with Reportlab + new_pdf = PdfFileReader(packet) + # read your existing PDF + existing_pdf = PdfFileReader(open("MitgliederausweisSVBaLVorlage_leer.pdf", "rb")) + output = PdfFileWriter() + # add the "watermark" (which is the new pdf) on the existing page + page = existing_pdf.pages[0] + page.mergePage(new_pdf.pages[0]) + output.addPage(page) + # finally, write "output" to a real file + output_stream = open("ausweis.pdf", "wb") + output.write(output_stream) + output_stream.close() + +def pdfs_erstellen(request, verein, mitglieder, templ_name, data_path, build_name): + from django.template import loader + + # lese alle Miglieder + from eh_app.qmodels import Mitglied + if mitglieder == "alle": + mm = Mitglied.objects.all() + else: + mm = [] + m_ids = mitglieder.split(",") + for m_id in m_ids: + o = Mitglied.objects + m = o.get(mitgliedsnr=m_id) + if not m: + raise RuntimeError(f"Mitglied mit id {m_id} konnte nicht geladen werden.") + mm.append(m) + template = loader.get_template(f"{verein}/{templ_name}") + + cf = config.getInstance() + ehappdir = cf.getConfig("ehappdir", "/opt/eh_app") + pdfersteller = f"{ehappdir}/bin/pdfersteller.sh" + if not os.path.exists(pdfersteller): + raise RuntimeError("Install-Skript nicht gefunden: "+pdfersteller) + + from eh_app.AWK.pdf_ersteller import PdfErsteller + build_base = os.path.join(data_path, "build") + build_path = os.path.join(build_base, build_name) + result_path = os.path.join(build_path, "ergebnis") + tpl_name_wo_ext = os.path.splitext(templ_name)[0] + template_path = os.path.join(data_path, f"{tpl_name_wo_ext}") + + # alte builds löschen + rc = os.system(f'cd {build_base}; rm -r *') + if rc != 0: + from eh_app.AWK import util + util.schreibe_log(f"Löschen alter builds fehlgeschlagen: {rc}") + + # lege Verzeichnis + Links auf Templatedateien an + os.makedirs(result_path) + rc = os.system(f'cd {build_path}; ln -s {template_path}/* .') + if rc != 0: + raise RuntimeError(f"Linkerstellung fehlgeschlagen: {rc}") + + basisbtr = int(cf.requireConfig("beitrag_basis")) + zusatzbtr = int(cf.requireConfig("beitrag_zusatz")) + + with PdfErsteller(request, template, basisbtr, zusatzbtr, build_path, pdfersteller) as pe: + + # Erstelle PDFs + for m in mm: + pe.erstellepdf(m) + + # Ergebnis packen + rc = os.system(f'cd {build_path}; tar czf ergebnis.tgz ergebnis') + if rc != 0: + raise RuntimeError(f"Packen (tar) fehlgeschlagen: {rc}") + + return pe.anzahl, pe.gesamtbetrag + +def vertragsliste_erstellen(verein, data_path): + build_path = os.path.join(data_path, "build") + ergebnis_datei = "vertragsliste_"+time.strftime("%Y%m%d%H%M%S", time.localtime())+".csv" + + # lese alle Miglieder + from eh_app.qmodels import Mitglied + mm = Mitglied.objects.all() + + with open(os.path.join(build_path,ergebnis_datei), "w") as d: + print("Name", "Vorname", "Straße", "PLZ", "Ort", "Grundstück", "Eintrittsdatum", sep=";", file=d ) + for m in mm: + gg = m.VersicherteGrundstucke() + for g in gg: + print(m.nachname, + m.vorname, + m.strasse, + m.plz, + m.ort, + g, + m.eintrittsdatum.strftime("%d.%m.%Y"), + sep=";", file=d ) + return ergebnis_datei + +def vorlagen_verwalten(verein, zu_loschen, vorlage_neu): + import subprocess + cf = config.getInstance(verein) + ehappdir = cf.getConfig("ehappdir", "/opt/eh_app") + workdir = cf.getConfig("data_path", "/var/mysite/eh_app") + djangodir = cf.getConfig("djangodir", "/opt/mysite") + install_sh = f"{ehappdir}/bin/install_vorlage.sh" + delete_sh = f"{ehappdir}/bin/delete_vorlage.sh" + ehapp_check = f"{ehappdir}/bin/ehapp-verein-check.sh" + + # prüfe Existenz der Skripte + if not os.path.exists(install_sh): + raise RuntimeError("Install-Skript nicht gefunden: "+install_sh) + if not os.path.exists(delete_sh): + raise RuntimeError("Delete-Skript nicht gefunden: "+delete_sh) + if not os.path.exists(ehapp_check): + raise RuntimeError("Check-Skript nicht gefunden: "+ehapp_check) + + # prüfe Dateistruktur + rc = subprocess.call(["bash", ehapp_check, workdir, verein]) + if rc != 0: + raise RuntimeError(f"install_sh failed: {rc}") + + + for vorlage in zu_loschen: + rc = os.system(f'{delete_sh} {verein} {vorlage} {ehappdir}') + if rc != 0: + raise RuntimeError(f"delete_sh fehlgeschlagen: {rc}") + if vorlage_neu: + vorlage_name = str(vorlage_neu) + path = os.path.join(workdir, "upload", vorlage_name) + with open(path, 'wb+') as destination: + for chunk in vorlage_neu.chunks(): + destination.write(chunk) + #raise RuntimeError(f"xxxxxxxx: {install_sh} {verein} {vorlage_name} {workdir} {ehappdir}") + rc = subprocess.call(["bash", install_sh, verein, vorlage_name, workdir, ehappdir]) + if rc != 0: + raise RuntimeError(f"install_sh failed: {rc}") + +def datenquelle_andern(verein, pfad, zu_loschen, zu_verwenden, zu_install): + cf = config.getInstance(verein) + akt_quelle = cf.getConfig("datenquelle", "") + + # Dateien löschen + for file in zu_loschen: + dpath = os.path.join(pfad, file) + os.unlink(dpath) + if akt_quelle == file: + # Datenquelle zurücksetzen + cf.setConfig("datenquelle") + + # Neue Datei verwenden + if zu_install: + datei_name = str(zu_install) + dpath = os.path.join(pfad, datei_name) + with open(dpath, 'wb+') as destination: + for chunk in zu_install.chunks(): + destination.write(chunk) + cf.setConfig("datenquelle", datei_name) + elif zu_verwenden: + cf.setConfig("datenquelle", zu_verwenden) diff --git a/python/eh_app/AWK/util.py b/python/eh_app/AWK/util.py new file mode 100644 index 0000000..b0fc371 --- /dev/null +++ b/python/eh_app/AWK/util.py @@ -0,0 +1,10 @@ +''' +Utilities für EHUtil +''' +import syslog +from http import cookies + +def schreibe_log(eintrag): + syslog.openlog("eh_app") + syslog.syslog(eintrag) + \ No newline at end of file diff --git a/python/eh_app/__init__.py b/python/eh_app/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_app/admin.py b/python/eh_app/admin.py new file mode 100644 index 0000000..d654f08 --- /dev/null +++ b/python/eh_app/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +# Register your models here. + +from django.contrib import admin + +from .models import ConfigData + +admin.site.register(ConfigData) diff --git a/python/eh_app/apps.py b/python/eh_app/apps.py new file mode 100644 index 0000000..0d52b41 --- /dev/null +++ b/python/eh_app/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class EhAppConfig(AppConfig): + name = 'eh_app' diff --git a/python/eh_app/forms.py b/python/eh_app/forms.py new file mode 100644 index 0000000..671c9bb --- /dev/null +++ b/python/eh_app/forms.py @@ -0,0 +1,138 @@ +from django import forms +''' +class UploadFileForm(forms.Form): + file1 = forms.FileField(label='S-Verein-Export') + +class DocumentForm(forms.Form): + docfile = forms.FileField( + label='Select a file', + help_text='max. 42 megabytes' + ) + +class TextInputForm(forms.Form): + text = forms.CharField( + label = "Text für das Anschreiben", + empty_value = "Bitte Text eingeben ...", + widget=forms.Textarea, + ) +''' +from django.db.models.fields import BooleanField +''' +- Doku zu Forms: +https://docs.djangoproject.com/en/2.2/topics/forms/ +- Doku zu Fields: +https://docs.djangoproject.com/en/2.2/ref/forms/fields/ +''' +class VorlagenForm(forms.Form): + vorlagen = forms.ChoiceField(choices=()) + def __init__(self, *args, templates=[('AB', 'ab'),('BC','bc')], **kwargs): + super().__init__(*args, **kwargs) + self.fields['vorlagen'].choices = templates + +class VorlagenVerwaltungForm(forms.Form): + vorlagen_zu_loschen = forms.MultipleChoiceField( + widget=forms.CheckboxSelectMultiple, + choices=(), + label="Vorlagen zum Löschen vormerken:", + required=False + ) + vorlage_neu = forms.FileField(label='Neue Vorlage installieren:', required=False) + def __init__(self, *args, vorlagen=[('AB', 'ab'),('BC','bc')], **kwargs): + self.vorlagen = vorlagen + super().__init__(*args, **kwargs) + self.fields['vorlagen_zu_loschen'].choices = vorlagen + +class DatenfileForm(forms.Form): + dateien = forms.ChoiceField(choices=()) + def __init__(self, *args, dateien=[('should', 'not'),('be','here')], **kwargs): + super().__init__(*args, **kwargs) + self.fields['dateien'].choices = dateien + +class DatenfileVerwaltungForm(forms.Form): + dateien_zu_loschen = forms.MultipleChoiceField( + widget=forms.CheckboxSelectMultiple, + choices=(), + label="Dateien zum Löschen vormerken:", + required=False + ) + datei_verwendung = forms.ChoiceField( + choices=(), + label="Datei zur Verwendung:", + required=False + ) + datei_neu = forms.FileField(label='Neue Datei verwenden:', required=False) + def __init__(self, *args, datenquellen=[('should', 'not'),('be','here')], **kwargs): + self.dateien = datenquellen + super().__init__(*args, **kwargs) + self.fields['dateien_zu_loschen'].choices = datenquellen + self.fields['datei_verwendung'].choices = [(None, "Auswählen ...")]+datenquellen + +class BeitragForm(forms.Form): + basisbeitrag = forms.IntegerField() + zusatzbeitrag = forms.IntegerField(label="Beitrag für Zusatzgrundstück") + +class MitgliederForm(forms.Form): + mg_auswahl = forms.ChoiceField( + choices = (), + widget=forms.RadioSelect, + label = "" + ) + def __init__(self, *args, zul="", **kwargs): + #self.templates = templates + super().__init__(*args, **kwargs) + self.zulstr = zul + self.fields['mg_auswahl'].choices = [("alle","Alle Mitglieder"), ("zul",f"Zuletzt ausgewählte Mitglieder: {zul}")] + +class MitgliederAuswahlForm(forms.Form): + mg_auswahl = forms.MultipleChoiceField( + widget=forms.CheckboxSelectMultiple, + choices=(), + label="" + ) + def __init__(self, *args, mitglieder=(), **kwargs): + #self.templates = templates + super().__init__(*args, **kwargs) + self.fields['mg_auswahl'].choices = mitglieder + +class KassenbriefForm_alt(forms.Form): + #briefpapier = forms.FileField(label='Briefpapier ändern:', required=False) + template = forms.ChoiceField(choices=()) + basisbeitrag = forms.IntegerField() + zusatzbeitrag = forms.IntegerField(label="Beitrag für Zusatzgrundstück") + def __init__(self, *args, templates=[('AB', 'ab'),('BC','bc')], **kwargs): + #self.templates = templates + super().__init__(*args, **kwargs) + self.fields['template'].choices = templates + +class EhmeldungForm(forms.Form): + VorZuname = forms.CharField( + label = "Vor- und Zuname",) + VorZunamePartner = forms.CharField( + label = "Vor- und Zuname Partner", + required=False) + Wohnanschrift = forms.CharField(label = "Wohnanschrift (Str, PLZ, Ort)") + Telefon = forms.CharField( + label = "Telefon", + required=False) + Email = forms.CharField( + label = "E-Mail", + required=False) + Geburtsdatum = forms.CharField( + label = "Geburtsdatum", + required=False) + VersichertesObjekt = forms.CharField( + label = "Versichertes Objekt", + required=False) + AnzahlWohnungen = forms.IntegerField(initial=1, label="Anzahl Wonungen") + Selbstgenutzt = forms.BooleanField(label="Selbstgenutzt", required=False) + Eigentumswohnung = forms.BooleanField(label="Eigentumswohnung", required=False) + Gewerblich = forms.BooleanField(label="(teilw.) gewerblich genutzt", required=False) + Eintrittsdatum = forms.CharField( + label = "Eintrittsdatum", + required=False) + +class LoginForm(forms.Form): + verein = forms.CharField(label = "Vereinskürzel") + name = forms.CharField(label = "Login-Name") + password = forms.CharField(widget=forms.PasswordInput, label = "Passwort") + next = forms.CharField(widget = forms.HiddenInput(), required = False) diff --git a/python/eh_app/migrations/.dummy b/python/eh_app/migrations/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_app/migrations/0001_initial.py b/python/eh_app/migrations/0001_initial.py new file mode 100644 index 0000000..84ca6ad --- /dev/null +++ b/python/eh_app/migrations/0001_initial.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.28 on 2024-11-08 19:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='ConfigData', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('verein', models.CharField(max_length=100)), + ('key', models.CharField(max_length=50)), + ('value', models.CharField(max_length=200)), + ], + ), + ] diff --git a/python/eh_app/migrations/__init__.py b/python/eh_app/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_app/models.py b/python/eh_app/models.py new file mode 100644 index 0000000..ff9d39a --- /dev/null +++ b/python/eh_app/models.py @@ -0,0 +1,14 @@ +from django.db import models + +""" +Falls hier Änderungen gemacht werden: +- ./manage.py makemigrations eh_app +- ./manage.py migrate +""" + +class ConfigData(models.Model): + verein = models.CharField(max_length=100) + key = models.CharField(max_length=50) + value = models.CharField(max_length=200) + + \ No newline at end of file diff --git a/python/eh_app/qmodels.py b/python/eh_app/qmodels.py new file mode 100644 index 0000000..c58d933 --- /dev/null +++ b/python/eh_app/qmodels.py @@ -0,0 +1,169 @@ +''' +Created on 04.12.2024 + +@author: sparky2021 +''' +from q import QError +from qif import QIF +from .AWK import config +import datetime +from django.utils.decorators import classproperty +import os + +def create_mitglied_from_dataset(data): + eintrittsdatum = datetime.datetime.strptime(data[8], '%d.%m.%Y') + if eintrittsdatum > datetime.datetime.today(): + return None + if data[9] != "": + austrittsdatum = datetime.datetime.strptime(data[9], '%d.%m.%Y') + if austrittsdatum < datetime.datetime.today(): + return None + m = Mitglied() + m.eintrittsdatum = eintrittsdatum + m.mitgliedsnr = data[0] + m.anrede = data[1] + m.vorname = data[2] + m.nachname = data[3] + m.strasse = data[4] + # "Zusatzadresse" + m.plz = data[5] + m.ort = data[6] + + # "Land" + # "Titel" + # "Geschlecht" + # "Familienstand" + # m.geburtsdatum = data[7] + # "Austrittsgrund" + # m.zahlungsart = data[16] + m.iban = data[11] + # m.bic = data[18] + # "Kontonummer" + # "Bankleitzahl" + # "Kreditinstitut" + # m.kontoinhaber = data[22] + # m.mandatsreferenz = data[23] + # m.debitorenkontonr = data[24] + # m.status = data[25] + # "Branche" + # "Notfallnummer" + # "Notfallkontakt" + # m.email = data[29] # "KommE-Mail_P1" + # "KommFax_P1" + # "KommMobil_P1" + # "KommWeb_P1" + # "KommTelefon_P1" + # "KommSkype_P1" + # m.abteilung_1 = data[35] + # m.abteilungseintritt_1 = data[36] + # m.abteilungsaustritt_1 = data[37] + # if data[67] == '""': + # m.stammnummer = 0 + # else: + # m.stammnummer = int(m.rd_opt_qval(data[67])) + # "Freifeldname_2" + # m.ver_grund = m.rd_opt_qval(data[69]) + # if len(m.ver_grund) == 0: + # m.ver_grund = f"{m.strasse}, {m.plz} {m.ort}" + # "Freifeldname_3" + # m.zeitung = data[71] + return m + +class MitgliederQuery: + def __init__(self): + self.data_source = QIF({"d" : ";", "H" : 1}) + ''' + d (separator) : ; + H (header line): yes + ''' + #cf = config.getInstance() + #self.file_name = os.path.join(cf.requireConfig("data_path"), "quellen", cf.requireConfig("datenquelle")) + self.file_name = "undefined. Set by .init(verein)" + self.att_list = "MitgliedschaftsNr, Mitgliedschaftsanrede, Vorname1, Nachname1, Strasse1, Plz1, Ort1, Geburtsdatum1, EintrittVerein, AustrittVerein, Zahlungsart, IBAN, BIC, Kontoinhaber, MandatsReferenz, Email" + #self.att_list = "Mitgliedschaftsanrede, Vorname1, Nachname1, Strasse1, Plz1, Ort1, Geburtsdatum1, EintrittVerein, AustrittVerein, Zahlungsart, IBAN, BIC, Kontoinhaber, MandatsReferenz, Email" + # 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + cf = config.getInstance() + self.file_name = os.path.join(cf.requireConfig("data_path"), "quellen", cf.requireConfig("datenquelle")) + + def all(self): + data_set = self.data_source.request(f"SELECT {self.att_list} FROM {self.file_name} ORDER BY Nachname1, Vorname1") + if type(data_set) is QError: + raise RuntimeError(data_set) + mm = [] + for data in data_set: + m = create_mitglied_from_dataset(data) + if m: + mm.append(m) + return mm + + def get(self, mitgliedsnr): + data_set = self.data_source.request(f"SELECT {self.att_list} FROM {self.file_name} WHERE MitgliedschaftsNr = {mitgliedsnr}") + if type(data_set) is QError: + raise RuntimeError(data_set) + if data_set[0]: + return create_mitglied_from_dataset(data_set[0]) + return None + + def VersicherteGrundstucke(self, mitglied): + # Hole Basiseintrag + + data_set = self.data_source.request(f"SELECT Objekt FROM {self.file_name} WHERE MitgliedschaftsNr = {mitglied.mitgliedsnr}") + if type(data_set) is QError: + raise RuntimeError(data_set) + if data_set[0][0]: + gr = data_set[0][0] + else: + gr = f"{mitglied.strasse}, {mitglied.plz} {mitglied.ort}" + gst = [gr] + + # Suche nach weiteren Grunstücken + data_set = self.data_source.request(f"SELECT `Weiteres Grundstück 1` FROM {self.file_name} WHERE MitgliedschaftsNr = {mitglied.mitgliedsnr}") + if type(data_set) is QError: + raise RuntimeError(data_set) + if data_set[0][0] != "": + gst.append(data_set[0][0]) + + data_set = self.data_source.request(f"SELECT `Weiteres Grundstück 2` FROM {self.file_name} WHERE MitgliedschaftsNr = {mitglied.mitgliedsnr}") + if type(data_set) is QError: + raise RuntimeError(data_set) + if data_set[0][0] != "": + gst.append(data_set[0][0]) + + data_set = self.data_source.request(f"SELECT `Weiteres Grundstück 3` FROM {self.file_name} WHERE MitgliedschaftsNr = {mitglied.mitgliedsnr}") + if type(data_set) is QError: + raise RuntimeError(data_set) + if data_set[0][0] != "": + gst.append(data_set[0][0]) + + return gst +class classproperty(object): + def __init__(self, f): + self.f = f + def __get__(self, obj, owner): + return self.f(owner) + +class Mitglied: + #def objects = MitgliederQuery() + _objects = None + @classproperty + def objects(cls): + if not cls._objects: + cls._objects = MitgliederQuery() + ############################ Hack ######################### + #cls._objects.init("svbal") + return cls._objects + + + #@property : nette Idee, funktioniert aber nicht auf Klassenebene + ''' + def objects(): + if not _objects: + _objects = MitgliederQuery() + print ("xxxxxxxxxxxxxxxxxxxxxxtype _objects",type(_objects)) + return _objects + ''' + def __init__(self): + pass + + def VersicherteGrundstucke(self): + return self.objects.VersicherteGrundstucke(self) diff --git a/python/eh_app/templates/.dummy b/python/eh_app/templates/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_app/templates/__init__.py b/python/eh_app/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_app/templates/datenquelle_andern.html b/python/eh_app/templates/datenquelle_andern.html new file mode 100644 index 0000000..a022df5 --- /dev/null +++ b/python/eh_app/templates/datenquelle_andern.html @@ -0,0 +1,18 @@ +{% load static %} + + + + Siedlerverein-Verwaltung by WagnerTech UG + + +

Datenquelle ändern

+
+{% csrf_token %} + +{{datenquelle_form}} +
+ +

+

+ + diff --git a/python/eh_app/templates/eh_app.html b/python/eh_app/templates/eh_app.html new file mode 100644 index 0000000..c288eac --- /dev/null +++ b/python/eh_app/templates/eh_app.html @@ -0,0 +1,18 @@ +{% load static %} + + + + + +

Anwendung Eigenheimerverband

+

Funktionen für Verein {{verein_name}}

+

Kassenbrief erstellen

+

Meldung Eigenheimerverband

+

Liste der Mitgliedschaften

+

Verwendete Datenquelle:

+

{{fehler_quelle}}

+

Datenquelle: {{datenquelle}} +

Datenquelle ändern

+ + + diff --git a/python/eh_app/templates/ehmeldung.html b/python/eh_app/templates/ehmeldung.html new file mode 100644 index 0000000..9d22bfa --- /dev/null +++ b/python/eh_app/templates/ehmeldung.html @@ -0,0 +1,32 @@ + +{% load static %} + + + + + WagnerTech - Eigenheimer - Meldung + + + + + +
+ {% csrf_token %} +

{{ form.VorZuname.label_tag }} {{ form.VorZuname }}

+

{{ form.VorZunamePartner.label_tag }} {{ form.VorZunamePartner }}

+

{{ form.Wohnanschrift.label_tag }} {{ form.Wohnanschrift }}

+

{{ form.Telefon.label_tag }} {{ form.Telefon }}

+

{{ form.Email.label_tag }} {{ form.Email }}

+

{{ form.Geburtsdatum.label_tag }} {{ form.Geburtsdatum }}

+

{{ form.VersichertesObjekt.label_tag }} {{ form.VersichertesObjekt }}

+

{{ form.AnzahlWohnungen.label_tag }} {{ form.AnzahlWohnungen }}

+

{{ form.Selbstgenutzt.label_tag }} {{ form.Selbstgenutzt }}

+

{{ form.Eigentumswohnung.label_tag }} {{ form.Eigentumswohnung }}

+

{{ form.Gewerblich.label_tag }} {{ form.Gewerblich }}

+

{{ form.Eintrittsdatum.label_tag }} {{ form.Eintrittsdatum }}

+ +

+
+ + + diff --git a/python/eh_app/templates/kassenbrief.html b/python/eh_app/templates/kassenbrief.html new file mode 100644 index 0000000..2c64ca8 --- /dev/null +++ b/python/eh_app/templates/kassenbrief.html @@ -0,0 +1,45 @@ +{% load static %} + + + Siedlerverein-Verwaltung by WagnerTech UG + + + +

Erstellung Kassenbrief

+

Überprüfen Sie folgende Eingabedaten:

+
+ +
+{% csrf_token %} + +

Datenquelle

+

{{fehler_quelle}}

+

Datenquelle: {{datenquelle}} +

Datenquelle ändern

+
+ +

Mitgliederauswahl

+ +{{mausw_form}} +
+Mitglieder auswählen +
+ +

Kassenbriefvorlagen

+ +{{vorlagen_form}} +
+
+Kassenbriefvorlagen verwalten +
+ +

Beiträge

+ +{{beitrag_form}} +
+
+ +

+

+ + diff --git a/python/eh_app/templates/kassenbrief_erfolg.html b/python/eh_app/templates/kassenbrief_erfolg.html new file mode 100644 index 0000000..56fa0fb --- /dev/null +++ b/python/eh_app/templates/kassenbrief_erfolg.html @@ -0,0 +1,16 @@ +{% load static %} + + + + + +

Erstellung Kassenbrief

+

Die Kassenbriefe wurden erfolgreich erstellt und können jetzt heruntergeladen werden.

+

Anzahl: {{anzahl}}

+

Gesamtbetrag: {{gesamtbetrag}}.-

+
+{% csrf_token %} + +
+ + diff --git a/python/eh_app/templates/kassenbrief_zusammenfassung.html b/python/eh_app/templates/kassenbrief_zusammenfassung.html new file mode 100644 index 0000000..41de1ee --- /dev/null +++ b/python/eh_app/templates/kassenbrief_zusammenfassung.html @@ -0,0 +1,23 @@ +{% load static %} + + + Siedlerverein-Verwaltung by WagnerTech UG + + + +

Erstellung Kassenbrief

+

Überprüfen Sie folgende Eingabedaten:

+ + + + + + +
Datenquelle{{datenquelle}}
Vorlage{{vorlage}}
Basisbeitrag{{basisbeitrag}}
Zusatzbeitrag{{zusatzbeitrag}}
Mitglieder{{miglieder}}
+
+{% csrf_token %} +  + +
+ + diff --git a/python/eh_app/templates/login.html b/python/eh_app/templates/login.html new file mode 100644 index 0000000..02b2036 --- /dev/null +++ b/python/eh_app/templates/login.html @@ -0,0 +1,19 @@ +{% load static %} + + + Siedlerverein-Verwaltung by WagnerTech UG + + + +

Siedlerverein-Verwaltung by WagnerTech UG

+

Login

+
+{% csrf_token %} +{{fehlertext}} + +{{form}} +
+

+

+ + diff --git a/python/eh_app/templates/mitglieder_auswahlen.html b/python/eh_app/templates/mitglieder_auswahlen.html new file mode 100644 index 0000000..89825e3 --- /dev/null +++ b/python/eh_app/templates/mitglieder_auswahlen.html @@ -0,0 +1,17 @@ +{% load static %} + + + Siedlerverein-Verwaltung by WagnerTech UG + + + +

Mitglieder auswählen

+
+{% csrf_token %} + +{{mausw_form}} +
+

+

+ + diff --git a/python/eh_app/templates/vorlagen_verwalten.html b/python/eh_app/templates/vorlagen_verwalten.html new file mode 100644 index 0000000..a26164c --- /dev/null +++ b/python/eh_app/templates/vorlagen_verwalten.html @@ -0,0 +1,17 @@ +{% load static %} + + + + Siedlerverein-Verwaltung by WagnerTech UG + + +

Vorlagen verwalten

+
+{% csrf_token %} + +{{vorlagen_form}} +
+

+

+ + diff --git a/python/eh_app/tests.py b/python/eh_app/tests.py new file mode 100644 index 0000000..8fac548 --- /dev/null +++ b/python/eh_app/tests.py @@ -0,0 +1,36 @@ +from django.test import TestCase + +# Create your tests here. +from .AWK import config + +class TestConfig(TestCase): + def test_setter(self): + cf = config.getInstance("svbal") + cf.setConfig("bla", "blub") + + val = cf.getConfig("bla") + assert (val == "blub") + + # change setting + cf.setConfig("bla", "bar") + val = cf.getConfig("bla") + assert (val == "bar") + + +class TestQModel(TestCase): + def setUp(self): + cf = config.getInstance("svbal") + cf.setConfig("DataFile", "../../Test/etc/TestExport.csv") + + def test_allinstances(self): + config.getInstance("svbal") + from eh_app.qmodels import Mitglied + mm = Mitglied.objects.all() + assert (len(mm) == 4) + gst = mm[0].VersicherteGrundstucke() + assert (len(gst) == 1) + for m in mm: + if m.nachname == "Wagner": + gst = m.VersicherteGrundstucke() + assert (len(gst) == 3) + \ No newline at end of file diff --git a/python/eh_app/urls.py b/python/eh_app/urls.py new file mode 100644 index 0000000..ee38c82 --- /dev/null +++ b/python/eh_app/urls.py @@ -0,0 +1,20 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('/datenquelle-andern/', views.datenquelle_andern), + path('/kassenbrief/', views.kassenbrief), + path('/vorlagen_verwalten/', views.vorlagen_verwalten), + path('/mitglieder_auswahlen/', views.mitglieder_auswahlen), + path('/kassenbrief/zusammenfassung', views.kassenbrief_zusammenfassung), + path('/kassenbrief/erfolg', views.kassenbrief_erfolg), + path('/kassenbrief/download', views.kassenbrief_download), + path('/ehmeldung/', views.ehmeldung), + path('/vertragsliste/', views.vertragsliste), + path('login/', views.login), + path('login', views.login), + path('/', views.index), + path('', views.index), + path('', views.login), +] diff --git a/python/eh_app/views.py b/python/eh_app/views.py new file mode 100644 index 0000000..600c7db --- /dev/null +++ b/python/eh_app/views.py @@ -0,0 +1,407 @@ +import os,sys + +from django.contrib.auth.decorators import login_required +from django.http import HttpResponse, HttpResponseRedirect, FileResponse +from django.shortcuts import render +from django.template import loader + +from .AWK import config, routines, util +#import .forms +from .forms import ( + EhmeldungForm, + VorlagenForm, + BeitragForm, + VorlagenVerwaltungForm, + MitgliederForm, + MitgliederAuswahlForm, + DatenfileVerwaltungForm ) +from eh_app.AWK.pdf_ersteller import PdfErsteller + +from mysite import settings +import time + +# Create your views here. + +# change working dir due to relative paths in scripts +os.chdir(settings.BASE_DIR) + +try: + home = os.environ["HOME"] + if os.path.exists(f"{home}/.eclipse/org.eclipse.platform_4.18.0_1473617060_linux_gtk_x86_64/plugins/org.python.pydev.core_8.3.0.202104101217/pysrc/"): + # its a eclipse development environment + sys.path.append(f"{home}/.eclipse/org.eclipse.platform_4.18.0_1473617060_linux_gtk_x86_64/plugins/org.python.pydev.core_8.3.0.202104101217/pysrc/") + import pydevd + pydevd.settrace() + +except: + pass + +LOGIN_URL = "/eh-app/login" + +def erstelle_datenquelle_liste(data_path): + dirlist = None + vorlagen_liste = [] + if os.path.isdir(data_path): + dirlist = os.listdir(data_path) # returns list + if dirlist: + for t in dirlist: + vorlagen_liste.append((t,t)) + return vorlagen_liste + +def erstelle_vorlagen_liste(verein): + # Erstelle Vorlagenliste + os.chdir(settings.BASE_DIR) + dirlist = os.listdir() + util.schreibe_log(str(dirlist)) + dirlist = None + vorlagen_liste = [] + if os.path.isdir("eh_app/templates/"+verein): + dirlist = os.listdir("eh_app/templates/"+verein) # returns list + if dirlist: + for t in dirlist: + tpl_name_wo_ext = os.path.splitext(t)[0] + vorlagen_liste.append((t,tpl_name_wo_ext)) + return vorlagen_liste + +@login_required(login_url=LOGIN_URL) +def vorlagen_verwalten(request, verein): + vorlagen_liste = erstelle_vorlagen_liste(verein) + + if request.method == 'POST': + # Form auswerten + form = VorlagenVerwaltungForm(request.POST, request.FILES, vorlagen=vorlagen_liste) + if form.is_valid(): + zu_loschen = form.cleaned_data['vorlagen_zu_loschen'] + zu_install = request.FILES.get('vorlage_neu', None) + routines.vorlagen_verwalten(verein, zu_loschen, zu_install) + else: + raise RuntimeError(f"Form not valid: {form.errors}") + # Verarbeitung in routines.py aufrufen + + # Wenn alles gut gegangen, zurück zum Kassenbrief + return HttpResponseRedirect(f'/eh-app/{verein}/kassenbrief') + else: + vorlagen_form = VorlagenVerwaltungForm(vorlagen=vorlagen_liste) + + template = loader.get_template("vorlagen_verwalten.html") + return HttpResponse(template.render( + { + "verein" : verein, + "vorlagen_form" : vorlagen_form }, + request)) + +@login_required(login_url=LOGIN_URL) +def datenquelle_andern(request, verein): + cf = config.getInstance(verein) + data_path = cf.requireConfig("data_path") + ehappdir = cf.getConfig("ehappdir", "/opt/eh_app") + vereinchecker = f"{ehappdir}/bin/ehapp-verein-check.sh" + if not os.path.exists(vereinchecker): + raise RuntimeError("ehapp-verein-check.sh nicht gefunden: "+vereinchecker) + + rc = os.system(f'{vereinchecker} {data_path}') + if rc != 0: + raise RuntimeError(f"ehapp-verein-check.sh fehlgeschlagen: {rc}") + quellen_path = os.path.join(data_path, "quellen") + datenquellen_liste = erstelle_datenquelle_liste(quellen_path) + + if request.method == 'POST': + # Form auswerten + form = DatenfileVerwaltungForm( + request.POST, + request.FILES, + datenquellen=datenquellen_liste) + if form.is_valid(): + zu_loschen = form.cleaned_data['dateien_zu_loschen'] + zu_verwenden = form.cleaned_data['datei_verwendung'] + zu_install = request.FILES.get('datei_neu', None) + routines.datenquelle_andern(verein, quellen_path, zu_loschen, zu_verwenden, zu_install) + else: + raise RuntimeError(f"Form not valid: {form.errors}") + # Verarbeitung in routines.py aufrufen + + # Wenn alles gut gegangen, zurück zum Kassenbrief + letzte_seite = request.POST.get("letzte_seite") + return HttpResponseRedirect(f'/eh-app/{verein}/{letzte_seite}') + else: + form = DatenfileVerwaltungForm(datenquellen=datenquellen_liste) + + letzte_seite = request.GET.get("letzte_seite") or "" + template = loader.get_template("datenquelle_andern.html") + return HttpResponse(template.render( + { + "verein" : verein, + "datenquelle_form" : form, + "letzte_seite" : letzte_seite }, + request)) + +@login_required(login_url=LOGIN_URL) +def mitglieder_auswahlen(request, verein): + mitglieder = (request.COOKIES.get('mausw2') or "").split(",") + config.getInstance(verein) # wird in qmodel gebraucht + from eh_app.qmodels import Mitglied + mm = Mitglied.objects.all() + mitglieder_liste = [] + for m in mm: + mitglieder_liste.append((m.mitgliedsnr, m.nachname+" "+m.vorname)) + + if request.method == 'POST': + # Form auswerten + form = MitgliederAuswahlForm( + request.POST, + request.FILES, + mitglieder=mitglieder_liste) + if form.is_valid(): + mg_ausw = form.cleaned_data['mg_auswahl'] + else: + raise RuntimeError(f"Form not valid: {form.errors}") + + # Wenn alles gut gegangen, zurück zum Kassenbrief + response = HttpResponseRedirect(f'/eh-app/{verein}/kassenbrief') + mg_ausw_str = ",".join(mg_ausw) + response.set_cookie("mausw2", mg_ausw_str) + return response + else: + mausw_form = MitgliederAuswahlForm(mitglieder=mitglieder_liste, initial={"mg_auswahl":mitglieder}) + + template = loader.get_template("mitglieder_auswahlen.html") + return HttpResponse(template.render( + { + "verein" : verein, + "mausw_form" : mausw_form }, + request)) + +@login_required(login_url=LOGIN_URL) +def kassenbrief(request, verein): + ''' + GET: + Diese Methode liest aus der Config die Einstellungen und zeigt sie an. + + POST: + Diese Methode lädt die Zusammenfassung + + --------------------------------------------------------------- + Templateauswahl + + Ausgewähltes Template: + Link -> Neue Templates installieren + --------------------------------------------------------------- + ''' + + mg_ausw = request.COOKIES.get('mausw1') or "leer" + mg_zul = request.COOKIES.get('mausw2') or "leer" + alles_ok = True + fehlertext_quelle = "" + fehlertext_vorlage = "" + fehlertext_beitrag = "" + vconf = config.getInstance(verein) + datenquelle = vconf.getConfig("datenquelle") + beitrag_basis = vconf.getConfig("beitrag_basis", 0) + beitrag_zusatz = vconf.getConfig("beitrag_zusatz", 0) + vorlagen_liste = erstelle_vorlagen_liste(verein) + vorlage = vconf.getConfig("vorlage", "") + mausw_form = MitgliederForm( + zul=mg_zul, + initial={"mg_auswahl" : mg_ausw,} + ) + if request.method == 'POST': + # Prüfe Datenquelle + if not datenquelle: + fehlertext_quelle = "Datenquelle auswählen" + alles_ok = False + + # Prüfe Vorlage + vorlagen_form = VorlagenForm(request.POST, request.FILES, templates=vorlagen_liste) + if not vorlagen_form.is_valid(): + raise RuntimeError("VorlagenForm invalid: "+str(vorlagen_form.errors)) + vorlage = vorlagen_form.cleaned_data["vorlagen"] + if not vorlage: + fehlertext_vorlage = "Bitte Kassenbriefvorlage auswählen" + alles_ok = False + + # Prüfe Beiträge + beitrag_form = BeitragForm(request.POST, request.FILES) + if not beitrag_form.is_valid(): + raise RuntimeError("BeitragForm invalid: Formular nochmal schicken") + beitrag_basis = beitrag_form.cleaned_data["basisbeitrag"] + beitrag_zusatz = beitrag_form.cleaned_data["zusatzbeitrag"] + if beitrag_basis == 0 or beitrag_zusatz == 0: + fehlertext_beitrag = "Beiträge ausfüllen" + alles_ok = False + + routines.aktualisiere_config(vconf, vorlage, beitrag_form.cleaned_data) + + # Speichere Migliederauswahl in Cookie + mausw_form = MitgliederForm(request.POST, request.FILES) + if not mausw_form.is_valid(): + raise RuntimeError("MitgliederForm invalid: "+str(mausw_form.errors)) + mausw = mausw_form.cleaned_data["mg_auswahl"] + + + if alles_ok: + response = HttpResponseRedirect('zusammenfassung') + response.set_cookie("mausw1", mausw) + return response + + # GET request / POST mit Fehlern + vorlagen_form = VorlagenForm( + templates=vorlagen_liste, + initial={'vorlagen' : vorlage } ) + beitrag_form = BeitragForm( + initial={ + "basisbeitrag" : beitrag_basis, + "zusatzbeitrag" : beitrag_zusatz, + }) + return render(request, 'kassenbrief.html', { + "datenquelle" : datenquelle or "Keine Datenquelle ausgewählt", + 'vorlagen_form': vorlagen_form, + 'beitrag_form' : beitrag_form, + 'mausw_form' : mausw_form, + 'verein' : verein, + 'fehler_quelle': fehlertext_quelle + }) + +@login_required(login_url=LOGIN_URL) +def kassenbrief_zusammenfassung(request, verein): + vconf = config.getInstance(verein) + datenquelle = vconf.getConfig("datenquelle") + beitrag_basis = vconf.getConfig("beitrag_basis", 0) + beitrag_zusatz = vconf.getConfig("beitrag_zusatz", 0) + vorlage = vconf.getConfig("vorlage") + mitglieder = request.COOKIES.get('mausw1') + if mitglieder == "zul": + mitglieder = request.COOKIES.get('mausw2') + if request.method == 'POST': + post = request.POST + if "erstellen" in post: + data_path = vconf.getConfig("data_path", "/var/ehapp") + build_name = time.strftime("%Y%m%d%H%M%S", time.localtime()) + + anzahl, gesamtbetrag = routines.pdfs_erstellen(request, verein, mitglieder, vorlage, data_path, build_name) + response = HttpResponseRedirect(f'/eh-app/{verein}/kassenbrief/erfolg') + response.set_cookie("build_name", build_name) + response.set_cookie("anzahl", anzahl) + response.set_cookie("gesamtbetrag", gesamtbetrag) + return response + else: + return HttpResponseRedirect(f'/eh-app/{verein}/kassenbrief') + + else: + tpl_name_wo_ext = os.path.splitext(vorlage)[0] + return render(request, 'kassenbrief_zusammenfassung.html', { + 'verein' : verein, + 'datenquelle' : datenquelle, + 'vorlage' : tpl_name_wo_ext, + 'basisbeitrag' : beitrag_basis, + 'zusatzbeitrag' : beitrag_zusatz, + 'miglieder' : mitglieder }) + +@login_required(login_url=LOGIN_URL) +def kassenbrief_erfolg(request, verein): + return render(request, 'kassenbrief_erfolg.html', { + 'verein' : verein, + 'anzahl' : request.COOKIES.get('anzahl'), + 'gesamtbetrag' : request.COOKIES.get('gesamtbetrag'), + }) + +@login_required(login_url=LOGIN_URL) +def kassenbrief_download(request, verein): + vconf = config.getInstance(verein) + data_path = vconf.getConfig("data_path", "/var/ehapp") + build_name = request.COOKIES.get('build_name') + + ergebnis_datei = os.path.join(data_path, "build", build_name, "ergebnis.tgz") + + response = FileResponse(open(ergebnis_datei, 'rb'), as_attachment=True, filename=f"{build_name}.tgz") + return response + +@login_required(login_url=LOGIN_URL) +def ehmeldung(request, verein): + # if this is a POST request we need to process the form data + if request.method == 'POST': + # create a form instance and populate it with data from the request: + form = EhmeldungForm(request.POST, request.FILES) + # check whether it's valid: + if form.is_valid(): + # bei Änderungen Konfiguration aktualisieren + routines.erstelle_ehmeldung(form.cleaned_data) + routines.erstelle_ausweis(form.cleaned_data) + # redirect to a new URL: + return HttpResponseRedirect('/thanks/') + + # if a GET (or any other method) we'll create a blank form + else: + form = EhmeldungForm() + + return render(request, 'ehmeldung.html', {'form': form, 'verein': verein}) + +@login_required(login_url=LOGIN_URL) +def vertragsliste(request, verein): + vconf = config.getInstance(verein) + data_path = vconf.getConfig("data_path", "/var/ehapp") + ergebnis_datei = routines.vertragsliste_erstellen(verein, data_path) + response = FileResponse(open(os.path.join(data_path, "build",ergebnis_datei), 'rb'), as_attachment=True, filename=ergebnis_datei) + return response + +def index(request, verein): + if request.user.is_authenticated: + print("index login ok") + else: + print("index not logged in") + + datenquelle = config.getInstance(verein).getConfig("datenquelle") + + template = loader.get_template('eh_app.html') + response = HttpResponse(template.render( + {"verein" : verein, + "verein_name" : verein, + "datenquelle" : datenquelle, + }, request)) + return response + +def login(request): + from django.contrib import auth + from .forms import LoginForm + + fehlertext = "" + # if this is a POST request we need to process the form data + if request.method == 'POST': + # create a form instance and populate it with data from the request: + form = LoginForm(request.POST, request.FILES) + # check whether it's valid: + if form.is_valid(): + user = auth.authenticate( + username = request.POST["name"], + password = request.POST["password"], + ) + verein = request.POST["verein"] + if user is not None: + + # check group + for group in user.groups.all(): + if group.name == verein: + auth.login(request, user) + if request.user.is_authenticated: + print("auth login ok") + else: + print("auth not logged in") + next = request.POST.get("next", None) + if next: + return HttpResponseRedirect(next) + else: + return HttpResponseRedirect(f'/eh-app/{verein}') + # login fehlgeschlagen + fehlertext = "Login fehlgeschlagen" + + # if a GET (or any other method) we'll create a blank form + else: + next = None + verein = "" + next = request.GET.get("next", None) + if next: + path_elems = next.split("/") + verein = path_elems[2] + form = LoginForm(initial={'next': next, 'verein': verein}) + + return render(request, 'login.html', {'form': form, 'fehlertext': fehlertext}) + \ No newline at end of file diff --git a/python/eh_util/ausweis/.dummy b/python/eh_util/ausweis/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/ausweis/AWK/.dummy b/python/eh_util/ausweis/AWK/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/ausweis/AWK/config.py b/python/eh_util/ausweis/AWK/config.py new file mode 100644 index 0000000..9011222 --- /dev/null +++ b/python/eh_util/ausweis/AWK/config.py @@ -0,0 +1,37 @@ +''' +Created on 02.08.2024 + +@author: sparky2021 +''' +from ausweis.models import ConfigData + +the_instance = None + +class Config: + ''' + Singleton Klasse für Konfiguration + ''' + + def __init__(self, verein): + self.verein = verein + + def getConfig(self, key): + data = ConfigData.objects.filter(verein=self.verein, key=key) + if data: + return data[0].value + return None + + def requireConfig(self, key): + data = self.getConfig(key) + if not data: + raise RuntimeError(f"Kein Eintag für: {self.verein}/{key}") + return data + + +def getInstance(verein=None): + global the_instance + if not the_instance: + if not verein: + raise RuntimeError("Bei der ersten Instanzierung muss der Verein mitgegeben werden.") + the_instance = Config(verein) + return the_instance diff --git a/python/eh_util/ausweis/AWK/routines.py b/python/eh_util/ausweis/AWK/routines.py new file mode 100644 index 0000000..619048f --- /dev/null +++ b/python/eh_util/ausweis/AWK/routines.py @@ -0,0 +1,12 @@ +from ausweis.AWK import config + +import os + +def handle_uploaded_file(csv_file): + csv_file_name = str(csv_file) + data_path = config.getInstance().requireConfig("data_path") + path = os.path.join(data_path, csv_file_name) + with open(path, 'wb+') as destination: + for chunk in csv_file.chunks(): + destination.write(chunk) + \ No newline at end of file diff --git a/python/eh_util/ausweis/__init__.py b/python/eh_util/ausweis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/ausweis/admin.py b/python/eh_util/ausweis/admin.py new file mode 100644 index 0000000..6c6f054 --- /dev/null +++ b/python/eh_util/ausweis/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +# Register your models here. + +#from django.contrib import admin + +#from .models import ConfigData + +#admin.site.register(ConfigData) diff --git a/python/eh_util/ausweis/apps.py b/python/eh_util/ausweis/apps.py new file mode 100644 index 0000000..071f4d2 --- /dev/null +++ b/python/eh_util/ausweis/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AusweisConfig(AppConfig): + name = 'ausweis' diff --git a/python/eh_util/ausweis/forms.py b/python/eh_util/ausweis/forms.py new file mode 100644 index 0000000..a16ef24 --- /dev/null +++ b/python/eh_util/ausweis/forms.py @@ -0,0 +1,17 @@ +from django import forms + +class UploadFileForm(forms.Form): + file1 = forms.FileField(label='S-Verein-Export') + +class DocumentForm(forms.Form): + docfile = forms.FileField( + label='Select a file', + help_text='max. 42 megabytes' + ) + +class TextInputForm(forms.Form): + text = forms.CharField( + label = "Text für das Anschreiben", + empty_value = "Bitte Text eingeben ...", + widget=forms.Textarea, + ) \ No newline at end of file diff --git a/python/eh_util/ausweis/migrations/.dummy b/python/eh_util/ausweis/migrations/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/ausweis/migrations/__init__.py b/python/eh_util/ausweis/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/ausweis/models.py b/python/eh_util/ausweis/models.py new file mode 100644 index 0000000..2c9d9a8 --- /dev/null +++ b/python/eh_util/ausweis/models.py @@ -0,0 +1,8 @@ +from django.db import models + +""" +Falls hier Änderungen gemacht werden: +- ./manage.py makemigrations ausweis +- ./manage.py migrate +""" + diff --git a/python/eh_util/ausweis/templates/.dummy b/python/eh_util/ausweis/templates/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/ausweis/templates/__init__.py b/python/eh_util/ausweis/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/ausweis/templates/anschreiben.html b/python/eh_util/ausweis/templates/anschreiben.html new file mode 100644 index 0000000..42947b0 --- /dev/null +++ b/python/eh_util/ausweis/templates/anschreiben.html @@ -0,0 +1,17 @@ + + + + + WagnerTech - Siedlerverein + + + +

Anschreiben für den Ausweis

+
+{% csrf_token %} +

{{ form.text.label_tag }} {{ form.text }}

+ +

+
+ + diff --git a/python/eh_util/ausweis/templates/aus_index.html b/python/eh_util/ausweis/templates/aus_index.html new file mode 100644 index 0000000..93369a6 --- /dev/null +++ b/python/eh_util/ausweis/templates/aus_index.html @@ -0,0 +1,17 @@ + + + Siedlerverein-Verwaltung by WagnerTech UG + + +

Erstellung Mitgliederausweise

+{% if csv_datei_name %} +

S-Verein-Export: {{csv_datei_name}}

+

Datei ändern

+{% else %} +

Datei hochladen

+{% endif %} +

Anschreiben erstellen

+

Alle Ausweise erstellen

+

Einzelnen Ausweis erstellen

+ + diff --git a/python/eh_util/ausweis/templates/upload.html b/python/eh_util/ausweis/templates/upload.html new file mode 100644 index 0000000..24ff0a2 --- /dev/null +++ b/python/eh_util/ausweis/templates/upload.html @@ -0,0 +1,19 @@ + + + + + WagnerTech - Siedlerverein + + + +

Upload der S-Verein-Exportdatei

+ +
+ {% csrf_token %} +

{{ form.file1.label_tag }} {{ form.file1 }}

+ +

+
+ + + diff --git a/python/eh_util/ausweis/tests.py b/python/eh_util/ausweis/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/python/eh_util/ausweis/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/python/eh_util/ausweis/urls.py b/python/eh_util/ausweis/urls.py new file mode 100644 index 0000000..6567bea --- /dev/null +++ b/python/eh_util/ausweis/urls.py @@ -0,0 +1,11 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('', views.index, name='index'), + path('upload', views.upload), + path('anschreiben', views.anschreiben), + path('alle_ausweise', views.alle_ausweise), + path('einzelausweis', views.einzelausweis), +] \ No newline at end of file diff --git a/python/eh_util/ausweis/views.py b/python/eh_util/ausweis/views.py new file mode 100644 index 0000000..c796800 --- /dev/null +++ b/python/eh_util/ausweis/views.py @@ -0,0 +1,49 @@ +from .AWK import routines, config +from .forms import UploadFileForm, DocumentForm, TextInputForm + +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import render +from django.template import loader + + +def index(request, verein): + + # check existence in DB + vconf = config.getInstance(verein) + csv_datei_name = vconf.getConfig("csv_datei_name") + if not csv_datei_name: + template = loader.get_template('aus_index.html') + context = { + 'csv_datei_name': csv_datei_name, + } + return HttpResponse(template.render(context, request)) + return render(request, 'index.html', {'csv_datei_name': csv_datei_name}) + +def upload(request, verein): + if request.method == 'POST': + # initialize configuration + config.getInstance(verein) + form = UploadFileForm(request.POST, request.FILES) + if form.is_valid(): + routines.handle_uploaded_file(request.FILES['file1']) + return HttpResponseRedirect(f'/{verein}/ausweis') + return HttpResponse("Dateiverarbeitung fehlerhaft") + else: + form = UploadFileForm() + return render(request, 'upload.html', {'form': form}) + +def anschreiben(request, verein): + if request.method == 'POST': + form = TextInputForm(request.POST) + if form.is_valid(): + return HttpResponse("Dateiverarbeitung ok") + return HttpResponse("Dateiverarbeitung fehlerhaft") + else: + form = TextInputForm() + return render(request, 'anschreiben.html', {'form': form}) + +def alle_ausweise(request, verein): + return HttpResponse("NIY") + +def einzelausweis(request, verein): + return HttpResponse("NIY") diff --git a/python/eh_util/eh_abgleich/AWK/.dummy b/python/eh_util/eh_abgleich/AWK/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_abgleich/AWK/SVereinControl.py b/python/eh_util/eh_abgleich/AWK/SVereinControl.py new file mode 100644 index 0000000..606325d --- /dev/null +++ b/python/eh_util/eh_abgleich/AWK/SVereinControl.py @@ -0,0 +1,61 @@ +from AWK.mitglied import Mitglied +class SVereinDatei(object): + ''' + classdocs + ''' + + def __init__(self, datei_name): + ''' + Constructor + ''' + self.datei_name = datei_name + self.mitglieder = {} + self.get_merker = 0 + + dh = open(datei_name) + line = dh.readline().rstrip() + export_header = line.split(";") + + sv_pn_spalte = -1 + + i = 0 + for kopf in export_header: + if kopf[0] == '"': + kopf = kopf[1:-1] + if kopf == 'Mitglieds-Nr': + sv_pn_spalte = i + i += 1 + + if sv_pn_spalte == -1: + raise RuntimeError("S-Verein-Export enthält keine Partnernummer") + + for line in dh: + data = line.rstrip().split(";") + pn = data[sv_pn_spalte] + if len(pn) == 0: + print("Diese Zeile (export) hat keine Migliedsnummer: ", line) + else: + if pn[0] == '"': + pn = pn[1:-1] + pn = int(pn) + self.mitglieder[pn] = Mitglied(data) + # setze zusätzliche Mitgliedschaften im Stamm + for mnr in self.mitglieder: + if self.mitglieder[mnr].stammnummer > 0: + self.mitglieder[self.mitglieder[mnr].stammnummer].add_weitere_mitgliedschaft(mnr) + + def get(self, mnr = None): + if mnr: + return self.mitglieder[mnr] + if self.get_merker == 0: + for m in self.mitglieder: + self.get_merker = m + return self.mitglieder[m] + return_next = False + for m in self.mitglieder: + if return_next: + self.get_merker = m + return self.mitglieder[m] + if m == self.get_merker: + return_next = True + return None diff --git a/python/eh_util/eh_abgleich/AWK/__init__.py b/python/eh_util/eh_abgleich/AWK/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_abgleich/AWK/ausweis.py b/python/eh_util/eh_abgleich/AWK/ausweis.py new file mode 100755 index 0000000..79d33a1 --- /dev/null +++ b/python/eh_util/eh_abgleich/AWK/ausweis.py @@ -0,0 +1,91 @@ +#!/usr/bin/python3 + +import sys +from mitglied import Mitglied + +""" +Testaufruf: +./ausweis.py Michael Wagner "25.5.1965" MitgliederausweisSVBaLVorlage_leer.pdf ./ +""" + +def erzeuge_ausweis(mitglied: Mitglied, ausgabetext, hintergrund_pdf, ausgabe_pfad): + from PyPDF2 import PdfFileWriter, PdfFileReader + from datetime import date + import io + from reportlab.pdfgen import canvas + from reportlab.lib.pagesizes import letter + + packet = io.BytesIO() + can = canvas.Canvas(packet, pagesize=letter) + + # Adressfeld + can.setFont("Helvetica", 12) + can.drawString(70, 640, mitglied.anrede) + can.drawString(70, 625, mitglied.vorname+" "+mitglied.nachname) + can.drawString(70, 610, mitglied.strasse) + can.drawString(70, 590, mitglied.plz+" "+mitglied.ort) + + # Datum + can.setFont("Helvetica", 9) + can.drawString(350, 520, "München, "+date.today().strftime('%d.%m.%Y')) + + # Anschreiben + can.drawString(70, 500, "Sehr geehrte/r "+mitglied.anrede+" "+mitglied.nachname+",") + text = can.beginText(70, 480) + text.textLines(ausgabetext) + can.drawText(text) + + + can.line(20, 180, 560, 180) + + # Ausweis + can.setFont("Helvetica", 12) + can.drawString(460, 100, mitglied.mitgliedsnr) + can.drawString(330, 80, mitglied.vorname+" "+mitglied.nachname) + can.setFont("Helvetica", 9) + can.drawString(400, 56, mitglied.geburtsdatum) + can.drawString(400, 42, mitglied.eintrittsdatum) + can.drawString(400, 20, mitglied.ver_grund) + + + can.save() + + #move to the beginning of the StringIO buffer + packet.seek(0) + + # create a new PDF with Reportlab + new_pdf = PdfFileReader(packet) + # read your existing PDF + existing_pdf = PdfFileReader(open(hintergrund_pdf, "rb")) + output = PdfFileWriter() + # add the "watermark" (which is the new pdf) on the existing page + page = existing_pdf.pages[0] + page.mergePage(new_pdf.pages[0]) + output.addPage(page) + # finally, write "output" to a real file + output_stream = open(ausgabe_pfad, "wb") + output.write(output_stream) + output_stream.close() + +if __name__ == "__main__": + if len(sys.argv) == 6: + nachname = sys.argv[1] + vorname = sys.argv[2] + geburtsdatum = sys.argv[3] + infile = sys.argv[4] + outpath = sys.argv[5] + else: + print ("Nachname: ") + nachname = input() + print ("Vorname:") + vorname = input() + print ("Geburtsdatum:") + geburtsdatum = input() + print ("Input-Datei: ") + infile = input() + print ("Ausgabepfad:") + outpath = input() + + mitglied = Mitglied(nachname, vorname, geburtsdatum) + erzeuge_ausweis(mitglied,infile, outpath) + diff --git a/python/eh_util/eh_abgleich/AWK/mitglied.py b/python/eh_util/eh_abgleich/AWK/mitglied.py new file mode 100644 index 0000000..a66b47a --- /dev/null +++ b/python/eh_util/eh_abgleich/AWK/mitglied.py @@ -0,0 +1,102 @@ +''' +Created on 29.02.2024 + +@author: sparky2021 +''' +class Mitglied(object): + ''' + classdocs + ''' + + def rd_opt_qval(self, s): + if s[0] == '"': + s = s[1:-1] + return s + + def __init__(self, data): + ''' + Constructor + ''' + self.weitere_mitgliedschaften = [] + self.anrede = data[0] + self.vorname = data[1] + self.nachname = data[2] + self.strasse = self.rd_opt_qval(data[3]) + # "Zusatzadresse" + self.plz = data[5] + self.ort = data[6] + # "Land" + # "Titel" + # "Geschlecht" + # "Familienstand" + self.mitgliedsnr = self.rd_opt_qval(data[11]) + self.geburtsdatum = data[12] + self.eintrittsdatum = data[13] + self.austrittsdatum = data[14] + # "Austrittsgrund" + self.zahlungsart = data[16] + self.iban = data[17] + self.bic = data[18] + # "Kontonummer" + # "Bankleitzahl" + # "Kreditinstitut" + self.kontoinhaber = data[22] + self.mandatsreferenz = data[23] + self.debitorenkontonr = data[24] + self.status = data[25] + # "Branche" + # "Notfallnummer" + # "Notfallkontakt" + self.email = data[29] # "KommE-Mail_P1" + # "KommFax_P1" + # "KommMobil_P1" + # "KommWeb_P1" + # "KommTelefon_P1" + # "KommSkype_P1" + self.abteilung_1 = data[35] + self.abteilungseintritt_1 = data[36] + self.abteilungsaustritt_1 = data[37] + # "Abteilungsstatus_1" + # "Abteilungsstatus DFB_1" + # "Abteilungsaustrittsgrund_1" + # "Abteilung_2" + # "Abteilungseintritt_2" + # "Abteilungsaustritt_2" + # "Abteilungsstatus_2" + # "Abteilungsstatus DFB_2" + # "Abteilungsaustrittsgrund_2" + # "Beitragsbezeichnung_1_1" + # "Beitragsstart_1_1" + # "Beitragsende_1_1" + # "BeitragBerechnetBis_1_1" + # "BeitragZahlweise_1_1" + # "BeitragFälligkeitsdatum_1_1" + # "BeitragVariabel_1_1" + # "BeitragFormel_1_1" + # "BeitragGrundbetrag1_1_1" + # "BeitragGB1Gesperrt_1_1" + # "BeitragGrundbetrag2_1_1" + # "BeitragGB2Gesperrt_1_1" + # "BeitragGrundbetrag3_1_1" + # "BeitragGB3Gesperrt_1_1" + # "BeitragGrundbetrag4_1_1" + # "BeitragGB4Gesperrt_1_1" + # "BeitragGrundbetrag5_1_1" + # "BeitragGB5Gesperrt_1_1" + # "BeitragZahlweiseGesperrt_1_1" + # "Freifeldname_1" + if data[67] == '""': + self.stammnummer = 0 + else: + self.stammnummer = int(self.rd_opt_qval(data[67])) + # "Freifeldname_2" + self.ver_grund = self.rd_opt_qval(data[69]) + if len(self.ver_grund) == 0: + self.ver_grund = f"{self.strasse}, {self.plz} {self.ort}" + # "Freifeldname_3" + self.zeitung = data[71] + + def add_weitere_mitgliedschaft(self, pn): + self.weitere_mitgliedschaften.append(pn) + + \ No newline at end of file diff --git a/python/eh_util/eh_abgleich/MyTest.py b/python/eh_util/eh_abgleich/MyTest.py new file mode 100755 index 0000000..a2ffb6b --- /dev/null +++ b/python/eh_util/eh_abgleich/MyTest.py @@ -0,0 +1,52 @@ +#!/usr/bin/python3 +from AWK.SVereinControl import SVereinDatei +from AWK import ausweis +import sys + +def mytest(datei_name): + daten = SVereinDatei(datei_name) + print ("Daten geladen") + m = daten.get() + while m: + if m.abteilung_1 == "Standard" and len(m.austrittsdatum) == 0: + austxt = "als Mitglied unser Siedlervereinigung Berg am Laim wollen wir Sie\nüber Ihren Mitgliedsbeitrag für 2024 informieren.\n\n" + austxt += "Ihr Beitrag für 2024 setzt sich wie folgt zusammen:\n" + austxt += f"Standardmitgliedschaft ({m.ver_grund}): 39.-" + betrag = 39 + weitere = m.weitere_mitgliedschaften + for weitere_mnr in weitere: + wm = daten.get(weitere_mnr) + austxt += f"\nZusatzgrundstück ({wm.ver_grund}): 32.-" + betrag += 32 + if len(m.iban) > 0: + iban = m.iban[0:3]+"XXXXXXXXXXXXX"+m.iban[-4:] + austxt += f"\n\nDer Betrag von {betrag}.- wird von Ihrem Konto {iban} mit Gläubiger ID DE08 ZZZ0 0000 9547 90\nund Mandatsreferenz {m.mandatsreferenz} eingezogen." + else: + austxt += f"\n\nBitte überweisen Sie den offnen Betrag von {betrag}.- auf unser Konto DE69 7019 0000 0003 2143 62\nbei der Münchner Bank." + austxt += "\n\nUnd nun endlich ist es so weit, dass wir Ihnen hiermit auch Ihren Mitgliedsausweis\n" + austxt += "zukommen lassen können. Bitte schneiden Sie den unteren Teil ab und unterschreiben Sie diesen.\n" + austxt += "Weitere Infos zum Einkaufen und die Einladung für die Hauptversammlung finden Sie in der Anlage.\n" + austxt += "Wir freuen uns auf Ihr Kommen. " + austxt += "\n\nMit freundlichen Grüßen,\nMichael Wagner\n(Vorstand)" + ausweis.erzeuge_ausweis(m, austxt, "MitgliederausweisSVBaLVorlage_leer.pdf", f"Ausweis_{m.nachname}_{m.mitgliedsnr}.pdf") + #return + m = daten.get() + print ("Ausweise erstellt.") + +def test_q(): + import qif + qif = qif.QIF({"d" : ";", "H" : 1}) + data = qif.request("select Vorname,Nachname from /home/sparky2021/SVBaL/tmp/export.csv") + + print(data) + + +if __name__ == '__main__': + numArgs = len(sys.argv) + if (numArgs != 2): + raise RuntimeError("usage: MyTest DATEI") + + #mytest(sys.argv[1]) + + test_q() + diff --git a/python/eh_util/eh_abgleich/__init__.py b/python/eh_util/eh_abgleich/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_abgleich/admin.py b/python/eh_util/eh_abgleich/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/python/eh_util/eh_abgleich/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/python/eh_util/eh_abgleich/apps.py b/python/eh_util/eh_abgleich/apps.py new file mode 100644 index 0000000..4e44d82 --- /dev/null +++ b/python/eh_util/eh_abgleich/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class EhAbgleichConfig(AppConfig): + name = 'eh_abgleich' diff --git a/python/eh_util/eh_abgleich/awk.py b/python/eh_util/eh_abgleich/awk.py new file mode 100644 index 0000000..c91675a --- /dev/null +++ b/python/eh_util/eh_abgleich/awk.py @@ -0,0 +1,2 @@ +def vergleiche_sverein_eheimer(f_sverein, f_eheimer): + return f"Es werden die Dateien {f_sverein} und {f_eheimer} vergleichen." \ No newline at end of file diff --git a/python/eh_util/eh_abgleich/forms.py b/python/eh_util/eh_abgleich/forms.py new file mode 100644 index 0000000..d6fb153 --- /dev/null +++ b/python/eh_util/eh_abgleich/forms.py @@ -0,0 +1,11 @@ +from django import forms + +class UploadFileForm(forms.Form): + file1 = forms.FileField(label='S-Verein-Export') + file2 = forms.FileField(label='Eigemheimer-Datei') + +class DocumentForm(forms.Form): + docfile = forms.FileField( + label='Select a file', + help_text='max. 42 megabytes' + ) \ No newline at end of file diff --git a/python/eh_util/eh_abgleich/migrations/.dummy b/python/eh_util/eh_abgleich/migrations/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_abgleich/migrations/__init__.py b/python/eh_util/eh_abgleich/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_abgleich/models.py b/python/eh_util/eh_abgleich/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/python/eh_util/eh_abgleich/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/python/eh_util/eh_abgleich/templates/.dummy b/python/eh_util/eh_abgleich/templates/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_abgleich/templates/__init__.py b/python/eh_util/eh_abgleich/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_abgleich/templates/index.html b/python/eh_util/eh_abgleich/templates/index.html new file mode 100644 index 0000000..4db5a23 --- /dev/null +++ b/python/eh_util/eh_abgleich/templates/index.html @@ -0,0 +1,14 @@ + + +

Eigenheimer - Werkzeuge

+

Hier befinden sich Werkzeuge, um den Datenbestand eines + Mitgliedsvereins des Eigenheimerverbands Bayern zu bearbeiten

+ +

Datenabgleich S-Verein - Verband

+

Dieses Werkzeug vergleicht die Export-Datei von S-Verein mit + der Mitgliedermeldung des Verbands. Als primäres Vergleichsmerkmal + wird dabei die Partnernummer verwendet.

+

Zum Abgleich

+ + + diff --git a/python/eh_util/eh_abgleich/templates/list.html b/python/eh_util/eh_abgleich/templates/list.html new file mode 100644 index 0000000..00aadd2 --- /dev/null +++ b/python/eh_util/eh_abgleich/templates/list.html @@ -0,0 +1,38 @@ + + + + + Minimal Django File Upload Example + + + + + {% if documents %} + + {% else %} +

No documents.

+ {% endif %} + + +
+ {% csrf_token %} +

{{ form.non_field_errors }}

+ +

{{ form.docfile.label_tag }} {{ form.docfile.help_text }}

+ +

+ {{ form.docfile.errors }} + {{ form.docfile }} +

+ + + +

+
+ + + diff --git a/python/eh_util/eh_abgleich/templates/upload.html b/python/eh_util/eh_abgleich/templates/upload.html new file mode 100644 index 0000000..29d1439 --- /dev/null +++ b/python/eh_util/eh_abgleich/templates/upload.html @@ -0,0 +1,20 @@ + + + + + WagnerTech - Eigenheimer - Abgleich + + + + + +
+ {% csrf_token %} +

{{ form.file1.label_tag }} {{ form.file1 }}

+

{{ form.file2.label_tag }} {{ form.file2 }}

+ +

+
+ + + diff --git a/python/eh_util/eh_abgleich/tests.py b/python/eh_util/eh_abgleich/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/python/eh_util/eh_abgleich/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/python/eh_util/eh_abgleich/urls.py b/python/eh_util/eh_abgleich/urls.py new file mode 100644 index 0000000..85458c9 --- /dev/null +++ b/python/eh_util/eh_abgleich/urls.py @@ -0,0 +1,9 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('', views.index, name='index'), + path('ss', views.upload_file, name='upload_file1'), + path('aa', views.list, name='list') +] diff --git a/python/eh_util/eh_abgleich/views.py b/python/eh_util/eh_abgleich/views.py new file mode 100644 index 0000000..73bca4b --- /dev/null +++ b/python/eh_util/eh_abgleich/views.py @@ -0,0 +1,44 @@ +from django.shortcuts import render +from django.http import HttpResponse, HttpResponseRedirect +from eh_abgleich.forms import UploadFileForm, DocumentForm + +# Create your views here. + + +def index(request): + return HttpResponse("Hello, world. You're at the polls index.") + + +# Imaginary function to handle an uploaded file. +#from somewhere import handle_uploaded_file + +def upload_file(request): + if request.method == 'POST': + form = UploadFileForm(request.POST, request.FILES) + if form.is_valid(): + #handle_uploaded_file(request.FILES['file']) + return HttpResponseRedirect('/success/url/') + else: + form = UploadFileForm() + return render(request, 'upload.html', {'form': form}) + +def list(request): + # Handle file upload + if request.method == 'POST': + form = DocumentForm(request.POST, request.FILES) + if form.is_valid(): + #newdoc = Document(docfile = request.FILES['docfile']) + #newdoc.save() + + # Redirect to the document list after POST + return HttpResponseRedirect(reverse('list')) + else: + form = DocumentForm() # A empty, unbound form + + # Load documents for the list page + #documents = Document.objects.all() + documents = {} + + + # Render list page with the documents and the form + return render(request, 'list.html', {'documents': documents, 'form': form}) diff --git a/python/eh_util/eh_util/__init__.py b/python/eh_util/eh_util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_util/forms.py b/python/eh_util/eh_util/forms.py new file mode 100644 index 0000000..f404bde --- /dev/null +++ b/python/eh_util/eh_util/forms.py @@ -0,0 +1,7 @@ +from django import forms + +class LoginForm(forms.Form): + verein = forms.CharField(label = "Vereinskürzel") + name = forms.CharField(label = "Login-Name") + password = forms.CharField(widget=forms.PasswordInput, label = "Passwort") + next = forms.CharField(widget = forms.HiddenInput(), required = False) diff --git a/python/eh_util/eh_util/settings.py b/python/eh_util/eh_util/settings.py new file mode 100644 index 0000000..d90a3e5 --- /dev/null +++ b/python/eh_util/eh_util/settings.py @@ -0,0 +1,124 @@ +""" +Django settings for eh_util project. + +Generated by 'django-admin startproject' using Django 2.2.28. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/2.2/ref/settings/ +""" + +import os + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +MEDIA_ROOT = BASE_DIR + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'x8xmlmq_%a3^rp7@1x0n%ugebrp!4@hc*5@=g%&6fo@5m3g!kw' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +LOGIN_URL = "/eh-util/login" + +# Application definition + +INSTALLED_APPS = [ + 'eh_app.apps.EhAppConfig', +# 'ausweis.apps.AusweisConfig', +# 'kassenbrief.apps.KassenbriefConfig', + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'eh_util.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, "eh_util/templates"), os.path.join(BASE_DIR, "eh_utils/eh_app/templates/svbal")], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'eh_util.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/2.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + #{ + # 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + #}, + #{ + # 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + #}, + #{ + # 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + #}, + #{ + # 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + #}, +] + + +# Internationalization +# https://docs.djangoproject.com/en/2.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/2.2/howto/static-files/ + +STATIC_URL = '/static/' diff --git a/python/eh_util/eh_util/templates/.dummy b/python/eh_util/eh_util/templates/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_util/templates/__init__.py b/python/eh_util/eh_util/templates/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/eh_util/templates/index.html b/python/eh_util/eh_util/templates/index.html new file mode 100644 index 0000000..450d7eb --- /dev/null +++ b/python/eh_util/eh_util/templates/index.html @@ -0,0 +1,10 @@ + + + Siedlerverein-Verwaltung by WagnerTech UG + + +

Siedlerverein-Verwaltung by WagnerTech UG

+

Wählen Sie Ihren Verein:

+

Siedlervereinigung Berg am Laim

+ + diff --git a/python/eh_util/eh_util/templates/vbasis.html b/python/eh_util/eh_util/templates/vbasis.html new file mode 100644 index 0000000..fee8c1f --- /dev/null +++ b/python/eh_util/eh_util/templates/vbasis.html @@ -0,0 +1,11 @@ + + + Siedlerverein-Verwaltung by WagnerTech UG + + +

Siedlerverein-Verwaltung by WagnerTech UG

+

Verfügbare Funktionen für {{verein}}:

+

Ausweiserstellung

+

Erstellung der Kassenbriefe

+ + diff --git a/python/eh_util/eh_util/urls.py b/python/eh_util/eh_util/urls.py new file mode 100644 index 0000000..344817a --- /dev/null +++ b/python/eh_util/eh_util/urls.py @@ -0,0 +1,30 @@ +"""eh_util URL Configuration + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/2.2/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import include, path +import eh_util +from . import views + +urlpatterns = [ + path('', views.index), + path('login/', views.login), + path('admin/', admin.site.urls), + path('eh-abgleich/', include('eh_abgleich.urls')), +# path('/', views.vbasis), +# path('/ausweis/', include('ausweis.urls')), +# path('/kassenbrief/', include('kassenbrief.urls')), + path('/', include('eh_app.urls')), +] diff --git a/python/eh_util/eh_util/views.py b/python/eh_util/eh_util/views.py new file mode 100644 index 0000000..0262e40 --- /dev/null +++ b/python/eh_util/eh_util/views.py @@ -0,0 +1,53 @@ +from django.http import HttpResponse, HttpResponseRedirect +from django.shortcuts import render +from django.template import loader + + +def index(request): + template = loader.get_template('index.html') + return HttpResponse(template.render({}, request)) + +def login(request): + from django.contrib import auth + from .forms import LoginForm + + fehlertext = "" + # if this is a POST request we need to process the form data + if request.method == 'POST': + # create a form instance and populate it with data from the request: + form = LoginForm(request.POST, request.FILES) + # check whether it's valid: + if form.is_valid(): + user = auth.authenticate( + username = request.POST["name"], + password = request.POST["password"], + ) + if user is None: + # login fehlgeschlagen + fehlertext = "Login fehlgeschlagen" + + else: + auth.login(request, user) + next = request.POST.get("next", None) + if next: + return HttpResponseRedirect(next) + else: + return HttpResponseRedirect('/') + + # if a GET (or any other method) we'll create a blank form + else: + next = None + verein = "" + next = request.GET.get("next", None) + if next: + path_elems = next.split("/") + verein = path_elems[1] + form = LoginForm(initial={'next': next, 'verein': verein}) + + return render(request, 'login.html', {'form': form, 'fehlertext': fehlertext}) + +def vbasis(request, verein): + template = loader.get_template("vbasis.html") + raise RuntimeError("blub") + return HttpResponse(template.render({"verein" : verein}, request)) + diff --git a/python/eh_util/eh_util/wsgi.py b/python/eh_util/eh_util/wsgi.py new file mode 100644 index 0000000..f5cccf3 --- /dev/null +++ b/python/eh_util/eh_util/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for eh_util project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eh_util.settings') + +application = get_wsgi_application() diff --git a/python/eh_util/kassenbrief/.dummy b/python/eh_util/kassenbrief/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/kassenbrief/__init__.py b/python/eh_util/kassenbrief/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/kassenbrief/admin.py b/python/eh_util/kassenbrief/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/python/eh_util/kassenbrief/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/python/eh_util/kassenbrief/apps.py b/python/eh_util/kassenbrief/apps.py new file mode 100644 index 0000000..32be9fe --- /dev/null +++ b/python/eh_util/kassenbrief/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class KassenbriefConfig(AppConfig): + name = 'kassenbrief' diff --git a/python/eh_util/kassenbrief/migrations/.dummy b/python/eh_util/kassenbrief/migrations/.dummy new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/kassenbrief/migrations/__init__.py b/python/eh_util/kassenbrief/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/python/eh_util/kassenbrief/models.py b/python/eh_util/kassenbrief/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/python/eh_util/kassenbrief/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/python/eh_util/kassenbrief/tests.py b/python/eh_util/kassenbrief/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/python/eh_util/kassenbrief/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/python/eh_util/kassenbrief/urls.py b/python/eh_util/kassenbrief/urls.py new file mode 100644 index 0000000..cad389e --- /dev/null +++ b/python/eh_util/kassenbrief/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('', views.index), +] \ No newline at end of file diff --git a/python/eh_util/kassenbrief/views.py b/python/eh_util/kassenbrief/views.py new file mode 100644 index 0000000..fab82f9 --- /dev/null +++ b/python/eh_util/kassenbrief/views.py @@ -0,0 +1,8 @@ +from django.http import HttpResponse +from django.shortcuts import render + +# Create your views here. + +def index(request): + + return HttpResponse("NYI") diff --git a/python/sv-merger/sv-merger.py b/python/sv-merger/sv-merger.py deleted file mode 100755 index 5e8fce5..0000000 --- a/python/sv-merger/sv-merger.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/python3 -''' -Created on 10.01.2024 - -@author: sparky2021 -''' - -import sys - -if __name__ == '__main__': - pass - -numArgs = len(sys.argv) -if (numArgs != 4): - raise RuntimeError("usage: sv-merger EXPORT DELTA FIELD") - - -dh_export = open(sys.argv[1]) -dh_delta = open(sys.argv[2]) -dh_import = open("import.csv", "w") - -# Lese die Kopfzeile aus EXPORT -line = dh_export.readline().rstrip() -print(line, file=dh_import) -export_header = line.split(";") - -sv_pn_spalte = -1 -sv_such_spalte = -1 - -i = 0 -for kopf in export_header: - kopf = kopf[1:-1] - if kopf == 'Freifeldwert_1': - sv_pn_spalte = i - if kopf == sys.argv[3]: - sv_such_spalte = i - i += 1 - -if sv_pn_spalte == -1: - raise RuntimeError("S-Verein-Export enthält keine Partnernummer") -if sv_such_spalte == -1: - raise RuntimeError("S-Verein-Export enthält nicht das gewünschte Datenfeld "+sys.argv[3]) - -line = dh_delta.readline().rstrip() -delta_header = line.split(";") - -delta_pn_sp = -1 -delta_such_sp = -1 - -i = 0 -for kopf in delta_header: - if kopf == "Partnernummer": - delta_pn_sp = i - if kopf == sys.argv[3]: - delta_such_sp = i - i += 1 - -if delta_pn_sp == -1: - raise RuntimeError("Delta-Datei enthält keine Partnernummer") -if delta_such_sp == -1: - raise RuntimeError("Delta-Datei enthält nicht das gewünschte Datenfeld") - -delta = {} -for line in dh_delta: - if len(line.rstrip()): - # not a empty line - data = line.split(";") - delta[int(data[delta_pn_sp])] = data[delta_such_sp] - -for line in dh_export: - data = line.rstrip().split(";") - pn = data[sv_pn_spalte][1:-1] - if len(pn): - pn = int(pn) - if pn in delta: - if len(delta[pn]): - if data[sv_such_spalte] != delta[pn]: - data[sv_such_spalte] = delta[pn] - print(";".join(data), file=dh_import) - -dh_export.close() -dh_delta.close() -dh_import.close() - -''' -Argumente: - -EXPORT: Export-Datei aus S-Verein - -DELTA: csv-Daten, die eingearbeitet werden sollen - -FIELD: Feld, das eingearbeitet werden soll, wenn - - Das Quellfeld nicht leer ist - - Sich der Wert von der EXPORT-Datei unterscheidet - -Die Synchronisation der Daten erfolgt anhand der Partnernummer. - -Das Ergebnis wird stets in die Datei import.csv geschrieben. -'''