mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-24 15:39:20 +00:00
Merge pull request #29115 from deepeshgarg007/deferred_revenue_multi_currency_jv
fix: Deferred revenue booking for multi currency invoices via JV
This commit is contained in:
@@ -254,11 +254,13 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
|||||||
enable_check = "enable_deferred_revenue" \
|
enable_check = "enable_deferred_revenue" \
|
||||||
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
|
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
|
||||||
|
|
||||||
|
accounts_frozen_upto = frappe.get_cached_value('Accounts Settings', 'None', 'acc_frozen_upto')
|
||||||
|
|
||||||
def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
|
def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
|
||||||
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
|
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
|
||||||
if not (start_date and end_date): return
|
if not (start_date and end_date): return
|
||||||
|
|
||||||
account_currency = get_account_currency(item.expense_account)
|
account_currency = get_account_currency(item.expense_account or item.income_account)
|
||||||
if doc.doctype == "Sales Invoice":
|
if doc.doctype == "Sales Invoice":
|
||||||
against, project = doc.customer, doc.project
|
against, project = doc.customer, doc.project
|
||||||
credit_account, debit_account = item.income_account, item.deferred_revenue_account
|
credit_account, debit_account = item.income_account, item.deferred_revenue_account
|
||||||
@@ -279,6 +281,10 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
|||||||
if not amount:
|
if not amount:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# check if books nor frozen till endate:
|
||||||
|
if getdate(end_date) >= getdate(accounts_frozen_upto):
|
||||||
|
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
|
||||||
|
|
||||||
if via_journal_entry:
|
if via_journal_entry:
|
||||||
book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
|
book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
|
||||||
base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
|
base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
|
||||||
@@ -406,8 +412,6 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
|
|||||||
'account': credit_account,
|
'account': credit_account,
|
||||||
'credit': base_amount,
|
'credit': base_amount,
|
||||||
'credit_in_account_currency': amount,
|
'credit_in_account_currency': amount,
|
||||||
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
|
|
||||||
'party': against,
|
|
||||||
'account_currency': account_currency,
|
'account_currency': account_currency,
|
||||||
'reference_name': doc.name,
|
'reference_name': doc.name,
|
||||||
'reference_type': doc.doctype,
|
'reference_type': doc.doctype,
|
||||||
@@ -420,8 +424,6 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
|
|||||||
'account': debit_account,
|
'account': debit_account,
|
||||||
'debit': base_amount,
|
'debit': base_amount,
|
||||||
'debit_in_account_currency': amount,
|
'debit_in_account_currency': amount,
|
||||||
'party_type': 'Customer' if doc.doctype == 'Sales Invoice' else 'Supplier',
|
|
||||||
'party': against,
|
|
||||||
'account_currency': account_currency,
|
'account_currency': account_currency,
|
||||||
'reference_name': doc.name,
|
'reference_name': doc.name,
|
||||||
'reference_type': doc.doctype,
|
'reference_type': doc.doctype,
|
||||||
|
|||||||
@@ -407,13 +407,14 @@ class JournalEntry(AccountsController):
|
|||||||
debit_or_credit = 'Debit' if d.debit else 'Credit'
|
debit_or_credit = 'Debit' if d.debit else 'Credit'
|
||||||
party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no,
|
party_account = get_deferred_booking_accounts(d.reference_type, d.reference_detail_no,
|
||||||
debit_or_credit)
|
debit_or_credit)
|
||||||
|
against_voucher = ['', against_voucher[1]]
|
||||||
else:
|
else:
|
||||||
if d.reference_type == "Sales Invoice":
|
if d.reference_type == "Sales Invoice":
|
||||||
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
|
party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1]
|
||||||
else:
|
else:
|
||||||
party_account = against_voucher[1]
|
party_account = against_voucher[1]
|
||||||
|
|
||||||
if (against_voucher[0] != d.party or party_account != d.account):
|
if (against_voucher[0] != cstr(d.party) or party_account != d.account):
|
||||||
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
|
frappe.throw(_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}")
|
||||||
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
|
.format(d.idx, field_dict.get(d.reference_type)[0], field_dict.get(d.reference_type)[1],
|
||||||
d.reference_type, d.reference_name))
|
d.reference_type, d.reference_name))
|
||||||
@@ -478,13 +479,22 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
def set_against_account(self):
|
def set_against_account(self):
|
||||||
accounts_debited, accounts_credited = [], []
|
accounts_debited, accounts_credited = [], []
|
||||||
for d in self.get("accounts"):
|
if self.voucher_type in ('Deferred Revenue', 'Deferred Expense'):
|
||||||
if flt(d.debit > 0): accounts_debited.append(d.party or d.account)
|
for d in self.get('accounts'):
|
||||||
if flt(d.credit) > 0: accounts_credited.append(d.party or d.account)
|
if d.reference_type == 'Sales Invoice':
|
||||||
|
field = 'customer'
|
||||||
|
else:
|
||||||
|
field = 'supplier'
|
||||||
|
|
||||||
for d in self.get("accounts"):
|
d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
|
||||||
if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
|
else:
|
||||||
if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
|
for d in self.get("accounts"):
|
||||||
|
if flt(d.debit > 0): accounts_debited.append(d.party or d.account)
|
||||||
|
if flt(d.credit) > 0: accounts_credited.append(d.party or d.account)
|
||||||
|
|
||||||
|
for d in self.get("accounts"):
|
||||||
|
if flt(d.debit > 0): d.against_account = ", ".join(list(set(accounts_credited)))
|
||||||
|
if flt(d.credit > 0): d.against_account = ", ".join(list(set(accounts_debited)))
|
||||||
|
|
||||||
def validate_debit_credit_amount(self):
|
def validate_debit_credit_amount(self):
|
||||||
for d in self.get('accounts'):
|
for d in self.get('accounts'):
|
||||||
|
|||||||
@@ -1781,47 +1781,6 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
|
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
|
||||||
|
|
||||||
def test_deferred_revenue_post_account_freeze_upto_by_admin(self):
|
|
||||||
frappe.set_user("Administrator")
|
|
||||||
|
|
||||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
|
||||||
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
|
|
||||||
|
|
||||||
deferred_account = create_account(account_name="Deferred Revenue",
|
|
||||||
parent_account="Current Liabilities - _TC", company="_Test Company")
|
|
||||||
|
|
||||||
item = create_item("_Test Item for Deferred Accounting")
|
|
||||||
item.enable_deferred_revenue = 1
|
|
||||||
item.deferred_revenue_account = deferred_account
|
|
||||||
item.no_of_months = 12
|
|
||||||
item.save()
|
|
||||||
|
|
||||||
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_save=True)
|
|
||||||
si.items[0].enable_deferred_revenue = 1
|
|
||||||
si.items[0].service_start_date = "2019-01-10"
|
|
||||||
si.items[0].service_end_date = "2019-03-15"
|
|
||||||
si.items[0].deferred_revenue_account = deferred_account
|
|
||||||
si.save()
|
|
||||||
si.submit()
|
|
||||||
|
|
||||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', getdate('2019-01-31'))
|
|
||||||
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', 'System Manager')
|
|
||||||
|
|
||||||
pda1 = frappe.get_doc(dict(
|
|
||||||
doctype='Process Deferred Accounting',
|
|
||||||
posting_date=nowdate(),
|
|
||||||
start_date="2019-01-01",
|
|
||||||
end_date="2019-03-31",
|
|
||||||
type="Income",
|
|
||||||
company="_Test Company"
|
|
||||||
))
|
|
||||||
|
|
||||||
pda1.insert()
|
|
||||||
self.assertRaises(frappe.ValidationError, pda1.submit)
|
|
||||||
|
|
||||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
|
||||||
frappe.db.set_value('Accounts Settings', None, 'frozen_accounts_modifier', None)
|
|
||||||
|
|
||||||
def test_fixed_deferred_revenue(self):
|
def test_fixed_deferred_revenue(self):
|
||||||
deferred_account = create_account(account_name="Deferred Revenue",
|
deferred_account = create_account(account_name="Deferred Revenue",
|
||||||
parent_account="Current Liabilities - _TC", company="_Test Company")
|
parent_account="Current Liabilities - _TC", company="_Test Company")
|
||||||
@@ -2482,6 +2441,74 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
frappe.db.set_value('Accounts Settings', None, 'over_billing_allowance', over_billing_allowance)
|
frappe.db.set_value('Accounts Settings', None, 'over_billing_allowance', over_billing_allowance)
|
||||||
|
|
||||||
|
def test_multi_currency_deferred_revenue_via_journal_entry(self):
|
||||||
|
deferred_account = create_account(account_name="Deferred Revenue",
|
||||||
|
parent_account="Current Liabilities - _TC", company="_Test Company")
|
||||||
|
|
||||||
|
acc_settings = frappe.get_single('Accounts Settings')
|
||||||
|
acc_settings.book_deferred_entries_via_journal_entry = 1
|
||||||
|
acc_settings.submit_journal_entries = 1
|
||||||
|
acc_settings.save()
|
||||||
|
|
||||||
|
item = create_item("_Test Item for Deferred Accounting")
|
||||||
|
item.enable_deferred_expense = 1
|
||||||
|
item.deferred_revenue_account = deferred_account
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
si = create_sales_invoice(customer='_Test Customer USD', currency='USD',
|
||||||
|
item=item.name, qty=1, rate=100, conversion_rate=60, do_not_save=True)
|
||||||
|
|
||||||
|
si.set_posting_time = 1
|
||||||
|
si.posting_date = '2019-01-01'
|
||||||
|
si.debit_to = '_Test Receivable USD - _TC'
|
||||||
|
si.items[0].enable_deferred_revenue = 1
|
||||||
|
si.items[0].service_start_date = "2019-01-01"
|
||||||
|
si.items[0].service_end_date = "2019-03-30"
|
||||||
|
si.items[0].deferred_expense_account = deferred_account
|
||||||
|
si.save()
|
||||||
|
si.submit()
|
||||||
|
|
||||||
|
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', getdate('2019-01-31'))
|
||||||
|
|
||||||
|
pda1 = frappe.get_doc(dict(
|
||||||
|
doctype='Process Deferred Accounting',
|
||||||
|
posting_date=nowdate(),
|
||||||
|
start_date="2019-01-01",
|
||||||
|
end_date="2019-03-31",
|
||||||
|
type="Income",
|
||||||
|
company="_Test Company"
|
||||||
|
))
|
||||||
|
|
||||||
|
pda1.insert()
|
||||||
|
pda1.submit()
|
||||||
|
|
||||||
|
expected_gle = [
|
||||||
|
["Sales - _TC", 0.0, 2089.89, "2019-01-28"],
|
||||||
|
[deferred_account, 2089.89, 0.0, "2019-01-28"],
|
||||||
|
["Sales - _TC", 0.0, 1887.64, "2019-02-28"],
|
||||||
|
[deferred_account, 1887.64, 0.0, "2019-02-28"],
|
||||||
|
["Sales - _TC", 0.0, 2022.47, "2019-03-15"],
|
||||||
|
[deferred_account, 2022.47, 0.0, "2019-03-15"]
|
||||||
|
]
|
||||||
|
|
||||||
|
gl_entries = gl_entries = frappe.db.sql("""select account, debit, credit, posting_date
|
||||||
|
from `tabGL Entry`
|
||||||
|
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
|
||||||
|
order by posting_date asc, account asc""", (si.items[0].name, si.posting_date), as_dict=1)
|
||||||
|
|
||||||
|
for i, gle in enumerate(gl_entries):
|
||||||
|
self.assertEqual(expected_gle[i][0], gle.account)
|
||||||
|
self.assertEqual(expected_gle[i][1], gle.credit)
|
||||||
|
self.assertEqual(expected_gle[i][2], gle.debit)
|
||||||
|
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
|
||||||
|
|
||||||
|
acc_settings = frappe.get_single('Accounts Settings')
|
||||||
|
acc_settings.book_deferred_entries_via_journal_entry = 0
|
||||||
|
acc_settings.submit_journal_entriessubmit_journal_entries = 0
|
||||||
|
acc_settings.save()
|
||||||
|
|
||||||
|
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
||||||
|
|
||||||
def get_sales_invoice_for_e_invoice():
|
def get_sales_invoice_for_e_invoice():
|
||||||
si = make_sales_invoice_for_ewaybill()
|
si = make_sales_invoice_for_ewaybill()
|
||||||
si.naming_series = 'INV-2020-.#####'
|
si.naming_series = 'INV-2020-.#####'
|
||||||
|
|||||||
@@ -185,8 +185,6 @@ class AccountsController(TransactionBase):
|
|||||||
frappe.throw(_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx))
|
frappe.throw(_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx))
|
||||||
elif getdate(self.posting_date) > getdate(d.service_end_date):
|
elif getdate(self.posting_date) > getdate(d.service_end_date):
|
||||||
frappe.throw(_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx))
|
frappe.throw(_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx))
|
||||||
elif getdate(self.posting_date) > getdate(d.service_start_date):
|
|
||||||
frappe.throw(_("Row #{0}: Service Start Date cannot be before Invoice Posting Date").format(d.idx))
|
|
||||||
|
|
||||||
def validate_invoice_documents_schedule(self):
|
def validate_invoice_documents_schedule(self):
|
||||||
self.validate_payment_schedule_dates()
|
self.validate_payment_schedule_dates()
|
||||||
|
|||||||
Reference in New Issue
Block a user