Files
erpnext/erpnext/public/js/controllers/taxes_and_totals.js
Frappe PR Bot 6e492ec514 chore: release v13 (#34531)
* chore: Update user manual link (#34478)

* chore: Update user manual link (#34478)

(cherry picked from commit be723bb9d4)

# Conflicts:
#	erpnext/patches.txt

* chore: resolve conflicts

* chore: Update version

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

* chore: Update version

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

* fix: Overallocation of 'qty' from Cr Notes to Parent Invoice

Cr Notes 'qty' are overallocated to parent invoice, when there are
mulitple instances of same item in Invoice.

(cherry picked from commit e2f19c6a14)

* refactor: Ignore linked Cr Notes in Report output

(cherry picked from commit d0715a82eb)

* test: Gross Profit report output for Cr notes

2 New test cases added.
1. Standalone Cr notes will be reported as normal Invoices
2. Cr notes against an Invoice will not overallocate qty if there are
multiple instances of same item

(cherry picked from commit cc61daeec4)

* fix: incorrect depr schedules after asset repair [v13] (#34520)

* fix: incorrect schedule after repair for WDV and DD

* chore: only fix schedules for assets with calc_depr true

* fix: incorrect schedule after repair for straight line and manual

* refactor: calc depr in asset repair and if statement (#34526)

refactor: minor asset repair of calc depr and if statement

* fix(client): Amount calculation for 0 qty debit notes (#34455)

fix(client): Amount calculation for 0 qty debit notes (#34455)

fix(client): Amount calculaton for 0 qty debit notes

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
(cherry picked from commit ee6c107d58)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>

* fix: german translations (#34312)

fix: german translations (#34312)

fix: some german translations
(cherry picked from commit 59c2e7ec3e)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

* fix: exchange gain/loss GL's should be removed if advance is cancelled (#34529)

* fix: report GL for invoice when advance has different exchange rate

If deferred revenue/expense is enabled for any item, don't repost.

* test: cancelling advance should remove exchange gain/loss

If there are no deferred revenue/expense cancelling advance should
cancel the exchange gain/loss booked due to different exchange rates
of payment and its linked invoice

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-03-21 18:20:16 +05:30

858 lines
32 KiB
JavaScript

// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
erpnext.taxes_and_totals = erpnext.payments.extend({
setup: function() {
this.fetch_round_off_accounts();
},
apply_pricing_rule_on_item: function(item) {
let effective_item_rate = item.price_list_rate;
let item_rate = item.rate;
if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
effective_item_rate = item.blanket_order_rate;
}
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 {
item.rate_with_margin = flt(effective_item_rate) + flt(item.margin_rate_or_amount);
}
item.base_rate_with_margin = flt(item.rate_with_margin) * flt(this.frm.doc.conversion_rate);
item_rate = flt(item.rate_with_margin , precision("rate", item));
if (item.discount_percentage) {
item.discount_amount = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100;
}
if (item.discount_amount) {
item_rate = flt((item.rate_with_margin) - (item.discount_amount), precision('rate', item));
item.discount_percentage = 100 * flt(item.discount_amount) / flt(item.rate_with_margin);
}
frappe.model.set_value(item.doctype, item.name, "rate", item_rate);
},
calculate_taxes_and_totals: async function(update_paid_amount) {
this.discount_amount_applied = false;
this._calculate_taxes_and_totals();
this.calculate_discount_amount();
// # Update grand total as per cash and non trade discount
if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) {
this.frm.doc.grand_total -= this.frm.doc.discount_amount;
this.frm.doc.base_grand_total -= this.frm.doc.base_discount_amount;
}
await this.calculate_shipping_charges();
// Advance calculation applicable to Sales /Purchase Invoice
if(in_list(["Sales Invoice", "POS Invoice", "Purchase Invoice"], this.frm.doc.doctype)
&& this.frm.doc.docstatus < 2 && !this.frm.doc.is_return) {
this.calculate_total_advance(update_paid_amount);
}
if (in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) && this.frm.doc.is_pos &&
this.frm.doc.is_return) {
if (this.frm.doc.doctype == "Sales Invoice") {
this.set_total_amount_to_default_mop();
}
this.calculate_paid_amount();
}
// Sales person's commission
if(in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"], this.frm.doc.doctype)) {
this.calculate_commission();
this.calculate_contribution();
}
// Update paid amount on return/debit note creation
if(this.frm.doc.doctype === "Purchase Invoice" && this.frm.doc.is_return
&& (this.frm.doc.grand_total > this.frm.doc.paid_amount)) {
this.frm.doc.paid_amount = flt(this.frm.doc.grand_total, precision("grand_total"));
}
this.frm.refresh_fields();
},
calculate_discount_amount: function() {
if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount")) {
this.set_discount_amount();
this.apply_discount_amount();
}
},
_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();
},
validate_conversion_rate: function() {
this.frm.doc.conversion_rate = flt(this.frm.doc.conversion_rate, (cur_frm) ? precision("conversion_rate") : 9);
var conversion_rate_label = frappe.meta.get_label(this.frm.doc.doctype, "conversion_rate",
this.frm.doc.name);
var company_currency = this.get_company_currency();
if(!this.frm.doc.conversion_rate) {
if(this.frm.doc.currency == company_currency) {
this.frm.set_value("conversion_rate", 1);
} else {
const subs = [conversion_rate_label, this.frm.doc.currency, company_currency];
const err_message = __('{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2}', subs);
frappe.throw(err_message);
}
}
},
calculate_item_values: function() {
let me = this;
if (!this.discount_amount_applied) {
for (const item of this.frm.doc.items || []) {
frappe.model.round_floats_in(item);
item.net_rate = item.rate;
item.qty = item.qty === undefined ? (me.frm.doc.is_return ? -1 : 1) : item.qty;
if (!(me.frm.doc.is_return || me.frm.doc.is_debit_note)) {
item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item));
}
else {
// allow for '0' qty on Credit/Debit notes
let qty = item.qty || me.frm.doc.is_debit_note ? 1 : -1;
item.net_amount = item.amount = flt(item.rate * qty, precision("amount", item));
}
item.item_tax_amount = 0.0;
item.total_weight = flt(item.weight_per_unit * item.stock_qty);
me.set_in_company_currency(item, ["price_list_rate", "rate", "amount", "net_rate", "net_amount"]);
}
}
},
set_in_company_currency: function(doc, fields) {
var me = this;
$.each(fields, function(i, f) {
doc["base_"+f] = flt(flt(doc[f], precision(f, doc)) * me.frm.doc.conversion_rate, precision("base_" + f, doc));
});
},
initialize_taxes: function() {
var me = this;
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
if (!tax.dont_recompute_tax) {
tax.item_wise_tax_detail = {};
}
var tax_fields = ["total", "tax_amount_after_discount_amount",
"tax_amount_for_current_item", "grand_total_for_current_item",
"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"];
if (cstr(tax.charge_type) != "Actual" &&
!(me.discount_amount_applied && me.frm.doc.apply_discount_on=="Grand Total")) {
tax_fields.push("tax_amount");
}
$.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; });
if (!this.discount_amount_applied && cur_frm) {
cur_frm.cscript.validate_taxes_and_charges(tax.doctype, tax.name);
me.validate_inclusive_tax(tax);
}
frappe.model.round_floats_in(tax);
});
},
fetch_round_off_accounts: function() {
let me = this;
frappe.flags.round_off_applicable_accounts = [];
if (me.frm.doc.company) {
return frappe.call({
"method": "erpnext.controllers.taxes_and_totals.get_round_off_applicable_accounts",
"args": {
"company": me.frm.doc.company,
"account_list": frappe.flags.round_off_applicable_accounts
},
callback: function(r) {
frappe.flags.round_off_applicable_accounts.push(...r.message);
}
});
}
},
determine_exclusive_rate: function() {
var me = this;
var has_inclusive_tax = false;
$.each(me.frm.doc["taxes"] || [], function(i, row) {
if(cint(row.included_in_print_rate)) has_inclusive_tax = true;
});
if(has_inclusive_tax==false) return;
$.each(me.frm.doc["items"] || [], function(n, item) {
var item_tax_map = me._load_item_tax_rate(item.item_tax_rate);
var cumulated_tax_fraction = 0.0;
var total_inclusive_tax_amount_per_qty = 0;
$.each(me.frm.doc["taxes"] || [], function(i, tax) {
var current_tax_fraction = me.get_current_tax_fraction(tax, item_tax_map);
tax.tax_fraction_for_current_item = current_tax_fraction[0];
var inclusive_tax_amount_per_qty = current_tax_fraction[1];
if(i==0) {
tax.grand_total_fraction_for_current_item = 1 + tax.tax_fraction_for_current_item;
} else {
tax.grand_total_fraction_for_current_item =
me.frm.doc["taxes"][i-1].grand_total_fraction_for_current_item +
tax.tax_fraction_for_current_item;
}
cumulated_tax_fraction += tax.tax_fraction_for_current_item;
total_inclusive_tax_amount_per_qty += inclusive_tax_amount_per_qty * flt(item.qty);
});
if(!me.discount_amount_applied && item.qty && (total_inclusive_tax_amount_per_qty || cumulated_tax_fraction)) {
var amount = flt(item.amount) - total_inclusive_tax_amount_per_qty;
item.net_amount = flt(amount / (1 + cumulated_tax_fraction));
item.net_rate = item.qty ? flt(item.net_amount / item.qty, precision("net_rate", item)) : 0;
me.set_in_company_currency(item, ["net_rate", "net_amount"]);
}
});
},
get_current_tax_fraction: function(tax, item_tax_map) {
// Get tax fraction for calculating tax exclusive amount
// from tax inclusive amount
var current_tax_fraction = 0.0;
var inclusive_tax_amount_per_qty = 0;
if(cint(tax.included_in_print_rate)) {
var tax_rate = this._get_tax_rate(tax, item_tax_map);
if(tax.charge_type == "On Net Total") {
current_tax_fraction = (tax_rate / 100.0);
} else if(tax.charge_type == "On Previous Row Amount") {
current_tax_fraction = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].tax_fraction_for_current_item;
} else if(tax.charge_type == "On Previous Row Total") {
current_tax_fraction = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_fraction_for_current_item;
} else if (tax.charge_type == "On Item Quantity") {
inclusive_tax_amount_per_qty = flt(tax_rate);
}
}
if(tax.add_deduct_tax && tax.add_deduct_tax == "Deduct") {
current_tax_fraction *= -1;
inclusive_tax_amount_per_qty *= -1;
}
return [current_tax_fraction, inclusive_tax_amount_per_qty];
},
_get_tax_rate: function(tax, item_tax_map) {
return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ?
flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate;
},
calculate_net_total: function() {
var me = this;
this.frm.doc.total_qty = this.frm.doc.total = this.frm.doc.base_total = this.frm.doc.net_total = this.frm.doc.base_net_total = 0.0;
$.each(this.frm.doc["items"] || [], function(i, item) {
me.frm.doc.total += item.amount;
me.frm.doc.total_qty += item.qty;
me.frm.doc.base_total += item.base_amount;
me.frm.doc.net_total += item.net_amount;
me.frm.doc.base_net_total += item.base_net_amount;
});
},
calculate_shipping_charges: function() {
// Do not apply shipping rule for POS
if (this.frm.doc.is_pos) {
return;
}
frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
if (frappe.meta.get_docfield(this.frm.doc.doctype, "shipping_rule", this.frm.doc.name)) {
return this.shipping_rule();
}
},
calculate_taxes: function() {
var me = this;
this.frm.doc.rounding_adjustment = 0;
var actual_tax_dict = {};
// maintain actual tax rate based on idx
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
if (tax.charge_type == "Actual") {
actual_tax_dict[tax.idx] = flt(tax.tax_amount, precision("tax_amount", tax));
}
});
$.each(this.frm.doc["items"] || [], function(n, item) {
var item_tax_map = me._load_item_tax_rate(item.item_tax_rate);
$.each(me.frm.doc["taxes"] || [], function(i, tax) {
// tax_amount represents the amount of tax for the current step
var current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map);
// Adjust divisional loss to the last item
if (tax.charge_type == "Actual") {
actual_tax_dict[tax.idx] -= current_tax_amount;
if (n == me.frm.doc["items"].length - 1) {
current_tax_amount += actual_tax_dict[tax.idx];
}
}
// accumulate tax amount into tax.tax_amount
if (tax.charge_type != "Actual" &&
!(me.discount_amount_applied && me.frm.doc.apply_discount_on=="Grand Total")) {
tax.tax_amount += current_tax_amount;
}
// store tax_amount for current item as it will be used for
// charge type = 'On Previous Row Amount'
tax.tax_amount_for_current_item = current_tax_amount;
// tax amount after discount amount
tax.tax_amount_after_discount_amount += current_tax_amount;
// for buying
if(tax.category) {
// if just for valuation, do not add the tax amount in total
// hence, setting it as 0 for further steps
current_tax_amount = (tax.category == "Valuation") ? 0.0 : current_tax_amount;
current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
}
// note: grand_total_for_current_item contains the contribution of
// item's amount, previously applied tax and the current tax on that item
if(i==0) {
tax.grand_total_for_current_item = flt(item.net_amount + current_tax_amount);
} else {
tax.grand_total_for_current_item =
flt(me.frm.doc["taxes"][i-1].grand_total_for_current_item + current_tax_amount);
}
// set precision in the last item iteration
if (n == me.frm.doc["items"].length - 1) {
me.round_off_totals(tax);
me.set_in_company_currency(tax,
["tax_amount", "tax_amount_after_discount_amount"]);
me.round_off_base_values(tax);
// in tax.total, accumulate grand total for each item
me.set_cumulative_total(i, tax);
me.set_in_company_currency(tax, ["total"]);
// adjust Discount Amount loss in last tax iteration
if ((i == me.frm.doc["taxes"].length - 1) && me.discount_amount_applied
&& me.frm.doc.apply_discount_on == "Grand Total" && me.frm.doc.discount_amount) {
me.frm.doc.rounding_adjustment = flt(me.frm.doc.grand_total -
flt(me.frm.doc.discount_amount) - tax.total, precision("rounding_adjustment"));
}
}
});
});
},
set_cumulative_total: function(row_idx, tax) {
var tax_amount = tax.tax_amount_after_discount_amount;
if (tax.category == 'Valuation') {
tax_amount = 0;
}
if (tax.add_deduct_tax == "Deduct") { tax_amount = -1*tax_amount; }
if(row_idx==0) {
tax.total = flt(this.frm.doc.net_total + tax_amount, precision("total", tax));
} else {
tax.total = flt(this.frm.doc["taxes"][row_idx-1].total + tax_amount, precision("total", tax));
}
},
_load_item_tax_rate: function(item_tax_rate) {
return item_tax_rate ? JSON.parse(item_tax_rate) : {};
},
get_current_tax_amount: function(item, tax, item_tax_map) {
var tax_rate = this._get_tax_rate(tax, item_tax_map);
var current_tax_amount = 0.0;
// To set row_id by default as previous row.
if(["On Previous Row Amount", "On Previous Row Total"].includes(tax.charge_type)) {
if (tax.idx === 1) {
frappe.throw(
__("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"));
}
if (!tax.row_id) {
tax.row_id = tax.idx - 1;
}
}
if(tax.charge_type == "Actual") {
// distribute the tax amount proportionally to each item row
var actual = flt(tax.tax_amount, precision("tax_amount", tax));
current_tax_amount = this.frm.doc.net_total ?
((item.net_amount / this.frm.doc.net_total) * actual) : 0.0;
} else if(tax.charge_type == "On Net Total") {
current_tax_amount = (tax_rate / 100.0) * item.net_amount;
} else if(tax.charge_type == "On Previous Row Amount") {
current_tax_amount = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].tax_amount_for_current_item;
} else if(tax.charge_type == "On Previous Row Total") {
current_tax_amount = (tax_rate / 100.0) *
this.frm.doc["taxes"][cint(tax.row_id) - 1].grand_total_for_current_item;
} else if (tax.charge_type == "On Item Quantity") {
current_tax_amount = tax_rate * item.qty;
}
if (!tax.dont_recompute_tax) {
this.set_item_wise_tax(item, tax, tax_rate, current_tax_amount);
}
return current_tax_amount;
},
set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
// store tax breakup for each item
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];
tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
},
round_off_totals: function(tax) {
if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
tax.tax_amount= Math.round(tax.tax_amount);
tax.tax_amount_after_discount_amount = Math.round(tax.tax_amount_after_discount_amount);
}
tax.tax_amount = flt(tax.tax_amount, precision("tax_amount", tax));
tax.tax_amount_after_discount_amount = flt(tax.tax_amount_after_discount_amount, precision("tax_amount", tax));
},
round_off_base_values: function(tax) {
if (frappe.flags.round_off_applicable_accounts.includes(tax.account_head)) {
tax.base_tax_amount= Math.round(tax.base_tax_amount);
tax.base_tax_amount_after_discount_amount = Math.round(tax.base_tax_amount_after_discount_amount);
}
},
manipulate_grand_total_for_inclusive_tax: function() {
var me = this;
// if fully inclusive taxes and diff
if (this.frm.doc["taxes"] && this.frm.doc["taxes"].length) {
var any_inclusive_tax = false;
$.each(this.frm.doc.taxes || [], function(i, d) {
if(cint(d.included_in_print_rate)) any_inclusive_tax = true;
});
if (any_inclusive_tax) {
var last_tax = me.frm.doc["taxes"].slice(-1)[0];
var non_inclusive_tax_amount = frappe.utils.sum($.map(this.frm.doc.taxes || [],
function(d) {
if(!d.included_in_print_rate) {
return flt(d.tax_amount_after_discount_amount);
}
}
));
var diff = me.frm.doc.total + non_inclusive_tax_amount
- flt(last_tax.total, precision("grand_total"));
if(me.discount_amount_applied && me.frm.doc.discount_amount) {
diff -= flt(me.frm.doc.discount_amount);
}
diff = flt(diff, precision("rounding_adjustment"));
if ( diff && Math.abs(diff) <= (5.0 / Math.pow(10, precision("tax_amount", last_tax))) ) {
me.frm.doc.rounding_adjustment = diff;
}
}
}
},
calculate_totals: function() {
// Changing sequence can because of rounding adjustment issue and on-screen discrepancy
var me = this;
var tax_count = this.frm.doc["taxes"] ? this.frm.doc["taxes"].length : 0;
this.frm.doc.grand_total = flt(tax_count
? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.rounding_adjustment)
: this.frm.doc.net_total);
if(in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"], this.frm.doc.doctype)) {
this.frm.doc.base_grand_total = (this.frm.doc.total_taxes_and_charges) ?
flt(this.frm.doc.grand_total * this.frm.doc.conversion_rate) : this.frm.doc.base_net_total;
} else {
// other charges added/deducted
this.frm.doc.taxes_and_charges_added = this.frm.doc.taxes_and_charges_deducted = 0.0;
if(tax_count) {
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
if (in_list(["Valuation and Total", "Total"], tax.category)) {
if(tax.add_deduct_tax == "Add") {
me.frm.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount);
} else {
me.frm.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount);
}
}
});
frappe.model.round_floats_in(this.frm.doc,
["taxes_and_charges_added", "taxes_and_charges_deducted"]);
}
this.frm.doc.base_grand_total = flt((this.frm.doc.taxes_and_charges_added || this.frm.doc.taxes_and_charges_deducted) ?
flt(this.frm.doc.grand_total * this.frm.doc.conversion_rate) : this.frm.doc.base_net_total);
this.set_in_company_currency(this.frm.doc,
["taxes_and_charges_added", "taxes_and_charges_deducted"]);
}
this.frm.doc.total_taxes_and_charges = flt(this.frm.doc.grand_total - this.frm.doc.net_total
- flt(this.frm.doc.rounding_adjustment), precision("total_taxes_and_charges"));
this.set_in_company_currency(this.frm.doc, ["total_taxes_and_charges", "rounding_adjustment"]);
// Round grand total as per precision
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "base_grand_total"]);
// rounded totals
this.set_rounded_total();
},
set_rounded_total: function() {
var disable_rounded_total = 0;
if(frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total", this.frm.doc.name)) {
disable_rounded_total = this.frm.doc.disable_rounded_total;
} else if (frappe.sys_defaults.disable_rounded_total) {
disable_rounded_total = frappe.sys_defaults.disable_rounded_total;
}
if (cint(disable_rounded_total)) {
this.frm.doc.rounded_total = 0;
this.frm.doc.base_rounded_total = 0;
return;
}
if(frappe.meta.get_docfield(this.frm.doc.doctype, "rounded_total", this.frm.doc.name)) {
this.frm.doc.rounded_total = round_based_on_smallest_currency_fraction(this.frm.doc.grand_total,
this.frm.doc.currency, precision("rounded_total"));
this.frm.doc.rounding_adjustment += flt(this.frm.doc.rounded_total - this.frm.doc.grand_total,
precision("rounding_adjustment"));
this.set_in_company_currency(this.frm.doc, ["rounding_adjustment", "rounded_total"]);
}
},
_cleanup: function() {
this.frm.doc.base_in_words = this.frm.doc.in_words = "";
if(this.frm.doc["items"] && this.frm.doc["items"].length) {
if(!frappe.meta.get_docfield(this.frm.doc["items"][0].doctype, "item_tax_amount", this.frm.doctype)) {
$.each(this.frm.doc["items"] || [], function(i, item) {
delete item["item_tax_amount"];
});
}
}
if(this.frm.doc["taxes"] && this.frm.doc["taxes"].length) {
var temporary_fields = ["tax_amount_for_current_item", "grand_total_for_current_item",
"tax_fraction_for_current_item", "grand_total_fraction_for_current_item"];
if(!frappe.meta.get_docfield(this.frm.doc["taxes"][0].doctype, "tax_amount_after_discount_amount", this.frm.doctype)) {
temporary_fields.push("tax_amount_after_discount_amount");
}
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
$.each(temporary_fields, function(i, fieldname) {
delete tax[fieldname];
});
if (!tax.dont_recompute_tax) {
tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail);
}
});
}
},
set_discount_amount: function() {
if(this.frm.doc.additional_discount_percentage) {
this.frm.doc.discount_amount = flt(flt(this.frm.doc[frappe.scrub(this.frm.doc.apply_discount_on)])
* this.frm.doc.additional_discount_percentage / 100, precision("discount_amount"));
}
},
apply_discount_amount: function() {
var me = this;
var distributed_amount = 0.0;
this.frm.doc.base_discount_amount = 0.0;
if (this.frm.doc.discount_amount) {
if(!this.frm.doc.apply_discount_on)
frappe.throw(__("Please select Apply Discount On"));
this.frm.doc.base_discount_amount = flt(this.frm.doc.discount_amount * this.frm.doc.conversion_rate,
precision("base_discount_amount"));
if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) {
return;
}
var total_for_discount_amount = this.get_total_for_discount_amount();
var net_total = 0;
// calculate item amount after Discount Amount
if (total_for_discount_amount) {
$.each(this.frm.doc["items"] || [], function(i, item) {
distributed_amount = flt(me.frm.doc.discount_amount) * item.net_amount / total_for_discount_amount;
item.net_amount = flt(item.net_amount - distributed_amount,
precision("base_amount", item));
net_total += item.net_amount;
// discount amount rounding loss adjustment if no taxes
if ((!(me.frm.doc.taxes || []).length || total_for_discount_amount==me.frm.doc.net_total || (me.frm.doc.apply_discount_on == "Net Total"))
&& i == (me.frm.doc.items || []).length - 1) {
var discount_amount_loss = flt(me.frm.doc.net_total - net_total
- me.frm.doc.discount_amount, precision("net_total"));
item.net_amount = flt(item.net_amount + discount_amount_loss,
precision("net_amount", item));
}
item.net_rate = item.qty ? flt(item.net_amount / item.qty, precision("net_rate", item)) : 0;
me.set_in_company_currency(item, ["net_rate", "net_amount"]);
});
this.discount_amount_applied = true;
this._calculate_taxes_and_totals();
}
}
},
get_total_for_discount_amount: function() {
if(this.frm.doc.apply_discount_on == "Net Total") {
return this.frm.doc.net_total;
} else {
var total_actual_tax = 0.0;
var actual_taxes_dict = {};
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
if (in_list(["Actual", "On Item Quantity"], tax.charge_type)) {
var tax_amount = (tax.category == "Valuation") ? 0.0 : tax.tax_amount;
tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
actual_taxes_dict[tax.idx] = tax_amount;
} else if (actual_taxes_dict[tax.row_id] !== null) {
var actual_tax_amount = flt(actual_taxes_dict[tax.row_id]) * flt(tax.rate) / 100;
actual_taxes_dict[tax.idx] = actual_tax_amount;
}
});
$.each(actual_taxes_dict, function(key, value) {
if (value) total_actual_tax += value;
});
return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total"));
}
},
calculate_total_advance: function(update_paid_amount) {
var total_allocated_amount = frappe.utils.sum($.map(this.frm.doc["advances"] || [], function(adv) {
return flt(adv.allocated_amount, precision("allocated_amount", adv));
}));
this.frm.doc.total_advance = flt(total_allocated_amount, precision("total_advance"));
if (this.frm.doc.write_off_outstanding_amount_automatically) {
this.frm.doc.write_off_amount = 0;
}
this.calculate_outstanding_amount(update_paid_amount);
this.calculate_write_off_amount();
},
is_internal_invoice: function() {
if (['Sales Invoice', 'Purchase Invoice'].includes(this.frm.doc.doctype)) {
if (this.frm.doc.company === this.frm.doc.represents_company) {
return true;
}
}
return false;
},
calculate_outstanding_amount: function(update_paid_amount) {
// NOTE:
// paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice
// total_advance is only for non POS Invoice
if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) && this.frm.doc.is_return){
this.calculate_paid_amount();
}
if (this.frm.doc.is_return || (this.frm.doc.docstatus > 0) || this.is_internal_invoice()) return;
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]);
if(in_list(["Sales Invoice", "POS Invoice", "Purchase Invoice"], this.frm.doc.doctype)) {
let grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total;
let base_grand_total = this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total;
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
var total_amount_to_pay = flt((grand_total - this.frm.doc.total_advance
- this.frm.doc.write_off_amount), precision("grand_total"));
} else {
var total_amount_to_pay = flt(
(flt(base_grand_total, precision("base_grand_total"))
- this.frm.doc.total_advance - this.frm.doc.base_write_off_amount),
precision("base_grand_total")
);
}
frappe.model.round_floats_in(this.frm.doc, ["paid_amount"]);
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
if(this.frm.refresh_field){
this.frm.refresh_field("paid_amount");
this.frm.refresh_field("base_paid_amount");
}
if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype)) {
let total_amount_for_payment = (this.frm.doc.redeem_loyalty_points && this.frm.doc.loyalty_amount)
? flt(total_amount_to_pay - this.frm.doc.loyalty_amount, precision("base_grand_total"))
: total_amount_to_pay;
this.set_default_payment(total_amount_for_payment, update_paid_amount);
this.calculate_paid_amount();
}
this.calculate_change_amount();
var paid_amount = (this.frm.doc.party_account_currency == this.frm.doc.currency) ?
this.frm.doc.paid_amount : this.frm.doc.base_paid_amount;
this.frm.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) +
flt(this.frm.doc.change_amount * this.frm.doc.conversion_rate), precision("outstanding_amount"));
}
},
set_total_amount_to_default_mop: function() {
let grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total;
let base_grand_total = this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total;
if(this.frm.doc.party_account_currency == this.frm.doc.currency) {
var total_amount_to_pay = flt((grand_total - this.frm.doc.total_advance
- this.frm.doc.write_off_amount), precision("grand_total"));
} else {
var total_amount_to_pay = flt(
(flt(base_grand_total, precision("base_grand_total"))
- this.frm.doc.total_advance - this.frm.doc.base_write_off_amount),
precision("base_grand_total")
);
}
this.frm.doc.payments.find(pay => {
if (pay.default) {
pay.amount = total_amount_to_pay;
}
});
this.frm.refresh_fields();
},
set_default_payment: function(total_amount_to_pay, update_paid_amount) {
var me = this;
var payment_status = true;
if(this.frm.doc.is_pos && (update_paid_amount===undefined || update_paid_amount)) {
$.each(this.frm.doc['payments'] || [], function(index, data) {
if(data.default && payment_status && total_amount_to_pay > 0) {
let base_amount, amount;
if (me.frm.doc.party_account_currency == me.frm.doc.currency) {
// if customer/supplier currency is same as company currency
// total_amount_to_pay is already in customer/supplier currency
// so base_amount has to be calculated using total_amount_to_pay
base_amount = flt(total_amount_to_pay * me.frm.doc.conversion_rate, precision("base_amount", data));
amount = flt(total_amount_to_pay, precision("amount", data));
} else {
base_amount = flt(total_amount_to_pay, precision("base_amount", data));
amount = flt(total_amount_to_pay / me.frm.doc.conversion_rate, precision("amount", data));
}
frappe.model.set_value(data.doctype, data.name, "base_amount", base_amount);
frappe.model.set_value(data.doctype, data.name, "amount", amount);
payment_status = false;
} else if(me.frm.doc.paid_amount) {
frappe.model.set_value(data.doctype, data.name, "amount", 0.0);
}
});
}
},
calculate_paid_amount: function() {
var me = this;
var paid_amount = 0.0;
var base_paid_amount = 0.0;
if(this.frm.doc.is_pos) {
$.each(this.frm.doc['payments'] || [], function(index, data){
data.base_amount = flt(data.amount * me.frm.doc.conversion_rate, precision("base_amount", data));
paid_amount += data.amount;
base_paid_amount += data.base_amount;
});
} else if(!this.frm.doc.is_return){
this.frm.doc.payments = [];
}
if (this.frm.doc.redeem_loyalty_points && this.frm.doc.loyalty_amount) {
base_paid_amount += this.frm.doc.loyalty_amount;
paid_amount += flt(this.frm.doc.loyalty_amount / me.frm.doc.conversion_rate, precision("paid_amount"));
}
this.frm.set_value('paid_amount', flt(paid_amount, precision("paid_amount")));
this.frm.set_value('base_paid_amount', flt(base_paid_amount, precision("base_paid_amount")));
},
calculate_change_amount: function() {
this.frm.doc.change_amount = 0.0;
this.frm.doc.base_change_amount = 0.0;
if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype)
&& this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return) {
var payment_types = $.map(this.frm.doc.payments, function(d) { return d.type; });
if (in_list(payment_types, 'Cash')) {
var grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total;
var base_grand_total = this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total;
this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - grand_total,
precision("change_amount"));
this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount -
base_grand_total, precision("base_change_amount"));
}
}
},
calculate_write_off_amount: function() {
if (this.frm.doc.write_off_outstanding_amount_automatically) {
this.frm.doc.write_off_amount = flt(this.frm.doc.outstanding_amount, precision("write_off_amount"));
this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate,
precision("base_write_off_amount"));
this.calculate_outstanding_amount(false);
}
}
});