diff --git a/.gitignore b/.gitignore index 2ac1ea1..ca74668 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.gpc *.csv -.venv \ No newline at end of file +.venv +.idea +__pycache__ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5304800 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +# Use an official lightweight Python image. +FROM python:3.9-slim + +# Set environment variables to disable bytecode generation and enable stdout/stderr logging. +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +# Set the working directory. +WORKDIR /app + +# Copy the requirements file and install dependencies. +COPY requirements.txt . +RUN pip install --upgrade pip && \ + pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application code. +COPY . . + +# Expose the port the app runs on. +EXPOSE 9775 + +# Use gunicorn to serve the app. +CMD ["gunicorn", "--bind", "0.0.0.0:9775", "app_main:app"] diff --git a/allegro.py b/allegro.py index 01397aa..16db6ef 100644 --- a/allegro.py +++ b/allegro.py @@ -5,6 +5,13 @@ from gpc import Data, Header, TransactionCode, CURRENCIES_GPC from utils import extract_order_number, extract_numbers, parse_date import mysql.connector import re +import io +import zipfile +from collections import defaultdict + +allegro_orders = None +orders_by_login = None + def load_allegro_orders(): @@ -31,7 +38,18 @@ def load_allegro_orders(): allegro_orders = cursor.fetchall() # results is now a list of dictionaries cursor.close() conn.close() - return allegro_orders + + # Group orders by allegro_login + orders_by_login = defaultdict(list) + for order in allegro_orders: + # Assuming 'allegro_login' is one of the keys in the order dictionary + orders_by_login[order['allegro_login']].append(order) + + # Sort each group's orders by date_purchased (assuming the date format is 'YYYY-MM-DD HH:MM:SS') + for login, order_list in orders_by_login.items(): + order_list.sort(key=lambda x: x['date_purchased']) + + return allegro_orders, orders_by_login def search_allegro_order(allegro_id): global allegro_orders @@ -40,7 +58,38 @@ def search_allegro_order(allegro_id): return order["orders_id"] return 0 -def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, mapping): +def find_first_order_after(allegro_login, payment_date_str, amount): + """ + Returns the first order for the given allegro_login with a date_purchased on or after payment_date_str. + payment_date_str should be in the format 'YYYY-MM-DD HH:MM:SS'. + """ + global allegro_orders, orders_by_login + payment_date = payment_date_str + for order in orders_by_login.get(allegro_login, []): + if (order['date_purchased'] >= payment_date) and (abs(round(float(order['order_total']) * float(order['currency_value']), 2) * 100) == abs(amount)): + return order['orders_id'] + return 0 + +def create_allegro_zip(gpc_lines): + # Prepare file contents (join lines and encode as UTF-8) + payu_content = "".join(gpc_lines["payu"]).encode("utf-8") + przelewy_content = "".join(gpc_lines["przelewy24"]).encode("utf-8") + finance_content = "".join(gpc_lines["allegro finance"]).encode("utf-8") + + # Create an in-memory ZIP file + mem_zip = io.BytesIO() + with zipfile.ZipFile(mem_zip, mode="w", compression=zipfile.ZIP_DEFLATED) as zf: + zf.writestr("allegro_payu.gpc", payu_content) + zf.writestr("allegro_przelewy.gpc", przelewy_content) + zf.writestr("allegro_finance.gpc", finance_content) + mem_zip.seek(0) # Reset pointer to the beginning of the ZIP data + return mem_zip + +def convert_csv_to_gpc_allegro(csv_file_path, gpc_file_path, account_number, currency, mapping): + + global allegro_orders, orders_by_login + + allegro_orders, orders_by_login = load_allegro_orders() gpc_lines = { "payu": [], @@ -129,7 +178,14 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m for row in reader: source_name = row[mapping['source_name']] - reference = search_allegro_order(source_name.split(";")[0].strip()) if ";" in row[mapping['source_name']] else 0 + value_str = row[mapping['source_amount']] + match = re.search(r'[-+]?\d*\.?\d+', value_str) + if match: + source_amount = float(match.group(0)) * 100 + else: + source_amount = 0.0 + + reference = find_first_order_after(source_name.split(";")[0].strip(), parse_date(row[mapping['created_on']]), source_amount) if ";" in row[mapping['source_name']] else 0 transaction_id = reference direction = row[mapping['direction']].lower() if mapping['direction'] is not None else None payer_account = extract_numbers(row[mapping['payer_account']].replace("Rachunek nadawcy: ", "").replace(" ", ""))[:16].ljust(16) if mapping['payer_number_exists'] else 0 @@ -137,12 +193,7 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m if payer_account.strip() == "": payer_account = 0 - value_str = row[mapping['source_amount']] - match = re.search(r'[-+]?\d*\.?\d+', value_str) - if match: - source_amount = float(match.group(0)) * 100 - else: - source_amount = 0.0 + created_on = parse_date(row[mapping['created_on']]) @@ -179,18 +230,20 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m gpc_lines[row[mapping['wallet_source']].lower()].append(gpc_data.to_string()) - with open("allegro_payu.gpc", mode='w', encoding='utf-8') as gpc_file: - gpc_file.writelines(gpc_lines["payu"]) + # with open("allegro_payu.gpc", mode='w', encoding='utf-8') as gpc_file: + # gpc_file.writelines(gpc_lines["payu"]) + # + # with open("allegro_przelewy.gpc", mode='w', encoding='utf-8') as gpc_file: + # gpc_file.writelines(gpc_lines["przelewy24"]) + # + # with open("allegro_finance.gpc", mode='w', encoding='utf-8') as gpc_file: + # gpc_file.writelines(gpc_lines["allegro finance"]) + # + # print(f"GPC file successfully created: allegro_payu.gpc") + # print(f"GPC file successfully created: allegro_przelewy.gpc") + # print(f"GPC file successfully created: allegro_finance.gpc") - with open("allegro_przelewy.gpc", mode='w', encoding='utf-8') as gpc_file: - gpc_file.writelines(gpc_lines["przelewy24"]) - - with open("allegro_finance.gpc", mode='w', encoding='utf-8') as gpc_file: - gpc_file.writelines(gpc_lines["allegro finance"]) - - print(f"GPC file successfully created: allegro_payu.gpc") - print(f"GPC file successfully created: allegro_przelewy.gpc") - print(f"GPC file successfully created: allegro_finance.gpc") + return create_allegro_zip(gpc_lines) # Example mappings @@ -215,8 +268,7 @@ mapping_allegro = { } -allegro_orders = load_allegro_orders() # Example usage: -convert_csv_to_gpc("allegro_payments.csv", "allegro_payments.gpc", account_number=3214724742, currency="PLN", mapping=mapping_allegro) +# convert_csv_to_gpc_allegro("allegro_payments.csv", "allegro_payments.gpc", account_number=3214724742, currency="PLN", mapping=mapping_allegro) diff --git a/app_main.py b/app_main.py new file mode 100644 index 0000000..90980d1 --- /dev/null +++ b/app_main.py @@ -0,0 +1,97 @@ +from flask import Flask, request, send_file, render_template, redirect +import io +import os +from csv2gpc import convert_csv_to_gpc, mapping_sparkasse, mapping_pko, mapping_wise +from allegro import convert_csv_to_gpc_allegro, mapping_allegro +import tempfile +import datetime + +app = Flask(__name__) + + +def convert_avizo_na_jednu_platbu(data): + # Replace with your real conversion code + return data + + +def convert_avizo_full_auto(data): + # Replace with your real conversion code + return data + + +def convert_allegro(data): + # Replace with your real conversion code + return data + + +def create_download_response(file_data: bytes, filename: str, mimetype: str = "text/plain"): + """Wrap the file data in a BytesIO stream and return it as a downloadable response.""" + return send_file( + io.BytesIO(file_data), + download_name=filename, + as_attachment=True, + mimetype=mimetype + ) + + + +@app.route("/", methods=["GET", "POST"]) +def index(): + if request.method == "POST": + if "file" not in request.files or request.files["file"].filename == "": + return redirect(request.url) + + file = request.files["file"] + option = request.form.get("option") + + suffix = os.path.splitext(file.filename)[1] + with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp_input: + file.save(tmp_input.name) + tmp_file_path = tmp_input.name + + day_of_year = int(datetime.date.today().strftime("%j")) + out_filename = f"{option}_vypis_{day_of_year}.gpc" + mimetype = "text/plain" + # Choose the conversion function based on the selected option + if option == "sparkasse": + result_data = convert_csv_to_gpc(tmp_file_path, f"{option}_vypis_{day_of_year}.gpc", + account_number=95850503000221267034, + currency="EUR", mapping=mapping_sparkasse) + elif option == "wise_huf": + result_data = convert_csv_to_gpc(tmp_file_path, f"{option}_vypis_{day_of_year}.gpc", + account_number=330005602964780100, + currency="HUF", mapping=mapping_wise) + elif option == "wise_ron": + result_data = convert_csv_to_gpc(tmp_file_path, f"{option}_vypis_{day_of_year}.gpc", + account_number=5602964780100, + currency="RON", mapping=mapping_wise) + elif option == "pko": + result_data = convert_csv_to_gpc(tmp_file_path, f"{option}_vypis_{day_of_year}.gpc", + account_number=95102013900000630206821286, + currency="PLN", mapping=mapping_pko) + + elif option == "allegro": + result_data = convert_csv_to_gpc_allegro(tmp_file_path, f"{option}_vypis_{day_of_year}.gpc", + account_number=3214724742, + currency="PLN", mapping=mapping_allegro) + return send_file( + result_data, + download_name="allegro_files.zip", + as_attachment=True, + mimetype="application/zip" + ) + + + + if isinstance(result_data, str): + result_bytes = result_data.encode("utf-8") + else: + result_bytes = result_data + + return create_download_response(result_bytes, out_filename, mimetype) + + return render_template("index.html") + + +if __name__ == '__main__': + app.run(debug=True) diff --git a/wise.py b/csv2gpc.py similarity index 92% rename from wise.py rename to csv2gpc.py index 55a8576..944024e 100644 --- a/wise.py +++ b/csv2gpc.py @@ -90,10 +90,12 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m gpc_lines.append(gpc_data.to_string()) - with open(gpc_file_path, mode='w', encoding='utf-8') as gpc_file: - gpc_file.writelines(gpc_lines) + # with open(gpc_file_path, mode='w', encoding='utf-8') as gpc_file: + # gpc_file.writelines(gpc_lines) - print(f"GPC file successfully created: {gpc_file_path}") + file_content = "".join(gpc_lines) + print(f"GPC file content successfully created for: {gpc_file_path}") + return file_content.encode("utf-8") # Example mappings mapping_wise = { @@ -148,6 +150,6 @@ mapping_sparkasse = { } # Example usage: -convert_csv_to_gpc("leden-2025-huf.csv", "wise-huf-2025.gpc", account_number=330005602964780100, currency="HUF", mapping=mapping_wise) +# convert_csv_to_gpc("leden-2025-huf.csv", "wise-huf-2025.gpc", account_number=330005602964780100, currency="HUF", mapping=mapping_wise) # convert_csv_to_gpc("pko_input.csv", "pko_output.gpc", account_number=95102013900000630206821286, currency="PLN", mapping=mapping_pko) # convert_csv_to_gpc("sparkasse_input.csv", "sparkasse_output.gpc", account_number=95850503000221267034, currency="EUR", mapping=mapping_sparkasse) diff --git a/paynl_aviza.py b/paynl_aviza.py index 9a078ef..592f942 100644 --- a/paynl_aviza.py +++ b/paynl_aviza.py @@ -4,7 +4,7 @@ import chardet from datetime import datetime from gpc import Data, Header, TransactionCode, CURRENCIES_GPC from utils import extract_order_number, extract_numbers, parse_date - +from decimal import Decimal, ROUND_HALF_UP def load_bank_transactions(csv_file): """ @@ -72,12 +72,21 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m if payer_account != 0: if payer_account.strip() == "": payer_account = 0 - source_amount = float(row[mapping['source_amount']].replace(',', '.')) * 100 # Convert to cents created_on = parse_date(row[mapping['created_on']]) + # Convert the value using Decimal instead of float + source_str = row[mapping['source_amount']].replace(',', '.') + source_amount = Decimal(source_str) + + # Quantize to 2 decimal places (rounding if necessary) + source_amount = source_amount.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) + + # Multiply by 100 to get cents (if that's what you need) and convert to integer + source_amount_cents = int(source_amount * 100) + # Determine transaction type if(direction is None): - if source_amount > 0: + if source_amount_cents > 0: transaction_code = TransactionCode.CREDIT else: transaction_code = TransactionCode.DEBET @@ -88,14 +97,14 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m transaction_code = TransactionCode.CREDIT # Convert currency - currency_code = CURRENCIES_GPC.get("CZK", "0000") + currency_code = "100" + str(transaction_code.value) # Create GPC Data object gpc_data = Data( account=account_number, payer_account=payer_account, no=transaction_id, - balance=source_amount, + balance=source_amount_cents, code=transaction_code, variable=int(reference) if reference.isdigit() else 0, constant_symbol=0, @@ -107,7 +116,8 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m ) gpc_lines.append(gpc_data.to_string()) - total_payout += source_amount + total_payout += source_amount_cents + # break # add fees # fees_data = Data( @@ -132,7 +142,7 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m # vyuctovani row payout_data = Data( account=account_number, - payer_account=0, + payer_account=2801379531, no=0, balance=total_payout, code=TransactionCode.DEBET, @@ -141,8 +151,8 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m constant_symbol=0, bank_code=0, specific_symbol=0, - client_name="", - currency=CURRENCIES_GPC.get("CZK", "0000"), + client_name="CG vyuctovani", + currency="1001", date=parse_date(corresponding_transaction['Datum']) ) @@ -158,9 +168,9 @@ def convert_csv_to_gpc(csv_file_path, gpc_file_path, account_number, currency, m new_balance=0, new_sign='+', turnover_debet=total_payout, - turnover_debet_sign='0', + turnover_debet_sign='+', turnover_credit=total_payout, - turnover_credit_sign='0', + turnover_credit_sign='+', transaction_list_no=3, date=datetime.now() ) @@ -194,6 +204,6 @@ mapping_paynl = { transactions_df = load_bank_transactions("bank_statement.csv") # Example usage: -convert_csv_to_gpc("Specification clearing 2024-05-10.csv", "avizo_paynl_test.gpc", account_number=2801379531, currency="EUR", mapping=mapping_paynl) +convert_csv_to_gpc("Specification clearing 2024-05-10.csv", "avizo_paynl_test.gpc", account_number=3498710000999117, currency="EUR", mapping=mapping_paynl) # convert_csv_to_gpc("pko_input.csv", "pko_output.gpc", account_number=95102013900000630206821286, currency="PLN", mapping=mapping_pko) # convert_csv_to_gpc("sparkasse_input.csv", "sparkasse_output.gpc", account_number=95850503000221267034, currency="EUR", mapping=mapping_sparkasse) diff --git a/requirements.txt b/requirements.txt index cf9dafd..8cbb279 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ chardet~=5.2.0 mysql-connector-python~=9.2.0 -pandas~=2.2.3 \ No newline at end of file +pandas~=2.2.3 +flask \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..6105db4 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,129 @@ + + +
+ +DE 9585 0503 0002 2126 7034
+.gpc vypis v EUR
+PL 95102013900000630206821286
+.gpc vypis v PLN
+.gpc vypis ALPZ, ALPU, ALAP
+330005602964780100
+.gpc vypis v HUF
+RO33 BREL 0005 6029 6478 0100
+.gpc vypis v RON
+