feat: Item Taxes based on net rate
This commit is contained in:
parent
865663857c
commit
8a7e283926
@ -54,6 +54,7 @@ class calculate_taxes_and_totals(object):
|
||||
if item.item_code and item.get('item_tax_template'):
|
||||
item_doc = frappe.get_cached_doc("Item", item.item_code)
|
||||
args = {
|
||||
'net_rate': item.net_rate or item.rate,
|
||||
'tax_category': self.doc.get('tax_category'),
|
||||
'posting_date': self.doc.get('posting_date'),
|
||||
'bill_date': self.doc.get('bill_date'),
|
||||
@ -78,7 +79,8 @@ class calculate_taxes_and_totals(object):
|
||||
taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True)
|
||||
|
||||
if item.item_tax_template not in taxes:
|
||||
frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format(
|
||||
item.item_tax_template = taxes[0]
|
||||
frappe.msgprint(_("Row {0}: Item Tax template updated as per validity and rate applied").format(
|
||||
item.idx, frappe.bold(item.item_code)
|
||||
))
|
||||
|
||||
|
@ -12,7 +12,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
|
||||
effective_item_rate = item.blanket_order_rate;
|
||||
}
|
||||
if(item.margin_type == "Percentage"){
|
||||
if(item.margin_type == "Percentage") {
|
||||
item.rate_with_margin = flt(effective_item_rate)
|
||||
+ flt(effective_item_rate) * ( flt(item.margin_rate_or_amount) / 100);
|
||||
} else {
|
||||
@ -73,15 +73,18 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
},
|
||||
|
||||
_calculate_taxes_and_totals: function() {
|
||||
this.validate_conversion_rate();
|
||||
this.calculate_item_values();
|
||||
this.initialize_taxes();
|
||||
this.determine_exclusive_rate();
|
||||
this.calculate_net_total();
|
||||
this.calculate_taxes();
|
||||
this.manipulate_grand_total_for_inclusive_tax();
|
||||
this.calculate_totals();
|
||||
this._cleanup();
|
||||
frappe.run_serially([
|
||||
() => this.validate_conversion_rate(),
|
||||
() => this.calculate_item_values(),
|
||||
() => this.update_item_tax_map(),
|
||||
() => this.initialize_taxes(),
|
||||
() => this.determine_exclusive_rate(),
|
||||
() => this.calculate_net_total(),
|
||||
() => this.calculate_taxes(),
|
||||
() => this.manipulate_grand_total_for_inclusive_tax(),
|
||||
() => this.calculate_totals(),
|
||||
() => this._cleanup()
|
||||
]);
|
||||
},
|
||||
|
||||
validate_conversion_rate: function() {
|
||||
@ -263,6 +266,68 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
|
||||
},
|
||||
|
||||
update_item_tax_map: function() {
|
||||
let me = this;
|
||||
let item_codes = [];
|
||||
let item_rates = {};
|
||||
$.each(this.frm.doc.items || [], function(i, item) {
|
||||
if(item.item_code) {
|
||||
// Use combination of name and item code in case same item is added multiple times
|
||||
item_codes.push([item.item_code, item.name]);
|
||||
item_rates[item.name] = item.net_rate;
|
||||
}
|
||||
});
|
||||
|
||||
if(item_codes.length) {
|
||||
return this.frm.call({
|
||||
method: "erpnext.stock.get_item_details.get_item_tax_info",
|
||||
args: {
|
||||
company: me.frm.doc.company,
|
||||
tax_category: cstr(me.frm.doc.tax_category),
|
||||
item_codes: item_codes,
|
||||
item_rates: item_rates
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$.each(me.frm.doc.items || [], function(i, item) {
|
||||
if(item.name && r.message.hasOwnProperty(item.name)) {
|
||||
item.item_tax_template = r.message[item.name].item_tax_template;
|
||||
item.item_tax_rate = r.message[item.name].item_tax_rate;
|
||||
me.add_taxes_from_item_tax_template(item.item_tax_rate);
|
||||
}
|
||||
else {
|
||||
item.item_tax_template = "";
|
||||
item.item_tax_rate = "{}";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.frm.refresh_fields();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
add_taxes_from_item_tax_template: function(item_tax_map) {
|
||||
let me = this;
|
||||
|
||||
if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
|
||||
if(typeof (item_tax_map) == "string") {
|
||||
item_tax_map = JSON.parse(item_tax_map);
|
||||
}
|
||||
|
||||
$.each(item_tax_map, function(tax, rate) {
|
||||
let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax);
|
||||
if(!found) {
|
||||
let child = frappe.model.add_child(me.frm.doc, "taxes");
|
||||
child.charge_type = "On Net Total";
|
||||
child.account_head = tax;
|
||||
child.rate = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
calculate_taxes: function() {
|
||||
var me = this;
|
||||
this.frm.doc.rounding_adjustment = 0;
|
||||
@ -406,6 +471,11 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
|
||||
let tax_detail = tax.item_wise_tax_detail;
|
||||
let key = item.item_code || item.item_name;
|
||||
|
||||
if(typeof (tax_detail) == "string") {
|
||||
tax.item_wise_tax_detail = JSON.parse(tax.item_wise_tax_detail);
|
||||
tax_detail = tax.item_wise_tax_detail;
|
||||
}
|
||||
|
||||
let item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate;
|
||||
if (tax_detail && tax_detail[key])
|
||||
item_wise_tax_amount += tax_detail[key][1];
|
||||
|
@ -6,6 +6,7 @@ frappe.provide('erpnext.accounts.dimensions');
|
||||
erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
setup: function() {
|
||||
this._super();
|
||||
let me = this;
|
||||
frappe.flags.hide_serial_batch_dialog = true;
|
||||
frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
@ -43,8 +44,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
cur_frm.cscript.calculate_stock_uom_rate(frm, cdt, cdn);
|
||||
});
|
||||
|
||||
|
||||
|
||||
frappe.ui.form.on(this.frm.cscript.tax_table, "rate", function(frm, cdt, cdn) {
|
||||
cur_frm.cscript.calculate_taxes_and_totals();
|
||||
});
|
||||
@ -121,7 +120,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
});
|
||||
|
||||
var me = this;
|
||||
if(this.frm.fields_dict["items"].grid.get_field('batch_no')) {
|
||||
this.frm.set_query("batch_no", "items", function(doc, cdt, cdn) {
|
||||
return me.set_query_for_batch(doc, cdt, cdn);
|
||||
@ -556,6 +554,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
name: me.frm.doc.name,
|
||||
project: item.project || me.frm.doc.project,
|
||||
qty: item.qty || 1,
|
||||
net_rate: item.rate,
|
||||
stock_qty: item.stock_qty,
|
||||
conversion_factor: item.conversion_factor,
|
||||
weight_per_unit: item.weight_per_unit,
|
||||
@ -712,26 +711,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
});
|
||||
},
|
||||
|
||||
add_taxes_from_item_tax_template: function(item_tax_map) {
|
||||
let me = this;
|
||||
|
||||
if(item_tax_map && cint(frappe.defaults.get_default("add_taxes_from_item_tax_template"))) {
|
||||
if(typeof (item_tax_map) == "string") {
|
||||
item_tax_map = JSON.parse(item_tax_map);
|
||||
}
|
||||
|
||||
$.each(item_tax_map, function(tax, rate) {
|
||||
let found = (me.frm.doc.taxes || []).find(d => d.account_head === tax);
|
||||
if(!found) {
|
||||
let child = frappe.model.add_child(me.frm.doc, "taxes");
|
||||
child.charge_type = "On Net Total";
|
||||
child.account_head = tax;
|
||||
child.rate = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
serial_no: function(doc, cdt, cdn) {
|
||||
var me = this;
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
@ -835,9 +814,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
|
||||
frappe.run_serially([
|
||||
() => me.frm.script_manager.trigger("currency"),
|
||||
() => me.update_item_tax_map(),
|
||||
() => me.apply_default_taxes(),
|
||||
() => me.apply_pricing_rule()
|
||||
() => me.apply_pricing_rule(),
|
||||
() => me.calculate_taxes_and_totals()
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -1816,7 +1795,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
item.item_tax_rate = r.message;
|
||||
me.add_taxes_from_item_tax_template(item.item_tax_rate);
|
||||
me.calculate_taxes_and_totals();
|
||||
}
|
||||
}
|
||||
@ -1827,43 +1805,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
},
|
||||
|
||||
update_item_tax_map: function() {
|
||||
var me = this;
|
||||
var item_codes = [];
|
||||
$.each(this.frm.doc.items || [], function(i, item) {
|
||||
if(item.item_code) {
|
||||
item_codes.push(item.item_code);
|
||||
}
|
||||
});
|
||||
|
||||
if(item_codes.length) {
|
||||
return this.frm.call({
|
||||
method: "erpnext.stock.get_item_details.get_item_tax_info",
|
||||
args: {
|
||||
company: me.frm.doc.company,
|
||||
tax_category: cstr(me.frm.doc.tax_category),
|
||||
item_codes: item_codes
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
$.each(me.frm.doc.items || [], function(i, item) {
|
||||
if(item.item_code && r.message.hasOwnProperty(item.item_code)) {
|
||||
if (!item.item_tax_template) {
|
||||
item.item_tax_template = r.message[item.item_code].item_tax_template;
|
||||
item.item_tax_rate = r.message[item.item_code].item_tax_rate;
|
||||
}
|
||||
me.add_taxes_from_item_tax_template(item.item_tax_rate);
|
||||
} else {
|
||||
item.item_tax_template = "";
|
||||
item.item_tax_rate = "{}";
|
||||
}
|
||||
});
|
||||
me.calculate_taxes_and_totals();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
is_recurring: function() {
|
||||
// set default values for recurring documents
|
||||
|
@ -128,6 +128,7 @@ class Item(WebsiteGenerator):
|
||||
self.validate_auto_reorder_enabled_in_stock_settings()
|
||||
self.cant_change()
|
||||
self.update_show_in_website()
|
||||
self.validate_item_tax_net_rate_range()
|
||||
|
||||
if not self.get("__islocal"):
|
||||
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
|
||||
@ -490,6 +491,11 @@ class Item(WebsiteGenerator):
|
||||
if self.disabled:
|
||||
self.show_in_website = False
|
||||
|
||||
def validate_item_tax_net_rate_range(self):
|
||||
for tax in self.get('taxes'):
|
||||
if tax.maximum_net_rate < tax.minimum_net_rate:
|
||||
frappe.throw(_("Row #{0}: Maximum Net Rate cannot be greater than Minimum Net Rate"))
|
||||
|
||||
def update_template_tables(self):
|
||||
template = frappe.get_doc("Item", self.variant_of)
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2013-02-22 01:28:01",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
@ -6,7 +7,9 @@
|
||||
"field_order": [
|
||||
"item_tax_template",
|
||||
"tax_category",
|
||||
"valid_from"
|
||||
"valid_from",
|
||||
"minimum_net_rate",
|
||||
"maximum_net_rate"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -33,11 +36,24 @@
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
"label": "Valid From"
|
||||
},
|
||||
{
|
||||
"fieldname": "maximum_net_rate",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Maximum Net Rate"
|
||||
},
|
||||
{
|
||||
"fieldname": "minimum_net_rate",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Minimum Net Rate"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2020-06-25 01:40:28.859752",
|
||||
"links": [],
|
||||
"modified": "2021-06-03 13:20:06.982303",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item Tax",
|
||||
|
@ -436,18 +436,22 @@ def get_barcode_data(items_list):
|
||||
return itemwise_barcode
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_item_tax_info(company, tax_category, item_codes):
|
||||
def get_item_tax_info(company, tax_category, item_codes, item_rates=None):
|
||||
out = {}
|
||||
if isinstance(item_codes, string_types):
|
||||
item_codes = json.loads(item_codes)
|
||||
|
||||
if isinstance(item_rates, string_types):
|
||||
item_rates = json.loads(item_rates)
|
||||
|
||||
for item_code in item_codes:
|
||||
if not item_code or item_code in out:
|
||||
if not item_code or item_code[1] in out:
|
||||
continue
|
||||
out[item_code] = {}
|
||||
item = frappe.get_cached_doc("Item", item_code)
|
||||
get_item_tax_template({"company": company, "tax_category": tax_category}, item, out[item_code])
|
||||
out[item_code]["item_tax_rate"] = get_item_tax_map(company, out[item_code].get("item_tax_template"), as_json=True)
|
||||
out[item_code[1]] = {}
|
||||
item = frappe.get_cached_doc("Item", item_code[0])
|
||||
args = {"company": company, "tax_category": tax_category, "net_rate": item_rates[item_code[1]]}
|
||||
get_item_tax_template(args, item, out[item_code[1]])
|
||||
out[item_code[1]]["item_tax_rate"] = get_item_tax_map(company, out[item_code[1]].get("item_tax_template"), as_json=True)
|
||||
|
||||
return out
|
||||
|
||||
@ -478,12 +482,13 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False):
|
||||
|
||||
for tax in taxes:
|
||||
tax_company = frappe.get_value("Item Tax Template", tax.item_tax_template, 'company')
|
||||
if tax.valid_from and tax_company == args['company']:
|
||||
if (tax.valid_from or tax.maximum_net_rate) and tax_company == args['company']:
|
||||
# In purchase Invoice first preference will be given to supplier invoice date
|
||||
# if supplier date is not present then posting date
|
||||
validation_date = args.get('transaction_date') or args.get('bill_date') or args.get('posting_date')
|
||||
|
||||
if getdate(tax.valid_from) <= getdate(validation_date):
|
||||
if getdate(tax.valid_from) <= getdate(validation_date) \
|
||||
and is_within_valid_range(args, tax):
|
||||
taxes_with_validity.append(tax)
|
||||
else:
|
||||
if tax_company == args['company']:
|
||||
@ -502,12 +507,25 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False):
|
||||
if not taxes_with_validity and (not taxes_with_no_validity):
|
||||
return None
|
||||
|
||||
# do not change if already a valid template
|
||||
if args.get('item_tax_template') in taxes:
|
||||
return arg.get('item_tax_template')
|
||||
|
||||
for tax in taxes:
|
||||
if cstr(tax.tax_category) == cstr(args.get("tax_category")):
|
||||
out["item_tax_template"] = tax.item_tax_template
|
||||
return tax.item_tax_template
|
||||
return None
|
||||
|
||||
def is_within_valid_range(args, tax):
|
||||
if not flt(tax.maximum_net_rate):
|
||||
# No range specified, just ignore
|
||||
return True
|
||||
elif flt(tax.minimum_net_rate) <= flt(args.get('net_rate')) <= flt(tax.maximum_net_rate):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_item_tax_map(company, item_tax_template, as_json=True):
|
||||
item_tax_map = {}
|
||||
|
Loading…
x
Reference in New Issue
Block a user