Merge branch 'develop' into lcv_multicurrency
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index 113bea0..533eda3 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -254,7 +254,8 @@
account_name = kwargs.get('account_name'),
account_type = kwargs.get('account_type'),
parent_account = kwargs.get('parent_account'),
- company = kwargs.get('company')
+ company = kwargs.get('company'),
+ account_currency = kwargs.get('account_currency')
))
account.save()
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index a749f0e..b0a864f 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -159,8 +159,8 @@
if not self.flags.from_repost and not self.voucher_type == 'Period Closing Voucher' \
and self.cost_center and _check_is_group():
- frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot
- be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
+ frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
+ self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
def validate_party(self):
validate_party_frozen_disabled(self.party_type, self.party)
@@ -170,7 +170,7 @@
account_currency = get_account_currency(self.account)
if not self.account_currency:
- self.account_currency = company_currency
+ self.account_currency = account_currency or company_currency
if account_currency != self.account_currency:
frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}")
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index b52678e..3c29561 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -443,7 +443,7 @@
else:
self.stock_received_but_not_billed = None
self.expenses_included_in_valuation = None
-
+
self.negative_expense_to_be_booked = 0.0
gl_entries = []
@@ -457,7 +457,7 @@
self.make_internal_transfer_gl_entries(gl_entries)
gl_entries = make_regional_gl_entries(gl_entries, self)
-
+
gl_entries = merge_similar_entries(gl_entries)
self.make_payment_gl_entries(gl_entries)
@@ -582,7 +582,8 @@
"against": item.expense_account,
"cost_center": item.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": item.project or self.project
}, item=item))
@@ -999,10 +1000,10 @@
self.delete_auto_created_batches()
self.make_gl_entries_on_cancel()
-
+
if self.update_stock == 1:
self.repost_future_sle_and_gle()
-
+
self.update_project()
frappe.db.set(self, 'status', 'Cancelled')
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 0f1aa23..7ac4cfe 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -110,12 +110,12 @@
self.set_inter_company_account()
validate_regional(self)
-
+
validate_einvoice_fields(self)
if self.doctype != 'Material Request':
apply_pricing_rule_on_transaction(self)
-
+
def before_cancel(self):
validate_einvoice_fields(self)
@@ -448,8 +448,10 @@
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", "Stock Entry"]:
self.validate_account_currency(gl_dict.account, account_currency)
+
+ if gl_dict.account and self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]:
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"),
self.company_currency)
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 8dd2e5b..76309f8 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -10,6 +10,7 @@
validate_taxes_and_charges, validate_inclusive_tax
from erpnext.stock.get_item_details import _get_item_tax_template
from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
+from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
class calculate_taxes_and_totals(object):
def __init__(self, doc):
@@ -758,3 +759,35 @@
for taxes in itemised_tax.values():
for tax_account in taxes:
taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
+
+class init_landed_taxes_and_totals(object):
+ def __init__(self, doc):
+ self.doc = doc
+ self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
+ self.set_account_currency()
+ self.set_exchange_rate()
+ self.set_amounts_in_company_currency()
+
+ def set_account_currency(self):
+ company_currency = erpnext.get_company_currency(self.doc.company)
+ for d in self.doc.get(self.tax_field):
+ 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.doc.company)
+ for d in self.doc.get(self.tax_field):
+ if d.account_currency == company_currency:
+ d.exchange_rate = 1
+ elif not d.exchange_rate or d.exchange_rate == 1 or self.doc.posting_date:
+ d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
+ account_currency=d.account_currency, company=self.doc.company)
+
+ if not d.exchange_rate:
+ frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
+
+ def set_amounts_in_company_currency(self):
+ for d in self.doc.get(self.tax_field):
+ d.amount = flt(d.amount, d.precision("amount"))
+ d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))
\ No newline at end of file
diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
index b24d621..c77b993 100644
--- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
+++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2013-02-22 01:28:02",
"doctype": "DocType",
"document_type": "Document",
@@ -29,6 +30,8 @@
"options": "Item",
"read_only": 1,
"reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1,
"width": "100px"
},
{
@@ -41,6 +44,8 @@
"print_width": "300px",
"read_only": 1,
"reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1,
"width": "120px"
},
{
@@ -50,7 +55,9 @@
"no_copy": 1,
"options": "Purchase Invoice\nPurchase Receipt",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "receipt_document",
@@ -59,25 +66,33 @@
"no_copy": 1,
"options": "receipt_document_type",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "col_break2",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Qty",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "rate",
"fieldtype": "Currency",
"label": "Rate",
"options": "Company:company:default_currency",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "amount",
@@ -88,14 +103,19 @@
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"read_only": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "applicable_charges",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Applicable Charges",
- "options": "Company:company:default_currency"
+ "options": "Company:company:default_currency",
+ "read_only_depends_on": "eval:parent.distribute_charges_based_on != 'Distribute Manually'",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "purchase_receipt_item",
@@ -104,22 +124,30 @@
"label": "Purchase Receipt Item",
"no_copy": 1,
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
- "options": "Cost Center"
+ "options": "Cost Center",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
- "label": "Accounting Dimensions"
+ "label": "Accounting Dimensions",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "dimension_col_break",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "0",
@@ -128,12 +156,15 @@
"fieldtype": "Check",
"hidden": 1,
"label": "Is Fixed Asset",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
}
],
"idx": 1,
"istable": 1,
- "modified": "2020-09-18 17:26:09.703215",
+ "links": [],
+ "modified": "2021-01-25 23:09:23.322282",
"modified_by": "Administrator",
"module": "Stock",
"name": "Landed Cost Item",
diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
index 64331c7..4fcdb4c 100644
--- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
+++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
@@ -6,8 +6,11 @@
"engine": "InnoDB",
"field_order": [
"expense_account",
+ "account_currency",
+ "exchange_rate",
"description",
"col_break3",
+ "base_amount",
"amount"
],
"fields": [
@@ -28,7 +31,7 @@
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Amount",
- "options": "Company:company:default_currency",
+ "options": "account_currency",
"reqd": 1
},
{
@@ -38,13 +41,33 @@
"in_list_view": 1,
"label": "Expense Account",
"mandatory_depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))",
- "options": "Account",
- "print_hide": 1
+ "options": "Account"
+ },
+ {
+ "fieldname": "account_currency",
+ "fieldtype": "Link",
+ "label": "Account Currency",
+ "options": "Currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "exchange_rate",
+ "fieldtype": "Float",
+ "label": "Exchange Rate",
+ "precision": "9"
+ },
+ {
+ "fieldname": "base_amount",
+ "fieldtype": "Currency",
+ "label": "Base Amount",
+ "options": "Company:company:default_currency",
+ "read_only": 1
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-12-04 00:22:14.373312",
+ "modified": "2020-12-26 01:07:23.233604",
"modified_by": "Administrator",
"module": "Stock",
"name": "Landed Cost Taxes and Charges",
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 5de1352..1abbc35 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
@@ -1,6 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
+{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
frappe.provide("erpnext.stock");
@@ -29,20 +30,9 @@
this.frm.add_fetch("receipt_document", "supplier", "supplier");
this.frm.add_fetch("receipt_document", "posting_date", "posting_date");
this.frm.add_fetch("receipt_document", "base_grand_total", "grand_total");
-
- this.frm.set_query("expense_account", "taxes", function() {
- return {
- query: "erpnext.controllers.queries.tax_account_query",
- filters: {
- "account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
- "company": me.frm.doc.company
- }
- };
- });
-
},
- refresh: function(frm) {
+ refresh: function() {
var help_content =
`<br><br>
<table class="table table-bordered" style="background-color: #f9f9f9;">
@@ -72,6 +62,11 @@
</table>`;
set_field_options("landed_cost_help", help_content);
+
+ if (this.frm.doc.company) {
+ let company_currency = frappe.get_doc(":Company", this.frm.doc.company).default_currency;
+ this.frm.set_currency_labels(["total_taxes_and_charges"], company_currency);
+ }
},
get_items_from_purchase_receipts: function() {
@@ -97,34 +92,36 @@
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.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() {
var me = this;
if(this.frm.doc.taxes.length) {
-
var total_item_cost = 0.0;
var based_on = this.frm.doc.distribute_charges_based_on.toLowerCase();
- $.each(this.frm.doc.items || [], function(i, d) {
- total_item_cost += flt(d[based_on])
- });
- var total_charges = 0.0;
- $.each(this.frm.doc.items || [], function(i, item) {
- item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
- item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
- total_charges += item.applicable_charges
- });
+ if (based_on != 'distribute manually') {
+ $.each(this.frm.doc.items || [], function(i, d) {
+ total_item_cost += flt(d[based_on])
+ });
- if (total_charges != this.frm.doc.total_taxes_and_charges){
- var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
- this.frm.doc.items.slice(-1)[0].applicable_charges += diff
+ var total_charges = 0.0;
+ $.each(this.frm.doc.items || [], function(i, item) {
+ item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
+ item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
+ total_charges += item.applicable_charges
+ });
+
+ if (total_charges != this.frm.doc.total_taxes_and_charges){
+ var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
+ this.frm.doc.items.slice(-1)[0].applicable_charges += diff
+ }
+ refresh_field("items");
}
- refresh_field("items");
}
},
distribute_charges_based_on: function (frm) {
@@ -134,7 +131,16 @@
items_remove: () => {
this.trigger('set_applicable_charges_for_item');
}
-
});
cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher);
+
+frappe.ui.form.on('Landed Cost Taxes and Charges', {
+ expense_account: function(frm, cdt, cdn) {
+ frm.events.set_account_currency(frm, cdt, cdn);
+ },
+
+ amount: function(frm, cdt, cdn) {
+ frm.events.set_base_amount(frm, cdt, cdn);
+ }
+});
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
index 0149280..059f925 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "naming_series:",
"creation": "2014-07-11 11:33:42.547339",
"doctype": "DocType",
@@ -7,6 +8,9 @@
"field_order": [
"naming_series",
"company",
+ "column_break_2",
+ "posting_date",
+ "section_break_5",
"purchase_receipts",
"purchase_receipt_items",
"get_items_from_purchase_receipts",
@@ -30,7 +34,9 @@
"options": "MAT-LCV-.YYYY.-",
"print_hide": 1,
"reqd": 1,
- "set_only_once": 1
+ "set_only_once": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "company",
@@ -40,24 +46,32 @@
"label": "Company",
"options": "Company",
"remember_last_selected_value": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "purchase_receipts",
"fieldtype": "Table",
"label": "Purchase Receipts",
"options": "Landed Cost Purchase Receipt",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "purchase_receipt_items",
"fieldtype": "Section Break",
- "label": "Purchase Receipt Items"
+ "label": "Purchase Receipt Items",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "get_items_from_purchase_receipts",
"fieldtype": "Button",
- "label": "Get Items From Purchase Receipts"
+ "label": "Get Items From Purchase Receipts",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "items",
@@ -65,42 +79,56 @@
"label": "Purchase Receipt Items",
"no_copy": 1,
"options": "Landed Cost Item",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "sec_break1",
"fieldtype": "Section Break",
- "label": "Applicable Charges"
+ "label": "Applicable Charges",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "taxes",
"fieldtype": "Table",
"label": "Taxes and Charges",
"options": "Landed Cost Taxes and Charges",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_9",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_taxes_and_charges",
"fieldtype": "Currency",
- "label": "Total Taxes and Charges",
+ "label": "Total Taxes and Charges (Company Currency)",
"options": "Company:company:default_currency",
"read_only": 1,
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "col_break1",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "distribute_charges_based_on",
"fieldtype": "Select",
"label": "Distribute Charges Based On",
- "options": "Qty\nAmount",
- "reqd": 1
+ "options": "Qty\nAmount\nDistribute Manually",
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "amended_from",
@@ -109,21 +137,51 @@
"no_copy": 1,
"options": "Landed Cost Voucher",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "sec_break2",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "landed_cost_help",
"fieldtype": "HTML",
- "label": "Landed Cost Help"
+ "label": "Landed Cost Help",
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "default": "Today",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "label": "Posting Date",
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
+ },
+ {
+ "fieldname": "section_break_5",
+ "fieldtype": "Section Break",
+ "hide_border": 1,
+ "show_days": 1,
+ "show_seconds": 1
}
],
"icon": "icon-usd",
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
- "modified": "2019-11-21 15:34:10.846093",
+ "links": [],
+ "modified": "2021-01-25 23:07:30.468423",
"modified_by": "Administrator",
"module": "Stock",
"name": "Landed Cost Voucher",
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 9ec6b89..69a8bf1 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -9,6 +9,7 @@
from frappe.model.document import Document
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.accounts.doctype.account.account import get_account_currency
+from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
class LandedCostVoucher(Document):
def get_items_from_purchase_receipts(self):
@@ -39,13 +40,15 @@
def validate(self):
self.check_mandatory()
+ self.validate_purchase_receipts()
+ init_landed_taxes_and_totals(self)
+ 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.validate_expense_accounts()
- 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"):
@@ -73,21 +76,37 @@
frappe.throw(_("Row {0}: Cost center is required for an item {1}")
.format(item.idx, item.item_code))
- def validate_expense_accounts(self):
- company_currency = erpnext.get_company_currency(self.company)
- for account in self.taxes:
- if get_account_currency(account.expense_account) != company_currency:
- frappe.throw(_("Row {}: Expense account currency should be same as company's default currency.").format(account.idx)
- + _("Please select expense account with account currency as {}.").format(frappe.bold(company_currency)),
- title=_("Invalid Account Currency"))
-
def set_total_taxes_and_charges(self):
- self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")])
+ 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') and self.distribute_charges_based_on != 'Distribute Manually':
+ 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 validate_applicable_charges_for_item(self):
based_on = self.distribute_charges_based_on.lower()
- total = sum([flt(d.get(based_on)) for d in self.get("items")])
+ if based_on != 'distribute manually':
+ total = sum([flt(d.get(based_on)) for d in self.get("items")])
+ else:
+ # consider for proportion while distributing manually
+ total = sum([flt(d.get('applicable_charges')) for d in self.get("items")])
if not total:
frappe.throw(_("Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'").format(based_on))
@@ -153,13 +172,13 @@
docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document,
'item_code': item.item_code }, fields=['name', 'docstatus'])
if not docs or len(docs) != item.qty:
- frappe.throw(_('There are not enough asset created or linked to {0}.').format(item.receipt_document)
- + _('Please create or link {0} Assets with respective document.').format(item.qty))
+ frappe.throw(_('There are not enough asset created or linked to {0}. Please create or link {1} Assets with respective document.').format(
+ item.receipt_document, item.qty))
if docs:
for d in docs:
if d.docstatus == 1:
- frappe.throw(_('{0} {1} has submitted Assets. Remove Item {2} from table to continue.')
- .format(item.receipt_document_type, frappe.bold(item.receipt_document), frappe.bold(item.item_code)))
+ frappe.throw(_('{2} <b>{0}</b> has submitted Assets. Remove Item <b>{1}</b> from table to continue.').format(
+ item.receipt_document, item.item_code, item.receipt_document_type))
def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
for item in receipt_document.get("items"):
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 b97213e..144101c 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 @@
import 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):
@@ -162,8 +163,8 @@
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)
+ self.assertEqual(flt(lcv.items[0].applicable_charges, 2), 41.07)
+ self.assertEqual(flt(lcv.items[2].applicable_charges, 2), 41.08)
def test_multiple_landed_cost_voucher_against_pr(self):
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
@@ -206,6 +207,46 @@
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)
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 61c5310..550c849 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -288,12 +288,15 @@
# 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))
@@ -728,7 +731,13 @@
for lcv in landed_cost_vouchers:
landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent)
- based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
+
+ #Use amount field for total item cost for manually cost distributed LCVs
+ if landed_cost_voucher_doc.distribute_charges_based_on == 'Distribute Manually':
+ based_on_field = 'amount'
+ else:
+ based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
+
total_item_cost = 0
for item in landed_cost_voucher_doc.items:
@@ -738,9 +747,16 @@
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
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index bd2fce8..9aaa669 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -3,6 +3,8 @@
frappe.provide("erpnext.stock");
frappe.provide("erpnext.accounts.dimensions");
+{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
+
frappe.ui.form.on('Stock Entry', {
setup: function(frm) {
frm.set_query('work_order', function() {
@@ -87,15 +89,6 @@
}
});
- frm.set_query("expense_account", "additional_costs", function() {
- return {
- query: "erpnext.controllers.queries.tax_account_query",
- filters: {
- "account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
- "company": frm.doc.company
- }
- };
- });
frm.add_fetch("bom_no", "inspection_required", "inspection_required");
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
@@ -551,7 +544,7 @@
calculate_total_additional_costs: function(frm) {
const total_additional_costs = frappe.utils.sum(
- (frm.doc.additional_costs || []).map(function(c) { return flt(c.amount); })
+ (frm.doc.additional_costs || []).map(function(c) { return flt(c.base_amount); })
);
frm.set_value("total_additional_costs",
@@ -730,8 +723,18 @@
};
frappe.ui.form.on('Landed Cost Taxes and Charges', {
- amount: function(frm) {
- frm.events.calculate_amount(frm);
+ amount: function(frm, cdt, cdn) {
+ frm.events.set_base_amount(frm, cdt, cdn);
+
+ // Adding this check because same table in used in LCV
+ // This causes an error if you try to post an LCV immediately after a Stock Entry
+ if (frm.doc.doctype == 'Stock Entry') {
+ frm.events.calculate_amount(frm);
+ }
+ },
+
+ expense_account: function(frm, cdt, cdn) {
+ frm.events.set_account_currency(frm, cdt, cdn);
}
});
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 9ced492..d623d5c 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -19,6 +19,7 @@
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
from erpnext.accounts.general_ledger import process_gl_map
+from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
import json
from six import string_types, itervalues, iteritems
@@ -195,7 +196,7 @@
and (sed.t_warehouse is null or sed.t_warehouse = '')""", self.project, as_list=1)
amount = amount[0][0] if amount else 0
- additional_costs = frappe.db.sql(""" select ifnull(sum(sed.amount), 0)
+ additional_costs = frappe.db.sql(""" select ifnull(sum(sed.base_amount), 0)
from
`tabStock Entry` se, `tabLanded Cost Taxes and Charges` sed
where
@@ -445,6 +446,7 @@
def calculate_rate_and_amount(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
self.set_basic_rate(reset_outgoing_rate, raise_error_if_no_rate)
+ init_landed_taxes_and_totals(self)
self.distribute_additional_costs()
self.update_valuation_rate()
self.set_total_incoming_outgoing_value()
@@ -533,7 +535,7 @@
if not any([d.item_code for d in self.items if d.t_warehouse]):
self.additional_costs = []
- self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")])
+ self.total_additional_costs = sum([flt(t.base_amount) for t in self.get("additional_costs")])
if self.purpose in ("Repack", "Manufacture"):
incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.is_finished_item])
@@ -773,13 +775,19 @@
for d in self.get("items"):
if d.t_warehouse:
item_account_wise_additional_cost.setdefault((d.item_code, d.name), {})
- item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, 0.0)
+ item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, {
+ "amount": 0.0,
+ "base_amount": 0.0
+ })
multiply_based_on = d.basic_amount if total_basic_amount else d.qty
- item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account] += \
+ item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["amount"] += \
flt(t.amount * multiply_based_on) / divide_based_on
+ item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["base_amount"] += \
+ flt(t.base_amount * multiply_based_on) / divide_based_on
+
if item_account_wise_additional_cost:
for d in self.get("items"):
for account, amount in iteritems(item_account_wise_additional_cost.get((d.item_code, d.name), {})):
@@ -790,7 +798,8 @@
"against": d.expense_account,
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
- "credit": amount
+ "credit_in_account_currency": flt(amount["amount"]),
+ "credit": flt(amount["base_amount"])
}, item=d))
gl_entries.append(self.get_gl_dict({
@@ -798,7 +807,7 @@
"against": account,
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
- "credit": -1 * amount # put it as negative credit instead of debit purposefully
+ "credit": -1 * amount['base_amount'] # put it as negative credit instead of debit purposefully
}, item=d))
return process_gl_map(gl_entries)
diff --git a/erpnext/stock/landed_taxes_and_charges_common.js b/erpnext/stock/landed_taxes_and_charges_common.js
new file mode 100644
index 0000000..f3f6196
--- /dev/null
+++ b/erpnext/stock/landed_taxes_and_charges_common.js
@@ -0,0 +1,62 @@
+let document_list = ['Landed Cost Voucher', 'Stock Entry'];
+
+document_list.forEach((doctype) => {
+ frappe.ui.form.on(doctype, {
+ refresh: function(frm) {
+ let tax_field = frm.doc.doctype == 'Landed Cost Voucher' ? 'taxes' : 'additional_costs';
+ frm.set_query("expense_account", tax_field, function() {
+ return {
+ filters: {
+ "account_type": ['in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]],
+ "company": frm.doc.company
+ }
+ };
+ });
+ },
+
+ set_account_currency: function(frm, cdt, cdn) {
+ let row = locals[cdt][cdn];
+ if (row.expense_account) {
+ frappe.db.get_value('Account', row.expense_account, 'account_currency', function(value) {
+ frappe.model.set_value(cdt, cdn, "account_currency", value.account_currency);
+ frm.events.set_exchange_rate(frm, cdt, cdn);
+ });
+ }
+ },
+
+ 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) {
+ row.exchange_rate = 1;
+ frm.set_df_property('taxes', 'hidden', 1, row.name, 'exchange_rate');
+ } else if (!row.exchange_rate || row.exchange_rate == 1) {
+ frm.set_df_property('taxes', 'hidden', 0, row.name, 'exchange_rate');
+ frappe.call({
+ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
+ args: {
+ posting_date: frm.doc.posting_date,
+ account: row.expense_account,
+ account_currency: row.account_currency,
+ company: frm.doc.company
+ },
+ callback: function(r) {
+ if (r.message) {
+ frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
+ }
+ }
+ });
+ }
+
+ frm.refresh_field('taxes');
+ },
+
+ set_base_amount: function(frm, cdt, cdn) {
+ let row = locals[cdt][cdn];
+ frappe.model.set_value(cdt, cdn, "base_amount",
+ flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row)));
+ }
+ });
+});
+