diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d6dab05fea9..bc48b8ae5c3 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '10.1.40' +__version__ = '10.1.41' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 159fb4c08b4..97c2be22599 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -815,28 +815,30 @@ frappe.ui.form.on('Payment Entry Reference', { reference_name: function(frm, cdt, cdn) { var row = locals[cdt][cdn]; - return frappe.call({ - method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_reference_details", - args: { - reference_doctype: row.reference_doctype, - reference_name: row.reference_name, - party_account_currency: frm.doc.payment_type=="Receive" ? - frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency - }, - callback: function(r, rt) { - if(r.message) { - $.each(r.message, function(field, value) { - frappe.model.set_value(cdt, cdn, field, value); - }) + if (row.reference_name && row.reference_doctype) { + return frappe.call({ + method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_reference_details", + args: { + reference_doctype: row.reference_doctype, + reference_name: row.reference_name, + party_account_currency: frm.doc.payment_type=="Receive" ? + frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency + }, + callback: function(r, rt) { + if(r.message) { + $.each(r.message, function(field, value) { + frappe.model.set_value(cdt, cdn, field, value); + }) - let allocated_amount = frm.doc.unallocated_amount > row.outstanding_amount ? - row.outstanding_amount : frm.doc.unallocated_amount; + let allocated_amount = frm.doc.unallocated_amount > row.outstanding_amount ? + row.outstanding_amount : frm.doc.unallocated_amount; - frappe.model.set_value(cdt, cdn, 'allocated_amount', allocated_amount); - frm.refresh_fields(); + frappe.model.set_value(cdt, cdn, 'allocated_amount', allocated_amount); + frm.refresh_fields(); + } } - } - }) + }) + } }, allocated_amount: function(frm) { diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 11fcde204b2..0d40b2188d9 100755 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1485,7 +1485,7 @@ "unique": 0 }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 1, "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, @@ -4493,7 +4493,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-06-27 20:38:28.471827", + "modified": "2018-07-06 02:38:40.310899", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 663042fc596..7dd7fb64f22 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1586,7 +1586,7 @@ "unique": 0 }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 1, "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, @@ -5363,25 +5363,25 @@ "translatable": 0, "unique": 0 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-file-text", - "idx": 181, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2018-06-28 12:33:36.665034", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Sales Invoice", - "name_case": "Title Case", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "fa fa-file-text", + "idx": 181, + "image_view": 0, + "in_create": 0, + "is_submittable": 1, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "menu_index": 0, + "modified": "2018-07-06 12:09:02.039783", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Invoice", + "name_case": "Title Case", + "owner": "Administrator", "permissions": [ { "amend": 1, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 7f11f3563ea..92c6ec48375 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -347,6 +347,7 @@ class SalesInvoice(SellingController): if not for_validate and not self.customer: self.customer = pos.customer + self.ignore_pricing_rule = pos.ignore_pricing_rule if pos.get('account_for_change_amount'): self.account_for_change_amount = pos.get('account_for_change_amount') diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index 1315b25d75c..cf8d5490e6f 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -39,6 +39,12 @@ frappe.query_reports["Budget Variance Report"] = { options: ["Cost Center", "Project"], default: "Cost Center", reqd: 1 - } + }, + { + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center" + }, ] } diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index c7ec9963f90..75739d90077 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -12,9 +12,13 @@ from six import iteritems def execute(filters=None): if not filters: filters = {} - + validate_filters(filters) columns = get_columns(filters) - cost_centers = get_cost_centers(filters) + if filters.get("cost_center"): + cost_centers = [filters.get("cost_center")] + else: + cost_centers = get_cost_centers(filters) + period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"]) cam_map = get_cost_center_account_month_map(filters) @@ -41,6 +45,10 @@ def execute(filters=None): return columns, data +def validate_filters(filters): + if filters.get("budget_against")=="Project" and filters.get("cost_center"): + frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center")) + def get_columns(filters): columns = [_(filters.get("budget_against")) + ":Link/%s:120"%(filters.get("budget_against")), _("Account") + ":Link/Account:120"] @@ -68,12 +76,16 @@ def get_cost_centers(filters): #Get cost center & target details def get_cost_center_target_details(filters): + cond = "" + if filters.get("cost_center"): + cond += " and b.cost_center='%s'" % frappe.db.escape(filters.get("cost_center")) + return frappe.db.sql(""" select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount from `tabBudget` b, `tabBudget Account` ba where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year=%s - and b.budget_against = %s and b.company=%s - """.format(budget_against=filters.get("budget_against").replace(" ", "_").lower()), + and b.budget_against = %s and b.company=%s {cond} + """.format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond), (filters.fiscal_year, filters.budget_against, filters.company), as_dict=True) #Get target distribution details of accounts of cost center diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 0032c80c971..ccd3a82f7d3 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -53,7 +53,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum delivery_note, d.income_account, d.cost_center, d.stock_qty, d.stock_uom ] - row += [d.base_net_rate/d.stock_qty, d.base_net_amount] \ + row += [(d.base_net_rate * d.qty)/d.stock_qty, d.base_net_amount] \ if d.stock_uom != d.uom else [d.base_net_rate, d.base_net_amount] total_tax = 0 @@ -133,7 +133,7 @@ def get_items(filters, additional_query_columns): `tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount, `tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail, - `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom {0} + `tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0} from `tabSales Invoice`, `tabSales Invoice Item` where `tabSales Invoice`.name = `tabSales Invoice Item`.parent and `tabSales Invoice`.docstatus = 1 %s %s diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 8a185c2e01e..9f39cbfe80c 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1386,7 +1386,7 @@ "unique": 0 }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 1, "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, @@ -3737,7 +3737,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-06-27 20:39:06.769610", + "modified": "2018-07-06 11:00:05.037716", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 68ad14090dc..b7e1e332d6e 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -832,7 +832,7 @@ "unique": 0 }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 1, "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, @@ -2779,7 +2779,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-06-27 20:39:36.281267", + "modified": "2018-07-06 02:45:48.616334", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 949ca69d89c..cfc630a0a02 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -154,6 +154,8 @@ class AccountsController(TransactionBase): self.meta.get_label(date_field), self) def validate_due_date(self): + if self.get('is_pos'): return + from erpnext.accounts.party import validate_due_date if self.doctype == "Sales Invoice": if not self.due_date: diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 854e9bb4a9e..7f72f30231a 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -178,6 +178,14 @@ def create_variant(item, args): @frappe.whitelist() def enqueue_multiple_variant_creation(item, args): # There can be innumerable attribute combinations, enqueue + if isinstance(args, basestring): + variants = json.loads(args) + total_variants = 1 + for key in variants: + total_variants *= len(variants[key]) + if total_variants >= 600: + frappe.msgprint("Please do not create more than 500 items at a time", raise_exception=1) + return frappe.enqueue("erpnext.controllers.item_variant.create_multiple_variants", item=item, args=args, now=frappe.flags.in_test); diff --git a/erpnext/hr/doctype/appraisal/appraisal.js b/erpnext/hr/doctype/appraisal/appraisal.js index 64d6f40e0e8..30317d96c03 100644 --- a/erpnext/hr/doctype/appraisal/appraisal.js +++ b/erpnext/hr/doctype/appraisal/appraisal.js @@ -24,6 +24,7 @@ cur_frm.cscript.refresh = function(doc,cdt,cdn){ } cur_frm.cscript.kra_template = function(doc, dt, dn) { + doc.goals = []; erpnext.utils.map_current_doc({ method: "erpnext.hr.doctype.appraisal.appraisal.fetch_appraisal_template", source_name: cur_frm.doc.kra_template, diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index 25a42f2b094..bc6ffc1522e 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -356,7 +356,7 @@ class LeaveApplication(Document): @frappe.whitelist() def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None): number_of_days = 0 - if half_day == 1: + if cint(half_day) == 1: if from_date == to_date: number_of_days = 0.5 else: diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js index a4b48af5a58..e4b8a202882 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js @@ -22,6 +22,21 @@ frappe.ui.form.on('BOM Update Tool', { frm.disable_save(); }, + replace: function(frm) { + if (frm.doc.current_bom && frm.doc.new_bom) { + frappe.call({ + method: "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.enqueue_replace_bom", + freeze: true, + args: { + args: { + "current_bom": frm.doc.current_bom, + "new_bom": frm.doc.new_bom + } + } + }); + } + }, + update_latest_price_in_all_boms: function() { frappe.call({ method: "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.enqueue_update_cost", diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.json b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.json index ab63c0b540b..b348bb78f3f 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.json +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.json @@ -123,7 +123,7 @@ "label": "Replace", "length": 0, "no_copy": 0, - "options": "replace_bom", + "options": "", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -208,7 +208,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2017-07-31 18:08:05.919276", + "modified": "2018-07-02 16:17:09.014102", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Update Tool", diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py index ec948eb5ed3..04f9717c08b 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -3,9 +3,10 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe +import frappe, json from frappe.utils import cstr, flt from frappe import _ +from six import string_types from erpnext.manufacturing.doctype.bom.bom import get_boms_in_bottom_up_order from frappe.model.document import Document @@ -17,12 +18,14 @@ class BOMUpdateTool(Document): updated_bom = [] for bom in bom_list: bom_obj = frappe.get_doc("BOM", bom) + bom_obj.get_doc_before_save() updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom) bom_obj.calculate_cost() bom_obj.update_parent_cost() bom_obj.db_update() - - frappe.msgprint(_("BOM replaced")) + if (getattr(bom_obj.meta, 'track_changes', False) + and bom_obj._doc_before_save and not bom_obj.flags.ignore_version): + bom_obj.save_version() def validate_bom(self): if cstr(self.current_bom) == cstr(self.new_bom): @@ -54,6 +57,14 @@ class BOMUpdateTool(Document): return bom_list +@frappe.whitelist() +def enqueue_replace_bom(args): + if isinstance(args, string_types): + args = json.loads(args) + + frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args) + frappe.msgprint(_("Queued for replacing the BOM. It may take a few minutes.")) + @frappe.whitelist() def enqueue_update_cost(): frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_cost") @@ -63,6 +74,14 @@ def update_latest_price_in_all_boms(): if frappe.db.get_single_value("Manufacturing Settings", "update_bom_costs_automatically"): update_cost() +def replace_bom(args): + args = frappe._dict(args) + + doc = frappe.get_doc("BOM Update Tool") + doc.current_bom = args.current_bom + doc.new_bom = args.new_bom + doc.replace_bom() + def update_cost(): bom_list = get_boms_in_bottom_up_order() for bom in bom_list: diff --git a/erpnext/non_profit/doctype/membership/membership.json b/erpnext/non_profit/doctype/membership/membership.json index 42e084d54f3..694a4fafeaa 100644 --- a/erpnext/non_profit/doctype/membership/membership.json +++ b/erpnext/non_profit/doctype/membership/membership.json @@ -15,6 +15,7 @@ "fields": [ { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -42,10 +43,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -73,10 +76,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -102,10 +107,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -133,15 +140,17 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "membership_validaty_section", + "fieldname": "membership_validity_section", "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, @@ -150,7 +159,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "membership validaty section", + "label": "Validity", "length": 0, "no_copy": 0, "permlevel": 0, @@ -163,10 +172,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -193,10 +204,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -223,10 +236,12 @@ "reqd": 1, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -252,10 +267,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -282,10 +299,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -312,10 +331,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -342,10 +363,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -373,10 +396,12 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -403,6 +428,7 @@ "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, "unique": 0 } ], @@ -416,7 +442,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-12-05 07:02:56.859408", + "modified": "2018-06-26 19:23:17.911121", "modified_by": "Administrator", "module": "Non Profit", "name": "Membership", @@ -425,7 +451,6 @@ "permissions": [ { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -445,7 +470,6 @@ }, { "amend": 0, - "apply_user_permissions": 0, "cancel": 0, "create": 1, "delete": 1, @@ -472,5 +496,6 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file + "track_seen": 0, + "track_views": 0 +} diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index ad7640061f3..d7bd06f081b 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, getdate, get_url +from frappe.utils import flt, getdate, get_url, now from frappe import _ from frappe.model.document import Document @@ -63,6 +63,7 @@ class Project(Document): self.validate_weights() self.sync_tasks() self.tasks = [] + self.load_tasks() self.send_welcome_email() def validate_project_name(self): @@ -87,36 +88,68 @@ class Project(Document): """sync tasks and remove table""" if self.flags.dont_sync_tasks: return task_names = [] + + existing_task_data = {} + for d in frappe.get_all('Project Task', + fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"], + filters = {'parent': self.name}): + existing_task_data.setdefault(d.task_id, d) + for t in self.tasks: if t.task_id: task = frappe.get_doc("Task", t.task_id) else: task = frappe.new_doc("Task") task.project = self.name - task.update({ - "subject": t.title, - "status": t.status, - "exp_start_date": t.start_date, - "exp_end_date": t.end_date, - "description": t.description, - "task_weight": t.task_weight - }) - self.map_custom_fields(t, task) + if not t.task_id or self.is_row_updated(t, existing_task_data): + task.update({ + "subject": t.title, + "status": t.status, + "exp_start_date": t.start_date, + "exp_end_date": t.end_date, + "description": t.description, + "task_weight": t.task_weight + }) - task.flags.ignore_links = True - task.flags.from_project = True - task.flags.ignore_feed = True - task.save(ignore_permissions=True) - task_names.append(task.name) + self.map_custom_fields(t, task) + + task.flags.ignore_links = True + task.flags.from_project = True + task.flags.ignore_feed = True + + if t.task_id: + task.update({ + "modified_by": frappe.session.user, + "modified": now() + }) + + task.validate() + task.db_update() + else: + task.save(ignore_permissions = True) + task_names.append(task.name) + else: + task_names.append(task.name) # delete for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}): frappe.delete_doc("Task", t.name) + def update_costing_and_percentage_complete(self): self.update_percent_complete() self.update_costing() + def is_row_updated(self, row, existing_task_data): + if self.get("__islocal") or not existing_task_data: return True + + d = existing_task_data.get(row.task_id) + + if (d and (row.title != d.title or row.status != d.status + or getdate(row.start_date) != getdate(d.start_date) or getdate(row.end_date) != getdate(d.end_date) + or row.description != d.description or row.task_weight != d.task_weight)): + return True + def map_custom_fields(self, source, target): project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname") @@ -177,8 +210,7 @@ class Project(Document): from_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) as total_sanctioned_amount from `tabExpense Claim` where project = %s - and docstatus = 1""", - self.name, as_dict=1)[0] + and docstatus = 1""", self.name, as_dict=1)[0] self.actual_start_date = from_time_sheet.start_date self.actual_end_date = from_time_sheet.end_date @@ -216,6 +248,10 @@ class Project(Document): self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0 + def after_rename(self, old_name, new_name, merge=False): + if old_name == self.copied_from: + frappe.db.set_value('Project', new_name, 'copied_from', new_name) + def send_welcome_email(self): url = get_url("/project/?name={0}".format(self.name)) messages = ( @@ -236,8 +272,7 @@ class Project(Document): user.welcome_email_sent = 1 def on_update(self): - self.load_tasks() - self.sync_tasks() + self.update_costing_and_percentage_complete() self.update_dependencies_on_duplicated_project() def update_dependencies_on_duplicated_project(self): @@ -260,9 +295,7 @@ class Project(Document): continue name = _task.name - depends_on_tasks = _task.depends_on_tasks - depends_on_tasks = [x for x in depends_on_tasks.split(',') if x] dependency_map[task.title] = [x['subject'] for x in frappe.get_list( 'Task Depends On', {"parent": name}, ['subject'])] @@ -274,7 +307,8 @@ class Project(Document): for dt in value: dt_name = frappe.db.get_value('Task', {"subject": dt, "project": self.name}) task_doc.append('depends_on', {"task": dt_name}) - task_doc.save() + + task_doc.update_db() def get_timeline_data(doctype, name): diff --git a/erpnext/projects/doctype/project_task/project_task.json b/erpnext/projects/doctype/project_task/project_task.json index 79790e57b06..d215d63bc93 100644 --- a/erpnext/projects/doctype/project_task/project_task.json +++ b/erpnext/projects/doctype/project_task/project_task.json @@ -61,7 +61,7 @@ "label": "Status", "length": 0, "no_copy": 1, - "options": "Open\nWorking\nPending Review\nClosed\nCancelled", + "options": "Open\nWorking\nPending Review\nOverdue\nClosed\nCancelled", "permlevel": 0, "precision": "", "print_hide": 0, @@ -371,7 +371,7 @@ "remember_last_selected_value": 1, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "unique": 0 } @@ -386,7 +386,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-12-19 14:49:15.886339", + "modified": "2018-07-05 19:34:31.204454", "modified_by": "Administrator", "module": "Projects", "name": "Project Task", diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json index 9323e206e93..6c56102d674 100644 --- a/erpnext/projects/doctype/task/task.json +++ b/erpnext/projects/doctype/task/task.json @@ -38,7 +38,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -71,7 +71,7 @@ "remember_last_selected_value": 1, "report_hide": 0, "reqd": 0, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "translatable": 0, "unique": 0 @@ -1315,7 +1315,7 @@ "istable": 0, "max_attachments": 5, "menu_index": 0, - "modified": "2018-05-10 03:47:12.256088", + "modified": "2018-06-26 11:46:06.678115", "modified_by": "Administrator", "module": "Projects", "name": "Task", diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index aa27d6159de..3c8328b1074 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -17,7 +17,7 @@ def setup(company=None, patch=True): def make_custom_fields(): invoice_fields = [ dict(fieldname='vat_section', label='VAT Details', fieldtype='Section Break', - insert_after='select_print_heading', print_hide=1, collapsible=1), + insert_after='group_same_items', print_hide=1, collapsible=1), dict(fieldname='permit_no', label='Permit Number', fieldtype='Data', insert_after='vat_section', print_hide=1), dict(fieldname='reverse_charge_applicable', label='Reverse Charge Applicable', diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index ff4add2d8e3..c708de037f0 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -190,7 +190,7 @@ cur_frm.cscript['Declare Order Lost'] = function(){ return cur_frm.call({ method: "declare_order_lost", doc: cur_frm.doc, - args: args.reason, + args: args, callback: function(r) { if(r.exc) { frappe.msgprint(__("There were errors.")); diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index ee09c08e371..ccbb942d402 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -1145,7 +1145,7 @@ "unique": 0 }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 1, "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, @@ -3163,8 +3163,8 @@ "istable": 0, "max_attachments": 1, "menu_index": 0, - "modified": "2018-06-27 20:35:10.333687", - "modified_by": "Administrator", + "modified": "2018-07-06 03:23:15.354674", + "modified_by": "Administrator", "module": "Selling", "name": "Quotation", "owner": "Administrator", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 509685aaedd..729256068be 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -70,10 +70,10 @@ class Quotation(SellingController): opp.status = None opp.set_status(update=True) - def declare_order_lost(self, arg): + def declare_order_lost(self, reason): if not self.has_sales_order(): frappe.db.set(self, 'status', 'Lost') - frappe.db.set(self, 'order_lost_reason', arg) + frappe.db.set(self, 'order_lost_reason', reason) self.update_opportunity() self.update_lead() else: diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 9223adb4702..c0969d05649 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -1204,7 +1204,7 @@ "unique": 0 }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 1, "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, @@ -3914,7 +3914,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-06-27 20:30:03.339611", + "modified": "2018-07-06 15:56:12.483019", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 9dde2c38e00..ffcda03ec03 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -1294,7 +1294,7 @@ "unique": 0 }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 1, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -3881,7 +3881,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-06-13 19:07:27.314521", + "modified": "2018-07-06 03:03:35.035396", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index 7da305caf92..1ef16615116 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -294,7 +294,7 @@ "unique": 0 }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 1, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -746,7 +746,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-12-03 18:45:10.293916", + "modified": "2018-07-06 18:24:17.148782", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.py b/erpnext/stock/doctype/material_request_item/material_request_item.py index 16f007f6a20..6c6ecfea831 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.py +++ b/erpnext/stock/doctype/material_request_item/material_request_item.py @@ -6,10 +6,12 @@ from __future__ import unicode_literals import frappe +from erpnext.controllers.print_settings import print_settings_for_item_table from frappe.model.document import Document class MaterialRequestItem(Document): - pass + def __setup__(self): + print_settings_for_item_table(self) def on_doctype_update(): frappe.db.add_index("Material Request Item", ["item_code", "warehouse"]) \ No newline at end of file diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index ac3ecdd6b4c..1f47317b2a9 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1072,7 +1072,7 @@ "unique": 0 }, { - "allow_bulk_edit": 0, + "allow_bulk_edit": 1, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -3436,7 +3436,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2018-06-13 19:07:37.183239", + "modified": "2018-07-06 02:59:59.609643", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt",