o
    
j`F                    @   s>  d Z ddlZddlZddlZddlZddlZddlmZ ddlmZm	Z	m
Z
 ddlmZmZ ddlZddlmZmZ ddlmZmZmZ zddlZddlmZ W n ey]   dZdZY nw dd	lmZmZmZmZm Z m!Z!m"Z"m#Z# dd
l$m%Z%m&Z&m'Z' ej(ej)dd e*e+Z,de-de.fddZ/dd Z0G dd dZ1dS )u   Orchestrator für das EEG-Abrechnungssystem.

Enthält die Klasse Abrechnungssystem sowie globale Hilfsfunktionen
(validate_params, validate_member_row, build_ust_hints).
Die eigentliche Fachlogik liegt in den core/-Modulen.
    N)OrderedDict)datetimedate	timedelta)ListDict)load_workbookWorkbook)FontPatternFill	Alignment)
filedialog)SEX_MALE
SEX_FEMALEDEFAULT_DATERECHNUNG_COLUMNS_AV_KONSTANTENMitgliedvalidate_member_rowbuild_ust_hints)	mask_iban
iban_validvereinsname_to_from_emailz)%(asctime)s - %(levelname)s - %(message)s)levelformatibanreturnc                 C      t | S )u3   Prüft die IBAN-Prüfsumme nach ISO 7064 MOD-97-10.)r   r    r   main.py_iban_valid.   s   r!   c              	   C   s  dd }g }||  dpd}dd||  dfdd||  dfd	d
||  d
ffD ]\}}}|s>||| dd q,|dkrsdd||  dfdd||  dfdd||  dffD ]\}}}|sr||| dd q`||  d
}|r|dd}|r|d nd}	t|	dks|	 s|ddd |dkr||  d}
|
rt|
s|ddd ||  ddd}|rt|dvs| r| s|ddd ||  ddd}|r| rt|dkr|dd d d!D ]G\}}||  |d"d#}|s||| dd qzt	|dkr*||| d$d W q t
y@   ||| d%d Y qw ||  d&}|sT|d&d'd n$zt|dkrd|d&d(d W n t
yw   |d&d)d Y nw |  d*}t|tr|rd+nd,}n || }|d-v rd+}n|d.v rd,}n
|d*d/d d+}||  d0 dd}|d,kr|s|d0d1d |S |d2rt|d3kr|d4d  s|d0d5d |S |r|d2rt|d3kr|d4d  s|d0d5d |S )6u   Prüft die Vereinsparameter (aus Formular oder Parameter.csv).

    Gibt eine Liste von Dicts zurück – je Fehler einen:
      {"field": "<form-feld-id>", "fehler": "<menschliche Meldung>"}
    c                 S   s   | d u rdS t |  S N )strstrip)vr   r   r    _s9   s   zvalidate_params.<locals>._sabbuchung_modusueberweisungVereinsnamevereinsnameu   StraßevereinsstrasseOrt
vereinsortz fehlt)fieldfehlereigenes_kontoIBAN	sepa_ibanBICsepa_biczCreditor-IDsepa_creditor_idN   r   r#      
vereinsplzz$PLZ muss 4-stellig sein (z. B. 3340)u)   Ungültige IBAN (Prüfsumme stimmt nicht)       u4   Ungültiger BIC (8 oder 11 Zeichen, Großbuchstaben)#   u8   Ungültige Creditor-ID (alphanumerisch, max. 35 Zeichen)))tarif_einspeisungzTarif Einspeisung)tarif_bezugzTarif Bezug,.u    muss größer 0 seinu    ist keine gültige Zahlfaelligkeitu   Fälligkeit fehltu    Fälligkeit muss größer 0 seinu%   Fälligkeit muss eine ganze Zahl seinkleinunternehmerJaNein)r#   jajyesytrue1wahrneinnnofalse0falschz'Kleinunternehmer muss Ja oder Nein sein
uid_nummerz6UID-Nummer fehlt (Pflicht wenn nicht Kleinunternehmer)ATUr=      z.UID-Nummer muss im Format ATU + 8 Ziffern sein)getappendsplitlenisdigitr!   replaceisalnumisupperfloat
ValueErrorint
isinstanceboollowerupper
startswith)paramsr'   errorsr(   labelr/   valortpartsplzr   biccidkeyrawfaellku_rawku_normku_struid_valr   r   r    validate_params3   s    




,,rx   c                
   @   s  e Zd ZdZdefddZdd Zdeded	efd
dZded	dfddZd	e	eef fddZ
dd Zdejde	eef fddZd	efddZdddZdefddZd	efdd Zd!edejde	eef d	dfd"d#Zdejde	eef fd$d%Zd&d' Zd(ed	efd)d*Zd!ed	d+fd,d-Zd!ed	efd.d/Zd0ed!ed1ed2ed	ef
d3d4Zd5ed6ed	dfd7d8Zd!ed	efd9d:Z 	dd!ed;ed	d<fd=d>Z!		dd!ed?ed;ed	efd@dAZ"d!ed	efdBdCZ#d!ed	dfdDdEZ$dFdG Z%e&dHedIed	dfdJdKZ'e&dLed	efdMdNZ(e&d	efdOdPZ)e&dQdR Z*dSedTed	efdUdVZ+e&dWedTed	efdXdYZ,dSedTed	efdZd[Z-	\ddHedTed]ed	efd^d_Z.d`ed	dfdadbZ/d!ed	dfdcddZ0d	efdedfZ1d	e2fdgdhZ3ddidjZ4d!ed	efdkdlZ5d;edme6d	dfdndoZ7ddpdqZ8ddrdsZ9dtdu Z:dved	dfdwdxZ;d;ed	dfdydzZ<d{ed	dfd|d}Z=e&	~ddeded	efddZ>dd!edefddZ?ddedefddZ@deded	dfddZAdedefddZBdeded	efddZCdded	dfddZDdddZEdddZFdS )Abrechnungssystemu   Orchestrator für den EEG-Abrechnungsprozess.

    Fachlogik delegiert an core/-Module. Alle öffentlichen Methoden bleiben
    rückwärtskompatibel (gleiche Signaturen wie v1.07a).
    	main_pathc                 C   s   || _ |  | _g | _t | _| jtt| jd d | _	d| _
d| _d| _d| _d| _d| _d| _d gd | _d gd | _d gd | _d gd | _g | _d| _d| _d| _d| _d S )N   Fälligkeit)daysr#   r              )rz   load_configconfig
mitgliederr   nowdatum_rechnungr   rb   datum_abbuchungfolder_rechnungenrechnungsnummertotal_gesamterzeugungtotal_gesamtverbrauch   total_restüberschusstotal_erzeugung_tftotal_eigendeckunghourly_eigenverbrauchhourly_autarkiegradhourly_avg_consumptionhourly_avg_generationnighttime_verbrauch_sorted	_ls_msgid	_gs_msgid_ls_ctrlsum_gs_ctrlsum)selfrz   r   r   r    __init__   s.   



zAbrechnungssystem.__init__c                 C   sD   t j| jd| j| _t j| jd| _| | j | | j dS )z#Legt Rechnungs- und SEPA-Ordner an.
RechnungenSEPAN)ospathjoinrz   perioder   folder_sepacreate_folderr   r   r   r    
init_Files   s   
zAbrechnungssystem.init_Files	root_pathfilenamer   c                 C   s4   t |D ]\}}}||v rt j||  S qdS )uP   Durchsucht rekursiv alle Unterordner nach filename. Gibt Pfad oder None zurück.N)r   walkr   r   )r   r   r   rootdirsfilesr   r   r    find_file_in_subfolders   s
   z)Abrechnungssystem.find_file_in_subfoldersfolder_nameNc                 C   s  z@t j|}t j|std| t j|dd t j|s(td| t |t js6t	d| t
d|  W d
S  tt	ttfyd } zt
d| d td	 W Y d
}~d
S d
}~w ty } zt
d| d td	 W Y d
}~d
S d
}~ww )z+Erstellt einen Ordner mit Fehlerbehandlung.u&   Übergeordneter Pfad existiert nicht: Texist_okzKonnte Ordner nicht erstellen: u!   Keine Schreibrechte für Ordner: zOrdner erstellt: z#Fehler beim Erstellen des Ordners:     – ABBRUCHr7   Nz*Unbekannter Fehler beim Ordner erstellen: )r   r   dirnameexistsFileNotFoundErrormakedirsOSErroraccessW_OKPermissionErrorloggerinfora   errorexit	Exception)r   r   
parent_direr   r   r    r      s*   zAbrechnungssystem.create_folderc              
      s  i }dt dtdt f fdd}tj jdd}z>tj|dd	g d
d}||d  |d 	tj
 dk@ |d 	tj
 j dk@  }|t t|d |d  W n tyu } ztd| d td W Y d	}~nd	}~ww dg di}||d| tj jdd}z)t|dd}t|}	W d	   n1 sw   Y  |dd |	 D  W n ty } ztd| d td W Y d	}~nd	}~ww |dd |dd |dd |d d |d!d" |d#d |d$d t|dd
  }
|
d%v rd|d< n|
d&v rd'|d< ntd(|d  d) td |d d'krwt|dd
  d*d}|sPtd+ td |d,rft|d-krf|d.d	  sstd/| d) td ||d< d0d1d2 fd3d4d2 fd5d6d2 fgd7g|d8d9d:v rg nd;d<d2 fd=d>d2 fg|d8d9d:v rg nd?d@d2 fgg g dAdB}||d| tdC |S )Du0   Lädt und validiert Config.csv + Parameter.json.entriessourcerequired_groupsc                    s  |  D ]\}}|dkr\|D ]K\}}|| vr'td| d| d td z|| | s>td| d| d td W q ttfyZ   td| d| d td Y qw q|d	kr|D ]R}|| vrxtd| d| d td tj j	| | }tj
|}tj|s  j	|}	|	rtd
|	 d| d qbtd| d| d td qbq|dkr|D ].\}}|| vrtd| d| d td || | std| d| d td qq|dv rL|D ]W}
t|
tr|
n|
df\}}|| vrtd| d| d td t| |  s0td| d| d td |rJ|| | sJtd| d| d td qq|dkr|D ]1}|| vrjtd| d| d td t| |  std| d| d td qSqdS )u-   Validiert alle Einträge einer Config-Quelle.	numerischzFehlender Eintrag in : r   r7   u   Ungültiger Wert für z in u!   : Keine gültige Zahl – ABBRUCH
dateipfadez2Datei nicht im erwarteten Pfad, aber gefunden in: z (Key: )zDatei nicht gefunden: u   ) – ABBRUCHiban_bicu   Ungültige )creditor_idemailbooleanNzLeerer Wert in u    für Key: freitext)itemsr   r   r   ra   AttributeErrorr   r   r   rz   basenamer   r   warningrc   tupler$   r%   )r   r   r   
group_namekeysrq   	validatorexpected_pathr   
found_pathentryr   r   r    validate_config_entries   s   



z>Abrechnungssystem.load_config.<locals>.validate_config_entriesConfigz
Config.csv;N)BezeichnungKeywortSuchwort)sepheadernamesr   r#   keywortr   z"Fehler beim Lesen von Config.csv: r   r7   r   )TB_MitgliedMitglied_IDMitglied_VornameMitglied_NachnameMitglied_SexMitglied_AdresseMitglied_PLZMitglied_OrtMitglied_MailMitglied_IBANMitglied_BICMitglied_FirmennameMitglied_CONSUMPTIONMitglied_GENERATIONEDA_direction_CEDA_direction_GEDA_EigendeckungEDA_ErzeugungEDA_OverflowEDA_GesamterzeugungEDA_GesamtverbrauchTB_Rechnungsnummer
TB_VorlagezParameter.jsonzutf-8)encodingc                 S   s&   i | ]\}}||d u rdnt |qS r"   )r$   ).0kr&   r   r   r    
<dictcomp>i  s   & z1Abrechnungssystem.load_config.<locals>.<dictcomp>z&Fehler beim Lesen der Parameter.json: KleinunternehmerrE   
UID_NummerMitglied_Steuersatz
SteuersatzMitglied_UIDRC_Kostentraegerr   Logo_FilenameAccent_Color)rG   rH   rI   rJ   rK   rL   rM   rN   rF   u)   Ungültiger Wert für Kleinunternehmer: 'u   ' – ABBRUCHr:   u.   UID-Nummer fehlt in Parameter.json – ABBRUCHrV   r=   rW   u   Ungültige UID-Nummer: 'Tarif_Bezugc                 S      t | dddkS NrA   rB   r   r`   r]   xr   r   r    <lambda>      z/Abrechnungssystem.load_config.<locals>.<lambda>Tarif_Einspeisungc                 S   r   r  r  r  r   r   r    r    r  r{   c                 S   s   t | dkS )Nr   rb   r  r   r   r    r    s    File_MitgliederAbbuchung_Modusr)   )abrechnungsvereinr)   	SEPA_IBANc                 S   s$   t | dddko| dd S )Nr:   r#      )r[   r]   r^   r  r   r   r    r    s    SEPA_BICc                 S   s   t | dv o|  o|  S )Nr;   )r[   r^   r_   r  r   r   r    r    s    SEPA_Creditor_IDc                 S   s   |   o	t| dkS )Nr>   )r^   r[   r  r   r   r    r    r  )r*   Vereinsstrasse
VereinsortFile_Rechnungsnummer)r   r   r   r   r   r   u/   Konfiguration erfolgreich geladen und geprüft.)dictr$   r   r   r   rz   pdread_csvnotnaastyper%   re   updatezipr   r   r   r   openjsonloadr   
setdefaultrX   rf   r]   rg   r[   r\   r   )r   r   r   config_path	config_dfr   required_keys_configparameter_path_pf_param_datart   rw   required_keys_parameterr   r   r    r      s   K
 



 





	
zAbrechnungssystem.load_configc                 C   s  t j| j| jd }tj|| jd d}| D ]H\}}t|| jd  s3t|| jd  s3q| 	|| j}|rHt
| d td || jd }t|sWd	nt|}|| jd
 }t|sjd	nt|}| d|  pxd}| jdd}	||	}
zt|
rt|
 d	krttt|
ddnd}W n ttfy   d}Y nw |dvrd}| jdd}||}d	}t|rt| rt|  dd	}|| jd }|du st|rt| s|nt| }| jttttt|| jd  ||| jd  r"t|| jd    nd	|| jd  || jd  tttt|| jd  || jd  || jd  || jd  || jd  || jd  |||d qt
t| j d  dS )!u)   Lädt die Mitglieder aus der Excel-Datei.r	  r   
sheet_namer   r   r   r7   r   r#   r   r:   	Unbekanntr   r   rA   rB   r   )r         r   r   r   Nr   r   r   r   r   r   r   r   )IDNameSexMailStrassePLZr-   r2   r4   ZP_ConsumptionZP_GenerationName4Rechnungr   UIDz Mitglieder geladen.)r   r   r   rz   r   r  
read_exceliterrowsr  validate_member_datar   r   r   rX   r$   r%   rb   r`   r]   ra   	TypeErrorrf   r   rY   r   re   r   r[   )r   
excel_pathdf_row	error_msgvornamenachnamename
steuer_col
steuer_rawsteuersatz_valuid_coluid_rawmitglied_uid_firmenname_raw_name4rechnungr   r   r    init_mitglieder  sz   




z!Abrechnungssystem.init_mitgliederr;  r   c                 C   s*  z}| |d }zt|rtt|nd}W n ttfy-   t|r)t|nd}Y nw | dd}| dd}t| |d | |d | |d	 | |d
 | |d | |d || |d | |d | |d | || |dW S  ty } zd| W  Y d}~S d}~ww )uW   Prüft eine Mitgliederzeile gegen die Config-Spalten. Delegiert an validate_member_row.r   r#   r   r   r   r   r   r   r   r   r   r   r   r   r   )	member_idr=  r>  mailr   ro   rn   rl   strassesex
steuersatzuidu-   Fehler beim Prüfen der Daten für Mitglied: N)	rX   r  r  r$   rb   ra   r7  r   r   )r   r;  r   plz_rawrn   r@  rC  r   r   r   r    r6    s6   z&Abrechnungssystem.validate_member_datac                 C   s  g }zt j| j| jd }W n ty    dddddg Y S w t j|s6ddddt j| dgS ztj	|| jd d}W n t
y_ } zdddd	| dgW  Y d
}~S d
}~ww | D ]\}}zt|| jd  }t|| jd  }W n# ty }	 z|t|d ddd|	 d W Y d
}	~	qdd
}	~	ww |s|sqd| || j}
|
r	z#|| jd dpd}|| jd dpd}| d|  pd}W n t
y   d}Y nw zt|| jd dpd}W n t
y   d}Y nw |t|d |||
d qd|S )uM   Prüft die komplette Mitgliederliste und gibt ALLE gefundenen Fehler zurück.r	  r   r#   z*File_Mitglieder fehlt in der Konfiguration)zeileidr?  r0   z Mitgliederdatei nicht gefunden: r   r%  z-Mitgliederdatei konnte nicht gelesen werden: Nr   r      zSpalte fehlt: r   r   r:   r'  r   )r   r   r   rz   r   KeyErrorr   r   r  r4  r   r5  r  rY   rb   r6  rX   r%   r$   )r   ri   r8  r9  r   idxr;  has_chas_gker<  r=  r>  r?  rI  r   r   r    validate_all_members  sn   
z&Abrechnungssystem.validate_all_membersc                 C   s  t  }| jD ]*}|jrt|jdd nd}|j  |f}||vr)g ||< || 	| qd}g }|
 D ]\}}t|dkrK|	|d  q9|d }|dd D ]}	| j|	j7  _| j|	j7  _t|jrwt|	jsw|	j|_t|jrt|	js|	j|_dD ]!}
t|	|
}t||
}|r|tkrt||
|tkr|nt|| qdD ]!}t|	|}t||}|r|tkrt|||tkr|nt|| q|d7 }td|	j d	|	j d
|j d	|j d	 qU|	| q9|rtdt| j dt| d| d || _dS )z?Fasst Mitglieder mit gleichem Namen UND gleicher IBAN zusammen.r:   r#   r   r7   N)Start_CStart_G)Ende_CEnde_GzZusammenfassung: z (ID: u   ) → r   zMitglieder zusammengefasst: u    → z (z zusammengefasst))r   r   r2   r$   r]   rf   r+  r%   re   rY   r   r[   Consumption
Generationr  isnar0  r1  getattrr   setattrminmaxr   r   r*  )r   groupsm
iban_cleanrq   merged_countnew_mitgliedermembersprimaryother
start_attr	other_valprim_valend_attrr   r   r    _merge_duplicate_membersB  sr   
 




z*Abrechnungssystem._merge_duplicate_membersreport_pathc                 C      ddl m} || |S )z3Liest den EDA-Report. Delegiert an core.eda_reader.r   )read_eda_report)core.eda_readerrs  )r   rq  rs  r   r   r    rs  y     
z!Abrechnungssystem.read_eda_reportc                 C   sr   t dd |D }t dd |D }|dkrtd dS t|| | d }|dkr7td	|d
d dS dS )uF   Prüft ob Consumption und Generation im Toleranzbereich liegen (±1%).c                 s       | ]}|j V  qd S Nr]  r   re  r   r   r    	<genexpr>      zAAbrechnungssystem.check_consumption_generation.<locals>.<genexpr>c                 s   rv  rw  r^  ry  r   r   r    rz    r{  r   u3   Generation ist 0 – Division durch Null vermieden!Fd         ?z#Abweichung Consumption/Generation: .2fu   % (max ±1%)T)sumr   r   abs)r   r   total_Ctotal_Gdeviation_percentr   r   r    check_consumption_generation~  s   
z.Abrechnungssystem.check_consumption_generationmitgliedc                 C   s   ||d  |d kr7| j ||d  7  _ |jtkr$t|j||d  n||d  |_t|j||d  |_n<||d  |d krs| j||d  ||d   7  _|jtkrat|j||d  n||d  |_t|j||d  |_d	|_	d
S )zMAktualisiert Verbrauchs-/Erzeugungsdaten eines Mitglieds aus einer EDA-Zeile.EDA_directionr   r   EDA_time_fromEDA_time_tor   r   r   TN)
r]  rY  r   rb  rc  r[  r^  rZ  r\  	DataCheck)r   r  r;  r   r   r   r    update_member_from_eda_row  s"   




z,Abrechnungssystem.update_member_from_eda_rowc              
   C   s
  zmt ||d  }|r| sW dS ||d  }||d |d fvr+d| d| W S ||d  }||d	  }t|sAt|rGd
| W S ||d  }||d  }||d  }	tdd |||	fD rkd| W S W dS  ty }
 zd|
 W  Y d}
~
S d}
~
ww )z"Validiert eine einzelne EDA-Zeile.EDA_ZPu(   Zählpunkt (ZP) fehlt oder ist ungültigr  r   r   u   Ungültige Richtung 'u   ' für Zählpunkt r  r  u'   Ungültige Zeitstempel für Zählpunkt r   r   r   c                 s   s    | ]}t |V  qd S rw  )r  r_  )r   rk   r   r   r    rz    s    z5Abrechnungssystem.validate_eda_row.<locals>.<genexpr>u*   Fehlende numerische Werte für Zählpunkt Nu#   Fehler beim Prüfen der EDA-Daten: )r$   r%   r  r_  anyr   )r   r;  r   zp	direction	time_fromtime_toeigendeckung	erzeugungoverflowr   r   r   r    validate_eda_row  s*   z"Abrechnungssystem.validate_eda_rowc                 C   s  t | jd dd}t | jd dd}t| jdd  dk}| jdd	 }| jD ]I}z$|rF|d	krF|jd
krF|d d }n|}t	|j
| |j|  d|_W q1 tyz } ztd|j d| d td W Y d}~q1d}~ww td dS )u@   Berechnet die Kosten (Rechnung/Gutschrift) für alle Mitglieder.r   rA   rB   r  r   rE   rG   r   r   r)        rR  u%   Fehler bei der Kostenberechnung für r   r   r7   Nu&   Kosten für alle Mitglieder berechnet.)r`   r   r]   r$   rX   r%   re   r   r   roundr]  r^  Kostenr   r   r   r+  r   r   )r   r@   r?   rD   rc_kostentraegerr  tarif_einspeisung_effr   r   r   r    calculate_kosten  s0   


z"Abrechnungssystem.calculate_kostenrechnungsnummer_pathc                 C   s   ddl m} ||S )uX   Legt Kassabuch an (falls nötig) und gibt die letzte Rechnungsnummer des Jahres zurück.r   )init_kassabuch)core.kassabuchr  )r   r  r  r   r   r    _generate_rechnungsnummer     z+Abrechnungssystem._generate_rechnungsnummerz
int | Nonec              	   C   s   t j| jd| jd }| |}|sdS | jd| jdd  }|dkrS|j	dkr0dnd	}| jd
d }| d| j
 d| d|j d|j 	dd }nt| |j	dkr\dndd}| ||||S )uM   Vergabe der Rechnungsnummer und Kassabuch-Eintrag. Gibt Zeilennummer zurück.r   r  Nr
  r(   r)   r   RGGSr*   r#    | r:      r   r   )r   r   r   rz   r   _get_rechnung_descriptionrX   r%   re   r  r   Rechnungsnummerr+  r`  _update_rechnungsnummern_datei)r   r  rn_pathdescriptionr(   doc_typr+   buchungstextr   r   r    handle_rechnungsnummer  s2   
z(Abrechnungssystem.handle_rechnungsnummerc                 C   s   |j  t kr|j  nd}|j t kr|j nd}|j t kr.|j nd}|j t kr>|j nd}|r[|r[|r[|r[t||d dt||d S |rl|rl|d d|d S |r}|r}|d d|d S dS )u>   Erstellt die Beschreibungszeile (Zeitraum) für das Kassabuch.N%d.%m.%Yz bis r#   )rY  r   r   rZ  r[  r\  rb  strftime)r   r  start_cstart_gende_cende_gr   r   r    r    s       z+Abrechnungssystem._get_rechnung_descriptionr   r  msgidc              
   C   s,   ddl m} |||||| j| j| j| jdS )z4Delegiert Kassabuch-Zeileneintrag an core.kassabuch.r   )update_kassabuch)r   r  r  r  r   r   r   r   )r  r  r   r   r   r   )r   r   r  r  r  r  r   r   r    r    s   z0Abrechnungssystem._update_rechnungsnummern_dateils_rowsgs_rowsc              
   C   sJ   ddl m} tj| jd| jd }||||| j| j| j	| j
| jd dS )z3Delegiert Kassabuch-Sammelzeilen an core.kassabuch.r   )write_kassabuch_batch_summaryr   r  )r   r  r  ls_msgidgs_msgid
ls_ctrlsum
gs_ctrlsumr   N)r  r  r   r   r   rz   r   r   r   r   r   r   )r   r  r  r  r  r   r   r    _write_kassabuch_batch_summary  s   
z0Abrechnungssystem._write_kassabuch_batch_summaryc           +   	   C   s  t | jdd  dk}| jdd}|jdk}|jdk}t|||j||d\}}t	t | jd 
d	d
}t	t | jd 
d	d
}	|d
d
d	}
|	d
d
d	}| jdd }d}d}|r|dkr|r|jdkr|j|	 d }|d
d
d	}d}d}d}d}|r|jdkr|	d }|j|	 d }|j| }|d
d
d	}|d
d
d	}|d
d
d	}d}d}d}|s|r|d }|j| d }|j| }|d
d
d	}|d
d
d	}|d
d
d	}|r|j| d
d
d	}|jd
d
d	}|jd}|jd}n d}d}|jtkr|jdnd}|jtkr*|jdnd}|rRd|j|	 d
d
d	} |jd
d
d	}!|jd}"|jd}#n d} d}!|jtkrb|jdnd}"|jtkrp|jdnd}#|jtkr{d}$n|jtkrd}$nd}$|jrt|jr|jdd nd}%|jrt|jr|jdd nd}&|jdk }'|'rd nd!}(|jd
d
d	})d"|j }*i d#| jd$ d%| jd& d'| jd( d)dd*|$d+|jd,|jr|jn|jd-|jd.|jd/|jd0| jdd1| j dd2|j!d3t"|j#d4|%d5|&d6|i d7|d8|d9|
d:|d;|"d<|#d=|!d>|d?| d@|)dA|dB|dC|(dD|'dE|*dF|j$pRd dG||||||||dHS )Iu9   Baut das Kontext-Dictionary für DOCX- und PDF-Templates.r   rE   rG   r   r#   r   )rD   
verein_uidmitglied_steuersatz	has_bezughas_einspeisungr   rA   rB   r  r  r   r   Nr)  r  20r(  gGz?g81s?.4fg333333?gVUUUUU?r  z0,00-LieberLiebezLiebe/ri
GUTSCHRIFTRECHNUNGzEEG-r+   r*   r,   r  r.   r  logoanreder?  	anschriftrK  rn   rl   rechnungsdatumfaelligkeitsdatumr   kontonummerzp_consumptionzp_generationconsumption_vonconsumption_bisconsumptionr@   kosten_consumptiongeneration_vongeneration_bis
generationr?   kosten_generationgesamtkostenust_hint_bezugust_hint_einspeisungr  is_gutschrift
mandat_refrE  rc_mwst_betrag)rc_mwst_satzlandwirt_mwst_betraglandwirt_netto_tariflandwirt_netto_betragbezug_mwst_betragbezug_netto_tarifbezug_netto_betrag)%r$   r   rX   r%   re   r]  r^  r   r   r`   r]   rY  r  r[  rZ  r   r\  r,  r   r   r0  r  r  r1  r  r*  r+  r2  r.  r/  r-   r   r   r  r   r2   r3  )+r   r  kleinuntr  r  r  r  r  tarif_b_numtarif_e_numtarif_bezug_strtarif_einspeisung_strr  rc_mwst_betrag_strrc_mwst_satz_str_rc_mwstlandwirt_mwst_betrag_strlandwirt_netto_tarif_strlandwirt_netto_betrag_str_lw_netto_tarif_lw_mwst_lw_netto_betragbezug_mwst_betrag_strbezug_netto_tarif_strbezug_netto_betrag_str_b_netto_tarif_b_mwst_b_netto_betragkosten_cconsumption_strcons_voncons_biskosten_ggeneration_strgen_vongen_bisr  zp_czp_gr  r  gesamtr  r   r   r    _build_docx_context%  sZ  











	

 !"#z%Abrechnungssystem._build_docx_contextoutput_pathz
str | Nonec                 C   s   ddl m} || ||S )zFRendert Rechnung als PDF via ReportLab. Delegiert an core.invoice_pdf.r   )render_invoice_pdf_html)core.invoice_pdfr  )r   r  r  r  r   r   r    r    s   z)Abrechnungssystem.render_invoice_pdf_htmltemplate_pathc                 C   s   ddl m} || |||S )zFRendert Rechnung als DOCX via docxtpl. Delegiert an core.invoice_docx.r   )render_invoice_docx)core.invoice_docxr
  )r   r  r	  r  r
  r   r   r    r
    s   z%Abrechnungssystem.render_invoice_docxc                 C   rr  )u<   Füllt Legacy-Excel-Vorlage. Delegiert an core.invoice_docx.r   )fill_template)r  r  )r   r  r  r   r   r    r    ru  zAbrechnungssystem.fill_templatec                 C   s&   ddl m} |||| j| j| j dS )zGErsetzt [Platzhalter] in Excel-Vorlage. Delegiert an core.invoice_docx.r   )_replace_placeholdersN)r  r  r   r   r   )r   wsr  r  r   r   r    r    s   
z'Abrechnungssystem._replace_placeholdersc                 C   s   ddl m} ||| S )u?   Erstellt InlineImage für Logo. Delegiert an core.invoice_docx.r   )_build_logo_image)r  r  )r   docr  r   r   r    r    ru  z#Abrechnungssystem._build_logo_image	docx_path	hex_colorc                 C   s   ddl m} || | dS )z:Setzt Akzentfarbe im DOCX. Delegiert an core.invoice_docx.r   )_apply_accent_colorN)r  r  )r  r  r  r   r   r    r    s   z%Abrechnungssystem._apply_accent_colorr   c                 C   r   )u4   Maskiert IBAN für Anzeige. Delegiert an core.utils.)r   r   r   r   r    
_mask_iban  s   zAbrechnungssystem._mask_ibanc                  C   s   ddl } | jg ddddd}d|jv rdS td | jg d	dd
d | jg ddddd}|jdkr<td dS td|j   dS )z=Stellt sicher, dass 'Microsoft Print to PDF' installiert ist.r   N)
powershell
-NoProfile-CommandzmGet-Printer -Name "Microsoft Print to PDF" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty NameTr  capture_outputtexttimeoutzMicrosoft Print to PDFuE   'Microsoft Print to PDF' nicht gefunden – versuche Installation …)r  r  r  z9Start-Service -Name Spooler -ErrorAction SilentlyContinue
   )r  r  )r  r  r  zgAdd-Printer -Name "Microsoft Print to PDF" -DriverName "Microsoft Print To PDF" -PortName "PORTPROMPT:"r)  z%'Microsoft Print to PDF' installiert.zInstallation fehlgeschlagen: F)	
subprocessrunstdoutr   r   
returncoder   stderrr%   )r  checkaddr   r   r    _ensure_ms_pdf_printer  s*   



z(Abrechnungssystem._ensure_ms_pdf_printerc               
      s  ddl } ddl}z_| jg ddddd}|jdkrf|j rf||j}t|tr,|g}d }}|D ]*}|	dd |	d	d
pAd
}  d| }t
 fdddD rV|}|du r\|}q2|sa|rf|pd|W S W n	 typ   Y nw zZddl}	|	|	j|	jB dd}
d }}|
D ]?}|d  |	 }z|	|d}W |	| n|	| w |	dd
}  d| }t
 fdddD r|}|du r|}q|p|W S  ty   Y dS w )u7   Sucht einen verfügbaren Drucker für Excel-PDF-Export.r   N)r  r  r  z;Get-Printer | Select-Object Name, PortName | ConvertTo-JsonTr  r  r+  r#   PortNamezPORTPROMPT:z on c                 3       | ]	}|   v V  qd S rw  rf   r   kwr?  r   r    rz  
      z8Abrechnungssystem._find_excel_printer.<locals>.<genexpr>)PDFXPSrR  	pPortNamec                 3   r&  rw  r'  r(  r*  r   r    rz  !  r+  )r  r  r  r   r  r%   loadsrc   r  rX   r  r   
win32printEnumPrintersPRINTER_ENUM_LOCALPRINTER_ENUM_CONNECTIONSOpenPrinter
GetPrinterClosePrinter)r  _jsonresultdata	preferredfallbackpportprinter_strr0  printershandler   r   r*  r    _find_excel_printer  sd   



z%Abrechnungssystem._find_excel_printerr8  output_pdf_pathc           	      C   s  ddl }ddl}d }}z|  tj|std| |jd}d|_	d|_
d|_|  }|s;|   |  }|r]z||_W n ty\ } ztd|  W Y d}~n
d}~ww td |j|}|d| |d d}W z|dur|d W n	 ty   Y nw z
|dur|  W n	 ty   Y nw z|  W d	S  ty   Y d	S w z|dur|d W n	 ty   Y nw z
|dur|  W n	 ty   Y nw z|  W w  ty   Y w w )
uL   PDF-Erstellung via Excel COM (win32com). Gibt True zurück wenn erfolgreich.r   NzExcel-Datei nicht gefunden: zExcel.ApplicationFr7   z%Drucker konnte nicht gesetzt werden: u   Kein Drucker verfügbar.T)win32com.client	pythoncomCoInitializer   r   r   r   clientDispatchVisibleDisplayAlertsAutomationSecurityrA  r$  ActivePrinterr   r   r   	WorkbooksOpenExportAsFixedFormatCloseQuitCoUninitialize)	r   r8  rB  win32comrD  excelworkbookprinterper   r   r    _create_pdf_excel_com*  s   




z'Abrechnungssystem._create_pdf_excel_com
input_pathc                    s  ddl }ddl}ddl}tjdkr| dd} |dd}tj|}tj	dd}tjdkrQtj
|sQtj| d}z	tj|d	d
 W n	 tyP   Y nw tj }tjdkre||d< |dd d|tjd }|jd|	ddp|jd|	dd}	g }
|	r|
|	 |
g d t   fdd|
D }
g }tdt|
 d|
  |
D ]}zd|j|dd| ddd|| gd	d	d|d}|jdkrtj|tjtj| d d }tj|r||krt|| W  d	S |d| d |  n|d| d!|j d"|j  dd#   W q t!|j"fy< } z|d| d$t#|j$ d%|  W Y d}~qd}~w ty_ } z|d| d$t#|j$ d%|  W Y d}~qd}~ww |D ]}t%d&|  qbd'S )(z=PDF-Erstellung via LibreOffice (headless). Format-agnostisch.r   Nnt\/EEG_LO_HOMEz/var/lib/eeg-lo-profilezeeg-lo-profileTr   HOMEPATHz</usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binzfile://soffice)r   libreoffice)z/usr/bin/sofficez/usr/bin/libreofficez/usr/local/bin/sofficez/usr/local/bin/libreofficer_  r`  z0C:\Program Files\LibreOffice\program\soffice.exez6C:\Program Files (x86)\LibreOffice\program\soffice.exec                    s"   g | ]}| v s  |s|qS r   )r#  r   cseenr   r    
<listcomp>z  s   " z=Abrechnungssystem._create_pdf_libreoffice.<locals>.<listcomp>zLibreOffice-Kandidaten (z): z
--headlessz-env:UserInstallation=z--convert-topdfz--outdir   )r  r  r  env.pdf[z] rc=0 aber kein PDF in z] rc=z stderr=i,  z] r   zLibreOffice-Fehler: F)&r  tempfileshutilr   r?  r]   r   r   environrX   isdirr   
gettempdirr   r   copyr  r   whichrY   extendsetr   r   r[   r  r   splitextr   r   renamer!  r%   r   TimeoutExpiredtype__name__r   )rX  rB  r  rk  rl  
output_dirlo_homerh  user_installresolvedlo_candidateserrsr_  r8  lo_pdfr   r   rc  r    _create_pdf_libreofficeV  s~   




$..z)Abrechnungssystem._create_pdf_libreofficec              
   C   st  t j|}t j|}t jdkr|dd}|dd}z-td | || t j|rIt j|r:t 	| tdt j
|  W dS W n tyd } ztd|  W Y d}~nd}~ww z,td	 | ||rt j|r|t 	| td
t j
|  W dS td W n ty } ztd|  W Y d}~nd}~ww tdt j
|  dS )uH   Erstellt PDF aus Excel. Versucht: COM → LibreOffice → Fallback XLSX.rY  r[  rZ  u    PDF-Erstellung via Excel COM …zPDF erstellt (Excel COM): TzExcel COM fehlgeschlagen: Nu"   PDF-Erstellung via LibreOffice …zPDF erstellt (LibreOffice): /LibreOffice nicht gefunden oder fehlgeschlagen.zLibreOffice fehlgeschlagen: u8   PDF-Erstellung nicht möglich – XLSX bleibt erhalten: F)r   r   abspathr?  r]   r   r   rW  r   remover   r   r   r  )r   r8  rB  r   r   r   r    
create_pdf  sD   




zAbrechnungssystem.create_pdfF	keep_docxc              
   C   s"  t j|}t j|}t jdkr|dd}|dd}zKtd | ||rb|sSt j|rSzt 	| W n t
yR } ztd|  W Y d}~nd}~ww tdt j|  W dS td	 W n ty } ztd
|  W Y d}~nd}~ww tdt j|  dS )z'Wandelt DOCX via LibreOffice in PDF um.rY  r[  rZ  u1   PDF-Erstellung via LibreOffice (DOCX → PDF) …u$   DOCX konnte nicht gelöscht werden: NzPDF erstellt: Tr  z*LibreOffice-Konvertierung fehlgeschlagen: u8   PDF-Erstellung nicht möglich – DOCX bleibt erhalten: F)r   r   r  r?  r]   r   r   r  r   r  r   r   r   r   )r   r  rB  r  r   r   r   r    create_pdf_from_docx  s8   


z&Abrechnungssystem.create_pdf_from_docxcreated_mitgliederc                 C   s   |D ]&}t j| jd|j d|j }dD ]}|| }t j|r't | qqt j| jr<t 	| js<t 
| j t j| jrOt 	| jsOt 
| j td dS )uE   Löscht alle erstellten Rechnungsdateien und räumt leere Ordner auf.	Rechnung_r:  )ri  z.docxz.xlsxzRollback abgeschlossen.N)r   r   r   r   r  r2  r   r  r   listdirrmdirr   r   )r   r  r  baseextr   r   r   r    rollback  s    
zAbrechnungssystem.rollbackc              
   C   s  zo|j dks|jdks|jdkrm|  jd7  _| jj d| jd|_tj	| j
d|j d|j d}| ||du}|s\td	 | |}| ||}|s\td
tj|  td|j d|j d W dS W dS  ty } ztd|j d|   d}~ww )uA   Rechnungsnummer vergeben, PDF erstellen (HTML → DOCX-Fallback).r   r7   r  04dr  r:  ri  Nu<   WeasyPrint nicht verfügbar – Fallback: DOCX + LibreOfficeu$   PDF fehlgeschlagen – DOCX bleibt: u   Rechnung für z erstellt (Nr: z).zFehler bei r   )r  r^  r]  r   r   yearr  r   r   r   r   r2  r  r   r   r
  r  r   r   r+  r   r   )r   r  pdf_basepdf_okr  r   r   r   r    process_mitglied  s6   


z"Abrechnungssystem.process_mitgliedc                 C   s$   | j d| j dd  dkS )z.True wenn Abbuchungsmodus = abrechnungsverein.r
  r(   r)   r  )r   rX   r%   re   r   r   r   r    _is_av_mode  s
   
zAbrechnungssystem._is_av_modec                 C   sF   |   rtd td td fS | jdd| jdd| jddfS )uG   Gibt (IBAN, BIC, Creditor_ID) zurück – AV-Modus: aus _AV_KONSTANTEN.r   ro   r   r  r#   r  r  )r  r   r   rX   r   r   r   r    	_sepa_ids  s   zAbrechnungssystem._sepa_idsc                 C      ddl m} ||  dS )u@   Prüft SEPA-Konfigurationswerte. Delegiert an core.sepa_handler.r   )_validate_sepa_configN)core.sepa_handlerr  )r   r  r   r   r    r  &     z'Abrechnungssystem._validate_sepa_configc                 C   s   |j r|jstd|j dd| j t|jt|j ttt|j	d |j|j
t | j | jd  d| j d|j d|  d dd	S )
u1   Erstellt ein SEPA-Payment-Dict für ein Mitglied.z	Mitglied u    hat keine gültige IBAN/BICzLastschrift-r}  r*   r  OOFFrR  )PmtInfIdr?  r2   amountr4   
mandate_idmandate_datecollection_dater  sequencer   rw  )r2   r4   ra   r+  r   r$   rb   r  r`   r  r*  r   todayr   r   r  r  )r   r  r   r   r    _create_sepa_payment+  s   

z&Abrechnungssystem._create_sepa_paymentxml_contentc              
   C   sl   zt |d}|| W d   W dS 1 sw   Y  W dS  ty5 } z
td| d| d}~ww )z"Schreibt XML-Inhalt in eine Datei.wbNz$Fehler beim Schreiben der XML-Datei r   )r  writeIOError)r   r  r  fr   r   r   r    _write_xml_file>  s   &z!Abrechnungssystem._write_xml_filec                 C   r  )z>Erstellt SEPA-Lastschrift-XML. Delegiert an core.sepa_handler.r   )create_sepa_xml_lastschriftN)r  r  )r   r  r   r   r    create_sepa_xml_LastschriftF  r  z-Abrechnungssystem.create_sepa_xml_Lastschriftc                 C   r  )z=Erstellt SEPA-Gutschrift-XML. Delegiert an core.sepa_handler.r   )create_sepa_xml_gutschriftN)r  r  )r   r  r   r   r    create_sepa_xml_GutschriftK  r  z,Abrechnungssystem.create_sepa_xml_Gutschriftc                 C   s   ddl m} || S )Delegiert an core.sepa_handler.r   )_create_sepa_ct_object)r  r  )r   r  r   r   r    r  P  r  z(Abrechnungssystem._create_sepa_ct_objectgutschrift_mitgliederc                 C      ddl m} ||||  dS )r  r   )_add_gutschrift_transactionsN)r  r  )r   sepa_ctr  r  r   r   r    r  U     z.Abrechnungssystem._add_gutschrift_transactionsc                 C   r  )r  r   )_export_sepa_ctN)r  r  )r   r  r  r  r   r   r    r  Z  r  z!Abrechnungssystem._export_sepa_ctsepa_folderc                 C   s   ddl m} || dS )r  r   )_validate_sepa_folderN)r  r  )r   r  r  r   r   r    r  _  r  z'Abrechnungssystem._validate_sepa_folderenergie-durchdacht.atr+   domainc                 C   s
   t | |S )zDelegiert an core.utils.)r   )r+   r  r   r   r    _vereinsname_to_from_emailf  s   
z,Abrechnungssystem._vereinsname_to_from_email
test_emailc                 C      ddl m} || || dS )zCVersendet die Rechnung per E-Mail. Delegiert an core.email_handler.r   )
send_emailN)core.email_handlerr  )r   r  r  r  r   r   r    r  l  r  zAbrechnungssystem.send_emailr#   to_addrr   c                 C   r  )z=Sendet SEPA-XMLs per E-Mail. Delegiert an core.email_handler.r   )send_sepa_emailN)r  r  )r   r  r   r  r   r   r    r  q  r  z!Abrechnungssystem.send_sepa_emailrows2fnamec           6         s  ddl m} ||  d}d}d}d}d}d}	td	|  t|	kr,td
 dS | }
| }| }| }| }| jdd}| jddg }tdt|
D ]5|
 }| }| }| }|r|dvr||fv r|rt	|t
tfr|||||| d qW|std dS t|	 }dd g }|d d }|r||kr|d| d|  fddt|	tD }dd |D tddtfddtdtD }|r|| d g }|D ]:}|d dkrq|d  tfd!d|	d D }|r-||d"  d#|d$  d#|d% dd&  d'| d( q|| D ]}td)|  q2|sR|sRtd*| d+t| d, d-}g }|D ]I}|d  tfd.d|	d D } t| |d  }!|!|kr||d"  d#|d$  d#|d% dd&  d/|d d0d1| d0d2|!d0d3 qX|D ]}td4|  q|std5t| d6 d7}"td8d | jD }#td9d | jD }$t|#|$ }%|%|"krtd:|#d0d;|$d0d<|%d0d= n
td>|%d?d@ | jdAdBfdCd|D }&zVddDlm}' ddElm  i dFdGdHdGdIdJdKdLdMdNdOdPdQdRdSdTdUdVdWdXdYdZd[dZd\dZd]d^d_d`dadbdcddi dedfdgdhdidjdkdldmdldndodpdqdrdqdsdqdtdudvdwdxdydzd{d|d}d~ddddddddddddddd	}(dG})t| jdd }*|*  }+|(|+},|,du r|( D ]\}-}.|-|+v s|+|-v r|.}, nq|,du r|)},t|)|, d d|'dddd|)i  fdd}/g 	|	d D ]7

d }0|0du r q|/|0 \}1}2|0|1k s|0|2kr't
fdd|&D }3	t|3d d q	jdd t	}4|4dkrH|4d 	fddtdD 		| _td|4 d	r^d	d ddndB  W dS  ty }5 ztd|5  g | _W Y d}5~5dS d}5~5ww )u   Prüft Spaltensummen, Zeitreihen-Vollständigkeit und Energiebilanz.
        Berechnet auch stündliche KPI-Kurven und die Nacht-Jahresdauerlinie.r   )compute_hourly_kpisr7   rW   r(     r     u(   Starte Integritätsprüfung EDA-Report: u9   Integritätsprüfung: Blatt 2 enthält keine Datenzeilen.Nr   
GENERATIONr   CONSUMPTION)MMTOTAL)rT  r  dircodepre_sum
n_expectedu2   Integritätsprüfung: Keine Datenspalten gefunden.c              	   S   sJ   | d u rd S t |  }dD ]}z	t||W   S  ty"   Y qw d S )N)z%d.%m.%Y %H:%Mz%d.%m.%Y %H:%M:%Sz%Y-%m-%d %H:%M:%S)r$   r%   r   strptimera   )r&   sfmtr   r   r    	_parse_ts  s   zDAbrechnungssystem._validate_eda_sheet_consistency.<locals>._parse_tsr  zAnzahl Datenzeilen u    ≠ Z13-Angabe c                    s   g | ]
} | d  qS r   r   r   i)r  r  r   r    re        zEAbrechnungssystem._validate_eda_sheet_consistency.<locals>.<listcomp>c                 S   s   g | ]}|d ur|qS rw  r   )r   tr   r   r    re    s    )minutesc                 3   s,    | ]}| |d     krd V  qdS )r7   Nr   r  )intervalvalid_tsr   r    rz    s    zDAbrechnungssystem._validate_eda_sheet_consistency.<locals>.<genexpr>u0    Zeitstempel-Lücke(n)/Sprünge im 15-Min-Rasterr  rT  c                 3   s     | ]}|  d u rdV  qd S )Nr7   r   r   rcir   r    rz    s    r  r  r  r  2   r   z Werte fehlenu   Vollständigkeitsprüfung: u!   Vollständigkeitsprüfung OK – z Zeitintervalle, u    Spalten vollständig.gMbP?c                 3   s*    | ]}t |  ttfr|  V  qd S rw  rc   rb   r`   r  r  r   r    rz        
z: Z16=r  u    ≠ Σ=u    (Δ=z kWh)zSpaltensummen-Abweichung: u&   Spaltensummen-Integrität OK – alle z Spalten korrekt.r~  c                 s   rv  rw  rx  ry  r   r   r    rz    r{  c                 s   rv  rw  r|  ry  r   r   r    rz    r{  u   Energiebilanz-Abweichung: Σ C=u    kWh, Σ G=u	    kWh, Δ=z kWhu   Energiebilanz OK – Δ=z.6fz kWh.r   r#   c                    s,   g | ]}|d   kr|d kr|d qS )r  r  rT  r   ra  )code_ges_verb	dir_c_valr   r    re    s
    )LocationInfo)sunwiengQ^0@viennagrazgzG.@linzgGz,@salzburggGz*@	innsbruckgHz&@
klagenfurtgQ,@villachg33333+@welsg
ףp=
,@steyrgףp=
,@u   st. pölteng=
ףp=/@u
   st pöltenu   sankt pöltendornbirng{Gz#@bregenzg     #@	feldkirchg333333#@leondingg     ,@traung{Gz,@klosterneuburggRQ0@u   mödlingg
ףp=J0@zkrems an der donaugQ8/@krems	amstetteng=
ףp-@zwaidhofen an der ybbsg
ףp=-@zwaidhofen/ybbs	waidhofenleobengGz..@
kapfenberggGz.@zbruck an der murg(\.@	wolfsberggGz-@kufsteingףp=
W(@imstg{Gz%@lienzg
ףp=)@zzell am seeg)@g=
ףp=+@g+@gRQ+@g{G*@g=
ףp=0@gQ0@gRQ/@gףp=
W.@g)\.@)	z	bad ischlgmundenu   vöcklabruckzried im innkreiszwiener neustadt
eisenstadthornzwettlmelkSuncityViennag      @AustriazEurope/ViennagH@c                    sV   | vr' j | jd}|d jd d }|d jd d }||f| < |  S )N)r   tzinfosunrise)r  sunset)observertimezoner]   )dr  srss)_astral_sun	_location_lon_offset
_sun_cacher   r    _get_sunrise_sunset(  s   zNAbrechnungssystem._validate_eda_sheet_consistency.<locals>._get_sunrise_sunsetc                 3   s*    | ]}t  | ttfr | V  qd S rw  r  )r   r  )r;  r   r    rz  7  r  r8   T)reversei  c                    s   g | ]
} t |  qS r   r  r  )nighttime_kwstepr   r    re  A  r  zNacht-Jahresdauerlinie: z Nachtstunden-Intervallez, Peak r  z kW)rt  r  r   r   r[   r   r   rX   rangerc   rb   r`   rY   r   r  r  r   astralr  
astral.sunr  r$   r%   re   r   r   r  sortr   r   )6r   r  r  r  IDX_ZPIDX_DIRIDX_CODE
IDX_N_INTVIDX_SUM_PREIDX_DATA_STARTrow_zprow_dirrow_code
row_n_intvrow_sum_pre	dir_g_val	data_colsr  dirnr  pren_data_rowserrors_completenessn_expected_ref
timestamps	gap_countmissing_colscolmissingmsgTOLERANCE_COL
sum_errorscomputeddiffTOLERANCE_BALANCEsum_csum_gbalance_diffges_verb_colsr  _AT_LON_VIENNA_LON	city_namecity_keycity_lonk_citylonr  tsr
  r  total_verb_kwhn_full_e_nightr   )r  r  r  r  r  r  r  r  r  r  r;  r  r  r  r    _validate_eda_sheet_consistencyx  s  



.

&

		








z1Abrechnungssystem._validate_eda_sheet_consistencyreport_pathsc              
   C   s  z|| _ |   |   |D ]}| | q| | js%td td | 	  | jj
dd d tj| jd| jd }| || _g }z| jD ]}|| | | qJW n tys } ztd| d	 | |  d
}~ww |   |   | jD ]$}|jdks|jdks|jdkr| jdddkr| | | | qtd W d
S  ty } ztjd| dd  d
}~ww )z/Hauptablauf (Legacy): Alles in einem Durchgang.z8Abbruch: Consumption und Generation weichen zu stark ab!r7   c                 S   2   | j dkr
d| jfS | j dk rd| jfS d| jfS Nr   r7   rR  r  r*  re  r   r   r    r  ]     z'Abrechnungssystem.run.<locals>.<lambda>rq   r   r  zFehler: u#   . Setze alle Änderungen zurück...Nr   	Send_MailFalseTruezFertig! Rechnungen erstellt.T)exc_info)r   r   rH  rs  r  r   r   r   r   r  r  r   r   r   rz   r   r  r   rY   r  r   r  r  r  r  r^  r]  rX   r  r  r   )r   rE  r   r_pr  created_rechnungenr  r   r   r   r    r  N  sR   






zAbrechnungssystem.runc                 C   s  || _ |   |   |D ]}| | qz| | j}|s#td W n ty> } ztd|  W Y d}~nd}~ww | 	  g }| jD ]}zt
t|ddpSd}W n tya   d}Y nw zt
t|ddpkd}	W n tyy   d}	Y nw zt
t|ddpd}
W n ty   d}
Y nw |t|d	d
t|dd
pd
t|dd
pd
t|dd
pd
t|dd
pd
t|dd
pd
t|dt|	dt|
dtt|ddpdt|
dkd qHd| _|S )zWPhase 1: Mitglieder + EDA einlesen, Kosten berechnen. Keine Dateien werden geschrieben.z,Consumption und Generation weichen stark ab.zBilanz-Check fehlgeschlagen: Nr]  r   r}   r^  r  r*  r#   r+  r-  r2   r0  r1  rW   rR  r   g{Gzt?)rQ  r?  rJ  r   r  r  r  r  kostenrM  has_invoiceT)r   r   rH  rs  r  r   r   r   r   r  r`   r`  rY   r  rb   r  	_prepared)r   rE  r   rP  oke_balr8  re  r  r  rR  r   r   r    prepare~  s^   




zAbrechnungssystem.preparer   start_rechnungsnummerc                 C   s
  t | dds
td|dkr&|| _td| d| jj d|d d	d
 ntj	| j
d| jd }| || _| jjdd d g }z| jD ]}|| | | qGW dS  ty   td z| | W   ty } ztd|  W Y d}~ d}~ww w )z6Phase 2: Rechnungsnummern vergeben + PDF pro Mitglied.rT  Fz5generate_pdfs() ohne vorheriges prepare() aufgerufen.r   zRechnungsnummern-Start: u    (nächste: r  r7   r  r   r   r  c                 S   rF  rG  rH  rI  r   r   r    r    rJ  z1Abrechnungssystem.generate_pdfs.<locals>.<lambda>rK  u%   Fehler in generate_pdfs – rollback.zRollback fehlgeschlagen: N)r`  RuntimeErrorr   r   r   r   r  r   r   r   rz   r   r  r   r  rY   r  r   r   r  )r   rX  r  createdre  e_rbr   r   r    generate_pdfs  sB   


zAbrechnungssystem.generate_pdfsc                 C   s(   t | dds
td|   |   dS )z9Phase 3: SEPA-Lastschrift- und -Gutschrift-XML schreiben.rT  Fz6build_sepa_xml() ohne vorheriges prepare() aufgerufen.N)r`  rY  r  r  r   r   r   r    build_sepa_xml  s   z Abrechnungssystem.build_sepa_xmlc                 C   sX  t | dds
tddD ]}t| |st| |d qdD ]}t| |s)t| |d qg g }}| jD ]C}z |jdkr<W q2| |}|d	u rGW q2|jdkrN|n|| W q2 tyu } zt	
d
t |dd d|  W Y d	}~q2d	}~ww | jdks| jdkrz	| || W d	S  ty } zt	d|  W Y d	}~d	S d	}~ww t	d d	S )u6   Phase 4: Kassabuch-Einträge + Sammelzeilen schreiben.rT  Fz7write_kassabuch() ohne vorheriges prepare() aufgerufen.)r   r   r#   )r   r   r}   r   Nu   Kassabuch-Zeile für r+  ?z fehlgeschlagen: z9Kassabuch-Sammelzeilen konnten nicht geschrieben werden: uA   Kassabuch: SEPA nicht mitgelaufen – Sammelzeilen übersprungen.)r`  rY  hasattrra  r   r  r  rY   r   r   r   r   r   r  r   r   )r   attrr  r  re  row_nre_hre_bsr   r   r    write_kassabuch  sB   





z!Abrechnungssystem.write_kassabuch)r   Nrw  )NN)F)r  )r#   r  )Grx  
__module____qualname____doc__r$   r   r   r   r   r   r   rH  r  Seriesr6  listrX  rp  rs  rd   r  r   r  r  r  rb   r  r  r  r  r  r  r  r  r
  r  r  r  staticmethodr  r  r$  rA  rW  r  r  r  r  r  r  r   r  r  r  bytesr  r  r  r  r  r  r  r  r  r  rD  r  rW  r\  r]  rd  r   r   r   r    ry      s    	 :B
27



 


2,F!
%


 W01
"ry   )2rg  r  loggingmathr   randomcollectionsr   r   r   r   typingr   r   pandasr  openpyxlr   r	   openpyxl.stylesr
   r   r   tkintertkr   ImportErrorcore.modelsr   r   r   r   r   r   r   r   
core.utilsr   r   r   basicConfigINFO	getLoggerrx  r   r$   rd   r!   rx   ry   r   r   r   r    <module>   s4    (
x