From 5a2030c2f22e1af5eeedcab053978758826dd34b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 17 Dec 2020 09:21:11 +0530 Subject: [PATCH] fix: Add server side methods and other fixes --- erpnext/controllers/accounts_controller.py | 2 +- .../landed_cost_voucher.js | 19 ++++--- .../landed_cost_voucher.py | 38 ++++++++++++-- .../test_landed_cost_voucher.py | 51 +++++++++++++++++-- .../purchase_receipt/purchase_receipt.py | 16 ++++-- 5 files changed, 106 insertions(+), 20 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 32c5d3a3b1..49c9f8c9d0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -442,7 +442,7 @@ class AccountsController(TransactionBase): account_currency = get_account_currency(gl_dict.account) if gl_dict.account and self.doctype not in ["Journal Entry", - "Period Closing Voucher", "Payment Entry"]: + "Period Closing Voucher", "Payment Entry", "Purchase Receipt", "Purchase Invoice"]: self.validate_account_currency(gl_dict.account, account_currency) set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js index a5ac60c4f5..82b5242704 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js @@ -96,9 +96,9 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({ set_total_taxes_and_charges: function() { var total_taxes_and_charges = 0.0; $.each(this.frm.doc.taxes || [], function(i, d) { - total_taxes_and_charges += flt(d.base_amount) + total_taxes_and_charges += flt(d.base_amount); }); - cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges); + this.frm.set_value("total_taxes_and_charges", total_taxes_and_charges); }, set_applicable_charges_for_item: function() { @@ -148,11 +148,16 @@ frappe.ui.form.on('Landed Cost Voucher', { } }, + onload: function(frm) { + let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; + frm.set_currency_labels(["total_taxes_and_charges"], company_currency); + }, + set_exchange_rate: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - if(row.account_currency == company_currency) { + if (row.account_currency == company_currency) { row.exchange_rate = 1; } else if (!row.exchange_rate || row.exchange_rate == 1) { frappe.call({ @@ -164,12 +169,12 @@ frappe.ui.form.on('Landed Cost Voucher', { company: frm.doc.company }, callback: function(r) { - if(r.message) { + if (r.message) { frappe.model.set_value(cdt, cdn, "exchange_rate", r.message); } } - }) - } + }); + }; }, set_base_amount: function(frm, cdt, cdn) { @@ -177,7 +182,7 @@ frappe.ui.form.on('Landed Cost Voucher', { frappe.model.set_value(cdt, cdn, "base_amount", flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row))); } -}) +}); frappe.ui.form.on('Landed Cost Taxes and Charges', { expense_account: function(frm, cdt, cdn) { diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index e39208991b..519077afa4 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -40,14 +40,17 @@ class LandedCostVoucher(Document): def validate(self): self.check_mandatory() + self.validate_purchase_receipts() + self.set_account_currency() self.set_exchange_rate() self.set_amounts_in_company_currency() + self.set_total_taxes_and_charges() if not self.get("items"): self.get_items_from_purchase_receipts() - else: - self.validate_applicable_charges_for_item() - self.validate_purchase_receipts() - self.set_total_taxes_and_charges() + + self.set_applicable_charges_on_item() + self.validate_applicable_charges_for_item() + def check_mandatory(self): if not self.get("purchase_receipts"): @@ -78,6 +81,33 @@ class LandedCostVoucher(Document): def set_total_taxes_and_charges(self): self.total_taxes_and_charges = sum([flt(d.base_amount) for d in self.get("taxes")]) + def set_applicable_charges_on_item(self): + if self.get('taxes'): + total_item_cost = 0.0 + total_charges = 0.0 + item_count = 0 + based_on_field = frappe.scrub(self.distribute_charges_based_on) + + for item in self.get('items'): + total_item_cost += item.get(based_on_field) + + for item in self.get('items'): + item.applicable_charges = flt(flt(item.get(based_on_field)) * flt(self.total_taxes_and_charges) / flt(total_item_cost), + item.precision('applicable_charges')) + total_charges += item.applicable_charges + item_count += 1 + + if total_charges != self.total_taxes_and_charges: + diff = self.total_taxes_and_charges - total_charges + self.get('items')[item_count - 1].applicable_charges += diff + + def set_account_currency(self): + company_currency = erpnext.get_company_currency(self.company) + for d in self.get('taxes'): + if not d.account_currency: + account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency') + d.account_currency = account_currency or company_currency + def set_exchange_rate(self): company_currency = erpnext.get_company_currency(self.company) for d in self.get('taxes'): diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 3f2c5daf66..79d306b5db 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -10,6 +10,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ import set_perpetual_inventory, get_gl_entries, test_records as pr_test_records, make_purchase_receipt from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.account.test_account import get_inventory_account +from erpnext.accounts.doctype.account.test_account import create_account class TestLandedCostVoucher(unittest.TestCase): def test_landed_cost_voucher(self): @@ -27,7 +28,7 @@ class TestLandedCostVoucher(unittest.TestCase): }, fieldname=["qty_after_transaction", "stock_value"], as_dict=1) - submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) pr_lc_value = frappe.db.get_value("Purchase Receipt Item", {"parent": pr.name}, "landed_cost_voucher_amount") self.assertEqual(pr_lc_value, 25.0) @@ -89,7 +90,7 @@ class TestLandedCostVoucher(unittest.TestCase): }, fieldname=["qty_after_transaction", "stock_value"], as_dict=1) - submit_landed_cost_voucher("Purchase Invoice", pi.name, pi.company) + create_landed_cost_voucher("Purchase Invoice", pi.name, pi.company) pi_lc_value = frappe.db.get_value("Purchase Invoice Item", {"parent": pi.name}, "landed_cost_voucher_amount") @@ -137,7 +138,7 @@ class TestLandedCostVoucher(unittest.TestCase): serial_no_rate = frappe.db.get_value("Serial No", "SN001", "purchase_rate") - submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company) serial_no = frappe.db.get_value("Serial No", "SN001", ["warehouse", "purchase_rate"], as_dict=1) @@ -160,7 +161,7 @@ class TestLandedCostVoucher(unittest.TestCase): }) pr.submit() - lcv = submit_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22) + lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22) self.assertEqual(lcv.items[0].applicable_charges, 41.07) self.assertEqual(lcv.items[2].applicable_charges, 41.08) @@ -206,6 +207,46 @@ class TestLandedCostVoucher(unittest.TestCase): self.assertEqual(pr.items[0].landed_cost_voucher_amount, 100) self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100) + def test_multi_currency_lcv(self): + ## Create USD Shipping charges_account + usd_shipping = create_account(account_name="Shipping Charges USD", + parent_account="Duties and Taxes - TCP1", company="_Test Company with perpetual inventory", + account_currency="USD") + + pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", + supplier_warehouse = "Stores - TCP1") + pr.submit() + + lcv = make_landed_cost_voucher(company = pr.company, receipt_document_type = "Purchase Receipt", + receipt_document=pr.name, charges=100, do_not_save=True) + + lcv.append("taxes", { + "description": "Shipping Charges", + "expense_account": usd_shipping, + "amount": 10 + }) + + lcv.save() + lcv.submit() + pr.load_from_db() + + # Considering exchange rate from USD to INR as 62.9 + self.assertEqual(lcv.total_taxes_and_charges, 729) + self.assertEqual(pr.items[0].landed_cost_voucher_amount, 729) + + gl_entries = frappe.get_all("GL Entry", fields=["account", "credit", "credit_in_account_currency"], + filters={"voucher_no": pr.name, "account": ("in", ["Shipping Charges USD - TCP1", "Expenses Included In Valuation - TCP1"])}) + + expected_gl_entries = { + "Shipping Charges USD - TCP1": [629, 10], + "Expenses Included In Valuation - TCP1": [100, 100] + } + + for entry in gl_entries: + amounts = expected_gl_entries.get(entry.account) + self.assertEqual(entry.credit, amounts[0]) + self.assertEqual(entry.credit_in_account_currency, amounts[1]) + def make_landed_cost_voucher(** args): args = frappe._dict(args) ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document) @@ -236,7 +277,7 @@ def make_landed_cost_voucher(** args): return lcv -def submit_landed_cost_voucher(receipt_document_type, receipt_document, company, charges=50): +def create_landed_cost_voucher(receipt_document_type, receipt_document, company, charges=50): ref_doc = frappe.get_doc(receipt_document_type, receipt_document) lcv = frappe.new_doc("Landed Cost Voucher") diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 97e0fa738c..2cb8d2b26d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -279,12 +279,15 @@ class PurchaseReceipt(BuyingController): # Amount added through landed-cost-voucher if d.landed_cost_voucher_amount and landed_cost_entries: for account, amount in iteritems(landed_cost_entries[(d.item_code, d.name)]): + account_currency = get_account_currency(account) gl_entries.append(self.get_gl_dict({ "account": account, + "account_currency": account_currency, "against": warehouse_account[d.warehouse]["account"], "cost_center": d.cost_center, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "credit": flt(amount), + "credit": flt(amount["base_amount"]), + "credit_in_account_currency": flt(amount["amount"]), "project": d.project }, item=d)) @@ -729,9 +732,16 @@ def get_item_account_wise_additional_cost(purchase_document): if item.receipt_document == purchase_document: for account in landed_cost_voucher_doc.taxes: item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {}) - item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, 0.0) - item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account] += \ + item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, { + "amount": 0.0, + "base_amount": 0.0 + }) + + item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["amount"] += \ account.amount * item.get(based_on_field) / total_item_cost + item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["base_amount"] += \ + account.base_amount * item.get(based_on_field) / total_item_cost + return item_account_wise_cost