diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index f19aaf833b..8c7f3d881f 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -335,7 +335,7 @@ def get_due_date_from_template(template_name, posting_date, bill_date): def validate_due_date(posting_date, due_date, party_type, party, company=None, bill_date=None, template_name=None): if getdate(due_date) < getdate(posting_date): - frappe.throw(_("Due Date cannot be before Posting Date")) + frappe.throw(_("Due Date cannot be before Posting / Supplier Invoice Date")) else: if not template_name: return diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 51747f67ae..86ceb2e4ab 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -57,6 +57,8 @@ class AccountsController(TransactionBase): _('{0} is blocked so this transaction cannot proceed'.format(supplier_name)), raise_exception=1) def validate(self): + + self.validate_qty_is_not_zero() if self.get("_action") and self._action != "update_after_submit": self.set_missing_values(for_validate=True) @@ -179,7 +181,7 @@ class AccountsController(TransactionBase): validate_due_date(self.posting_date, self.due_date, "Customer", self.customer, self.company, self.payment_terms_template) elif self.doctype == "Purchase Invoice": - validate_due_date(self.posting_date, self.due_date, + validate_due_date(self.bill_date or self.posting_date, self.due_date, "Supplier", self.supplier, self.company, self.bill_date, self.payment_terms_template) def set_price_list_currency(self, buying_or_selling): @@ -359,6 +361,11 @@ class AccountsController(TransactionBase): return gl_dict + def validate_qty_is_not_zero(self): + for item in self.items: + if not item.qty: + frappe.throw("Item quantity can not be zero") + def validate_account_currency(self, account, account_currency=None): valid_currency = [self.company_currency] if self.get("currency") and self.currency != self.company_currency: @@ -405,7 +412,8 @@ class AccountsController(TransactionBase): if d.against_order: allocated_amount = flt(d.amount) else: - allocated_amount = min(self.grand_total - advance_allocated, d.amount) + amount = self.rounded_total or self.grand_total + allocated_amount = min(amount - advance_allocated, d.amount) advance_allocated += flt(allocated_amount) self.append("advances", { diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 719f4c732a..7739592ced 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -41,6 +41,7 @@ class SellingController(StockController): self.validate_selling_price() self.set_qty_as_per_stock_uom() self.set_po_nos() + self.set_gross_profit() set_default_income_account_for_item(self) def set_missing_values(self, for_validate=False): @@ -348,6 +349,12 @@ class SellingController(StockController): if po_nos and po_nos[0].get('po_no'): self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no]))) + def set_gross_profit(self): + if self.doctype == "Sales Order": + for item in self.items: + item.gross_profit = flt(((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item)) + + def validate_items(self): # validate items to see if they have is_sales_item enabled from erpnext.controllers.buying_controller import validate_item_type diff --git a/erpnext/education/doctype/fees/fees.json b/erpnext/education/doctype/fees/fees.json index ac32717721..2413967442 100644 --- a/erpnext/education/doctype/fees/fees.json +++ b/erpnext/education/doctype/fees/fees.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, @@ -991,39 +992,6 @@ "translatable": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "paid_amount", - "fieldtype": "Currency", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Paid Amount", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -1360,7 +1328,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-08-21 14:44:48.968839", + "modified": "2018-11-26 20:42:14.467284", "modified_by": "Administrator", "module": "Education", "name": "Fees", diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py index bfe6af4bdb..aa616e6206 100644 --- a/erpnext/education/doctype/fees/fees.py +++ b/erpnext/education/doctype/fees/fees.py @@ -112,7 +112,10 @@ def get_fee_list(doctype, txt, filters, limit_start, limit_page_length=20, order user = frappe.session.user student = frappe.db.sql("select name from `tabStudent` where student_email_id= %s", user) if student: - return frappe. db.sql('''select name, program, due_date, paid_amount, outstanding_amount, grand_total from `tabFees` + return frappe. db.sql(''' + select name, program, due_date, grand_total - outstanding_amount as paid_amount, + outstanding_amount, grand_total, currency + from `tabFees` where student= %s and docstatus=1 order by due_date asc limit {0} , {1}''' .format(limit_start, limit_page_length), student, as_dict = True) diff --git a/erpnext/hr/doctype/leave_application/leave_application_list.js b/erpnext/hr/doctype/leave_application/leave_application_list.js index d7588da4dd..f69b182737 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_list.js +++ b/erpnext/hr/doctype/leave_application/leave_application_list.js @@ -1,3 +1,10 @@ frappe.listview_settings['Leave Application'] = { - add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"] + add_fields: ["leave_type", "employee", "employee_name", "total_leave_days", "from_date", "to_date"], + get_indicator: function (doc) { + if (doc.status === "Approved") { + return [__("Approved"), "green", "status,=,Approved"]; + } else if (doc.status === "Rejected") { + return [__("Rejected"), "red", "status,=,Rejected"]; + } + } }; diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.js b/erpnext/hr/doctype/salary_slip/salary_slip.js index affbb552f7..86c50d08ba 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.js +++ b/erpnext/hr/doctype/salary_slip/salary_slip.js @@ -177,7 +177,7 @@ var calculate_earning_total = function(doc, dt, dn, reset_amount) { if(cint(tbl[i].depends_on_lwp) == 1) { tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) / cint(doc.total_working_days)*100)/100; - } else if(reset_amount) { + } else if(reset_amount && tbl[i].default_amount) { tbl[i].amount = tbl[i].default_amount; } if(!tbl[i].do_not_include_in_total) { @@ -198,7 +198,7 @@ var calculate_ded_total = function(doc, dt, dn, reset_amount) { for(var i = 0; i < tbl.length; i++){ if(cint(tbl[i].depends_on_lwp) == 1) { tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_working_days)*100)/100; - } else if(reset_amount) { + } else if(reset_amount && tbl[i].default_amount) { tbl[i].amount = tbl[i].default_amount; } if(!tbl[i].do_not_include_in_total) { diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py index b80b0beba3..e88164f917 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py @@ -13,3 +13,11 @@ class ManufacturingSettings(Document): def get_mins_between_operations(): return relativedelta(minutes=cint(frappe.db.get_single_value("Manufacturing Settings", "mins_between_operations")) or 10) + +@frappe.whitelist() +def is_material_consumption_enabled(): + if not hasattr(frappe.local, 'material_consumption'): + frappe.local.material_consumption = cint(frappe.db.get_single_value('Manufacturing Settings', + 'material_consumption')) + + return frappe.local.material_consumption \ No newline at end of file diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js index b8f324a85f..c1a9c448b4 100644 --- a/erpnext/projects/doctype/task/task.js +++ b/erpnext/projects/doctype/task/task.js @@ -80,15 +80,15 @@ frappe.ui.form.on("Task", { } }, - is_group: function(frm) { + is_group: function (frm) { frappe.call({ - method:"erpnext.projects.doctype.task.task.check_if_child_exists", + method: "erpnext.projects.doctype.task.task.check_if_child_exists", args: { name: frm.doc.name }, - callback: function(r){ - if(r.message){ - frappe.msgprint(__('Cannot convert it to non-group. Child Tasks exist.')); + callback: function (r) { + if (r.message.length > 0) { + frappe.msgprint(__(`Cannot convert it to non-group. The following child Tasks exist: ${r.message.join(", ")}.`)); frm.reload_doc(); } } diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 3dc52d4ebe..649d73a63f 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -2,12 +2,15 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe, json -from frappe.utils import getdate, date_diff, add_days, cstr +import json + +import frappe from frappe import _, throw +from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate from frappe.utils.nestedset import NestedSet + class CircularReferenceError(frappe.ValidationError): pass class Task(NestedSet): @@ -157,8 +160,10 @@ class Task(NestedSet): @frappe.whitelist() def check_if_child_exists(name): - return frappe.db.sql("""select name from `tabTask` - where parent_task = %s""", name) + child_tasks = frappe.get_all("Task", filters={"parent_task": name}) + child_tasks = [get_link_to_form("Task", task.name) for task in child_tasks] + return child_tasks + def get_project(doctype, txt, searchfield, start, page_len, filters): from erpnext.controllers.queries import get_match_cond @@ -237,4 +242,4 @@ def add_multiple_tasks(data, parent): new_task.insert() def on_doctype_update(): - frappe.db.add_index("Task", ["lft", "rgt"]) \ No newline at end of file + frappe.db.add_index("Task", ["lft", "rgt"]) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 5f435ced74..9a35aedb72 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -32,7 +32,6 @@ class SalesOrder(SellingController): def validate(self): super(SalesOrder, self).validate() - self.validate_order_type() self.validate_delivery_date() self.validate_proj_cust() @@ -342,9 +341,11 @@ class SalesOrder(SellingController): delivered_qty += item.delivered_qty tot_qty += item.qty - - self.db_set("per_delivered", flt(delivered_qty/tot_qty) * 100, - update_modified=False) + + if tot_qty != 0: + self.db_set("per_delivered", flt(delivered_qty/tot_qty) * 100, + update_modified=False) + def set_indicator(self): """Set indicator for portal""" diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index 718f29c611..7dc7c754bc 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.js +++ b/erpnext/selling/report/sales_analytics/sales_analytics.js @@ -73,7 +73,8 @@ frappe.query_reports["Sales Analytics"] = { events: { onCheckRow: function(data) { row_name = data[2].content; - row_values = data.slice(4).map(function (column) { + length = data.length + row_values = data.slice(4,length-1).map(function (column) { return column.content; }) entry = { @@ -102,12 +103,12 @@ frappe.query_reports["Sales Analytics"] = { labels: raw_data.labels, datasets: new_datasets } - + setTimeout(() => { frappe.query_report.chart.update(new_data) },200) - - + + setTimeout(() => { frappe.query_report.chart.draw(true); }, 800) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 2cc2f70401..8d99a9b789 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -166,7 +166,7 @@ class Analytics(object): for entity, period_data in iteritems(self.entity_periodic_data): row = { "entity": entity, - "entity_name": self.entity_names.get(entity) + "entity_name": self.entity_names.get(entity) } total = 0 for dummy, end_date in self.periodic_daterange: @@ -177,7 +177,7 @@ class Analytics(object): row["total"] = total self.data.append(row) - + def get_rows_by_group(self): self.get_periodic_data() out = [] @@ -185,7 +185,7 @@ class Analytics(object): for d in reversed(self.group_entries): row = { "entity": d.name, - "indent": self.depth_map.get(d.name) + "indent": self.depth_map.get(d.name) } total = 0 for dummy, end_date in self.periodic_daterange: @@ -275,7 +275,8 @@ class Analytics(object): self.parent_child_map = frappe._dict(frappe.db.sql(""" select name, supplier_group from `tabSupplier`""")) def get_chart_data(self): - labels = [d.get("label") for d in self.columns[2:]] + length = len(self.columns) + labels = [d.get("label") for d in self.columns[2:length-1]] self.chart = { "data": { 'labels': labels, diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 0c58fb2679..40bbc2c15f 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -363,7 +363,8 @@ def replace_abbr(company, old, new): for d in doc: _rename_record(d) - for dt in ["Warehouse", "Account", "Cost Center"]: + for dt in ["Warehouse", "Account", "Cost Center", "Department", "Location", + "Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]: _rename_records(dt) frappe.db.commit() diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index a26992a5ae..ccd5f363a4 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -199,12 +199,15 @@ frappe.ui.form.on('Stock Entry', { }, validate_purpose_consumption: function(frm) { - frappe.model.get_value('Manufacturing Settings', {'name': 'Manufacturing Settings'}, 'material_consumption', function(d) { - if (d.material_consumption==0 && frm.doc.purpose=="Material Consumption for Manufacture") { + frappe.call({ + method: "erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings.is_material_consumption_enabled", + }).then(r => { + if (cint(r.message) == 0 + && frm.doc.purpose=="Material Consumption for Manufacture") { frm.set_value("purpose", 'Manufacture'); frappe.throw(__('Material Consumption is not set in Manufacturing Settings.')); } - }) + }); }, company: function(frm) { diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.js b/erpnext/stock/report/stock_analytics/stock_analytics.js index 6010ea9ee2..bebc84e057 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.js +++ b/erpnext/stock/report/stock_analytics/stock_analytics.js @@ -88,10 +88,9 @@ frappe.query_reports["Stock Analytics"] = { events: { onCheckRow: function(data) { row_name = data[2].content; - row_values = data.slice(6).map(function (column) { + row_values = data.slice(7).map(function (column) { return column.content; }) - entry = { 'name':row_name, 'values':row_values @@ -118,12 +117,12 @@ frappe.query_reports["Stock Analytics"] = { labels: raw_data.labels, datasets: new_datasets } - + setTimeout(() => { frappe.query_report.chart.update(new_data) },200) - - + + setTimeout(() => { frappe.query_report.chart.draw(true); }, 800) diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index 5a8a672b63..dad8be1b8c 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -167,13 +167,11 @@ def get_data(filters): return data def get_chart_data(columns): - labels = [d.get("label") for d in columns[4:]] + labels = [d.get("label") for d in columns[5:]] chart = { "data": { 'labels': labels, - 'datasets':[ - { "values": ['0' for d in columns[4:]] } - ] + 'datasets':[] } } chart["type"] = "line" diff --git a/erpnext/templates/includes/fee/fee_row.html b/erpnext/templates/includes/fee/fee_row.html index ac2b1006ad..d5fd682236 100644 --- a/erpnext/templates/includes/fee/fee_row.html +++ b/erpnext/templates/includes/fee/fee_row.html @@ -5,13 +5,13 @@ {{ doc.program }}