Skip to content

Commit

Permalink
Merge pull request #36658 from frappe/version-14-hotfix
Browse files Browse the repository at this point in the history
chore: release v14
  • Loading branch information
deepeshgarg007 authored Aug 16, 2023
2 parents ddcf555 + 67c8350 commit 787118d
Show file tree
Hide file tree
Showing 56 changed files with 2,103 additions and 366 deletions.
9 changes: 8 additions & 1 deletion erpnext/accounts/doctype/gl_entry/gl_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,14 @@ def on_update(self):
validate_balance_type(self.account, adv_adj)
validate_frozen_account(self.account, adv_adj)

if frappe.db.get_value("Account", self.account, "account_type") not in [
if (
self.voucher_type == "Journal Entry"
and frappe.get_cached_value("Journal Entry", self.voucher_no, "voucher_type")
== "Exchange Gain Or Loss"
):
return

if frappe.get_cached_value("Account", self.account, "account_type") not in [
"Receivable",
"Payable",
]:
Expand Down
12 changes: 11 additions & 1 deletion erpnext/accounts/doctype/journal_entry/journal_entry.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"engine": "InnoDB",
"field_order": [
"entry_type_and_date",
"is_system_generated",
"title",
"voucher_type",
"naming_series",
Expand Down Expand Up @@ -533,13 +534,22 @@
"label": "Process Deferred Accounting",
"options": "Process Deferred Accounting",
"read_only": 1
},
{
"default": "0",
"depends_on": "eval:doc.is_system_generated == 1;",
"fieldname": "is_system_generated",
"fieldtype": "Check",
"label": "Is System Generated",
"no_copy": 1,
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 176,
"is_submittable": 1,
"links": [],
"modified": "2023-03-01 14:58:59.286591",
"modified": "2023-08-10 14:32:22.366895",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",
Expand Down
55 changes: 34 additions & 21 deletions erpnext/accounts/doctype/journal_entry/journal_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
)
from erpnext.accounts.party import get_party_account
from erpnext.accounts.utils import (
cancel_exchange_gain_loss_journal,
get_account_currency,
get_balance_on,
get_stock_accounts,
Expand Down Expand Up @@ -87,9 +88,8 @@ def on_submit(self):
self.update_invoice_discounting()

def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries

unlink_ref_doc_from_payment_entries(self)
# References for this Journal are removed on the `on_cancel` event in accounts_controller
super(JournalEntry, self).on_cancel()
self.ignore_linked_doctypes = (
"GL Entry",
"Stock Ledger Entry",
Expand Down Expand Up @@ -487,11 +487,12 @@ def validate_against_jv(self):
)

if not against_entries:
frappe.throw(
_(
"Journal Entry {0} does not have account {1} or already matched against other voucher"
).format(d.reference_name, d.account)
)
if self.voucher_type != "Exchange Gain Or Loss":
frappe.throw(
_(
"Journal Entry {0} does not have account {1} or already matched against other voucher"
).format(d.reference_name, d.account)
)
else:
dr_or_cr = "debit" if d.credit > 0 else "credit"
valid = False
Expand Down Expand Up @@ -574,7 +575,9 @@ def validate_reference_doc(self):
else:
party_account = against_voucher[1]

if against_voucher[0] != cstr(d.party) or party_account != d.account:
if (
against_voucher[0] != cstr(d.party) or party_account != d.account
) and self.voucher_type != "Exchange Gain Or Loss":
frappe.throw(
_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}").format(
d.idx,
Expand Down Expand Up @@ -756,25 +759,33 @@ def set_exchange_rate(self):
)
):

# Modified to include the posting date for which to retreive the exchange rate
d.exchange_rate = get_exchange_rate(
self.posting_date,
d.account,
d.account_currency,
self.company,
d.reference_type,
d.reference_name,
d.debit,
d.credit,
d.exchange_rate,
)
ignore_exchange_rate = False
if self.get("flags") and self.flags.get("ignore_exchange_rate"):
ignore_exchange_rate = True

if not ignore_exchange_rate:
# Modified to include the posting date for which to retreive the exchange rate
d.exchange_rate = get_exchange_rate(
self.posting_date,
d.account,
d.account_currency,
self.company,
d.reference_type,
d.reference_name,
d.debit,
d.credit,
d.exchange_rate,
)

if not d.exchange_rate:
frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))

def create_remarks(self):
r = []

if self.flags.skip_remarks_creation:
return

if self.user_remark:
r.append(_("Note: {0}").format(self.user_remark))

Expand Down Expand Up @@ -923,6 +934,8 @@ def make_gl_entries(self, cancel=0, adv_adj=0):
merge_entries=merge_entries,
update_outstanding=update_outstanding,
)
if cancel:
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))

@frappe.whitelist()
def get_balance(self, difference_account=None):
Expand Down
2 changes: 2 additions & 0 deletions erpnext/accounts/doctype/journal_entry/test_journal_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import unittest

import frappe
from frappe.tests.utils import change_settings
from frappe.utils import flt, nowdate

from erpnext.accounts.doctype.account.test_account import get_inventory_account
Expand All @@ -13,6 +14,7 @@


class TestJournalEntry(unittest.TestCase):
@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
def test_journal_entry_with_against_jv(self):
jv_invoice = frappe.copy_doc(test_records[2])
base_jv = frappe.copy_doc(test_records[0])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@
"fieldtype": "Select",
"label": "Reference Type",
"no_copy": 1,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement"
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry"
},
{
"fieldname": "reference_name",
Expand Down Expand Up @@ -284,7 +284,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2022-10-26 20:03:10.906259",
"modified": "2023-06-16 14:11:13.507807",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
Expand Down
2 changes: 1 addition & 1 deletion erpnext/accounts/doctype/payment_entry/payment_entry.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges";

frappe.ui.form.on('Payment Entry', {
onload: function(frm) {
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', "Repost Payment Ledger"];
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', "Journal Entry", "Repost Payment Ledger"];

if(frm.doc.__islocal) {
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
Expand Down
38 changes: 31 additions & 7 deletions erpnext/accounts/doctype/payment_entry/payment_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@
)
from erpnext.accounts.general_ledger import make_gl_entries, process_gl_map
from erpnext.accounts.party import get_party_account
from erpnext.accounts.utils import get_account_currency, get_balance_on, get_outstanding_invoices
from erpnext.accounts.utils import (
cancel_exchange_gain_loss_journal,
get_account_currency,
get_balance_on,
get_outstanding_invoices,
)
from erpnext.controllers.accounts_controller import (
AccountsController,
get_supplier_block_status,
Expand Down Expand Up @@ -61,7 +66,7 @@ def setup_party_account_field(self):
def validate(self):
self.setup_party_account_field()
self.set_missing_values()
self.set_missing_ref_details()
self.set_missing_ref_details(force=True)
self.validate_payment_type()
self.validate_party_details()
self.set_exchange_rate()
Expand Down Expand Up @@ -101,6 +106,7 @@ def on_cancel(self):
"Repost Payment Ledger",
"Repost Payment Ledger Items",
)
super(PaymentEntry, self).on_cancel()
self.make_gl_entries(cancel=1)
self.update_outstanding_amounts()
self.update_advance_paid()
Expand Down Expand Up @@ -361,7 +367,7 @@ def set_source_exchange_rate(self, ref_doc=None):
else:
if ref_doc:
if self.paid_from_account_currency == ref_doc.currency:
self.source_exchange_rate = ref_doc.get("exchange_rate")
self.source_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get("conversion_rate")

if not self.source_exchange_rate:
self.source_exchange_rate = get_exchange_rate(
Expand All @@ -374,7 +380,7 @@ def set_target_exchange_rate(self, ref_doc=None):
elif self.paid_to and not self.target_exchange_rate:
if ref_doc:
if self.paid_to_account_currency == ref_doc.currency:
self.target_exchange_rate = ref_doc.get("exchange_rate")
self.target_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get("conversion_rate")

if not self.target_exchange_rate:
self.target_exchange_rate = get_exchange_rate(
Expand Down Expand Up @@ -783,10 +789,25 @@ def calculate_base_allocated_amount_for_reference(self, d) -> float:
flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount")
)
else:

# Use source/target exchange rate, so no difference amount is calculated.
# then update exchange gain/loss amount in reference table
# if there is an exchange gain/loss amount in reference table, submit a JE for that

exchange_rate = 1
if self.payment_type == "Receive":
exchange_rate = self.source_exchange_rate
elif self.payment_type == "Pay":
exchange_rate = self.target_exchange_rate

base_allocated_amount += flt(
flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount")
)

allocated_amount_in_pe_exchange_rate = flt(
flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
)
d.exchange_gain_loss = base_allocated_amount - allocated_amount_in_pe_exchange_rate
return base_allocated_amount

def set_total_allocated_amount(self):
Expand Down Expand Up @@ -977,6 +998,10 @@ def make_gl_entries(self, cancel=0, adv_adj=0):
gl_entries = self.build_gl_map()
gl_entries = process_gl_map(gl_entries)
make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj)
if cancel:
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
else:
self.make_exchange_gain_loss_journal()

def add_party_gl_entries(self, gl_entries):
if self.party_account:
Expand Down Expand Up @@ -1878,7 +1903,6 @@ def get_payment_entry(
payment_type=None,
reference_date=None,
):
reference_doc = None
doc = frappe.get_doc(dt, dn)
over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (
Expand Down Expand Up @@ -2019,7 +2043,7 @@ def get_payment_entry(
update_accounting_dimensions(pe, doc)

if party_account and bank:
pe.set_exchange_rate(ref_doc=reference_doc)
pe.set_exchange_rate(ref_doc=doc)
pe.set_amounts()

if discount_amount:
Expand Down
43 changes: 21 additions & 22 deletions erpnext/accounts/doctype/payment_entry/test_payment_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ class TestPaymentEntry(FrappeTestCase):
def tearDown(self):
frappe.db.rollback()

def get_journals_for(self, voucher_type: str, voucher_no: str) -> list:
journals = []
if voucher_type and voucher_no:
journals = frappe.db.get_all(
"Journal Entry Account",
filters={"reference_type": voucher_type, "reference_name": voucher_no, "docstatus": 1},
fields=["parent"],
)
return journals

def test_payment_entry_against_order(self):
so = make_sales_order()
pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Cash - _TC")
Expand Down Expand Up @@ -591,21 +601,15 @@ def test_payment_entry_against_si_usd_to_usd_with_deduction_in_base_currency(sel
pe.target_exchange_rate = 45.263
pe.reference_no = "1"
pe.reference_date = "2016-01-01"

pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 94.80,
},
)

pe.save()

self.assertEqual(flt(pe.difference_amount, 2), 0.0)
self.assertEqual(flt(pe.unallocated_amount, 2), 0.0)

# the exchange gain/loss amount is captured in reference table and a separate Journal will be submitted for them
# payment entry will not be generating difference amount
self.assertEqual(flt(pe.references[0].exchange_gain_loss, 2), -94.74)

def test_payment_entry_retrieves_last_exchange_rate(self):
from erpnext.setup.doctype.currency_exchange.test_currency_exchange import (
save_new_records,
Expand Down Expand Up @@ -792,33 +796,28 @@ def test_payment_entry_exchange_gain_loss(self):
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
pe.source_exchange_rate = 55

pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": -500,
},
)
pe.save()

self.assertEqual(pe.unallocated_amount, 0)
self.assertEqual(pe.difference_amount, 0)

self.assertEqual(pe.references[0].exchange_gain_loss, 500)
pe.submit()

expected_gle = dict(
(d[0], d)
for d in [
["_Test Receivable USD - _TC", 0, 5000, si.name],
["_Test Receivable USD - _TC", 0, 5500, si.name],
["_Test Bank USD - _TC", 5500, 0, None],
["_Test Exchange Gain/Loss - _TC", 0, 500, None],
]
)

self.validate_gl_entries(pe.name, expected_gle)

# Exchange gain/loss should have been posted through a journal
exc_je_for_si = self.get_journals_for(si.doctype, si.name)
exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name)

self.assertEqual(exc_je_for_si, exc_je_for_pe)
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 0)

Expand Down
Loading

0 comments on commit 787118d

Please sign in to comment.