Merge pull request #3785 from nabinhait/fix3
[fix] newsletter list unique check
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 2e64f99..6c67379 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -790,6 +790,58 @@
set_perpetual_inventory(0)
+ def test_discount_on_net_total(self):
+ si = frappe.copy_doc(test_records[2])
+ si.apply_discount_on = "Net Total"
+ si.discount_amount = 625
+ si.insert()
+
+ expected_values = {
+ "keys": ["price_list_rate", "discount_percentage", "rate", "amount",
+ "base_price_list_rate", "base_rate", "base_amount",
+ "net_rate", "base_net_rate", "net_amount", "base_net_amount"],
+ "_Test Item Home Desktop 100": [50, 0, 50, 500, 50, 50, 500, 25, 25, 250, 250],
+ "_Test Item Home Desktop 200": [150, 0, 150, 750, 150, 150, 750, 75, 75, 375, 375],
+ }
+
+ # check if children are saved
+ self.assertEquals(len(si.get("items")),
+ len(expected_values)-1)
+
+ # check if item values are calculated
+ for d in si.get("items"):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(d.get(k), expected_values[d.item_code][i])
+
+ # check net total
+ self.assertEquals(si.base_total, 1250)
+ self.assertEquals(si.total, 1250)
+ self.assertEquals(si.base_net_total, 625)
+ self.assertEquals(si.net_total, 625)
+
+ # check tax calculation
+ expected_values = {
+ "keys": ["tax_amount", "tax_amount_after_discount_amount",
+ "base_tax_amount_after_discount_amount"],
+ "_Test Account Shipping Charges - _TC": [100, 100, 100],
+ "_Test Account Customs Duty - _TC": [62.5, 62.5, 62.5],
+ "_Test Account Excise Duty - _TC": [70, 70, 70],
+ "_Test Account Education Cess - _TC": [1.4, 1.4, 1.4],
+ "_Test Account S&H Education Cess - _TC": [.7, 0.7, 0.7],
+ "_Test Account CST - _TC": [17.2, 17.2, 17.2],
+ "_Test Account VAT - _TC": [78.13, 78.13, 78.13],
+ "_Test Account Discount - _TC": [-95.49, -95.49, -95.49]
+ }
+
+ for d in si.get("taxes"):
+ for i, k in enumerate(expected_values["keys"]):
+ self.assertEquals(d.get(k), expected_values[d.account_head][i])
+
+
+ self.assertEquals(si.total_taxes_and_charges, 234.44)
+ self.assertEquals(si.base_grand_total, 859.44)
+ self.assertEquals(si.grand_total, 859.44)
+
def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice")
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 1623f3f..bb8c65b 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -11,6 +11,8 @@
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
from erpnext.controllers.sales_and_purchase_return import validate_return
+force_item_fields = ("item_name", "item_group", "barcode", "brand", "stock_uom")
+
class CustomerFrozen(frappe.ValidationError): pass
class AccountsController(TransactionBase):
@@ -18,12 +20,12 @@
if self.get("_action") and self._action != "update_after_submit":
self.set_missing_values(for_validate=True)
self.validate_date_with_fiscal_year()
-
+
if self.meta.get_field("currency"):
self.calculate_taxes_and_totals()
if not self.meta.get_field("is_return") or not self.is_return:
self.validate_value("base_grand_total", ">=", 0)
-
+
validate_return(self)
self.set_total_in_words()
@@ -35,7 +37,7 @@
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
-
+
self.validate_party()
def on_submit(self):
@@ -86,7 +88,7 @@
if self.doctype == "Sales Invoice":
if not self.due_date:
frappe.throw(_("Due Date is mandatory"))
-
+
validate_due_date(self.posting_date, self.due_date, "Customer", self.customer, self.company)
elif self.doctype == "Purchase Invoice":
validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company)
@@ -142,7 +144,8 @@
for fieldname, value in ret.items():
if item.meta.get_field(fieldname) and \
- item.get(fieldname) is None and value is not None:
+ (item.get(fieldname) is None or fieldname in force_item_fields) \
+ and value is not None:
item.set(fieldname, value)
if fieldname == "cost_center" and item.meta.get_field("cost_center") \
@@ -349,14 +352,14 @@
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if frozen_accounts_modifier in frappe.get_roles():
return
-
+
party_type = None
if self.meta.get_field("customer"):
party_type = 'Customer'
elif self.meta.get_field("supplier"):
party_type = 'Supplier'
-
+
if party_type:
party = self.get(party_type.lower())
if frappe.db.get_value(party_type, party, "is_frozen"):
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index cebeaf5..cfde04b 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -218,7 +218,7 @@
tuple(item_codes))
return serialized_items
-
+
def get_incoming_rate_for_sales_return(self, item_code, against_document):
incoming_rate = 0.0
if against_document and item_code:
@@ -229,12 +229,12 @@
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
return incoming_rate
-
+
def update_reserved_qty(self, d):
if d['reserved_qty'] < 0 :
# Reduce reserved qty from reserved warehouse mentioned in so
if not d["reserved_warehouse"]:
- frappe.throw(_("Reserved Warehouse is missing in Sales Order"))
+ frappe.throw(_("Delivery Warehouse is missing in Sales Order"))
args = {
"item_code": d['item_code'],
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 6b59cea..d526f66 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -219,7 +219,7 @@
# adjust Discount Amount loss in last tax iteration
if i == (len(self.doc.get("taxes")) - 1) and self.discount_amount_applied \
- and self.doc.discount_amount:
+ and self.doc.discount_amount and self.doc.apply_discount_on == "Grand Total":
self.adjust_discount_amount_loss(tax)
@@ -303,9 +303,9 @@
for tax in self.doc.get("taxes"):
if tax.category in ["Valuation and Total", "Total"]:
if tax.add_deduct_tax == "Add":
- self.doc.taxes_and_charges_added += flt(tax.tax_amount)
+ self.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount)
else:
- self.doc.taxes_and_charges_deducted += flt(tax.tax_amount)
+ self.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount)
self.doc.round_floats_in(self.doc, ["taxes_and_charges_added", "taxes_and_charges_deducted"])
diff --git a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
index c4cebb8..e302cc7 100644
--- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
+++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py
@@ -44,31 +44,35 @@
""" Pull sales orders which are pending to deliver based on criteria selected"""
so_filter = item_filter = ""
if self.from_date:
- so_filter += ' and so.transaction_date >= "' + self.from_date + '"'
+ so_filter += " and so.transaction_date >= %(from_date)s"
if self.to_date:
- so_filter += ' and so.transaction_date <= "' + self.to_date + '"'
+ so_filter += " and so.transaction_date <= %(to_date)s"
if self.customer:
- so_filter += ' and so.customer = "' + self.customer + '"'
+ so_filter += " and so.customer = %(customer)s"
if self.fg_item:
- item_filter += ' and item.name = "' + self.fg_item + '"'
+ item_filter += " and item.name = %(item)s"
open_so = frappe.db.sql("""
select distinct so.name, so.transaction_date, so.customer, so.base_grand_total
from `tabSales Order` so, `tabSales Order Item` so_item
where so_item.parent = so.name
and so.docstatus = 1 and so.status != "Stopped"
- and so.company = %s
- and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) %s
+ and so.company = %(company)s
+ and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) {0}
and (exists (select name from `tabItem` item where item.name=so_item.item_code
- and (item.is_pro_applicable = 1
- or item.is_sub_contracted_item = 1 %s)
+ and (item.is_pro_applicable = 1 or item.is_sub_contracted_item = 1 {1}))
or exists (select name from `tabPacked Item` pi
where pi.parent = so.name and pi.parent_item = so_item.item_code
and exists (select name from `tabItem` item where item.name=pi.item_code
- and (item.is_pro_applicable = 1
- or item.is_sub_contracted_item = 1) %s)))
- """ % ('%s', so_filter, item_filter, item_filter), self.company, as_dict=1)
+ and (item.is_pro_applicable = 1 or item.is_sub_contracted_item = 1) {2})))
+ """.format(so_filter, item_filter, item_filter), {
+ "from_date": self.from_date,
+ "to_date": self.to_date,
+ "customer": self.customer,
+ "item": self.fg_item,
+ "company": self.company
+ }, as_dict=1)
self.add_so_in_table(open_so)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 78e1609..de26ff3 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -184,4 +184,5 @@
erpnext.patches.v5_4.fix_reserved_qty_and_sle_for_packed_items # 30-07-2015
execute:frappe.reload_doctype("Leave Type")
execute:frappe.db.sql("update `tabLeave Type` set include_holiday=0")
-erpnext.patches.v5_4.set_root_and_report_type
\ No newline at end of file
+erpnext.patches.v5_4.set_root_and_report_type
+erpnext.patches.v5_4.notify_system_managers_regarding_wrong_tax_calculation
diff --git a/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py b/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py
new file mode 100644
index 0000000..8096a37
--- /dev/null
+++ b/erpnext/patches/v5_4/notify_system_managers_regarding_wrong_tax_calculation.py
@@ -0,0 +1,39 @@
+# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.email import sendmail_to_system_managers
+from frappe.utils import get_url_to_form
+
+def execute():
+ wrong_records = []
+ for dt in ("Quotation", "Sales Order", "Delivery Note", "Sales Invoice",
+ "Purchase Order", "Purchase Receipt", "Purchase Invoice"):
+ records = frappe.db.sql_list("""select name from `tab{0}`
+ where apply_discount_on = 'Net Total' and ifnull(discount_amount, 0) != 0
+ and modified >= '2015-02-17' and docstatus=1""".format(dt))
+
+ if records:
+ records = [get_url_to_form(dt, d) for d in records]
+ wrong_records.append([dt, records])
+
+ if wrong_records:
+ content = """Dear System Manager,
+
+Due to an error related to Discount Amount on Net Total, tax calculation might be wrong in the following records. We did not fix the tax amount automatically because it can corrupt the entries, so we request you to check these records and amend if you found the calculation wrong.
+
+Please check following Entries:
+
+%s
+
+
+Regards,
+
+Administrator""" % "\n".join([(d[0] + ": " + ", ".join(d[1])) for d in wrong_records])
+
+ sendmail_to_system_managers("[Important] [ERPNext] Tax calculation might be wrong, please check.", content)
+
+ print "="*50
+ print content
+ print "="*50
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 1e238d6..0b3ca7f 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -256,7 +256,8 @@
me.round_off_totals(tax);
// 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")
+ 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.adjust_discount_amount_loss(tax);
}
});
@@ -365,9 +366,9 @@
$.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);
+ 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);
+ me.frm.doc.taxes_and_charges_deducted += flt(tax.tax_amount_after_discount_amount);
}
}
})
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 608d01a..089c067 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -108,9 +108,13 @@
def check_stock_uom_with_bin(self):
if not self.get("__islocal"):
+ if self.stock_uom == frappe.db.get_value("Item", self.name, "stock_uom"):
+ return
+
matched=True
ref_uom = frappe.db.get_value("Stock Ledger Entry",
{"item_code": self.name}, "stock_uom")
+
if ref_uom:
if cstr(ref_uom) != cstr(self.stock_uom):
matched = False
@@ -128,7 +132,7 @@
(self.stock_uom, self.name))
if not matched:
- frappe.throw(_("Default Unit of Measure can not be changed directly because you have already made some transaction(s) with another UOM. To change default UOM, use 'UOM Replace Utility' tool under Stock module."))
+ frappe.throw(_("Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. To change default UOM, use 'UOM Replace Utility' tool under Stock module.").format(self.name))
def update_template_tables(self):
template = frappe.get_doc("Item", self.variant_of)
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 249815f..797b2a0 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -82,8 +82,7 @@
frappe.throw(_("Stock cannot exist for Item {0} since has variants").format(self.item_code),
ItemTemplateCannotHaveStock)
- if not self.stock_uom:
- self.stock_uom = item_det.stock_uom
+ self.stock_uom = item_det.stock_uom
def check_stock_frozen_date(self):
stock_frozen_upto = frappe.db.get_value('Stock Settings', None, 'stock_frozen_upto') or ''