Skip to content

Commit

Permalink
Merge pull request #101 from alyf-de/ec-reco-v15
Browse files Browse the repository at this point in the history
fix: Broken EC Reconciliation (frontport #100)
  • Loading branch information
marination authored Jun 18, 2024
2 parents 5bf6369 + dd2df13 commit 6499e1f
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 16 deletions.
8 changes: 5 additions & 3 deletions .github/helper/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ sudo apt update && sudo apt install redis-server libcups2-dev

pip install frappe-bench

git clone https://github.com/frappe/frappe --branch version-14 --depth 1
git clone https://github.com/frappe/frappe --branch version-15 --depth 1
bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench

mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
Expand All @@ -27,11 +27,13 @@ sed -i 's/schedule:/# schedule:/g' Procfile
sed -i 's/socketio:/# socketio:/g' Procfile
sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile

bench get-app payments --branch version-14
bench get-app erpnext --branch version-14
bench get-app payments --branch version-15
bench get-app erpnext --branch version-15
bench get-app hrms --branch version-15
bench get-app banking "${GITHUB_WORKSPACE}"

bench start &> bench_run_logs.txt &
bench new-site --db-root-password root --admin-password admin test_site --install-app erpnext
bench --site test_site install-app hrms
bench --site test_site install-app banking
bench setup requirements --dev
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Copyright (c) 2023, ALYF GmbH and contributors
# For license information, please see license.txt
import json
import datetime
from typing import Union

import frappe
Expand Down Expand Up @@ -71,8 +72,8 @@ def get_bank_transactions(
def create_journal_entry_bts(
bank_transaction_name: str,
reference_number: str = None,
reference_date: str = None,
posting_date: str = None,
reference_date: str | datetime.date = None,
posting_date: str | datetime.date = None,
entry_type: str = None,
second_account: str = None,
mode_of_payment: str = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
create_payment_entry_bts,
)

from hrms.hr.doctype.expense_claim.test_expense_claim import make_expense_claim


class TestBankReconciliationToolBeta(AccountsTestMixin, FrappeTestCase):
@classmethod
Expand Down Expand Up @@ -327,6 +329,58 @@ def test_unpaid_voucher_and_jv_against_transaction(self):
self.assertEqual(bt.status, "Reconciled")
self.assertEqual(bt.unallocated_amount, 0)

def test_unpaid_expense_claims_fully_reconcile(self):
"""
Test if 2 unpaid expense claims fully reconcile against a Bank Transaction.
Test if they are paid and then the PE is reconciled.
"""
bt = create_bank_transaction(
withdrawal=300, reference_no="expense-cl-001234", bank_account=self.bank_account
)
expense_claim = make_expense_claim(
payable_account=frappe.db.get_value(
"Company", bt.company, "default_payable_account"
),
amount=200,
sanctioned_amount=200,
company=bt.company,
account="Travel Expenses - _TC",
)
expense_claim_2 = make_expense_claim(
payable_account=frappe.db.get_value(
"Company", bt.company, "default_payable_account"
),
amount=100,
sanctioned_amount=100,
company=bt.company,
account="Travel Expenses - _TC",
)
reconcile_vouchers(
bt.name,
json.dumps(
[
{"payment_doctype": "Expense Claim", "payment_name": expense_claim.name},
{"payment_doctype": "Expense Claim", "payment_name": expense_claim_2.name},
]
),
)

bt.reload()
expense_claim.reload()
expense_claim_2.reload()
self.assertEqual(
bt.payment_entries[0].allocated_amount, 300
) # one PE against 2 expense claims
self.assertEqual(len(bt.payment_entries), 1)
self.assertEqual(bt.unallocated_amount, 0)

self.assertEqual(expense_claim.total_amount_reimbursed, 200)
self.assertEqual(expense_claim_2.total_amount_reimbursed, 100)

pe = get_pe_references([expense_claim.name, expense_claim_2.name])
self.assertEqual(pe[0].allocated_amount, 200)
self.assertEqual(pe[1].allocated_amount, 100)


def get_pe_references(vouchers: list):
return frappe.get_all(
Expand Down
2 changes: 1 addition & 1 deletion banking/klarna_kosma_integration/test_kosma.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def test_transactions_creation(self):
self.assertEqual(get_count("Bank Transaction"), 17)
self.assertEqual(test_transn_doc.withdrawal, 3242.29)
self.assertEqual(test_transn_doc.deposit, 0.0)
self.assertEqual(test_transn_doc.status, "Settled")
self.assertEqual(test_transn_doc.status, "Unreconciled")
self.assertEqual(test_transn_doc.date, getdate("2022-12-03"))
self.assertEqual(test_transn_doc.bank_party_name, "Hans Mustermann")
self.assertEqual(test_transn_doc.bank_party_iban, "DE18000000006636981175")
Expand Down
8 changes: 0 additions & 8 deletions banking/klarna_kosma_integration/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,6 @@ def new_bank_transaction(account: str, transaction: Dict) -> bool:
debit = 0 if is_credit else float(amount)
credit = float(amount) if is_credit else 0

state_map = {
"PROCESSED": "Settled",
"PENDING": "Pending",
"CANCELED": "Settled", # TODO: is this status ok ? Should we even consider making cancelled/failed records
"FAILED": "Settled",
}
status = state_map[transaction.get("state")]
transaction_id = transaction.get("transaction_id")

if not transaction_id and transaction.get("state") == "PENDING":
Expand All @@ -283,7 +276,6 @@ def new_bank_transaction(account: str, transaction: Dict) -> bool:
{
"doctype": "Bank Transaction",
"date": getdate(transaction.get("value_date") or transaction.get("date")),
"status": status,
"bank_account": account,
"deposit": credit,
"withdrawal": debit,
Expand Down
10 changes: 8 additions & 2 deletions banking/overrides/bank_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ def make_pe_against_invoices(self, invoices_to_bill):
)
payment_entry.posting_date = self.date
payment_entry.reference_no = self.reference_number or first_invoice[DOCNAME]
payment_entry.reference_date = self.date

# clear references to allocate invoices correctly with splits
payment_entry.references = []
Expand Down Expand Up @@ -154,14 +155,18 @@ def make_pe_against_invoices(self, invoices_to_bill):
def prepare_invoices_to_split(self, invoices):
invoices_to_split = []
for invoice in invoices:
is_expense_claim = invoice[DOCTYPE] == "Expense Claim"
total_field = "grand_total" if is_expense_claim else "base_grand_total"
due_date_field = "posting_date" if is_expense_claim else "due_date"

invoice_data = frappe.db.get_value(
invoice[DOCTYPE],
invoice[DOCNAME],
[
"name as voucher_no",
"posting_date",
"base_grand_total as invoice_amount",
"due_date",
f"{total_field} as invoice_amount",
f"{due_date_field} as due_date",
],
as_dict=True,
)
Expand All @@ -172,6 +177,7 @@ def prepare_invoices_to_split(self, invoices):
return invoices_to_split

def validate_invoices_to_bill(self, invoices_to_bill):
"""Validate if the invoices are of the same doctype and party."""
unique_doctypes = {invoice[DOCTYPE] for invoice in invoices_to_bill}
if len(unique_doctypes) > 1:
frappe.throw(
Expand Down

0 comments on commit 6499e1f

Please sign in to comment.