From d14a64e71f4cab190bf978946499ffbacbe15cc1 Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Tue, 21 Jan 2025 11:38:56 +0100 Subject: [PATCH] feat: Basic Stancer reconciliation --- .../auto_bank_reconciliation.py | 4 + .../stancer_reconciliation.py | 111 ++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 erpnext/accounts/page/bank_reconciliation/stancer_reconciliation.py diff --git a/erpnext/accounts/page/bank_reconciliation/auto_bank_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/auto_bank_reconciliation.py index 8675e550114..92da042ab96 100644 --- a/erpnext/accounts/page/bank_reconciliation/auto_bank_reconciliation.py +++ b/erpnext/accounts/page/bank_reconciliation/auto_bank_reconciliation.py @@ -12,6 +12,9 @@ from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import BankRe from erpnext.accounts.page.bank_reconciliation.gocardless_reconciliation import ( reconcile_gocardless_payouts, ) +from erpnext.accounts.page.bank_reconciliation.stancer_reconciliation import ( + reconcile_stancer_payouts, +) from erpnext.accounts.page.bank_reconciliation.stripe_reconciliation import ( reconcile_stripe_payouts, ) @@ -39,6 +42,7 @@ def _reconcile_transactions(bank_transactions): reconcile_stripe_payouts(bank_transactions) reconcile_gocardless_payouts(bank_transactions) + reconcile_stancer_payouts(bank_transactions) class AutoBankReconciliation: diff --git a/erpnext/accounts/page/bank_reconciliation/stancer_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/stancer_reconciliation.py new file mode 100644 index 00000000000..3829e89b0ee --- /dev/null +++ b/erpnext/accounts/page/bank_reconciliation/stancer_reconciliation.py @@ -0,0 +1,111 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import re + +import frappe +from frappe.utils import cint +from payments.payment_gateways.doctype.stancer_settings.api import StancerPaymentsAPI, StancerPayoutsAPI + +from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import BankReconciliation + + +def reconcile_stancer_payouts(bank_transactions): + stancer_transactions = [ + transaction + for transaction in bank_transactions + if "iliad payout stancer" in (transaction.get("description") or "").lower() + ] + if not stancer_transactions: + return + + bank_account_number = frappe.get_cached_value( + "Bank Account", stancer_transactions[0].get("bank_account"), "account" + ) + stancer_payment_gateways = frappe.get_list( + "Mode of Payment Account", + filters={"default_account": bank_account_number, "payment_gateway": ("is", "set")}, + pluck="payment_gateway", + ) + + stancer_accounts = frappe.get_all( + "Payment Gateway", + filters={ + "disabled": 0, + "gateway_settings": "Stancer Settings", + "name": ("in", stancer_payment_gateways), + }, + pluck="gateway_controller", + ) + + if not stancer_accounts: + return + + _reconcile_stancer_payouts(bank_transactions=stancer_transactions, stancer_accounts=stancer_accounts) + + +def _reconcile_stancer_payouts(bank_transactions, stancer_accounts): + reconciled_transactions = [] + for stancer_account in stancer_accounts: + stancer_settings = frappe.get_doc("Stancer Settings", stancer_account) + + for bank_transaction in bank_transactions: + if bank_transaction.get("name") not in reconciled_transactions: + bank_reconciliation = StancerReconciliation(stancer_settings, bank_transaction) + bank_reconciliation.reconcile() + if bank_reconciliation.documents: + reconciled_transactions.append(bank_transaction.get("name")) + + +class StancerReconciliation: + def __init__(self, stancer_settings, bank_transaction): + self.stancer_settings = stancer_settings + self.bank_transaction = bank_transaction + self.date = self.bank_transaction.get("date") + self.payments = [] + self.filtered_payout = {} + self.documents = [] + + def reconcile(self): + self.get_payouts_and_transactions() + self.get_payment_references() + + if self.documents: + BankReconciliation([self.bank_transaction], self.documents).reconcile() + + def get_payouts_and_transactions(self): + found_reference = re.search(r"Ild78-Payout-(.*?) Stancer", self.bank_transaction.get("description")) + if found_reference: + reference = found_reference.group(1) + has_more = True + start = 0 + payout_number = None + payouts_api = StancerPayoutsAPI(self.stancer_settings) + while has_more: + payouts = payouts_api.get_list(params={"start": start}) + print("payouts", payouts) + for payout in payouts.get("payouts", []): + print("payout", payout) + if payout.get("reference") == reference: + payout_number = payout.get("id") + has_more = False + break + else: + if payouts.get("range", {}).get("has_more"): + start += cint(payout.get("range", {}).get("limit")) + + if payout_number: + payments = StancerPaymentsAPI(self.stancer_settings).get_list( + params={"payout": payout_number, "limit": 100} + ) + + for payment in payments.get("payments"): + self.payments.append(payment.get("id")) + + def get_payment_references(self): + for payment in self.payments: + payment_entry = frappe.db.get_value( + "Payment Entry", dict(reference_no=payment, docstatus=1, status=("=", "Unreconciled")) + ) + if payment_entry: + self.documents.append(frappe.get_doc("Payment Entry", payment_entry).as_dict()) -- GitLab