Merge pull request #24127 from deepeshgarg007/lcv_multicurrency
feat: Multi currency in landed cost voucher
This commit is contained in:
commit
a16ad3063f
@ -254,7 +254,8 @@ def create_account(**kwargs):
|
||||
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()
|
||||
|
@ -159,8 +159,8 @@ class GLEntry(Document):
|
||||
|
||||
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 @@ class GLEntry(Document):
|
||||
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}")
|
||||
|
@ -583,7 +583,8 @@ class PurchaseInvoice(BuyingController):
|
||||
"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))
|
||||
|
||||
|
@ -468,8 +468,10 @@ 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", "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)
|
||||
|
||||
|
@ -10,6 +10,7 @@ from erpnext.controllers.accounts_controller import validate_conversion_rate, \
|
||||
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 @@ def get_rounded_tax_amount(itemised_tax, precision):
|
||||
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"))
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
||||
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 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
||||
</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 @@ 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.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 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
@ -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",
|
||||
|
@ -9,6 +9,7 @@ from frappe.model.meta import get_field_precision
|
||||
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 @@ class LandedCostVoucher(Document):
|
||||
|
||||
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 @@ class LandedCostVoucher(Document):
|
||||
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 @@ class LandedCostVoucher(Document):
|
||||
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"):
|
||||
|
@ -10,6 +10,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||
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 @@ class TestLandedCostVoucher(unittest.TestCase):
|
||||
|
||||
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 @@ 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)
|
||||
|
@ -288,12 +288,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))
|
||||
|
||||
@ -728,7 +731,13 @@ def get_item_account_wise_additional_cost(purchase_document):
|
||||
|
||||
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 @@ 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
|
||||
|
||||
|
@ -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_indicator_formatter('item_code', function(doc) {
|
||||
@ -95,15 +97,6 @@ frappe.ui.form.on('Stock Entry', {
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
@ -559,7 +552,7 @@ frappe.ui.form.on('Stock Entry', {
|
||||
|
||||
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",
|
||||
@ -738,8 +731,18 @@ var validate_sample_quantity = function(frm, cdt, cdn) {
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -19,6 +19,7 @@ from frappe.model.mapper import get_mapped_doc
|
||||
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 @@ class StockEntry(StockController):
|
||||
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 @@ class StockEntry(StockController):
|
||||
|
||||
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 @@ class StockEntry(StockController):
|
||||
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 @@ class StockEntry(StockController):
|
||||
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 @@ class StockEntry(StockController):
|
||||
"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 @@ class StockEntry(StockController):
|
||||
"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)
|
||||
|
62
erpnext/stock/landed_taxes_and_charges_common.js
Normal file
62
erpnext/stock/landed_taxes_and_charges_common.js
Normal file
@ -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)));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user