From 3b04cfc8122ce21db6c2049c9e6714ae8fba7439 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 25 Sep 2017 15:25:00 +0530 Subject: [PATCH 01/27] minor fix --- erpnext/setup/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index f003ce4b1c..49c439ba5c 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -120,8 +120,9 @@ def enable_all_roles_and_domains(): _role.save() # add all roles to users - user = frappe.get_doc("User", "Administrator") - user.add_roles(*[role.get("name") for role in roles]) + if roles: + user = frappe.get_doc("User", "Administrator") + user.add_roles(*[role.get("name") for role in roles]) domains = frappe.get_list("Domain") if not domains: From c314485d558edba7bd4272374b8470f1bae3e804 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 28 Sep 2017 18:55:40 +0530 Subject: [PATCH 02/27] Fixes based on test case --- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- erpnext/controllers/item_variant.py | 4 +++- .../doctype/production_order/production_order.py | 9 +++++++-- erpnext/projects/doctype/project/project.py | 5 +++++ erpnext/regional/india/setup.py | 2 +- erpnext/selling/doctype/sales_order/test_sales_order.py | 2 +- .../item_variant_settings/item_variant_settings.js | 4 ++-- .../item_variant_settings/item_variant_settings.py | 4 ++-- 8 files changed, 22 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 4dae78c8c4..900a6e9d95 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1084,7 +1084,7 @@ class TestSalesInvoice(unittest.TestCase): si.items[0].price_list_rate = price_list_rate si.items[0].margin_type = 'Percentage' si.items[0].margin_rate_or_amount = 25 - si.insert() + si.save() self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate)) def test_outstanding_amount_after_advance_jv_cancelation(self): diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index ff11eb258d..5b5cd9e690 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -174,7 +174,8 @@ def copy_attributes_to_variant(item, variant): # copy non no-copy fields - exclude_fields = ["item_code", "item_name", "show_in_website"] + exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website", + "show_variant_in_website", "opening_stock", "variant_of", "valuation_rate", "variant_based_on"] if item.variant_based_on=='Manufacturer': # don't copy manufacturer values if based on part no @@ -186,6 +187,7 @@ def copy_attributes_to_variant(item, variant): if (field.reqd or field.fieldname in allow_fields) and field.fieldname not in exclude_fields: if variant.get(field.fieldname) != item.get(field.fieldname): variant.set(field.fieldname, item.get(field.fieldname)) + variant.variant_of = item.name variant.has_variants = 0 if not variant.description: diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index f04fddfae3..31aedb38f0 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -370,8 +370,13 @@ class ProductionOrder(Document): self.actual_start_date = None self.actual_end_date = None if self.get("operations"): - self.actual_start_date = min([d.actual_start_time for d in self.get("operations") if d.actual_start_time]) - self.actual_end_date = max([d.actual_end_time for d in self.get("operations") if d.actual_end_time]) + actual_start_dates = [d.actual_start_time for d in self.get("operations") if d.actual_start_time] + if actual_start_dates: + self.actual_start_date = min(actual_start_dates) + + actual_end_dates = [d.actual_end_time for d in self.get("operations") if d.actual_end_time] + if actual_end_dates: + self.actual_end_date = max(actual_end_dates) def delete_timesheet(self): for timesheet in frappe.get_all("Timesheet", ["name"], {"production_order": self.name}): diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 2cb93f02d1..9f4c2b9c35 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -53,12 +53,17 @@ class Project(Document): return frappe.get_all("Task", "*", {"project": self.name}, order_by="exp_start_date asc") def validate(self): + self.validate_project_name() self.validate_dates() self.validate_weights() self.sync_tasks() self.tasks = [] self.send_welcome_email() + def validate_project_name(self): + if frappe.db.exists("Project", self.project_name): + frappe.throw(_("Project {0} already exists").format(self.project_name)) + def validate_dates(self): if self.expected_start_date and self.expected_end_date: if getdate(self.expected_end_date) < getdate(self.expected_start_date): diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index fb40e32e37..4e23591e07 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -12,7 +12,7 @@ def setup(company=None, patch=True): make_custom_fields() add_permissions() add_custom_roles_for_reports() - frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes') + frappe.enqueue('erpnext.regional.india.setup.add_hsn_sac_codes', now=frappe.flags.in_test) add_print_formats() if not patch: update_address_template() diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 7c0d7f9dca..9c5c82edc4 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -494,7 +494,7 @@ class TestSalesOrder(unittest.TestCase): so.items[0].price_list_rate = price_list_rate = 100 so.items[0].margin_type = 'Percentage' so.items[0].margin_rate_or_amount = 25 - so.insert() + so.save() new_so = frappe.copy_doc(so) new_so.save(ignore_permissions=True) diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js index f3404cc78b..df78572dcb 100644 --- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js +++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js @@ -4,8 +4,8 @@ frappe.ui.form.on('Item Variant Settings', { setup: function(frm) { const allow_fields = []; - const exclude_fields = ["item_code", "item_name", "show_in_website", "show_variant_in_website", - "opening_stock", "variant_of", "valuation_rate", "variant_based_on"]; + const exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website", + "show_variant_in_website", "opening_stock", "variant_of", "valuation_rate", "variant_based_on"]; frappe.model.with_doctype('Item', () => { frappe.get_meta('Item').fields.forEach(d => { diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py index 80462d1ab8..0c6acd4290 100644 --- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py +++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py @@ -10,8 +10,8 @@ class ItemVariantSettings(Document): def set_default_fields(self): self.fields = [] fields = frappe.get_meta('Item').fields - exclude_fields = ["item_code", "item_name", "show_in_website", "show_variant_in_website", - "standard_rate", "opening_stock", "image", "description", + exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website", + "show_variant_in_website", "standard_rate", "opening_stock", "image", "description", "variant_of", "valuation_rate", "description", "variant_based_on", "website_image", "thumbnail", "website_specifiations", "web_long_description"] From 6d61a45f422913f925499713dd9391815bae078c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 29 Sep 2017 10:35:21 +0530 Subject: [PATCH 03/27] Removed default permission from GST doctypes --- .../doctype/gst_hsn_code/gst_hsn_code.json | 27 +++---------------- .../doctype/gst_settings/gst_settings.json | 27 +++---------------- 2 files changed, 6 insertions(+), 48 deletions(-) diff --git a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.json b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.json index 7b3a8d6b72..2a2145c7f7 100644 --- a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.json +++ b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.json @@ -84,34 +84,13 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-08-31 14:38:52.220743", - "modified_by": "ewdszx@ed.ews", + "modified": "2017-09-29 14:38:52.220743", + "modified_by": "Administrator", "module": "Regional", "name": "GST HSN Code", "name_case": "", "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], + "permissions": [], "quick_entry": 1, "read_only": 0, "read_only_onload": 0, diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.json b/erpnext/regional/doctype/gst_settings/gst_settings.json index 04065e29df..67084b45f8 100644 --- a/erpnext/regional/doctype/gst_settings/gst_settings.json +++ b/erpnext/regional/doctype/gst_settings/gst_settings.json @@ -83,34 +83,13 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2017-08-31 14:39:15.625952", - "modified_by": "ewdszx@ed.ews", + "modified": "2017-09-29 14:39:15.625952", + "modified_by": "Administrator", "module": "Regional", "name": "GST Settings", "name_case": "", "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], + "permissions": [], "quick_entry": 1, "read_only": 0, "read_only_onload": 0, From bdb4c542e7acc0c93e60bc6ea64801188f4235f9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 29 Sep 2017 10:39:32 +0530 Subject: [PATCH 04/27] default permission from GST doctypes --- erpnext/regional/india/setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 4e23591e07..68a52cc5ee 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -72,7 +72,6 @@ def add_custom_roles_for_reports(): def add_permissions(): for doctype in ('GST HSN Code', 'GST Settings'): - add_permission(doctype, 'Accounts Manager', 0) add_permission(doctype, 'All', 0) def add_print_formats(): From 9c339145b213db5ab3b7e8ad758affc54a4b39ea Mon Sep 17 00:00:00 2001 From: Makarand Bauskar Date: Fri, 29 Sep 2017 15:02:51 +0530 Subject: [PATCH 05/27] [Enhance] Custom notification messages for subscription documents (#10970) * [minor] configurable subscription email message and subject for notification * [minor] added description for subject field --- .../doctype/subscription/subscription.json | 99 ++++++++++++++++++- .../doctype/subscription/subscription.py | 27 +++-- 2 files changed, 118 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json index 85779533ea..902b06290e 100644 --- a/erpnext/accounts/doctype/subscription/subscription.json +++ b/erpnext/accounts/doctype/subscription/subscription.json @@ -439,7 +439,7 @@ "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 0, + "collapsible": 1, "columns": 0, "fieldname": "notification", "fieldtype": "Section Break", @@ -495,6 +495,38 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "depends_on": "eval: doc.notify_by_email", + "description": "To add dynamic subject, use jinja tags like\n\n
New {{ doc.doctype }} #{{ doc.name }}
", + "fieldname": "subject", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Subject", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -593,6 +625,69 @@ "bold": 0, "collapsible": 1, "columns": 0, + "depends_on": "eval:doc.notify_by_email", + "fieldname": "section_break_20", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Message", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "Please find attached {{ doc.doctype }} #{{ doc.name }}", + "fieldname": "message", + "fieldtype": "Text", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Message", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "columns": 0, + "depends_on": "eval: !doc.__islocal", "fieldname": "section_break_16", "fieldtype": "Section Break", "hidden": 0, @@ -690,7 +785,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-09-14 12:09:38.471458", + "modified": "2017-09-28 18:27:48.522098", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index c9df7d461e..b7ea96f0ce 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -7,6 +7,7 @@ import frappe import calendar from frappe import _ from frappe.desk.form import assign_to +from frappe.utils.jinja import validate_template from dateutil.relativedelta import relativedelta from frappe.utils.user import get_system_managers from frappe.utils import cstr, getdate, split_emails, add_days, today @@ -20,6 +21,9 @@ class Subscription(Document): self.validate_next_schedule_date() self.validate_email_id() + validate_template(self.subject or "") + validate_template(self.message or "") + def before_submit(self): self.set_next_schedule_date() @@ -114,7 +118,7 @@ def create_documents(data, schedule_date): doc = make_new_document(data, schedule_date) if data.notify_by_email and data.recipients: print_format = data.print_format or "Standard" - send_notification(doc, print_format, data.recipients) + send_notification(doc, data, print_format=print_format) frappe.db.commit() except Exception: @@ -174,14 +178,25 @@ def get_next_date(dt, mcount, day=None): return dt -def send_notification(new_rv, print_format='Standard', recipients=None): +def send_notification(new_rv, subscription_doc, print_format='Standard'): """Notify concerned persons about recurring document generation""" print_format = print_format - frappe.sendmail(recipients, - subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name), - message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name), - attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name, print_format=print_format)]) + if not subscription_doc.subject: + subject = _("New {0}: #{1}").format(new_rv.doctype, new_rv.name) + elif "{" in subscription_doc.subject: + subject = frappe.render_template(subscription_doc.subject, {'doc': new_rv}) + + if not subscription_doc.message: + message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name) + elif "{" in subscription_doc.message: + message = frappe.render_template(subscription_doc.message, {'doc': new_rv}) + + attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, + file_name=new_rv.name, print_format=print_format)] + + frappe.sendmail(subscription_doc.recipients, + subject=subject, message=message, attachments=attachments) def notify_errors(doc, doctype, party, owner, name): recipients = get_system_managers(only_name=True) From 945f5027484a5a86119578fb69879ce62877c69f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 29 Sep 2017 15:11:50 +0530 Subject: [PATCH 06/27] Fixes for updating item variant from template (#10975) * Fixes for updating item variant from template * More fixes for test cases --- erpnext/controllers/item_variant.py | 16 +++++++++++++--- erpnext/projects/doctype/project/project.py | 2 +- erpnext/stock/doctype/item/test_item.py | 2 ++ .../item_variant_settings.js | 2 +- .../item_variant_settings.py | 2 +- .../doctype/stock_entry/test_stock_entry.py | 5 +++-- .../stock/doctype/warehouse/test_records.json | 7 ------- erpnext/utilities/transaction_base.py | 2 +- 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 5b5cd9e690..e5564404ec 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import cstr, flt -import json +import json, copy class ItemVariantExistsError(frappe.ValidationError): pass class InvalidItemAttributeValueError(frappe.ValidationError): pass @@ -175,18 +175,28 @@ def copy_attributes_to_variant(item, variant): # copy non no-copy fields exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website", - "show_variant_in_website", "opening_stock", "variant_of", "valuation_rate", "variant_based_on"] + "show_variant_in_website", "opening_stock", "variant_of", "valuation_rate"] if item.variant_based_on=='Manufacturer': # don't copy manufacturer values if based on part no exclude_fields += ['manufacturer', 'manufacturer_part_no'] allow_fields = [d.field_name for d in frappe.get_all("Variant Field", fields = ['field_name'])] + if "variant_based_on" not in allow_fields: + allow_fields.append("variant_based_on") for field in item.meta.fields: # "Table" is part of `no_value_field` but we shouldn't ignore tables if (field.reqd or field.fieldname in allow_fields) and field.fieldname not in exclude_fields: if variant.get(field.fieldname) != item.get(field.fieldname): - variant.set(field.fieldname, item.get(field.fieldname)) + if field.fieldtype == "Table": + variant.set(field.fieldname, []) + for d in item.get(field.fieldname): + row = copy.deepcopy(d) + if row.get("name"): + row.name = None + variant.append(field.fieldname, row) + else: + variant.set(field.fieldname, item.get(field.fieldname)) variant.variant_of = item.name variant.has_variants = 0 diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 9f4c2b9c35..460ddc6210 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -61,7 +61,7 @@ class Project(Document): self.send_welcome_email() def validate_project_name(self): - if frappe.db.exists("Project", self.project_name): + if self.get("__islocal") and frappe.db.exists("Project", self.project_name): frappe.throw(_("Project {0} already exists").format(self.project_name)) def validate_dates(self): diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 34e3af6102..c3f399a536 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -120,6 +120,8 @@ class TestItem(unittest.TestCase): self.assertRaises(ItemVariantExistsError, variant.save) def test_copy_fields_from_template_to_variants(self): + frappe.delete_doc_if_exists("Item", "_Test Variant Item-XL", force=1) + fields = [{'field_name': 'item_group'}, {'field_name': 'is_stock_item'}] allow_fields = [d.get('field_name') for d in fields] set_item_variant_settings(fields) diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js index df78572dcb..24f7e31a0c 100644 --- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js +++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js @@ -5,7 +5,7 @@ frappe.ui.form.on('Item Variant Settings', { setup: function(frm) { const allow_fields = []; const exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website", - "show_variant_in_website", "opening_stock", "variant_of", "valuation_rate", "variant_based_on"]; + "show_variant_in_website", "opening_stock", "variant_of", "valuation_rate"]; frappe.model.with_doctype('Item', () => { frappe.get_meta('Item').fields.forEach(d => { diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py index 0c6acd4290..678de1a9ba 100644 --- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py +++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py @@ -12,7 +12,7 @@ class ItemVariantSettings(Document): fields = frappe.get_meta('Item').fields exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website", "show_variant_in_website", "standard_rate", "opening_stock", "image", "description", - "variant_of", "valuation_rate", "description", "variant_based_on", + "variant_of", "valuation_rate", "description", "website_image", "thumbnail", "website_specifiations", "web_long_description"] for d in fields: diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 4bcbcc4b6f..0aecb78ddd 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -11,7 +11,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError from erpnext.stock.stock_ledger import get_previous_sle from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation -from erpnext.stock.doctype.item.test_item import set_item_variant_settings +from erpnext.stock.doctype.item.test_item import set_item_variant_settings, make_item_variant from frappe.tests.test_permissions import set_user_permission_doctypes from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.accounts.doctype.account.test_account import get_inventory_account @@ -46,6 +46,7 @@ class TestStockEntry(unittest.TestCase): make_stock_entry(item_code=item_code, target=warehouse, qty=1, basic_rate=10) sle = get_sle(item_code = item_code, warehouse = warehouse)[0] + self.assertEqual([[1, 10]], frappe.safe_eval(sle.stock_queue)) # negative qty @@ -74,7 +75,6 @@ class TestStockEntry(unittest.TestCase): frappe.db.set_default("allow_negative_stock", 0) def test_auto_material_request(self): - from erpnext.stock.doctype.item.test_item import make_item_variant make_item_variant() self._test_auto_material_request("_Test Item") self._test_auto_material_request("_Test Item", material_request_type="Transfer") @@ -82,6 +82,7 @@ class TestStockEntry(unittest.TestCase): def test_auto_material_request_for_variant(self): fields = [{'field_name': 'reorder_levels'}] set_item_variant_settings(fields) + make_item_variant() template = frappe.get_doc("Item", "_Test Variant Item") if not template.reorder_levels: diff --git a/erpnext/stock/doctype/warehouse/test_records.json b/erpnext/stock/doctype/warehouse/test_records.json index af3bd231fc..014cf3e501 100644 --- a/erpnext/stock/doctype/warehouse/test_records.json +++ b/erpnext/stock/doctype/warehouse/test_records.json @@ -13,13 +13,6 @@ "warehouse_name": "_Test Scrap Warehouse", "is_group": 0 }, - { - "company": "_Test Company", - "create_account_under": "Stock Assets - _TC", - "doctype": "Warehouse", - "warehouse_name": "_Test Warehouse", - "is_group": 0 - }, { "company": "_Test Company", "create_account_under": "Fixed Assets - _TC", diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 0e3a4f9525..65310aa9e3 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -25,7 +25,7 @@ class TransactionBase(StatusUpdater): if not getattr(self, 'set_posting_time', None): now = now_datetime() self.posting_date = now.strftime('%Y-%m-%d') - self.posting_time = now.strftime('%H:%M:%S') + self.posting_time = now.strftime('%H:%M:%S.%f') def add_calendar_event(self, opts, force=False): if cstr(self.contact_by) != cstr(self._prev.contact_by) or \ From 367b90e3ae9162d9d06dfb90136c617ebc0ad6a6 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Fri, 29 Sep 2017 15:17:48 +0530 Subject: [PATCH 07/27] Add Data Import Tool desktop icon (#10916) fixes #8332 --- erpnext/config/desktop.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/config/desktop.py b/erpnext/config/desktop.py index ef1ff103fa..8fb66b1b70 100644 --- a/erpnext/config/desktop.py +++ b/erpnext/config/desktop.py @@ -268,5 +268,13 @@ def get_data(): "icon": "octicon octicon-plus", "type": "module", "label": _("Healthcare") - } + }, + { + "module_name": "Data Import Tool", + "color": "#7f8c8d", + "icon": "octicon octicon-circuit-board", + "type": "page", + "link": "data-import-tool", + "label": _("Data Import Tool") + }, ] From 3f7d96ecba62b9f9d148a8f499f49e8b55beadfb Mon Sep 17 00:00:00 2001 From: Shridhar Patil Date: Fri, 29 Sep 2017 15:18:43 +0530 Subject: [PATCH 08/27] Unassign from todo. (#10896) Unassign from todo when the status is changed to Closed or Cancelled --- erpnext/projects/doctype/task/task.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 43240b20e7..52ae132078 100644 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -47,7 +47,7 @@ class Task(Document): from frappe.desk.form.assign_to import clear clear(self.doctype, self.name) - + def validate_progress(self): if self.progress > 100: frappe.throw(_("Progress % for a task cannot be more than 100.")) @@ -63,6 +63,12 @@ class Task(Document): self.check_recursion() self.reschedule_dependent_tasks() self.update_project() + self.unassign_todo() + + def unassign_todo(self): + if self.status == "Closed" or self.status == "Cancelled": + from frappe.desk.form.assign_to import clear + clear(self.doctype, self.name) def update_total_expense_claim(self): self.total_expense_claim = frappe.db.sql("""select sum(total_sanctioned_amount) from `tabExpense Claim` @@ -120,7 +126,7 @@ class Task(Document): def has_webform_permission(doc): project_user = frappe.db.get_value("Project User", {"parent": doc.project, "user":frappe.session.user} , "user") if project_user: - return True + return True @frappe.whitelist() def get_events(start, end, filters=None): @@ -154,7 +160,7 @@ def get_project(doctype, txt, searchfield, start, page_len, filters): order by name limit %(start)s, %(page_len)s """ % {'key': searchfield, 'txt': "%%%s%%" % frappe.db.escape(txt), 'mcond':get_match_cond(doctype), - 'start': start, 'page_len': page_len}) + 'start': start, 'page_len': page_len}) @frappe.whitelist() @@ -170,4 +176,5 @@ def set_tasks_as_overdue(): where exp_end_date is not null and exp_end_date < CURDATE() and `status` not in ('Closed', 'Cancelled')""") - + + From 1b61dfd9eadc78fdfef9d60f2f6656ef099d8b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Rold=C3=A1n?= Date: Fri, 29 Sep 2017 06:49:12 -0300 Subject: [PATCH 09/27] translated (#10886) --- .../emails/recurring_document_failed.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/templates/emails/recurring_document_failed.html b/erpnext/templates/emails/recurring_document_failed.html index ea48034f41..27c43bc0fc 100644 --- a/erpnext/templates/emails/recurring_document_failed.html +++ b/erpnext/templates/emails/recurring_document_failed.html @@ -1,11 +1,11 @@

{{_("Recurring")}} {{ type }} {{ _("Failed")}}

-

An error occured while creating recurring {{ type }} {{ name }} for {{ party }}.

-

This could be because of some invalid Email Addresses in the {{ type }}.

-

To stop sending repetitive error notifications from the system, we have checked "Disabled" field in the subscription {{ subscription}} for the {{ type }} {{ name }}.

-

Please correct the {{ type }} and unchcked "Disabled" in the {{ subscription }} for making recurring again.

+

{{_("An error occured while creating recurring")}} {{ type }} {{ name }} {{_("for")}} {{ party }}.

+

{{_("This could be because of some invalid Email Addresses in the")}} {{ type }}.

+

{{_("To stop sending repetitive error notifications from the system, we have checked "Disabled" field in the subscription")}} {{ subscription}} {{_("for the")}} {{ type }} {{ name }}.

+

{{_("Please correct the")}} {{ type }} {{_('and unchcked "Disabled" in the')}} {{ subscription }} {{_("for making recurring again.")}}


-

It is necessary to take this action today itself for the above mentioned recurring {{ type }} -to be generated. If delayed, you will have to manually change the "Repeat on Day of Month" field -of this {{ type }} for generating the recurring {{ type }} in the subscription {{ subscription }}.

-

[This email is autogenerated]

+

{{_("It is necessary to take this action today itself for the above mentioned recurring")}} {{ type }} +{{_('to be generated. If delayed, you will have to manually change the "Repeat on Day of Month" field +of this')}} {{ type }} {{_("for generating the recurring")}} {{ type }} {{_("in the subscription")}} {{ subscription }}.

+

[{{_("This email is autogenerated")}}]

From b79c4a9ff639e5ec6001bb87c84c8256e8902bb3 Mon Sep 17 00:00:00 2001 From: Shreya Shah Date: Fri, 29 Sep 2017 15:20:48 +0530 Subject: [PATCH 10/27] Getting last purchase price of an item (#10897) * Added a column last purchase rate * Removed button last purchase rate * Get last purchase rate on adding an item * Added test case for last purchase rate * Replaced cur_frm with frm * Update purchase_order.js --- .../doctype/purchase_order/purchase_order.js | 24 ++--- .../purchase_order/purchase_order.json | 33 +------ .../doctype/purchase_order/purchase_order.py | 5 +- ..._purchase_order_with_last_purchase_rate.js | 99 +++++++++++++++++++ .../purchase_order_item.json | 33 ++++++- erpnext/tests/ui/tests.txt | 1 + 6 files changed, 148 insertions(+), 47 deletions(-) create mode 100644 erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_last_purchase_rate.js diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index a51246bcb8..8134e7e701 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -1,3 +1,4 @@ + // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt @@ -24,6 +25,18 @@ frappe.ui.form.on("Purchase Order", { }, }); +frappe.ui.form.on("Purchase Order Item", { + item_code: function(frm) { + frappe.call({ + method: "get_last_purchase_rate", + doc: frm.doc, + callback: function(r, rt) { + frm.trigger('calculate_taxes_and_totals'); + } + }) + } +}); + erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({ refresh: function(doc, cdt, cdn) { var me = this; @@ -214,17 +227,6 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( delivered_by_supplier: function(){ cur_frm.cscript.update_status('Deliver', 'Delivered') - }, - - get_last_purchase_rate: function() { - frappe.call({ - "method": "get_last_purchase_rate", - "doc": cur_frm.doc, - callback: function(r, rt) { - cur_frm.dirty(); - cur_frm.cscript.calculate_taxes_and_totals(); - } - }) } }); diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 919707c08d..07a80b87bd 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1206,37 +1206,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)", - "fieldname": "get_last_purchase_rate", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Get last purchase rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3458,7 +3427,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-09-19 11:22:30.190589", + "modified": "2017-09-22 16:11:49.856808", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 56f3059f2e..e2f5a9dca8 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -116,14 +116,13 @@ class PurchaseOrder(BuyingController): d.discount_percentage = last_purchase_details['discount_percentage'] d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0) d.price_list_rate = d.base_price_list_rate / conversion_rate - d.rate = d.base_rate / conversion_rate + d.last_purchase_rate = d.base_rate / conversion_rate else: - msgprint(_("Last purchase rate not found")) item_last_purchase_rate = frappe.db.get_value("Item", d.item_code, "last_purchase_rate") if item_last_purchase_rate: d.base_price_list_rate = d.base_rate = d.price_list_rate \ - = d.rate = item_last_purchase_rate + = d.last_purchase_rate = item_last_purchase_rate # Check for Closed status def check_for_closed_status(self): diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_last_purchase_rate.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_last_purchase_rate.js new file mode 100644 index 0000000000..d19f017425 --- /dev/null +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_last_purchase_rate.js @@ -0,0 +1,99 @@ +QUnit.module('Buying'); + +QUnit.test("test: purchase order with last purchase rate", function(assert) { + assert.expect(5); + let done = assert.async(); + + frappe.run_serially([ + () => { + return frappe.tests.make('Purchase Order', [ + {supplier: 'Test Supplier'}, + {is_subcontracted: 'No'}, + {currency: 'INR'}, + {items: [ + [ + {"item_code": 'Test Product 4'}, + {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, + {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, + {"qty": 1}, + {"rate": 800}, + {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} + ], + [ + {"item_code": 'Test Product 1'}, + {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, + {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, + {"qty": 1}, + {"rate": 400}, + {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} + ] + ]} + ]); + }, + + () => { + // Get item details + assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item 1 name correct"); + assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item 2 name correct"); + }, + + () => frappe.timeout(1), + + () => frappe.tests.click_button('Submit'), + () => frappe.tests.click_button('Yes'), + () => frappe.timeout(3), + + () => frappe.tests.click_button('Close'), + () => frappe.timeout(1), + + () => { + return frappe.tests.make('Purchase Order', [ + {supplier: 'Test Supplier'}, + {is_subcontracted: 'No'}, + {currency: 'INR'}, + {items: [ + [ + {"item_code": 'Test Product 4'}, + {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, + {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, + {"qty": 1}, + {"rate": 600}, + {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} + ], + [ + {"item_code": 'Test Product 1'}, + {"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)}, + {"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)}, + {"qty": 1}, + {"rate": 200}, + {"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))} + ] + ]} + ]); + }, + + () => frappe.timeout(2), + + // Get the last purchase rate of items + () => { + assert.ok(cur_frm.doc.items[0].last_purchase_rate == 800, "Last purchase rate of item 1 correct"); + }, + () => { + assert.ok(cur_frm.doc.items[1].last_purchase_rate == 400, "Last purchase rate of item 2 correct"); + }, + + () => frappe.tests.click_button('Submit'), + () => frappe.tests.click_button('Yes'), + () => frappe.timeout(3), + + () => frappe.tests.click_button('Close'), + + () => frappe.timeout(1), + + () => { + assert.ok(cur_frm.doc.status == 'To Receive and Bill', "Submitted successfully"); + }, + + () => done() + ]); +}); \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 2dd7b6c0ed..1ddce628a2 100755 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -655,6 +655,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "last_purchase_rate", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Last Purchase Rate", + "length": 0, + "no_copy": 0, + "options": "currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1714,7 +1745,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2017-08-02 22:15:47.411235", + "modified": "2017-09-22 16:47:08.783546", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/tests/ui/tests.txt b/erpnext/tests/ui/tests.txt index 909216b92e..199d886deb 100644 --- a/erpnext/tests/ui/tests.txt +++ b/erpnext/tests/ui/tests.txt @@ -128,3 +128,4 @@ erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js erpnext/accounts/doctype/payment_entry/tests/test_payment_against_invoice.js +erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_last_purchase_rate.js \ No newline at end of file From 5510d0751d23371895795bff307e024a0e326624 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Fri, 29 Sep 2017 10:51:59 +0100 Subject: [PATCH 11/27] correctly set frm company currency (#10944) --- erpnext/public/js/controllers/transaction.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 5f35ea44de..908d591e70 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -378,7 +378,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ if(me.frm.doc.company && me.frm.fields_dict.currency) { var company_currency = me.get_company_currency(); var company_doc = frappe.get_doc(":Company", me.frm.doc.company); - if (!me.frm.doc.currency) { + + if (!me.frm.doc.currency || me.frm.doc.currency != company_currency) { me.frm.set_value("currency", company_currency); } From bf379957456dac594de19fbf1c2b3ae229aa980e Mon Sep 17 00:00:00 2001 From: Javier Wong Date: Fri, 29 Sep 2017 17:53:08 +0800 Subject: [PATCH 12/27] [Enhancement] Sales and Purchase Default UOM (#10929) Option for specifying an optional default Sales and Purchase UOM at the item master level. --- erpnext/stock/doctype/item/item.json | 64 +++++++++++++++++++++++++++- erpnext/stock/get_item_details.py | 12 +++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index b168631970..525321c5a9 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -1473,6 +1473,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "purchase_uom", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Default Purchase Unit of Measure", + "length": 0, + "no_copy": 0, + "options": "UOM", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -2069,6 +2100,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "sales_uom", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Default Sales Unit of Measure", + "length": 0, + "no_copy": 0, + "options": "UOM", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -3143,7 +3205,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2017-07-06 18:28:36.645217", + "modified": "2017-09-27 14:08:02.948326", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 72a83c6c1b..0b92c250aa 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -164,6 +164,16 @@ def get_basic_details(args, item): warehouse = user_default_warehouse or item.default_warehouse or args.warehouse + #Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master + if args.get('doctype') in ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']: + uom = item.sales_uom if item.sales_uom else item.stock_uom + elif args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']: + uom = item.purchase_uom if item.purchase_uom else item.stock_uom + else: + uom = item.stock_uom + + args.uom = uom + out = frappe._dict({ "item_code": item.name, "item_name": item.item_name, @@ -178,7 +188,7 @@ def get_basic_details(args, item): "batch_no": None, "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in item.get("taxes")))), - "uom": item.stock_uom, + "uom": uom, "min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "", "qty": args.qty or 1.0, "stock_qty": args.qty or 1.0, From 5d8fd477bdc9df5e07634a962ce345125b1b8463 Mon Sep 17 00:00:00 2001 From: Pawan Mehta Date: Fri, 29 Sep 2017 15:23:54 +0530 Subject: [PATCH 13/27] [fix] #10840 (#10844) --- .../doctype/sales_invoice/sales_invoice.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 11d1825388..be01184882 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -520,6 +520,24 @@ frappe.ui.form.on('Sales Invoice', { }; }); }, + //When multiple companies are set up. in case company name is changed set default company address + company:function(frm){ + if (frm.doc.company) + { + frappe.call({ + method:"frappe.contacts.doctype.address.address.get_default_address", + args:{ doctype:'Company',name:frm.doc.company}, + callback: function(r){ + if (r.message){ + frm.set_value("company_address",r.message) + } + else { + frm.set_value("company_address","") + } + } + }) + } + }, project: function(frm){ frm.call({ From 6488645d426b96f2b4b1a4bbac52e254d51ea1c7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 29 Sep 2017 16:38:17 +0530 Subject: [PATCH 14/27] Fixed ui tests for production order --- .../doctype/production_order/test_production_order.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_order/test_production_order.js b/erpnext/manufacturing/doctype/production_order/test_production_order.js index 7ce67ba430..32cc3efd97 100644 --- a/erpnext/manufacturing/doctype/production_order/test_production_order.js +++ b/erpnext/manufacturing/doctype/production_order/test_production_order.js @@ -59,8 +59,6 @@ QUnit.test("test: production order", function (assert) { // Confirm the production order timesheet, save and submit it () => frappe.click_link("TS-00"), () => frappe.timeout(1), - () => frappe.click_button("Save"), - () => frappe.timeout(1), () => frappe.click_button("Submit"), () => frappe.timeout(1), () => frappe.click_button("Yes"), From 79a1d2a3b08ff584c6b7dacb7f5119d0ea3897b3 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 29 Sep 2017 18:14:10 +0530 Subject: [PATCH 15/27] Fixes for uom in get_item_details --- erpnext/controllers/accounts_controller.py | 3 --- erpnext/stock/get_item_details.py | 11 ++++++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d04143d77d..a9677b0e8d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -187,9 +187,6 @@ class AccountsController(TransactionBase): if stock_qty != len(get_serial_nos(item.get('serial_no'))): item.set(fieldname, value) - elif fieldname == "conversion_factor" and not item.get("conversion_factor"): - item.set(fieldname, value) - if ret.get("pricing_rule"): # if user changed the discount percentage then set user's discount percentage ? item.set("discount_percentage", ret.get("discount_percentage")) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 72a83c6c1b..539e8a5667 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -164,6 +164,15 @@ def get_basic_details(args, item): warehouse = user_default_warehouse or item.default_warehouse or args.warehouse + #Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master + if not args.uom: + if args.get('doctype') in ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']: + args.uom = item.sales_uom if item.sales_uom else item.stock_uom + elif args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']: + args.uom = item.purchase_uom if item.purchase_uom else item.stock_uom + else: + args.uom = item.stock_uom + out = frappe._dict({ "item_code": item.name, "item_name": item.item_name, @@ -178,7 +187,7 @@ def get_basic_details(args, item): "batch_no": None, "item_tax_rate": json.dumps(dict(([d.tax_type, d.tax_rate] for d in item.get("taxes")))), - "uom": item.stock_uom, + "uom": args.uom, "min_order_qty": flt(item.min_order_qty) if args.doctype == "Material Request" else "", "qty": args.qty or 1.0, "stock_qty": args.qty or 1.0, From 4b99fe15cc087e70041a06f26862e4b3cab94cc3 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Sat, 30 Sep 2017 11:41:26 +0530 Subject: [PATCH 16/27] [version] 9.x.x-develop --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 48ed7415d7..6d411cd1d7 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -11,7 +11,7 @@ app_email = "info@erpnext.com" app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" -develop_version = '8.x.x-beta' +develop_version = '9.x.x-develop' error_report_email = "support@erpnext.com" From b1bf5021198c567c02e93a42da53b3695b039a83 Mon Sep 17 00:00:00 2001 From: Makarand Bauskar Date: Mon, 2 Oct 2017 11:38:03 +0530 Subject: [PATCH 17/27] [hotfix] validate company name on 'The Brand' Slide instead of 'Your Organization' (#11007) --- erpnext/public/js/setup_wizard.js | 8 ++++---- erpnext/setup/setup_wizard/healthcare.py | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 88178f42ce..7c274f18db 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -86,6 +86,10 @@ erpnext.setup.slides_settings = [ }); }, validate: function() { + if ((this.values.company_name || "").toLowerCase() == "company") { + frappe.msgprint(__("Company Name cannot be Company")); + return false; + } if (!this.values.company_abbr) { return false; } @@ -135,10 +139,6 @@ erpnext.setup.slides_settings = [ frappe.msgprint(__("Please enter valid Financial Year Start and End Dates")); return false; } - if ((this.values.company_name || "").toLowerCase() == "company") { - frappe.msgprint(__("Company Name cannot be Company")); - return false; - } return true; }, diff --git a/erpnext/setup/setup_wizard/healthcare.py b/erpnext/setup/setup_wizard/healthcare.py index ebc644e87b..43a02371f4 100644 --- a/erpnext/setup/setup_wizard/healthcare.py +++ b/erpnext/setup/setup_wizard/healthcare.py @@ -191,21 +191,21 @@ def create_healthcare_item_groups(): def create_lab_test_items(): records = [ {"doctype": "Item", "item_code": "MCH", "item_name": "MCH", "item_group": "Laboratory", - "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + "stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, {"doctype": "Item", "item_code": "LDL", "item_name": "LDL", "item_group": "Laboratory", - "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + "stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, {"doctype": "Item", "item_code": "GTT", "item_name": "GTT", "item_group": "Laboratory", - "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + "stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, {"doctype": "Item", "item_code": "HDL", "item_name": "HDL", "item_group": "Laboratory", - "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + "stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, {"doctype": "Item", "item_code": "BILT", "item_name": "BILT", "item_group": "Laboratory", - "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + "stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, {"doctype": "Item", "item_code": "BILD", "item_name": "BILD", "item_group": "Laboratory", - "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + "stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, {"doctype": "Item", "item_code": "BP", "item_name": "BP", "item_group": "Laboratory", - "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, + "stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1}, {"doctype": "Item", "item_code": "BS", "item_name": "BS", "item_group": "Laboratory", - "stock_uom": "Unit", "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1} + "stock_uom": _("Unit"), "is_stock_item": 0, "is_purchase_item": 0, "is_sales_item": 1} ] insert_record(records) From cb38e599e5a720bf3789db12edb41f6c18448c29 Mon Sep 17 00:00:00 2001 From: Makarand Bauskar Date: Mon, 2 Oct 2017 11:40:43 +0530 Subject: [PATCH 18/27] [minor] don't create lead if customer contact is already created against contact_email (#10976) * [minor] don't create lead if customer contact is already created against contact_email * [tests] added tests cases for opportunity to check if lead is required or not --- .../crm/doctype/opportunity/opportunity.py | 20 ++++++++++- .../doctype/opportunity/test_opportunity.py | 36 ++++++++++++++++++- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 4251cae954..970fd570ff 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -42,10 +42,28 @@ class Opportunity(TransactionBase): if not self.with_items: self.items = [] - def make_new_lead_if_required(self): """Set lead against new opportunity""" if not (self.lead or self.customer) and self.contact_email: + # check if customer is already created agains the self.contact_email + customer = frappe.db.sql("""select + distinct `tabDynamic Link`.link_name as customer + from + `tabContact`, + `tabDynamic Link` + where `tabContact`.email_id='{0}' + and + `tabContact`.name=`tabDynamic Link`.parent + and + ifnull(`tabDynamic Link`.link_name, '')<>'' + and + `tabDynamic Link`.link_doctype='Customer' + """.format(self.contact_email), as_dict=True) + if customer and customer[0].customer: + self.customer = customer[0].customer + self.enquiry_from = "Customer" + return + lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email}) if not lead_name: sender_name = get_fullname(self.contact_email) diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 4cd20ea72d..61b583ce3b 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import today +from erpnext.crm.doctype.lead.lead import make_customer from erpnext.crm.doctype.opportunity.opportunity import make_quotation import unittest @@ -25,12 +26,45 @@ class TestOpportunity(unittest.TestCase): doc = frappe.get_doc('Opportunity', doc.name) self.assertEquals(doc.status, "Quotation") + def test_make_new_lead_if_required(self): + args = { + "doctype": "Opportunity", + "contact_email":"new.opportunity@example.com", + "enquiry_type": "Sales", + "with_items": 0, + "transaction_date": today() + } + # new lead should be created against the new.opportunity@example.com + opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) + + self.assertTrue(opp_doc.lead) + self.assertEquals(opp_doc.enquiry_from, "Lead") + self.assertEquals(frappe.db.get_value("Lead", opp_doc.lead, "email_id"), + 'new.opportunity@example.com') + + # create new customer and create new contact against 'new.opportunity@example.com' + customer = make_customer(opp_doc.lead).insert(ignore_permissions=True) + frappe.get_doc({ + "doctype": "Contact", + "email_id": "new.opportunity@example.com", + "first_name": "_Test Opportunity Customer", + "links": [{ + "link_doctype": "Customer", + "link_name": customer.name + }] + }).insert(ignore_permissions=True) + + opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) + self.assertTrue(opp_doc.customer) + self.assertEquals(opp_doc.enquiry_from, "Customer") + self.assertEquals(opp_doc.customer, customer.name) + def make_opportunity(**args): args = frappe._dict(args) opp_doc = frappe.get_doc({ "doctype": "Opportunity", - "enquiry_from": "Customer" or args.enquiry_from, + "enquiry_from": args.enquiry_from or "Customer", "enquiry_type": "Sales", "with_items": args.with_items or 0, "transaction_date": today() From 1f10d693e908af29dae0363d758271de838b1288 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 2 Oct 2017 13:20:51 +0530 Subject: [PATCH 19/27] Don't set currency as company currency if default currency is different (#11011) --- .../production_planning_tool/production_planning_tool.py | 7 +++---- erpnext/public/js/controllers/transaction.js | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) 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 815e504447..1d57a2faa0 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/production_planning_tool.py @@ -12,10 +12,6 @@ from erpnext.manufacturing.doctype.bom.bom import validate_bom_no from erpnext.manufacturing.doctype.production_order.production_order import get_item_details class ProductionPlanningTool(Document): - def __init__(self, arg1, arg2=None): - super(ProductionPlanningTool, self).__init__(arg1, arg2) - self.item_dict = {} - def clear_table(self, table_name): self.set(table_name, []) @@ -398,6 +394,9 @@ class ProductionPlanningTool(Document): return bom_wise_item_details def make_items_dict(self, item_list): + if not getattr(self, "item_dict", None): + self.item_dict = {} + for i in item_list: self.item_dict.setdefault(i[0], []).append([flt(i[1]), i[2], i[3], i[4], i[5]]) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 908d591e70..c83e6df92c 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -379,7 +379,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ var company_currency = me.get_company_currency(); var company_doc = frappe.get_doc(":Company", me.frm.doc.company); - if (!me.frm.doc.currency || me.frm.doc.currency != company_currency) { + if (!me.frm.doc.currency) { me.frm.set_value("currency", company_currency); } From 7eba1a35d33b77bf69f6e9102e243cd57509a489 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 2 Oct 2017 15:59:27 +0530 Subject: [PATCH 20/27] Controller init args fix (#11015) * Controller init args fix * cleanup useless code --- .../accounts/doctype/journal_entry/journal_entry.py | 4 ++-- .../doctype/purchase_invoice/purchase_invoice.py | 4 ++-- .../accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- erpnext/accounts/party.py | 6 ++++-- .../buying/doctype/purchase_order/purchase_order.py | 4 ++-- erpnext/controllers/accounts_controller.py | 4 ++-- erpnext/controllers/buying_controller.py | 2 +- erpnext/controllers/selling_controller.py | 3 ++- .../doctype/installation_note/installation_note.py | 4 ++-- erpnext/selling/doctype/quotation/quotation.py | 11 +++-------- erpnext/selling/doctype/sales_order/sales_order.py | 4 ++-- erpnext/setup/doctype/email_digest/email_digest.py | 7 +++---- erpnext/stock/doctype/delivery_note/delivery_note.py | 4 ++-- .../doctype/purchase_receipt/purchase_receipt.py | 4 ++-- erpnext/stock/doctype/serial_no/serial_no.py | 4 ++-- .../stock_reconciliation/stock_reconciliation.py | 4 ++-- 16 files changed, 35 insertions(+), 38 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 375d85d1b7..face5ede25 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -12,8 +12,8 @@ from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amo from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status class JournalEntry(AccountsController): - def __init__(self, arg1, arg2=None): - super(JournalEntry, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(JournalEntry, self).__init__(*args, **kwargs) def get_feed(self): return self.voucher_type diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a46c4b96c3..78c5682ef8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -22,8 +22,8 @@ form_grid_templates = { } class PurchaseInvoice(BuyingController): - def __init__(self, arg1, arg2=None): - super(PurchaseInvoice, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(PurchaseInvoice, self).__init__(*args, **kwargs) self.status_updater = [{ 'source_dt': 'Purchase Invoice Item', 'target_dt': 'Purchase Order Item', diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 7a787c4cba..6ab614863b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -27,8 +27,8 @@ form_grid_templates = { } class SalesInvoice(SellingController): - def __init__(self, arg1, arg2=None): - super(SalesInvoice, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(SalesInvoice, self).__init__(*args, **kwargs) self.status_updater = [{ 'source_dt': 'Sales Invoice Item', 'target_field': 'billed_amt', diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 69f40f8b25..bcec0a29c9 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -68,7 +68,8 @@ def set_address_details(out, party, party_type, doctype=None, company=None): billing_address_field = "customer_address" if party_type == "Lead" \ else party_type.lower() + "_address" out[billing_address_field] = get_default_address(party_type, party.name) - out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field])) + if doctype: + out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field])) # address display out.address_display = get_address_display(out[billing_address_field]) @@ -77,7 +78,8 @@ def set_address_details(out, party, party_type, doctype=None, company=None): if party_type in ["Customer", "Lead"]: out.shipping_address_name = get_default_address(party_type, party.name, 'is_shipping_address') out.shipping_address = get_address_display(out["shipping_address_name"]) - out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name)) + if doctype: + out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name)) if doctype and doctype in ['Delivery Note', 'Sales Invoice']: out.update(get_company_address(company)) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index e2f5a9dca8..36cef4396b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -20,8 +20,8 @@ form_grid_templates = { } class PurchaseOrder(BuyingController): - def __init__(self, arg1, arg2=None): - super(PurchaseOrder, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(PurchaseOrder, self).__init__(*args, **kwargs) self.status_updater = [{ 'source_dt': 'Purchase Order Item', 'target_dt': 'Material Request Item', diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a9677b0e8d..4f49c7747b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -15,8 +15,8 @@ from erpnext.exceptions import InvalidCurrency force_item_fields = ("item_group", "barcode", "brand", "stock_uom") class AccountsController(TransactionBase): - def __init__(self, arg1, arg2=None): - super(AccountsController, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(AccountsController, self).__init__(*args, **kwargs) @property def company_currency(self): diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 9cc061677e..1f9051d433 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -61,7 +61,7 @@ class BuyingController(StockController): # set contact and address details for supplier, if they are not mentioned if getattr(self, "supplier", None): - self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions)) + self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions, doctype=self.doctype, company=self.company)) self.set_missing_item_details(for_validate) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index d881f18b33..c1028a598d 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -49,7 +49,8 @@ class SellingController(StockController): if getattr(self, "customer", None): from erpnext.accounts.party import _get_party_details party_details = _get_party_details(self.customer, - ignore_permissions=self.flags.ignore_permissions) + ignore_permissions=self.flags.ignore_permissions, + doctype=self.doctype, company=self.company) if not self.meta.get_field("sales_team"): party_details.pop("sales_team") diff --git a/erpnext/selling/doctype/installation_note/installation_note.py b/erpnext/selling/doctype/installation_note/installation_note.py index 720247da56..9f730f4878 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.py +++ b/erpnext/selling/doctype/installation_note/installation_note.py @@ -12,8 +12,8 @@ from erpnext.stock.utils import get_valid_serial_nos from erpnext.utilities.transaction_base import TransactionBase class InstallationNote(TransactionBase): - def __init__(self, arg1, arg2=None): - super(InstallationNote, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(InstallationNote, self).__init__(*args, **kwargs) self.status_updater = [{ 'source_dt': 'Installation Note Item', 'target_dt': 'Delivery Note Item', diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 1cdd840428..f0cce5ffb7 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -32,7 +32,7 @@ class Quotation(SellingController): self.validate_valid_till() if self.items: self.with_items = 1 - + def validate_valid_till(self): if self.valid_till and self.valid_till < self.transaction_date: frappe.throw(_("Valid till date cannot be before transaction date")) @@ -79,15 +79,10 @@ class Quotation(SellingController): else: frappe.throw(_("Cannot set as Lost as Sales Order is made.")) - def check_item_table(self): - if not self.get('items'): - frappe.throw(_("Please enter item details")) - def on_submit(self): - self.check_item_table() - # Check for Approving Authority - frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total, self) + frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, + self.company, self.base_grand_total, self) #update enquiry status self.update_opportunity() diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 8720482549..613b0bb31f 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -22,8 +22,8 @@ form_grid_templates = { class WarehouseRequired(frappe.ValidationError): pass class SalesOrder(SellingController): - def __init__(self, arg1, arg2=None): - super(SalesOrder, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(SalesOrder, self).__init__(*args, **kwargs) def validate(self): super(SalesOrder, self).validate() diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index c85a541d84..8d1fb3d4a6 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -16,14 +16,13 @@ user_specific_content = ["calendar_events", "todo_list"] from frappe.model.document import Document class EmailDigest(Document): - def __init__(self, arg1, arg2=None): - super(EmailDigest, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(EmailDigest, self).__init__(*args, **kwargs) self.from_date, self.to_date = self.get_from_to_date() self.set_dates() self._accounts = {} - self.currency = frappe.db.get_value("Company", self.company, - "default_currency") + self.currency = frappe.db.get_value("Company", self.company, "default_currency") def get_users(self): """get list of users""" diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index f5a99afbd2..dd00398695 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -21,8 +21,8 @@ form_grid_templates = { } class DeliveryNote(SellingController): - def __init__(self, arg1, arg2=None): - super(DeliveryNote, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(DeliveryNote, self).__init__(*args, **kwargs) self.status_updater = [{ 'source_dt': 'Delivery Note Item', 'target_dt': 'Sales Order Item', diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 2d089c4419..e49f9937a5 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -19,8 +19,8 @@ form_grid_templates = { } class PurchaseReceipt(BuyingController): - def __init__(self, arg1, arg2=None): - super(PurchaseReceipt, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(PurchaseReceipt, self).__init__(*args, **kwargs) self.status_updater = [{ 'source_dt': 'Purchase Receipt Item', 'target_dt': 'Purchase Order Item', diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index c39efa06f7..80c93ef434 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -20,8 +20,8 @@ class SerialNoNotExistsError(ValidationError): pass class SerialNoDuplicateError(ValidationError): pass class SerialNo(StockController): - def __init__(self, arg1, arg2=None): - super(SerialNo, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(SerialNo, self).__init__(*args, **kwargs) self.via_stock_ledger = False def validate(self): diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 360ebca11e..0f91e43223 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -14,8 +14,8 @@ class OpeningEntryAccountError(frappe.ValidationError): pass class EmptyStockReconciliationItemsError(frappe.ValidationError): pass class StockReconciliation(StockController): - def __init__(self, arg1, arg2=None): - super(StockReconciliation, self).__init__(arg1, arg2) + def __init__(self, *args, **kwargs): + super(StockReconciliation, self).__init__(*args, **kwargs) self.head_row = ["Item Code", "Warehouse", "Quantity", "Valuation Rate"] def validate(self): From 7b6eaee05b36f58f3fccebac21dec7924e10b6d8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 3 Oct 2017 01:09:46 +0530 Subject: [PATCH 21/27] Fixes to handle async events (#11018) * Fixes to handle async events * transaction.js code cleanup * Don't map taxes and charges while making PO from SO for drop-ship * Removed print --- erpnext/public/js/controllers/transaction.js | 53 ++++++++++--------- .../student_applicant/student_applicant.py | 1 - .../doctype/sales_order/sales_order.py | 3 +- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index c83e6df92c..5b647f80d2 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -101,27 +101,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ return me.set_query_for_batch(doc, cdt, cdn) }); } - }, - onload: function() { - var me = this; - if(this.frm.doc.__islocal) { - var today = frappe.datetime.get_today(), - currency = frappe.defaults.get_user_default("currency"); - - $.each({ - currency: currency, - price_list_currency: currency, - status: "Draft", - is_subcontracted: "No", - }, function(fieldname, value) { - if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) - me.frm.set_value(fieldname, value); - }); - - if(this.frm.doc.company && !this.frm.doc.amended_from) { - this.frm.trigger("company"); - } - } if(this.frm.fields_dict["taxes"]) { this["taxes_remove"] = this.calculate_taxes_and_totals; @@ -153,11 +132,36 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ return { filters: filters - } + }; }); } + }, + onload: function() { + var me = this; this.setup_quality_inspection(); + + if(this.frm.doc.__islocal) { + var currency = frappe.defaults.get_user_default("currency"); + + let set_value = (fieldname, value) => { + if(me.frm.fields_dict[fieldname] && !me.frm.doc[fieldname]) { + return me.frm.set_value(fieldname, value); + } + }; + + return frappe.run_serially([ + () => set_value('currency', currency), + () => set_value('price_list_currency', currency), + () => set_value('status', 'Draft'), + () => set_value('is_subcontracted', 'No'), + () => { + if(this.frm.doc.company && !this.frm.doc.amended_from) { + this.frm.trigger("company"); + } + } + ]); + } }, setup_quality_inspection: function() { @@ -195,13 +199,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, onload_post_render: function() { - var me = this; if(this.frm.doc.__islocal && !(this.frm.doc.taxes || []).length && !(this.frm.doc.__onload ? this.frm.doc.__onload.load_after_mapping : false)) { - this.apply_default_taxes(); + frappe.after_ajax(() => this.apply_default_taxes()); } else if(this.frm.doc.__islocal && this.frm.doc.company && this.frm.doc["items"] && !this.frm.doc.is_pos) { - me.calculate_taxes_and_totals(); + frappe.after_ajax(() => this.calculate_taxes_and_totals()); } if(frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "item_code")) { this.setup_item_selector(); diff --git a/erpnext/schools/doctype/student_applicant/student_applicant.py b/erpnext/schools/doctype/student_applicant/student_applicant.py index 081fa065db..aeeffcedac 100644 --- a/erpnext/schools/doctype/student_applicant/student_applicant.py +++ b/erpnext/schools/doctype/student_applicant/student_applicant.py @@ -13,7 +13,6 @@ class StudentApplicant(Document): if self.student_admission: naming_series = frappe.db.get_value('Student Admission', self.student_admission, 'naming_series_for_student_applicant') - print(naming_series) if naming_series: self.naming_series = naming_series diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 613b0bb31f..98333c495e 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -696,7 +696,8 @@ def make_purchase_order_for_drop_shipment(source_name, for_supplier, target_doc= "contact_display", "contact_mobile", "contact_email", - "contact_person" + "contact_person", + "taxes_and_charges" ], "validation": { "docstatus": ["=", 1] From d3e21fff66d29e845c27910eeb2a183e851d0b27 Mon Sep 17 00:00:00 2001 From: KanchanChauhan Date: Wed, 4 Oct 2017 15:20:34 +0530 Subject: [PATCH 22/27] [Minor] Employee name as standard filter (#11043) --- erpnext/hr/doctype/employee/employee.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 87f05377cd..227302c16c 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -149,7 +149,7 @@ "in_filter": 0, "in_global_search": 1, "in_list_view": 1, - "in_standard_filter": 0, + "in_standard_filter": 1, "label": "Full Name", "length": 0, "no_copy": 0, @@ -431,7 +431,7 @@ "no_copy": 0, "oldfieldname": "gender", "oldfieldtype": "Select", - "options": "Gender", + "options": "Gender", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -2432,7 +2432,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-06-13 14:29:13.694009", + "modified": "2017-10-04 11:42:02.495731", "modified_by": "Administrator", "module": "HR", "name": "Employee", From 7e5a9f5c0ef178e2b622b49002bdb666e314a9b6 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 4 Oct 2017 15:51:34 +0530 Subject: [PATCH 23/27] Update new bom rate while replacing BOM (#11045) --- .../doctype/bom_update_tool/bom_update_tool.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 91b5070dbd..e3c61ed516 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -30,12 +30,13 @@ class BOMUpdateTool(Document): frappe.throw(_("The selected BOMs are not for the same item")) def update_new_bom(self): - current_bom_unitcost = frappe.db.sql("""select total_cost/quantity - from `tabBOM` where name = %s""", self.current_bom) - current_bom_unitcost = current_bom_unitcost and flt(current_bom_unitcost[0][0]) or 0 + new_bom_unitcost = frappe.db.sql("""select total_cost/quantity + from `tabBOM` where name = %s""", self.new_bom) + new_bom_unitcost = flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0 + frappe.db.sql("""update `tabBOM Item` set bom_no=%s, rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2""", - (self.new_bom, current_bom_unitcost, current_bom_unitcost, self.current_bom)) + (self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom)) def get_parent_boms(self): return [d[0] for d in frappe.db.sql("""select distinct parent From 3d0d4b2157218bf2fe4aeeaf3430fec9c1b25d5f Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 4 Oct 2017 15:51:55 +0530 Subject: [PATCH 24/27] update subscription period only if relevant date field exists (#11046) --- erpnext/accounts/doctype/subscription/subscription.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 8745ac6ed4..b40169a36c 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -142,7 +142,7 @@ def get_subscription_entries(date): def create_documents(data, schedule_date): try: doc = make_new_document(data, schedule_date) - if doc.from_date: + if getattr(doc, "from_date", None): update_subscription_period(data, doc) if data.notify_by_email and data.recipients: @@ -154,7 +154,7 @@ def create_documents(data, schedule_date): frappe.db.rollback() frappe.db.begin() frappe.log_error(frappe.get_traceback()) - disabled_subscription(data) + disable_subscription(data) frappe.db.commit() if data.reference_document and not frappe.flags.in_test: notify_error_to_user(data) @@ -166,7 +166,7 @@ def update_subscription_period(data, doc): frappe.db.set_value('Subscription', data.name, 'from_date', from_date) frappe.db.set_value('Subscription', data.name, 'to_date', to_date) -def disabled_subscription(data): +def disable_subscription(data): subscription = frappe.get_doc('Subscription', data.name) subscription.db_set('disabled', 1) From 32456b0f14bfe05435fcdca20bf5114a155309a3 Mon Sep 17 00:00:00 2001 From: Doridel Cahanap Date: Wed, 4 Oct 2017 18:24:44 +0800 Subject: [PATCH 25/27] [minor edits] BOM Stock Report (#11012) * HTML for BOM Stock Report to show filters in PDF * Added BOM Stock Report in Manufacturing Config under Report --- erpnext/config/manufacturing.py | 6 +++++ .../bom_stock_report/bom_stock_report.html | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html diff --git a/erpnext/config/manufacturing.py b/erpnext/config/manufacturing.py index 11711ad004..086d61b847 100644 --- a/erpnext/config/manufacturing.py +++ b/erpnext/config/manufacturing.py @@ -123,6 +123,12 @@ def get_data(): "is_query_report": True, "name": "BOM Search", "doctype": "BOM" + }, + { + "type": "report", + "is_query_report": True, + "name": "BOM Stock Report", + "doctype": "BOM" } ] }, diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html new file mode 100644 index 0000000000..119a4fc629 --- /dev/null +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html @@ -0,0 +1,27 @@ +

{%= __("BOM Stock Report") %}

+
{%= filters.bom %}
+
{%= filters.warehouse %}
+
+ + + + + + + + + + + + + {% for(var i=0, l=data.length; i + + + + + + + {% } %} + +
{%= __("Item") %}{%= __("Description") %}{%= __("Required Qty") %}{%= __("In Stock Qty") %}{%= __("Enough Parts to Build") %}
{%= data[i][ __("Item")] %}{%= data[i][ __("Description")] %} {%= data[i][ __("Required Qty")] %} {%= data[i][ __("In Stock Qty")] %} {%= data[i][ __("Enough Parts to Build")] %}
\ No newline at end of file From 1b67d71139dfaa06bde9cdac39a0b9ad9a9b5a49 Mon Sep 17 00:00:00 2001 From: Makarand Bauskar Date: Wed, 4 Oct 2017 15:55:56 +0530 Subject: [PATCH 26/27] [minor] add the Lead in email account -> append_to field (#10983) --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 6d411cd1d7..a6dbd20671 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -47,7 +47,7 @@ treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Grou update_website_context = "erpnext.shopping_cart.utils.update_website_context" my_account_context = "erpnext.shopping_cart.utils.update_my_account_context" -email_append_to = ["Job Applicant", "Opportunity", "Issue"] +email_append_to = ["Job Applicant", "Lead", "Opportunity", "Issue"] calendars = ["Task", "Production Order", "Leave Application", "Sales Order", "Holiday List"] From 61287e3c53618f3d1620a69f2e576a43b653a6ae Mon Sep 17 00:00:00 2001 From: Javier Wong Date: Wed, 4 Oct 2017 18:31:34 +0800 Subject: [PATCH 27/27] [fix] Change Sample Item Error Message to Zero Valuation Rate (#10935) Change Sample Item Error Message to Zero Valuation Rate --- erpnext/stock/stock_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 7c6b34bd90..180ccbb3e2 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -461,6 +461,6 @@ def get_valuation_rate(item_code, warehouse, voucher_type, voucher_no, if not allow_zero_rate and not valuation_rate \ and cint(erpnext.is_perpetual_inventory_enabled(company)): frappe.local.message_log = [] - frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a sample item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting/cancelling this entry").format(item_code, voucher_type, voucher_no)) + frappe.throw(_("Valuation rate not found for the Item {0}, which is required to do accounting entries for {1} {2}. If the item is transacting as a zero valuation rate item in the {1}, please mention that in the {1} Item table. Otherwise, please create an incoming stock transaction for the item or mention valuation rate in the Item record, and then try submiting/cancelling this entry").format(item_code, voucher_type, voucher_no)) return valuation_rate