From 80c85dd17cf0c0fdcf07d2e0c2151b2852a0c611 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 12 Aug 2021 15:39:07 +0530 Subject: [PATCH 01/11] fix: Account currency validation for first transaction --- erpnext/controllers/accounts_controller.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a09290567e..9641d82501 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -14,7 +14,7 @@ from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_a from erpnext.utilities.transaction_base import TransactionBase from erpnext.buying.utils import update_last_purchase_rate from erpnext.controllers.sales_and_purchase_return import validate_return -from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled +from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled, get_party_gle_currency from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_transaction, apply_pricing_rule_for_free_items, get_applied_pricing_rules) from erpnext.exceptions import InvalidCurrency @@ -113,6 +113,7 @@ class AccountsController(TransactionBase): self.validate_party() self.validate_currency() + self.validate_party_account_currency() if self.doctype == 'Purchase Invoice': self.calculate_paid_amount() @@ -1030,6 +1031,19 @@ class AccountsController(TransactionBase): # at quotation / sales order level and we shouldn't stop someone # from creating a sales invoice if sales order is already created + def validate_party_account_currency(self): + if self.doctype not in ('Sales Invoice', 'Purchase Invoice'): + return + + party_type, party = self.get_party() + party_gle_currency = get_party_gle_currency(party_type, party, self.company) + party_account = self.get('debit_to') if self.doctype == 'Sales Invoice' else self.get('credit_to') + party_account_currency = get_account_currency(party_account) + + if not party_gle_currency and (party_account_currency != self.currency): + frappe.throw(_("Party Account {0} currency and document currency should be same").format(frappe.bold(party_account))) + + def delink_advance_entries(self, linked_doc_name): total_allocated_amount = 0 for adv in self.advances: From f00620a3ca8d507dc947b82abdb5a90c6759bde5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 15 Aug 2021 21:18:13 +0530 Subject: [PATCH 02/11] fix: Add party account validation for journal entry --- .../accounts/doctype/journal_entry/journal_entry.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 937597bc55..44a0182703 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -37,6 +37,7 @@ class JournalEntry(AccountsController): self.validate_party() self.validate_entries_for_advance() self.validate_multi_currency() + self.validate_party_account_currency() self.set_amounts_in_company_currency() self.validate_debit_credit_amount() @@ -433,6 +434,18 @@ class JournalEntry(AccountsController): self.set_exchange_rate() + def validate_party_account_currency(self): + for d in self.get("accounts"): + if self.party_type not in ('Customer', 'Supplier'): + continue + + party_gle_currency = get_party_gle_currency(self.party_type, self.party, self.company) + party_account_currency = get_account_currency(d.account) + + if not party_gle_currency and (party_account_currency != self.currency): + frappe.throw(_("Row {0}: Party Account {1} currency and document currency should be same").format( + frappe.bold(d.idx), frappe.bold(d.account))) + def set_amounts_in_company_currency(self): for d in self.get("accounts"): d.debit_in_account_currency = flt(d.debit_in_account_currency, d.precision("debit_in_account_currency")) From bcaf4752952f4aa7819c057ce61c8bd2ef69df78 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 15 Aug 2021 21:19:18 +0530 Subject: [PATCH 03/11] fix: Healthcare module accounting test cases --- erpnext/healthcare/doctype/lab_test/test_lab_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.py b/erpnext/healthcare/doctype/lab_test/test_lab_test.py index c9f0029ed8..c3847ea3d1 100644 --- a/erpnext/healthcare/doctype/lab_test/test_lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/test_lab_test.py @@ -147,6 +147,7 @@ def create_sales_invoice(): sales_invoice.customer = frappe.db.get_value('Patient', patient, 'customer') sales_invoice.due_date = getdate() sales_invoice.company = '_Test Company' + sales_invoice.currency = 'INR' sales_invoice.debit_to = get_receivable_account('_Test Company') tests = [insulin_resistance_template, blood_test_template] From 0a618817dc76e7da00e8ae16521bf554b5fd9704 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Aug 2021 10:40:26 +0530 Subject: [PATCH 04/11] Revert "fix: Add party account validation for journal entry" This reverts commit f00620a3ca8d507dc947b82abdb5a90c6759bde5. --- .../accounts/doctype/journal_entry/journal_entry.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 44a0182703..937597bc55 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -37,7 +37,6 @@ class JournalEntry(AccountsController): self.validate_party() self.validate_entries_for_advance() self.validate_multi_currency() - self.validate_party_account_currency() self.set_amounts_in_company_currency() self.validate_debit_credit_amount() @@ -434,18 +433,6 @@ class JournalEntry(AccountsController): self.set_exchange_rate() - def validate_party_account_currency(self): - for d in self.get("accounts"): - if self.party_type not in ('Customer', 'Supplier'): - continue - - party_gle_currency = get_party_gle_currency(self.party_type, self.party, self.company) - party_account_currency = get_account_currency(d.account) - - if not party_gle_currency and (party_account_currency != self.currency): - frappe.throw(_("Row {0}: Party Account {1} currency and document currency should be same").format( - frappe.bold(d.idx), frappe.bold(d.account))) - def set_amounts_in_company_currency(self): for d in self.get("accounts"): d.debit_in_account_currency = flt(d.debit_in_account_currency, d.precision("debit_in_account_currency")) From 60915e874d9f466618b313be65023a62591d0f97 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 21 Aug 2021 23:05:48 +0530 Subject: [PATCH 05/11] test: Update test cases for currency change validation --- .../period_closing_voucher/test_period_closing_voucher.py | 6 ++++-- erpnext/controllers/accounts_controller.py | 3 +++ .../doctype/patient_appointment/patient_appointment.py | 3 +++ erpnext/healthcare/doctype/therapy_plan/therapy_plan.py | 3 +++ erpnext/non_profit/doctype/membership/membership.py | 3 ++- erpnext/non_profit/doctype/membership/test_membership.py | 2 +- 6 files changed, 16 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index f17a5c51a0..18f9549fee 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -81,7 +81,8 @@ class TestPeriodClosingVoucher(unittest.TestCase): income_account="Sales - TPC", expense_account="Cost of Goods Sold - TPC", rate=400, - debit_to="Debtors - TPC" + debit_to="Debtors - TPC", + currency="USD" ) create_sales_invoice( company=company, @@ -89,7 +90,8 @@ class TestPeriodClosingVoucher(unittest.TestCase): income_account="Sales - TPC", expense_account="Cost of Goods Sold - TPC", rate=200, - debit_to="Debtors - TPC" + debit_to="Debtors - TPC", + currency="USD" ) pcv = frappe.get_doc({ diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a801245ee5..654e0ebca0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1113,6 +1113,9 @@ class AccountsController(TransactionBase): if self.doctype not in ('Sales Invoice', 'Purchase Invoice'): return + if self.is_opening == 'Yes': + return + party_type, party = self.get_party() party_gle_currency = get_party_gle_currency(party_type, party, self.company) party_account = self.get('debit_to') if self.doctype == 'Sales Invoice' else self.get('credit_to') diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index cdd4ad39c8..12f2fe19e0 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -14,6 +14,7 @@ from frappe.core.doctype.sms_settings.sms_settings import send_sms from erpnext.hr.doctype.employee.employee import is_holiday from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account from erpnext.healthcare.utils import check_fee_validity, get_service_item_and_practitioner_charge, manage_fee_validity +from erpnext import get_company_currency class PatientAppointment(Document): def validate(self): @@ -164,6 +165,8 @@ def create_sales_invoice(appointment_doc): sales_invoice = frappe.new_doc('Sales Invoice') sales_invoice.patient = appointment_doc.patient sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') + sales_invoice.currency = frappe.get_value('Customer', sales_invoice.customer, 'default_currency') \ + or get_company_currency(appointment_doc.currency) sales_invoice.appointment = appointment_doc.name sales_invoice.due_date = getdate() sales_invoice.company = appointment_doc.company diff --git a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py index e209660434..c29f6a0018 100644 --- a/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py +++ b/erpnext/healthcare/doctype/therapy_plan/therapy_plan.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe.utils import flt, today +from erpnext import get_company_currency class TherapyPlan(Document): def validate(self): @@ -73,6 +74,8 @@ def make_sales_invoice(reference_name, patient, company, therapy_plan_template): si.company = company si.patient = patient si.customer = frappe.db.get_value('Patient', patient, 'customer') + si.currency = frappe.get_value('Customer', si.customer, 'default_currency') \ + or get_company_currency(si.company) item = frappe.db.get_value('Therapy Plan Template', therapy_plan_template, 'linked_item') price_list, price_list_currency = frappe.db.get_values('Price List', {'selling': 1}, ['name', 'currency'])[0] diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index b584116df3..88284f8bf3 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -14,6 +14,7 @@ from frappe.utils import add_days, add_years, nowdate, getdate, add_months, get_ from erpnext.non_profit.doctype.member.member import create_member from frappe import _ import erpnext +from erpnext import get_company_currency class Membership(Document): def validate(self): @@ -176,7 +177,7 @@ def make_invoice(membership, member, plan, settings): "doctype": "Sales Invoice", "customer": member.customer, "debit_to": settings.membership_debit_account, - "currency": membership.currency, + "currency": membership.currency or get_company_currency(settings.company), "company": settings.company, "is_pos": 0, "items": [ diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py index 5ad2088fc3..5c876dfedf 100644 --- a/erpnext/non_profit/doctype/membership/test_membership.py +++ b/erpnext/non_profit/doctype/membership/test_membership.py @@ -80,7 +80,7 @@ def make_membership(member, payload={}): "member": member, "membership_status": "Current", "membership_type": "_rzpy_test_milythm", - "currency": "INR", + "currency": "USD", "paid": 1, "from_date": nowdate(), "amount": 100 From c10a22529c8b612ca31d2221700e45184426753d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 22 Aug 2021 18:05:24 +0530 Subject: [PATCH 06/11] test: fix property name --- .../doctype/patient_appointment/patient_appointment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 12f2fe19e0..fb0bf993f2 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -166,7 +166,7 @@ def create_sales_invoice(appointment_doc): sales_invoice.patient = appointment_doc.patient sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer') sales_invoice.currency = frappe.get_value('Customer', sales_invoice.customer, 'default_currency') \ - or get_company_currency(appointment_doc.currency) + or get_company_currency(appointment_doc.company) sales_invoice.appointment = appointment_doc.name sales_invoice.due_date = getdate() sales_invoice.company = appointment_doc.company From 30876a105ca9ebb7f51741068b50385ca5a915c5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 22 Aug 2021 23:48:23 +0530 Subject: [PATCH 07/11] test: Set default currency for patient --- .../doctype/patient_appointment/test_patient_appointment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index 9dd4a2c73c..b18c8ee220 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -206,6 +206,7 @@ def create_patient(): patient = frappe.new_doc('Patient') patient.first_name = '_Test Patient' patient.sex = 'Female' + patient.default_currency = 'INR' patient.save(ignore_permissions=True) patient = patient.name return patient From 417d6abcf48668d053b413dae577f7c775c08416 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 26 Aug 2021 17:13:36 +0530 Subject: [PATCH 08/11] fix: Party account validation in JV --- .../accounts/doctype/journal_entry/journal_entry.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 937597bc55..88e747b44d 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -13,6 +13,7 @@ from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amo from erpnext.accounts.doctype.invoice_discounting.invoice_discounting \ import get_party_account_based_on_invoice_discounting from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts +from erpnext.accounts.party import get_party_gle_currency from six import string_types, iteritems @@ -35,6 +36,7 @@ class JournalEntry(AccountsController): self.clearance_date = None self.validate_party() + self.validate_party_account_currency() self.validate_entries_for_advance() self.validate_multi_currency() self.set_amounts_in_company_currency() @@ -194,6 +196,16 @@ class JournalEntry(AccountsController): if account_type in ["Receivable", "Payable"]: if not (d.party_type and d.party): frappe.throw(_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format(d.idx, d.account)) + + def validate_party_account_currency(self): + for d in self.get("accounts"): + if d.party_type in ('Customer', 'Supplier'): + party_gle_currency = get_party_gle_currency(d.party_type, d.party, self.company) + party_account_currency = get_account_currency(d.account) + party_currency = frappe.db.get_value(d.party_type, d.party, 'default_currency') + + if not party_gle_currency and (party_account_currency != party_currency): + frappe.throw(_("Party Account {0} currency and default party currency should be same").format(frappe.bold(d.account))) def check_credit_limit(self): customers = list(set(d.party for d in self.get("accounts") From 5b8726405d54c2a0601d379dbc78482e19858cda Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 15 May 2022 21:10:52 +0530 Subject: [PATCH 09/11] fix: Remove validation from Journal Entry --- .../doctype/journal_entry/journal_entry.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 1ce927f529..d28c3a8687 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -16,7 +16,7 @@ from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import ( from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import ( get_party_tax_withholding_details, ) -from erpnext.accounts.party import get_party_account, get_party_gle_currency +from erpnext.accounts.party import get_party_account from erpnext.accounts.utils import ( get_account_currency, get_balance_on, @@ -48,7 +48,6 @@ class JournalEntry(AccountsController): self.clearance_date = None self.validate_party() - self.validate_party_account_currency() self.validate_entries_for_advance() self.validate_multi_currency() self.set_amounts_in_company_currency() @@ -343,20 +342,6 @@ class JournalEntry(AccountsController): ) ) - def validate_party_account_currency(self): - for d in self.get("accounts"): - if d.party_type in ("Customer", "Supplier"): - party_gle_currency = get_party_gle_currency(d.party_type, d.party, self.company) - party_account_currency = get_account_currency(d.account) - party_currency = frappe.db.get_value(d.party_type, d.party, "default_currency") - - if not party_gle_currency and (party_account_currency != party_currency): - frappe.throw( - _("Party Account {0} currency and default party currency should be same").format( - frappe.bold(d.account) - ) - ) - def check_credit_limit(self): customers = list( set( From 65232edfd5634cb270a20cab076d2ad3cc644e28 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 May 2022 10:52:58 +0530 Subject: [PATCH 10/11] test: Update test cases --- .../period_closing_voucher/test_period_closing_voucher.py | 7 ++++++- erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index be669a959c..3b938ea1ca 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -79,6 +79,7 @@ class TestPeriodClosingVoucher(unittest.TestCase): rate=400, debit_to="Debtors - TPC", currency="USD", + customer="_Test Customer USD", ) create_sales_invoice( company=company, @@ -88,6 +89,7 @@ class TestPeriodClosingVoucher(unittest.TestCase): rate=200, debit_to="Debtors - TPC", currency="USD", + customer="_Test Customer USD", ) pcv = self.make_period_closing_voucher(submit=False) @@ -121,14 +123,17 @@ class TestPeriodClosingVoucher(unittest.TestCase): surplus_account = create_account() cost_center = create_cost_center("Test Cost Center 1") - create_sales_invoice( + si = create_sales_invoice( company=company, income_account="Sales - TPC", expense_account="Cost of Goods Sold - TPC", cost_center=cost_center, rate=400, debit_to="Debtors - TPC", + currency="USD", + customer="_Test Customer USD", ) + jv = make_journal_entry( account1="Cash - TPC", account2="Sales - TPC", diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 4cf19b4454..3bd0cd2e83 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -752,7 +752,7 @@ class TestPricingRule(unittest.TestCase): title="_Test Pricing Rule with Min Qty - 2", ) - si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1, currency="USD") + si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1) item = si.items[0] item.stock_qty = 1 si.save() From bc3473770947ed5796a8e7d6fa718b2a55f326eb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 25 May 2022 15:42:47 +0530 Subject: [PATCH 11/11] chore: Update test case --- erpnext/accounts/party.py | 15 +++++++++++++++ .../purchase_receipt/test_purchase_receipt.py | 9 +++++++++ 2 files changed, 24 insertions(+) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index db741d97e1..f4a44bd362 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -897,3 +897,18 @@ def get_default_contact(doctype, name): return None else: return None + + +def add_party_account(party_type, party, company, account): + doc = frappe.get_doc(party_type, party) + account_exists = False + for d in doc.get("accounts"): + if d.account == account: + account_exists = True + + if not account_exists: + accounts = {"company": company, "account": account} + + doc.append("accounts", accounts) + + doc.save() diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index ce3bd56d55..7fbfa62939 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1285,6 +1285,14 @@ class TestPurchaseReceipt(FrappeTestCase): from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import ( make_purchase_invoice as create_purchase_invoice, ) + from erpnext.accounts.party import add_party_account + + add_party_account( + "Supplier", + "_Test Supplier USD", + "_Test Company with perpetual inventory", + "_Test Payable USD - TCP1", + ) pi = create_purchase_invoice( company="_Test Company with perpetual inventory", @@ -1293,6 +1301,7 @@ class TestPurchaseReceipt(FrappeTestCase): expense_account="_Test Account Cost for Goods Sold - TCP1", currency="USD", conversion_rate=70, + supplier="_Test Supplier USD", ) pr = create_purchase_receipt(pi.name)