finalize mt940 format
This commit is contained in:
parent
f5f1bae719
commit
491eb038ca
264
app/app.py
264
app/app.py
@ -76,10 +76,14 @@ def get_merged_df(table_name):
|
||||
stripe_adjustment['norm_date'] = pd.to_datetime(stripe_adjustment['Created'], format='%Y-%m-%d %H:%M')
|
||||
stripe_adjustment['norm_amount'] = stripe_adjustment['Amount'].astype(str).str.replace(',', '.').astype(float)
|
||||
stripe_adjustment['norm_zweck'] = "Korrekturen"
|
||||
stripe_adjustment['norm_name'] = "Verrechnung Korrekturen"
|
||||
stripe_adjustment['norm_currency'] = stripe_adjustment['Currency'].astype(str).str.upper()
|
||||
|
||||
stripe_stripeFee['norm_date'] = pd.to_datetime(stripe_stripeFee['Created'], format='%Y-%m-%d %H:%M')
|
||||
stripe_stripeFee['norm_amount'] = stripe_stripeFee['Amount'].astype(str).str.replace(',', '.').astype(float)
|
||||
stripe_stripeFee['norm_zweck'] = "Stripe"
|
||||
stripe_stripeFee['norm_name'] = "Verrechnung Stripe"
|
||||
stripe_stripeFee['norm_currency'] = stripe_stripeFee['Currency'].astype(str).str.upper()
|
||||
|
||||
# Extract the “py_…” token from stripe_refund description
|
||||
stripe_refund['norm_payment_id'] = stripe_refund['Description'].str.extract(r'(py_[A-Za-z0-9]+)')
|
||||
@ -92,6 +96,7 @@ def get_merged_df(table_name):
|
||||
stripe_charge['norm_date'] = pd.to_datetime(stripe_charge['Created'], format='%Y-%m-%d %H:%M')
|
||||
stripe_charge['norm_amount'] = stripe_charge['Amount'].astype(str).str.replace(',', '.').astype(float)
|
||||
stripe_charge['norm_email'] = stripe_charge['Customer Email'].fillna('').astype(str)
|
||||
stripe_charge['norm_currency'] = stripe_charge['Currency'].astype(str).str.upper()
|
||||
stripe_charge['norm_name'] = stripe_charge.apply(
|
||||
lambda r: r['Customer Name'] or r['Details'], axis=1
|
||||
)
|
||||
@ -110,6 +115,7 @@ def get_merged_df(table_name):
|
||||
raisenow['norm_amount'] = raisenow['Betrag'].astype(float)
|
||||
raisenow['norm_email'] = raisenow['E-Mail-Adresse'].astype(str)
|
||||
raisenow['norm_name'] = raisenow['Vorname'].astype(str) + ' ' + raisenow['Nachname'].astype(str)
|
||||
raisenow['norm_currency'] = raisenow['Währung'].astype(str).str.upper()
|
||||
|
||||
# start with two‐step assignment
|
||||
raisenow['norm_zweck'] = raisenow.apply(
|
||||
@ -244,7 +250,7 @@ def get_merged_df(table_name):
|
||||
srow = stripe_charge.loc[s_idx].to_dict()
|
||||
rrow = raisenow.loc[r_idx].to_dict()
|
||||
# drop any overlapping keys so we never get suffixes
|
||||
for k in ['norm_amount','norm_name','norm_date','norm_email','idx_stripe']:
|
||||
for k in ['norm_name','norm_date','norm_email','norm_amount','norm_currency','idx_stripe']:
|
||||
rrow.pop(k, None)
|
||||
# now combine so stripe values win for those keys, and raisenow adds its own columns
|
||||
merged = {**srow, **rrow}
|
||||
@ -252,7 +258,7 @@ def get_merged_df(table_name):
|
||||
|
||||
combined = pd.DataFrame(merged_rows)
|
||||
|
||||
starting_columns = ['norm_name', 'norm_date', 'norm_email', 'norm_amount', 'norm_zweck']
|
||||
starting_columns = ['norm_name', 'norm_date', 'norm_email', 'norm_amount', 'norm_currency', 'norm_zweck']
|
||||
# reorder columns to put the most important ones first
|
||||
combined = pd.concat([
|
||||
combined[starting_columns],
|
||||
@ -274,11 +280,15 @@ def get_merged_df(table_name):
|
||||
stripe_only = stripe_charge[~stripe_charge['idx_stripe'].isin(used)]
|
||||
result = pd.concat([combined, stripe_only, stripe_adjustment, stripe_stripeFee], ignore_index=True)
|
||||
# add the Stripe fees to the end of the table
|
||||
# Set the timestamp to the last day of the month used by the dataset
|
||||
latest_date = pd.to_datetime(result['norm_date']).max()
|
||||
total_timestamp = pd.Timestamp(year=latest_date.year, month=latest_date.month, day=1) + pd.offsets.MonthEnd(0)
|
||||
total_timestamp = total_timestamp.replace(hour=23, minute=59, second=59, microsecond=0)
|
||||
new_rows = [
|
||||
{'norm_zweck': 'Buchungsgebühren', 'norm_amount': total_stripe_charge_fees * (-1)},
|
||||
{'norm_zweck': 'Rückbuchungsgebühren', 'norm_amount': total_stripe_refund_fees * (-1)},
|
||||
{'norm_zweck': 'Korrekturgebühren', 'norm_amount': total_stripe_adjustment_fees * (-1)},
|
||||
{'norm_zweck': 'Stripe Gebühren', 'norm_amount': total_stripe_stripeFee_fees * (-1)}
|
||||
{'norm_name': 'Verrechnung Gebühren', 'norm_currency': 'EUR', 'norm_date': total_timestamp, 'norm_zweck': 'Buchungsgebühren', 'norm_amount': total_stripe_charge_fees * (-1)},
|
||||
{'norm_name': 'Verrechnung Gebühren', 'norm_currency': 'EUR', 'norm_date': total_timestamp, 'norm_zweck': 'Rückbuchungsgebühren', 'norm_amount': total_stripe_refund_fees * (-1)},
|
||||
{'norm_name': 'Verrechnung Gebühren', 'norm_currency': 'EUR', 'norm_date': total_timestamp, 'norm_zweck': 'Korrekturgebühren', 'norm_amount': total_stripe_adjustment_fees * (-1)},
|
||||
{'norm_name': 'Verrechnung Gebühren', 'norm_currency': 'EUR', 'norm_date': total_timestamp, 'norm_zweck': 'Stripe Gebühren', 'norm_amount': total_stripe_stripeFee_fees * (-1)}
|
||||
]
|
||||
new_rows_df = pd.DataFrame(new_rows)
|
||||
result = pd.concat([result, new_rows_df], ignore_index=True)
|
||||
@ -292,6 +302,27 @@ def get_merged_df(table_name):
|
||||
for i, row in result.iterrows():
|
||||
if (pd.isna(row.get('norm_email')) or row.get('norm_email') == '') and pd.notna(row.get('E-Mail-Adresse')):
|
||||
result.at[i, 'norm_email'] = f"{row.get('E-Mail-Adresse')}".strip()
|
||||
|
||||
# only keep required columns
|
||||
result = result[[
|
||||
'norm_name', 'norm_date', 'norm_email', 'norm_amount','norm_currency', 'norm_zweck'
|
||||
]]
|
||||
|
||||
# rename columns to match CAMT format
|
||||
result = result.rename(columns={
|
||||
'norm_name': 'name',
|
||||
'norm_date': 'booking_date',
|
||||
'norm_email': 'E-Mail',
|
||||
'norm_amount': 'amount',
|
||||
'norm_currency': 'currency',
|
||||
'norm_zweck': 'remittance'
|
||||
})
|
||||
|
||||
# copy booking_date to value_date
|
||||
result['value_date'] = result['booking_date']
|
||||
|
||||
|
||||
result['credit_debit'] = result['amount'].apply(lambda x: 'CRDT' if x >= 0 else 'DBIT')
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unknown table_name '{table_name}'")
|
||||
@ -349,8 +380,8 @@ def get_table():
|
||||
})
|
||||
|
||||
|
||||
@app.route('/download')
|
||||
def download():
|
||||
@app.route('/download_xlsx')
|
||||
def download_xlsx():
|
||||
sheets = {
|
||||
name: get_merged_df(name)
|
||||
for name in [
|
||||
@ -390,6 +421,33 @@ def download():
|
||||
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
)
|
||||
|
||||
|
||||
@app.route('/download_mt940')
|
||||
def download_mt940():
|
||||
df = get_merged_df('export')
|
||||
|
||||
mt940_string = generate_mt940(
|
||||
df,
|
||||
account_iban = "11223344/55667788",
|
||||
transaction_ref = "REFEXCELEXPORT",
|
||||
statement_number= "00000",
|
||||
opening_balance = 0.0,
|
||||
txn_code = "NMSC",
|
||||
txn_ref = "NONREF",
|
||||
info_prefix = "169?00RAISENOW??20"
|
||||
)
|
||||
|
||||
output = BytesIO()
|
||||
output.write(mt940_string.encode('utf-8'))
|
||||
output.seek(0)
|
||||
|
||||
return send_file(
|
||||
output,
|
||||
as_attachment=True,
|
||||
download_name='export_mt940.txt',
|
||||
mimetype='text/plain'
|
||||
)
|
||||
|
||||
@app.route('/clear_session', methods=['POST'])
|
||||
def clear_session():
|
||||
"""
|
||||
@ -398,98 +456,134 @@ def clear_session():
|
||||
session.clear()
|
||||
return jsonify({'status': 'session cleared'})
|
||||
|
||||
def export_to_special_format(
|
||||
df: pd.DataFrame,
|
||||
reference: str,
|
||||
account: str,
|
||||
statement_number: int,
|
||||
opening_date: datetime,
|
||||
opening_balance: float,
|
||||
currency: str,
|
||||
closing_date: datetime = None,
|
||||
closing_balance: float = None
|
||||
) -> str:
|
||||
def wrap_string(text: str, max_length: int = 65) -> str:
|
||||
"""
|
||||
Convert a DataFrame of transactions into the special SWIFT-like file format.
|
||||
Wraps `text` at exactly `max_length` characters, inserting "\r\n" every max_length chars.
|
||||
Existing line breaks are preserved (each line is wrapped separately).
|
||||
"""
|
||||
wrapped_lines = []
|
||||
# split on any existing newline (handles "\n", "\r\n", etc.)
|
||||
for line in text.splitlines():
|
||||
# chop each line into max_length pieces
|
||||
for i in range(0, len(line), max_length):
|
||||
wrapped_lines.append(line[i : i + max_length])
|
||||
# re-join with Windows-style breaks
|
||||
return "\r\n".join(wrapped_lines)
|
||||
|
||||
def generate_mt940(df: pd.DataFrame,
|
||||
account_iban: str,
|
||||
transaction_ref: str,
|
||||
statement_number: str = None,
|
||||
opening_balance: float = 0.0,
|
||||
txn_code: str = 'NMSC',
|
||||
txn_ref: str = 'NONREF',
|
||||
info_prefix: str = None) -> str:
|
||||
"""
|
||||
Generate an MT940 text statement in the “custom” style shown above.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
df : pd.DataFrame
|
||||
Must contain columns:
|
||||
- 'value_date' (datetime)
|
||||
- 'booking_date' (datetime)
|
||||
- 'dc' (str): 'C' for credit, 'D' for debit
|
||||
- 'amount' (float)
|
||||
- optional 'transaction_code' (str)
|
||||
- optional 'bank_reference' (str)
|
||||
- 'narrative' (str)
|
||||
reference : str
|
||||
Message reference for :20:
|
||||
account : str
|
||||
Account number for :25:
|
||||
statement_number : int
|
||||
Statement sequence for :28C: (will be zero-padded to 5 digits)
|
||||
opening_date : datetime
|
||||
Opening balance date
|
||||
opening_balance : float
|
||||
Opening balance amount (positive)
|
||||
currency : str
|
||||
Three-letter currency code (e.g. 'EUR')
|
||||
closing_date : datetime, optional
|
||||
Closing balance date
|
||||
closing_balance : float, optional
|
||||
Closing balance amount (positive)
|
||||
Columns required:
|
||||
- 'booking_date' (datetime or str YYYY-MM-DD)
|
||||
- 'value_date' (datetime or str YYYY-MM-DD)
|
||||
- 'amount' (float)
|
||||
- 'currency' (str, e.g. 'EUR')
|
||||
- 'credit_debit' (str, 'CRDT' or 'DBIT')
|
||||
- 'remittance' (str, used inside your info_prefix section)
|
||||
- 'name' (str, appended after “?32” in tag 86)
|
||||
account_iban : str
|
||||
Goes into tag 25 exactly as you want it (e.g. "11223344/55667788").
|
||||
transaction_ref : str
|
||||
Tag 20 (e.g. "REFEXCELEXPORT")
|
||||
statement_number : str, optional
|
||||
If given, used verbatim for tag 28C (e.g. "00000"); otherwise falls back to
|
||||
"{transaction_ref}/1"
|
||||
opening_balance : float, optional
|
||||
Starting balance for tag 60F
|
||||
txn_code : str, optional
|
||||
The 3-letter code in your :61: line (default "NMSC")
|
||||
txn_ref : str, optional
|
||||
The literal reference after that code (default "NONREF")
|
||||
info_prefix : str, optional
|
||||
If you set e.g. "169?00RAISENOW??20", your tag 86 lines become
|
||||
169?00RAISENOW??20<remittance> ?32<name>
|
||||
If you leave it `None`, we fall back to a simple `<name> <remittance>` join.
|
||||
|
||||
Returns
|
||||
-------
|
||||
str
|
||||
The formatted file content.
|
||||
A single string with CRLF line endings.
|
||||
"""
|
||||
# normalize & sort
|
||||
df2 = df.copy()
|
||||
df2['value_date'] = pd.to_datetime(df2['value_date'])
|
||||
df2['booking_date'] = pd.to_datetime(df2['booking_date'])
|
||||
df2.sort_values('value_date', inplace=True)
|
||||
|
||||
# constant currency
|
||||
currency = df2['currency'].iat[0]
|
||||
|
||||
lines = []
|
||||
# Header
|
||||
lines.append(f":20:{reference}")
|
||||
lines.append(f":25:{account}")
|
||||
lines.append(f":28C:{statement_number:05d}")
|
||||
# header
|
||||
lines.append(f":20:{transaction_ref}")
|
||||
lines.append(f":25:{account_iban}")
|
||||
if statement_number is None:
|
||||
lines.append(f":28C:{transaction_ref}/1")
|
||||
else:
|
||||
lines.append(f":28C:{statement_number}")
|
||||
|
||||
# Opening balance :60F:
|
||||
od = opening_date.strftime('%y%m%d')
|
||||
ob = f"{opening_balance:,.2f}".replace(',', 'X').replace('.', ',').replace('X', '')
|
||||
lines.append(f":60F:C{od}{currency}{ob}")
|
||||
# opening balance
|
||||
first_dt = df2['value_date'].iat[0]
|
||||
ob_sign = 'C' if opening_balance >= 0 else 'D'
|
||||
ob_amt = abs(opening_balance)
|
||||
ob_str = f"{ob_amt:.2f}".replace('.', ',')
|
||||
lines.append(f":60F:{ob_sign}{first_dt.strftime('%y%m%d')}{currency}{ob_str}")
|
||||
|
||||
# Transactions
|
||||
for _, row in df.iterrows():
|
||||
vd = row['value_date'].strftime('%y%m%d')
|
||||
bd = row['booking_date'].strftime('%m%d')
|
||||
dc = row['dc']
|
||||
amt = f"{row['amount']:,.2f}".replace(',', 'X').replace('.', ',').replace('X', '')
|
||||
tcode = row.get('transaction_code', '')
|
||||
bref = row.get('bank_reference', '')
|
||||
lines.append(f":61:{vd}{bd}{dc}{amt}{tcode}{bref}")
|
||||
lines.append(f":86:{row['narrative']}")
|
||||
# transactions
|
||||
for _, row in df2.iterrows():
|
||||
vd = row['value_date']
|
||||
bd = row['booking_date']
|
||||
sign = 'C' if row['credit_debit']=='CRDT' else 'D'
|
||||
amt = abs(row['amount'])
|
||||
amt_str = f"{amt:.2f}".replace('.', ',')
|
||||
|
||||
# Closing balance :62F:
|
||||
if closing_date and closing_balance is not None:
|
||||
cd = closing_date.strftime('%y%m%d')
|
||||
cb = f"{closing_balance:,.2f}".replace(',', 'X').replace('.', ',').replace('X', '')
|
||||
lines.append(f":62F:C{cd}{currency}{cb}")
|
||||
# :61:YYMMDDMMDD[C|D]amount<txn_code><txn_ref>
|
||||
lines.append(
|
||||
f":61:{vd.strftime('%y%m%d')}"
|
||||
f"{bd.strftime('%m%d')}"
|
||||
f"{sign}{amt_str}"
|
||||
f"{txn_code}{txn_ref}"
|
||||
)
|
||||
|
||||
return "\n".join(lines)
|
||||
# :86: either structured or simple fallback
|
||||
raw_rem = row.get('remittance', '')
|
||||
raw_name = row.get('name', '')
|
||||
rem = '' if pd.isna(raw_rem) else str(raw_rem)
|
||||
name = '' if pd.isna(raw_name) else str(raw_name)
|
||||
|
||||
# Example usage:
|
||||
# df = pd.DataFrame([...])
|
||||
# content = export_to_special_format(
|
||||
# df,
|
||||
# reference='REFEXCELEXPORT',
|
||||
# account='11223344/55667788',
|
||||
# statement_number=0,
|
||||
# opening_date=datetime(2025,3,6),
|
||||
# opening_balance=0.00,
|
||||
# currency='EUR',
|
||||
# closing_date=datetime(2025,3,6),
|
||||
# closing_balance=12048.71
|
||||
# )
|
||||
# with open('statement.txt', 'w') as f:
|
||||
# f.write(content)
|
||||
if info_prefix:
|
||||
# your “169?00RAISENOW??20<remittance> ?32<name>”
|
||||
lines.append(f":86:{info_prefix}{rem} ?32{name}")
|
||||
else:
|
||||
# old-style "<name> <remittance>"
|
||||
info = " ".join(filter(None, [name, rem]))
|
||||
lines.append(f":86:{info}")
|
||||
|
||||
# closing balance
|
||||
net_mv = sum(
|
||||
row['amount'] if row['credit_debit']=='CRDT' else -row['amount']
|
||||
for _, row in df2.iterrows()
|
||||
)
|
||||
closing = opening_balance + net_mv
|
||||
cb_sign = 'C' if closing >= 0 else 'D'
|
||||
cb_amt = abs(closing)
|
||||
cb_str = f"{cb_amt:.2f}".replace('.', ',')
|
||||
last_dt = df2['value_date'].iat[-1]
|
||||
lines.append(f":62F:{cb_sign}{last_dt.strftime('%y%m%d')}{currency}{cb_str}")
|
||||
|
||||
file_str = "\r\n".join(lines)
|
||||
|
||||
return wrap_string(file_str)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@ -11,13 +11,13 @@
|
||||
"Hilfe für Kinderheim in Rumänien": {"description": "Mission „Stephanus“ Arbeit in Rumänien durch Hilfsgütern und Finanzen für das Kinderheim „Debora“ in der Stadt Arad, die von den Gemeinden in Molbergen und Pforzheim unterstütz werden. Projektleiter: Ovidio Tiran Mobil 0151/27631582 htt-info@web.de, Verwendungszweck: „Rumänien Hilfe“"},
|
||||
"Hilfe für Uganda": {"project_ID": "018", "description": "HILFE FÜR UGANDA - Im Oktober 2010 wurde durch A. Konradi eine Partnerorganisation (Tochtergesellschaft) von CDH-Stephanus e. V. „Stephanus Uganda“ im Dorf Seeta-Lukinga (Kyewanise) ins Leben gerufen. Dafür wurde ein Grundstück (12,5 Acre) für die Missionsstation gekauft. Das Hauptziel des Projektes ist es, die Lebensbedingungen der Bevölkerungsgruppe der Region (Mpigi District) im Sinne von Armutsbewältigung; sowie „geistlicher“ und körperlicher Gesundheit nachhaltig zu verbessern. Im März 2011 wurde dort ein Wasserbrunnen zur Versorgung von sauberem Wasser gebohrt. Im Jahr 2013 wurde ein Kinderheim errichtet, das zurzeit 35 waise bzw. benachteiligte Kinder beherbergt. Darüber hinaus wurde dort eine Schule errichtet. Durch unser Angebot von ‚Erziehung und Bildung’ ermöglichen wir es den jungen Menschen, beruflichen Beschränkungen auszuweichen. Projektleiter: Albert Hiller Mobil. 0163/6114900, Jack David Kayindu Mobil 0157/30656004, Verwendungszweck: „Uganda“"},
|
||||
"Hilfe für Reha Zentrum Chasara Lechaim in Israel": {"project_ID": "019", "description": "HILFE REHA-ZENTRUM CHASARA LECHAIM IN ISRAEL - Die Mission Stephanus unterstützt in Israel ein Rehabilitationszentrum für Alkohol- und Drogenabhängige. Im Rehabilitationszentrum leben durchschnittlich 10 Personen. Projektleiter: Erich Dojan, Mobil 0172/1390640, erichdojan@gmx.de, Verwendungszweck: „Chasara-Lechaim“, Infoquelle: Infos werden direkt von Eduard Betiev aus Israel geschickt."},
|
||||
"Hilfe für Kinder Pateschaften in Uganda": {"project_ID": "020", "description": "KIINDER PATENSCHAFTEN IN UGANDA - Patenschaften in Form von Heimpatenschaften und Schulpatenschaften möglich! Projektleiter: Alexander Penkowski Mobil. 0177/4756150, Jack David Kayindu Mobil 0157/30656004, Verwendungszweck: „Kinderpatenschaft Uganda“"},
|
||||
"Hilfe für Kinder Pateschaften in Uganda": {"project_ID": "020", "description": "KIINDER PATENSCHAFTEN IN UGANDA - Patenschaften in Form von Heimpatenschaften und Schulpatenschaften möglich! Projektleiter: Alexander Penkowski Mobil. 0177/4756150, Jack David Kayindu Mobil 0157/30656004, Verwendungszweck: „Kinderpatenschaft Uganda“"},
|
||||
"Hilfe für Gemeinde in Israel": {"project_ID": "021", "description": "HILFE FÜR GEMEINDE IN ISRAEL - Die Mission Stephanus unterstützt in Israel eine Gemeinde und dadurch die Evangelisation und ein Rehabilitationszentrum für Alkohol- und Drogenabhängige. Im Rehabilitationszentrum leben durchschnittlich 5 Personen. Infoquelle: Infos werden direkt aus Israel von Valentin Negura zugeschickt. Projektleiter: Viktor Folz, Mobil 0175/2400696, viktor.folz58@gmail.com, Verwendungszweck: „Israel“, Verantwortliche in Israel: Kisselyov Valery, Leiter der Mission „Teiwat apachamim“, Maalot, Str. Snir 5 / 3 Box 7314, Israel, E-Mail maalot98@yandex.ru und Valentin Negura, der Gemeindeverantwortliche in Maalot"},
|
||||
"Missionar Kellinger": {"project_ID": "022", "description": "Im Januar 2022 reiste das junge Ehepaar Samuel und Eva Kellinger mit ihrem kleinen Sohn nach Uganda. Vor Ort betreuten sie das Stephanus-Projekt. Er leistet zusammen mit einem Pastor Gemeinde und Jugendarbeit. Projektleiter: Matthias Krüger, Verwendungszweck: „Missionar in Afrika“"},
|
||||
"Liebe drängt uns... (Buch": {"project_ID": "023", "description": ""},
|
||||
"Konferen": {"project_ID": "024", "description": ""},
|
||||
"Liebe drängt uns... (Buch": {"project_ID": "023"},
|
||||
"Konferen": {"project_ID": "024"},
|
||||
"Hilfe für Togo": {"project_ID": "025", "description": "PROJEKT TOGO – Hilfe für WESTAFRIKA - Togo (Westafrika) – Hilfe zur Selbsthilfe (Getreideanbau, Viehzucht), Die Gemeinde Ulmen unterstützt das Projekt in Togo in Zusammenarbeit mit dem einheimischen Pastor Williams. Ziel dieses Projekts sind der Gemeindeaufbau und die Evangelisation. Außerdem werden Waisenkinder oder Halbwaisen unterstützt. Projektleiter: Paul Altmann, Tel. 0176 46046914, paulaltmann65@gmail.com, Verwendungszweck: „TOGO“"},
|
||||
"Missionar Hense": {"project_ID": "026", "description": ""},
|
||||
"Missionar Hense": {"project_ID": "026"},
|
||||
"Hilfe für Kurzfristige Projekte - div. Gemeinden": {"project_ID": "034", "description": "Kurzfristige Projekte - Hilfsaktion für bedürftige, kranke oder in Not geratene Menschen, Projektleiter: Wall Nikolaus, 1. Vorsitzender Mobil. 01794740945, CDH-Stephanus e. V., speyer@cdh-stephanus.de, Verwendungszweck: Aktuelle Not, z. B. Hausbrandt Fam. XY"},
|
||||
"Hilfe für Katastrophen und Kriege": {"project_ID": "035", "description": "HILFE FÜR KATASTROPHEN & KRIEGE - Eine Katastrophe ist immer ein unerwartetes Ereignis, bei dem zahlreiche Menschen getötet oder verletzt werden, oder aber ihr Eigentum beschädigt oder zerstört wird. Es gibt Hilfseinsätze nach Naturkatastrophen, technischen Unfällen (Brand, Explosionen), terroristischen Anschlägen und kriegerischen Ereignissen. Es muss sofort gehandelt werden, daher ist eine Rücklage für dieses Projekt notwendig. Projektleiter: Mission Stephanus e. V., speyer@cdh-stephanus.de Tel. 06232-9191555, Verwendungszweck: „Katastrophen & Kriege“ mit einem aktuellen Hinweis z.B. Hochwasser"},
|
||||
"Erdbeben Türkei/Syrien": {"project_ID": "036", "description": "ERDBEBEN IN DER TÜRKEI / SYRIEN - Eine Katastrophe ist immer ein unerwartetes Ereignis, bei dem zahlreiche Menschen getötet oder verletzt werden, oder aber ihr Eigentum beschädigt oder zerstört wird. Es muss sofort gehandelt werden, daher ist eine Rücklage für dieses Projekt notwendig. Projektleiter: Mission Stephanus e. V., speyer@cdh-stephanus.de Tel. 06232-9191555, Verwendungszweck: „Erdbeben Türkei“"},
|
||||
@ -27,7 +27,7 @@
|
||||
"Hilfe für Rybnitza": {"project_ID": "050", "description": "Hilfe für Rybniza/Projektleiter vor Ort Panchina, Projektleiter: Wall Nikolaus, 1. Vorsitzender Mobil. 01794740945, CDH-Stephanus e. V., speyer@cdh-stephanus.de, Verwendungszweck: Verwendungszweck: Panchina"},
|
||||
"Hilfe für Chisinau / Altenheim": {"project_ID": "053", "description": "HILFE FÜR CHISINAU IN MOLDAU - Projektleiter: Wassiljew Jakob Mob. 0162/1077390, wassiljew@icloud.com, Verwendungszweck: „Altenheim Sarepta“"},
|
||||
"Hilfe Save Haus Talita (Äthiopien)": {"description": "Projektstart gescheitert und wird gelöscht"},
|
||||
"Hilfe für Nischnij Novgorod (Russischeföderation)": {"description": ""},
|
||||
"Hilfe für Nischnij Novgorod (Russischeföderation)": {},
|
||||
"Hilfe für Rehazentrum und neue Gemeinde in Nischni Novgorod (Baschmakov Andrej": {"project_ID": "050", "description": "Hilfe für Nischnij Novgorod (Russische Föderation), Projektleiter: Wall Nikolaus, 1. Vorsitzender Mobil. 01794740945, CDH-Stephanus e. V., speyer@cdh-stephanus.de, Verwendungszweck: Nischnij Novgorod"},
|
||||
"Hilfe für Kurzfristige Kranke": {"project_ID": "069", "description": "Hilfe für Kranke (Sofothilfe für Kranke (Medikamente, Prothesen, OPs), Projektleiter: Wall Nikolaus, 1. Vorsitzender Mobil. 01794740945, CDH-Stephanus e. V., speyer@cdh-stephanus.de, Verwendungszweck: Sofothilfe für Kranke (Medikamente, Prothesen, OPs)"},
|
||||
"Hilfe für Haus der Barmerzigkeut „Phildelphia“": {"project_ID": "071", "description": "Haus der Barmherzigkeit „PHiladelphia“ in Krivoj Rog, Ukraine, Projektleiter: Waldemar Just, Mobil. 0152 21554953 oder 06232/98668, CDH-Stephanus e. V., speyer@cdh-stephanus.de, Verwendungszweck: Philadelphia"},
|
||||
@ -40,7 +40,7 @@
|
||||
"Brüderkasse": {"project_ID": "090", "description": "Projektleiter: Wall Nikolaus, 1. Vorsitzender Mobil. 01794740945, CDH-Stephanus e. V., speyer@cdh-stephanus.de"},
|
||||
"Missionsreise": {"project_ID": "095", "description": "Alle Reisen die nicht mit einem bestimmten Projekt verbunden sind."},
|
||||
"Reisen Projekt Verfolgte Christen": {"project_ID": "205", "description": "HILFE FÜR VERFOLGTE CHRISTEN - Dieses Projekt ist für die Unterstützung der verfolgten Christen, für finanzielle und materielle Hilfe, Schulungen von Pastoren und Ausstattung der Gemeinden gedacht. Projektleiter: Alexander Siebert, Mobil 0172/1474429, siebertalexander82@gmail.com, Verwendungszweck: „Verfolgte Christen“"},
|
||||
"Reisen Äthiopie": {"project_ID": "213", "description": ""},
|
||||
"Reisen Äthiopie": {"project_ID": "213"},
|
||||
"Reisen Sri Lanka": {"project_ID": "216", "description": "Missions-Reisegruppen nach Sri Lanka, Projektleiter: Irina Jähger Tel. 0172/1474429, Verwendungszweck: Namen_Vorname_Sri Lanka"},
|
||||
"Reisen Uganda": {"project_ID": "218", "description": "Missions-Reisegruppen nach Uganda, Projektleiter: Irina Jähger Tel. 0172/1474429, Verwendungszweck: Namen_Vorname_Ugandareise"}
|
||||
}
|
||||
|
||||
@ -40,16 +40,17 @@
|
||||
<button type="submit" class="btn btn-primary btn-sm">Upload</button>
|
||||
</form>
|
||||
<button id="download-excel" class="btn btn-success btn-sm me-3">Excel Download</button>
|
||||
<button id="download-mt940" class="btn btn-info btn-sm me-3">MT940 Download</button>
|
||||
<button id="clear-session" class="btn btn-warning btn-sm me-3">Daten löschen</button>
|
||||
<div class="d-flex align-items-center">
|
||||
<label for="table-select" class="form-label me-2 mb-0">Table:</label>
|
||||
<select id="table-select" class="form-select form-select-sm">
|
||||
<option value="export">Export</option>
|
||||
<option value="stripe_import">Stripe Import</option>
|
||||
<option value="raiseNow_import">RaiseNow Import</option>
|
||||
<option value="raisenow_import">RaiseNow Import</option>
|
||||
<option value="merged">Merged</option>
|
||||
<option value="stripe_only">Stripe Only</option>
|
||||
<option value="raisenow_only">RaiseNow Only</option>
|
||||
<option value="export">Export</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -73,7 +74,9 @@
|
||||
<script>
|
||||
const uploadForm = document.getElementById('upload-form');
|
||||
const tableSelect = document.getElementById('table-select');
|
||||
const downloadBtn = document.getElementById('download-excel');
|
||||
const download_xlsx_btn = document.getElementById('download-excel');
|
||||
const download_camt_btn = document.getElementById('download-camt');
|
||||
const download_mt940_btn = document.getElementById('download-mt940');
|
||||
const clearBtn = document.getElementById('clear-session');
|
||||
const loadingOverlay = document.getElementById('loadingOverlay');
|
||||
let table;
|
||||
@ -96,10 +99,10 @@
|
||||
|
||||
tableSelect.addEventListener('change', () => loadTable(tableSelect.value));
|
||||
|
||||
downloadBtn.addEventListener('click', async () => {
|
||||
download_xlsx_btn.addEventListener('click', async () => {
|
||||
showLoading();
|
||||
try {
|
||||
const resp = await fetch('/download');
|
||||
const resp = await fetch('/download_xlsx');
|
||||
if (!resp.ok) throw new Error('Download failed');
|
||||
const blob = await resp.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
@ -117,6 +120,27 @@
|
||||
} finally { hideLoading(); }
|
||||
});
|
||||
|
||||
download_mt940_btn.addEventListener('click', async () => {
|
||||
showLoading();
|
||||
try {
|
||||
const resp = await fetch('/download_mt940');
|
||||
if (!resp.ok) throw new Error('Download failed');
|
||||
const blob = await resp.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
const disposition = resp.headers.get('Content-Disposition') || '';
|
||||
const match = disposition.match(/filename="?([^";]+)"?/);
|
||||
a.download = match ? match[1] : 'mt940.txt';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
} catch (err) {
|
||||
console.error(err); alert(err.message);
|
||||
} finally { hideLoading(); }
|
||||
});
|
||||
|
||||
clearBtn.addEventListener('click', async () => {
|
||||
if (!confirm('Are you sure you want to clear the server session? This will reset all loaded data.')) return;
|
||||
showLoading();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user