From 93dd3f42f3c6899c25c8f6dd08401d9b15a76183 Mon Sep 17 00:00:00 2001
From: Lorenz Stechauner <lorenz.stechauner@necronda.net>
Date: Thu, 14 Sep 2023 22:31:27 +0200
Subject: [PATCH] Database: Add member_email_address table

---
 sql/v01/10.create.sql | 18 +++++++++++++++---
 wgmaster/import.py    |  4 ++--
 wgmaster/migrate.py   | 33 ++++++++++++++++++++-------------
 3 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/sql/v01/10.create.sql b/sql/v01/10.create.sql
index 1cf1636..111ec25 100644
--- a/sql/v01/10.create.sql
+++ b/sql/v01/10.create.sql
@@ -336,8 +336,6 @@ CREATE TABLE member (
     postal_dest      TEXT    NOT NULL,
     address          TEXT    NOT NULL,
 
-    email            TEXT CHECK (email REGEXP '^[^@ ]+@([a-z0-9_\x2Däöüß]+\.)+[a-z]{2,}$') DEFAULT NULL,
-
     default_kgnr     INTEGER,
     contact_postal   INTEGER NOT NULL CHECK (contact_postal IN (TRUE, FALSE)) DEFAULT TRUE,
     contact_email    INTEGER NOT NULL CHECK (contact_email  IN (TRUE, FALSE)) DEFAULT FALSE,
@@ -381,6 +379,7 @@ CREATE TABLE member_billing_address (
 CREATE TABLE member_telephone_number (
     mgnr    INTEGER NOT NULL,
     nr      INTEGER NOT NULL,
+
     type    TEXT    NOT NULL CHECK (type REGEXP '^[a-z_]+$'),
     number  TEXT    NOT NULL CHECK (number REGEXP '^\+[0-9]{1,3}( [0-9]+)+(-[0-9]+)?$' AND LENGTH(REPLACE(REPLACE(REPLACE(number, '+', ''), ' ', ''), '-', '')) <= 15),
     comment TEXT DEFAULT NULL,
@@ -391,6 +390,19 @@ CREATE TABLE member_telephone_number (
         ON DELETE CASCADE
 ) STRICT;
 
+CREATE TABLE member_email_address (
+    mgnr    INTEGER NOT NULL,
+    nr      INTEGER NOT NULL,
+
+    address TEXT    NOT NULL CHECK (address REGEXP '^[^@ ]+@([a-z0-9_\x2Däöüß]+\.)+[a-z]{2,}$'),
+    comment TEXT DEFAULT NULL,
+
+    CONSTRAINT pk_member_email_address PRIMARY KEY (mgnr, nr),
+    CONSTRAINT fk_member_email_address_member FOREIGN KEY (mgnr) REFERENCES member (mgnr)
+        ON UPDATE CASCADE
+        ON DELETE CASCADE
+) STRICT;
+
 CREATE TABLE area_commitment (
     fbnr      INTEGER NOT NULL,
     mgnr      INTEGER NOT NULL,
@@ -756,4 +768,4 @@ CREATE TABLE credit (
     CONSTRAINT fk_credit_payment_member FOREIGN KEY (year, avnr, mgnr) REFERENCES payment_member (year, avnr, mgnr)
         ON UPDATE CASCADE
         ON DELETE RESTRICT
-);
+) STRICT;
diff --git a/wgmaster/import.py b/wgmaster/import.py
index 72ae7f4..2a4dbc3 100755
--- a/wgmaster/import.py
+++ b/wgmaster/import.py
@@ -12,8 +12,8 @@ import utils
 
 DIR: str
 
-TABLES = ['client_parameter', 'branch', 'wb_gl', 'wb_kg', 'wb_rd', 'wine_attribute', 'wine_cultivation',
-          'area_commitment_type', 'member', 'member_billing_address', 'member_telephone_number', 'area_commitment',
+TABLES = ['client_parameter', 'branch', 'wb_gl', 'wb_kg', 'wb_rd', 'wine_attribute', 'wine_cultivation', 'area_commitment_type',
+          'member', 'member_billing_address', 'member_telephone_number', 'member_email_address', 'area_commitment',
           'season', 'modifier', 'delivery', 'delivery_part', 'delivery_part_attribute', 'delivery_part_modifier',
           'payment_variant', 'payment_delivery_part']
 
diff --git a/wgmaster/migrate.py b/wgmaster/migrate.py
index 4d8db07..a9709c1 100755
--- a/wgmaster/migrate.py
+++ b/wgmaster/migrate.py
@@ -240,6 +240,8 @@ def normalize_phone_nr(nr: Optional[str], ort: str = None) -> Optional[str]:
         nr = nr.replace('-', '')
     if nr[0] == '0':
         nr = '+43 ' + nr[1:]
+    elif nr.startswith('43'):
+        nr = '+' + nr
     elif CLIENT == WG.WINZERKELLER and ort:
         ort = ort.upper().strip()
         if ort in ('PILLICHSDORF', 'OBERSDORF', 'WOLKERSDORF', 'WOLFPASSING', 'PUTZING', 'GROSSENGERSDORF',
@@ -750,18 +752,19 @@ def migrate_members(in_dir: str, out_dir: str) -> None:
     mgnrs = [m['MGNR'] for m in members]
     fbs = parse_flaechenbindungen(in_dir)
 
-    with utils.csv_open(f'{out_dir}/member.csv') as f_m,\
+    with utils.csv_open(f'{out_dir}/member.csv') as f_m, \
             utils.csv_open(f'{out_dir}/member_billing_address.csv') as f_mba, \
-            utils.csv_open(f'{out_dir}/member_telephone_number.csv') as f_tel,\
+            utils.csv_open(f'{out_dir}/member_telephone_number.csv') as f_tel, \
+            utils.csv_open(f'{out_dir}/member_email_address.csv') as f_email, \
             utils.csv_open(f'{out_dir}/wb_kg.csv', 'a') as f_kg:
         f_m.header(
             'mgnr', 'predecessor_mgnr', 'prefix', 'given_name', 'middle_names', 'family_name', 'suffix',
             'birthday', 'entry_date', 'exit_date', 'business_shares', 'accounting_nr', 'zwstid',
             'lfbis_nr', 'ustid_nr', 'volllieferant', 'buchführend', 'funktionär', 'active', 'deceased',
-            'iban', 'bic', 'country', 'postal_dest', 'address',
-            'email', 'default_kgnr', 'comment')
+            'iban', 'bic', 'country', 'postal_dest', 'address', 'default_kgnr', 'comment')
         f_mba.header('mgnr', 'name', 'country', 'postal_dest', 'address')
         f_tel.header('mgnr', 'nr', 'type', 'number', 'comment')
+        f_email.header('mgnr', 'nr', 'address', 'comment')
 
         for m in members:
             mgnr: int = m['MGNR']
@@ -896,15 +899,16 @@ def migrate_members(in_dir: str, out_dir: str) -> None:
                     convert(mgnr, 'Adresse', address_old, address)
 
             email: Optional[str] = m['EMail']
+            emails = []
             if email is not None:
-                if email.isupper():
-                    email = email.lower()
-                if not EMAIL_RE.fullmatch(email):
-                    invalid(mgnr, 'E-Mail', m['EMail'], active)
-                    email = None
-                else:
-                    parts = email.split('@')
-                    email = f'{parts[0]}@{parts[1].lower()}'
+                for email in email.split(' + '):
+                    if email.isupper():
+                        email = email.lower()
+                    if not EMAIL_RE.fullmatch(email):
+                        invalid(mgnr, 'E-Mail', m['EMail'], active)
+                    else:
+                        parts = email.split('@')
+                        emails.append(f'{parts[0]}@{parts[1].lower()}')
 
             zwstid = m['ZNR'] and BRANCH_MAP[m['ZNR']] or len(BRANCH_MAP) == 1 and list(BRANCH_MAP.values())[0]
             if CLIENT == WG.WINZERKELLER and plz == 1228:
@@ -942,7 +946,7 @@ def migrate_members(in_dir: str, out_dir: str) -> None:
                 m['Geburtsjahr'], m['Eintrittsdatum'], m['Austrittsdatum'], m['Geschäftsanteile1'] or 0,
                 m['BHKontonummer'], zwstid, bnr, ustid_nr,
                 m['Volllieferant'] or False, m['Buchführend'] or False, funktionaer, active, deceased,
-                iban, bic, AUSTRIA, postal_dest, address or '-', email, kgnr, m['Anmerkung']
+                iban, bic, AUSTRIA, postal_dest, address or '-', kgnr, m['Anmerkung']
             )
 
             phone_1: Optional[str] = m['Telefon']
@@ -1074,6 +1078,9 @@ def migrate_members(in_dir: str, out_dir: str) -> None:
                         else:
                             f_tel.row(mgnr, len(numbers), 'landline', phone_3, None)
 
+            for i, email in enumerate(emails):
+                f_email.row(mgnr, i + 1, email, None)
+
             MEMBER_MAP[mgnr] = {
                 'default_kgnr': kgnr
             }