diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js
index 3c135d466c1..001fc26ffe7 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js
@@ -24,26 +24,6 @@ frappe.ui.form.on('Asset Maintenance', {
return indicator;
}
);
-
- frm.set_query('select_serial_no', function(doc){
- return {
- asset: frm.doc.asset_name
- }
- })
- },
-
- select_serial_no: (frm) => {
- let serial_nos = frm.doc.serial_no || frm.doc.select_serial_no;
- if (serial_nos) {
- serial_nos = serial_nos.split('\n');
- serial_nos.push(frm.doc.select_serial_no);
-
- const unique_sn = serial_nos.filter(function(elem, index, self) {
- return index === self.indexOf(elem);
- });
-
- frm.set_value("serial_no", unique_sn.join('\n'));
- }
},
refresh: (frm) => {
@@ -93,25 +73,6 @@ frappe.ui.form.on('Asset Maintenance Task', {
},
end_date: (frm, cdt, cdn) => {
get_next_due_date(frm, cdt, cdn);
- },
- assign_to: (frm, cdt, cdn) => {
- var d = locals[cdt][cdn];
- if (frm.doc.__islocal) {
- frappe.model.set_value(cdt, cdn, "assign_to", "");
- frappe.model.set_value(cdt, cdn, "assign_to_name", "");
- frappe.throw(__("Please save before assigning task."));
- }
- if (d.assign_to) {
- return frappe.call({
- method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.assign_tasks',
- args: {
- asset_maintenance_name: frm.doc.name,
- assign_to_member: d.assign_to,
- maintenance_task: d.maintenance_task,
- next_due_date: d.next_due_date
- }
- });
- }
}
});
diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
index ba63dd661d7..d6adde6a371 100644
--- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py
@@ -16,12 +16,11 @@ class AssetMaintenance(Document):
throw(_("Start date should be less than end date for task {0}").format(task.maintenance_task))
if getdate(task.next_due_date) < getdate(nowdate()):
task.maintenance_status = "Overdue"
+ if not task.assign_to and self.docstatus == 0:
+ throw(_("Row #{}: Please asign task to a member.").format(task.idx))
def on_update(self):
for task in self.get('asset_maintenance_tasks'):
- if not task.assign_to:
- task.db_set("assign_to", self.maintenance_manager)
- task.db_set("assign_to_name", self.maintenance_manager_name)
assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date)
self.sync_maintenance_tasks()
@@ -108,7 +107,7 @@ def update_maintenance_log(asset_maintenance, item_code, item_name, task):
@frappe.whitelist()
def get_team_members(doctype, txt, searchfield, start, page_len, filters):
- return frappe.db.get_values('Maintenance Team Member', {'parent':filters.get("maintenance_team")})
+ return frappe.db.get_values('Maintenance Team Member', { 'parent': filters.get("maintenance_team") })
@frappe.whitelist()
def get_maintenance_log(asset_name):
diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
index 6c2fd67a9af..392fbdd2af7 100644
--- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
@@ -125,13 +125,15 @@ def get_maintenance_tasks():
"start_date": nowdate(),
"periodicity": "Monthly",
"maintenance_type": "Preventive Maintenance",
- "maintenance_status": "Planned"
+ "maintenance_status": "Planned",
+ "assign_to": "marcus@abc.com"
},
{"maintenance_task": "Check Gears",
"start_date": nowdate(),
"periodicity": "Yearly",
"maintenance_type": "Calibration",
- "maintenance_status": "Planned"
+ "maintenance_status": "Planned",
+ "assign_to": "thalia@abc.com"
}
]
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index a4bed51f479..3e6c7dc788f 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1194,11 +1194,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
action = "add" if perm_type == 'create' else "update"
frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions"))
- def get_new_child_item():
+ def get_new_child_item(item_row):
if parent_doctype == "Sales Order":
- return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
+ return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
if parent_doctype == "Purchase Order":
- return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d)
+ return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row)
def validate_quantity(child_item, d):
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
@@ -1219,7 +1219,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
if not d.get("docname"):
new_child_flag = True
check_permissions(parent, 'create')
- child_item = get_new_child_item()
+ child_item = get_new_child_item(d)
else:
check_permissions(parent, 'write')
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index 50b17abbe6d..1f95e004244 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -102,7 +102,7 @@ def validate_item_attribute_value(attributes_list, attribute, attribute_value, i
frappe.throw(_("{0} is not a valid Value for Attribute {1} of Item {2}.").format(
frappe.bold(attribute_value), frappe.bold(attribute), frappe.bold(item)), InvalidItemAttributeValueError, title=_("Invalid Value"))
else:
- msg = _("The value {0} is already assigned to an exisiting Item {1}.").format(
+ msg = _("The value {0} is already assigned to an existing Item {1}.").format(
frappe.bold(attribute_value), frappe.bold(item))
msg += "
" + _("To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.").format(frappe.bold("Allow Rename Attribute Value"))
diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
index 1b0c9f60b6e..6dedaa8c530 100644
--- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
+++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
@@ -73,10 +73,16 @@ def link_customer_and_address(raw_billing_data, raw_shipping_data, customer_name
if customer_exists:
frappe.rename_doc("Customer", old_name, customer_name)
- billing_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Billing"})
- shipping_address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": "Shipping"})
- rename_address(billing_address, customer)
- rename_address(shipping_address, customer)
+ for address_type in ("Billing", "Shipping",):
+ try:
+ address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email, "address_type": address_type})
+ rename_address(address, customer)
+ except (
+ frappe.DoesNotExistError,
+ frappe.DuplicateEntryError,
+ frappe.ValidationError,
+ ):
+ pass
else:
create_address(raw_billing_data, customer, "Billing")
create_address(raw_shipping_data, customer, "Shipping")
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
index 64c3b2d2730..25ffd281099 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
@@ -8,6 +8,7 @@ import json
from frappe import _
from frappe.model.document import Document
from frappe.utils import get_request_session
+from requests.exceptions import HTTPError
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from erpnext.erpnext_integrations.utils import get_webhook_address
from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log
@@ -29,19 +30,24 @@ class ShopifySettings(Document):
webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
# url = get_shopify_url('admin/webhooks.json', self)
created_webhooks = [d.method for d in self.webhooks]
- url = get_shopify_url('admin/api/2019-04/webhooks.json', self)
+ url = get_shopify_url('admin/api/2020-04/webhooks.json', self)
for method in webhooks:
session = get_request_session()
try:
- d = session.post(url, data=json.dumps({
+ res = session.post(url, data=json.dumps({
"webhook": {
"topic": method,
"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'),
"format": "json"
}
}), headers=get_header(self))
- d.raise_for_status()
- self.update_webhook_table(method, d.json())
+ res.raise_for_status()
+ self.update_webhook_table(method, res.json())
+
+ except HTTPError as e:
+ error_message = res.json().get('errors', e)
+ make_shopify_log(status="Warning", exception=error_message, rollback=True)
+
except Exception as e:
make_shopify_log(status="Warning", exception=e, rollback=True)
@@ -50,13 +56,18 @@ class ShopifySettings(Document):
deleted_webhooks = []
for d in self.webhooks:
- url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self)
+ url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self)
try:
res = session.delete(url, headers=get_header(self))
res.raise_for_status()
deleted_webhooks.append(d)
+
+ except HTTPError as e:
+ error_message = res.json().get('errors', e)
+ make_shopify_log(status="Warning", exception=error_message, rollback=True)
+
except Exception as e:
- frappe.log_error(message=frappe.get_traceback(), title=e)
+ frappe.log_error(message=e, title='Shopify Webhooks Issue')
for d in deleted_webhooks:
self.remove(d)
@@ -125,4 +136,3 @@ def setup_custom_fields():
}
create_custom_fields(custom_fields)
-
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
index bde101123db..f9f0bb3cecc 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py
@@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo
shopify_variants_attr_list = ["option1", "option2", "option3"]
def sync_item_from_shopify(shopify_settings, item):
- url = get_shopify_url("admin/api/2019-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
+ url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
session = get_request_session()
try:
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py
index ad32e946312..40f31016bc4 100644
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py
+++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.py
@@ -67,6 +67,7 @@ def validate_service_item(item, msg):
if frappe.db.get_value("Item", item, "is_stock_item") == 1:
frappe.throw(_(msg))
+@frappe.whitelist()
def get_practitioner_list(doctype, txt, searchfield, start, page_len, filters=None):
fields = ["name", "first_name", "mobile_phone"]
diff --git a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
index d5050e73d46..0a6386f103f 100644
--- a/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
+++ b/erpnext/hr/report/employee_leave_balance/employee_leave_balance.py
@@ -49,7 +49,7 @@ def get_data(filters, leave_types):
conditions = get_conditions(filters)
if filters.to_date <= filters.from_date:
- frappe.throw(_("From date can not be greater than than To date"))
+ frappe.throw(_("'From Date should be less than To Date"))
active_employees = frappe.get_all("Employee",
filters=conditions,
@@ -160,4 +160,4 @@ def get_department_leave_approver_map(department=None):
for k, v in approver_list:
approvers.setdefault(k, []).append(v)
- return approvers
\ No newline at end of file
+ return approvers
diff --git a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py
index 21646425bb6..70510492be9 100644
--- a/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py
+++ b/erpnext/hr/report/employee_leave_balance_summary/employee_leave_balance_summary.py
@@ -10,7 +10,7 @@ from erpnext.hr.report.employee_leave_balance.employee_leave_balance import calc
def execute(filters=None):
if filters.to_date <= filters.from_date:
- frappe.throw(_('From date can not be greater than than To date'))
+ frappe.throw(_('From Date should be less than To Date'))
columns = get_columns()
data = get_data(filters)
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js
index ded3a51dd60..cf2644e0053 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js
@@ -2,4 +2,13 @@
// For license information, please see license.txt
frappe.ui.form.on('Quality Procedure', {
+ refresh: function(frm) {
+ frm.set_query("procedure","processes", (frm) =>{
+ return {
+ filters: {
+ name: ["not in", [frm.parent_quality_procedure, frm.name]]
+ }
+ };
+ });
+ }
});
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
index e44e0733967..b3c0d948909 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
@@ -1,5 +1,6 @@
{
"actions": [],
+ "allow_rename": 1,
"autoname": "format:PRC-{quality_procedure_name}",
"creation": "2018-10-06 00:06:29.756804",
"doctype": "DocType",
@@ -72,7 +73,7 @@
],
"is_tree": 1,
"links": [],
- "modified": "2020-03-18 18:26:05.511984",
+ "modified": "2020-06-17 17:25:03.434953",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Procedure",
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
index 3ce5ea015f5..1952e578673 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
@@ -10,13 +10,8 @@ from frappe import _
class QualityProcedure(NestedSet):
nsm_parent_field = 'parent_quality_procedure'
- def on_save(self):
- for process in self.processes:
- if process.procedure:
- doc = frappe.get_doc("Quality Procedure", process.procedure)
- if doc.parent_quality_procedure:
- frappe.throw(_("{0} already has a Parent Procedure {1}.".format(process.procedure, doc.parent_quality_procedure)))
- self.is_group = 1
+ def before_save(self):
+ self.check_for_incorrect_child()
def on_update(self):
self.set_parent()
@@ -47,11 +42,21 @@ class QualityProcedure(NestedSet):
doc.save(ignore_permissions=True)
def set_parent(self):
+ for process in self.processes:
+ # Set parent for only those children who don't have a parent
+ parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
+ if not parent_quality_procedure and process.procedure:
+ frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name)
+
+ def check_for_incorrect_child(self):
for process in self.processes:
if process.procedure:
- doc = frappe.get_doc("Quality Procedure", process.procedure)
- doc.parent_quality_procedure = self.name
- doc.save(ignore_permissions=True)
+ # Check if any child process belongs to another parent.
+ parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
+ if parent_quality_procedure and parent_quality_procedure != self.name:
+ frappe.throw(_("{0} already has a Parent Procedure {1}.".format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure))),
+ title=_("Invalid Child Procedure"))
+ self.is_group = 1
@frappe.whitelist()
def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False):
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
index 6df6f656aa1..ef48ab6c6e2 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
@@ -16,6 +16,7 @@ frappe.treeview_settings["Quality Procedure"] = {
},
],
breadcrumb: "Setup",
+ disable_add_node: true,
root_label: "All Quality Procedures",
get_tree_root: false,
menu_items: [
diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
index 0a67fa505ee..3925dbb8aca 100644
--- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
+++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
@@ -1,6 +1,8 @@
{
+ "actions": [],
"creation": "2019-05-26 00:10:00.248885",
"doctype": "DocType",
+ "editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"process_description",
@@ -23,7 +25,8 @@
}
],
"istable": 1,
- "modified": "2019-05-26 22:05:49.007189",
+ "links": [],
+ "modified": "2020-06-17 15:44:38.937915",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Procedure Process",
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 9a5d2ebd910..53c43fb2b60 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -299,6 +299,7 @@ def get_loyalty_programs(doc):
return lp_details
+@frappe.whitelist()
def get_customer_list(doctype, txt, searchfield, start, page_len, filters=None):
from erpnext.controllers.queries import get_fields
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 05e4aa892b0..ffb66354fa0 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -868,7 +868,8 @@ def make_purchase_order(source_name, for_supplier=None, selected_items=[], targe
],
"field_no_map": [
"rate",
- "price_list_rate"
+ "price_list_rate",
+ "item_tax_template"
],
"postprocess": update_item,
"condition": lambda doc: doc.ordered_qty < doc.qty and doc.supplier == supplier and doc.item_code in selected_items
diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json
index aba6a791a4e..28d1d16a051 100644
--- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json
+++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:title",
@@ -49,7 +50,7 @@
"fieldname": "terms_and_conditions_help",
"fieldtype": "HTML",
"label": "Terms and Conditions Help",
- "options": "
Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n\n\nThe fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)
\n\nTemplates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.
" + "options": "Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n\n\nThe fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)
\n\nTemplates are compiled using the Jinja Templating Language. To learn more about Jinja, read this documentation.
" }, { "fieldname": "applicable_modules_section", @@ -81,7 +82,8 @@ ], "icon": "icon-legal", "idx": 1, - "modified": "2019-07-04 13:31:30.393425", + "links": [], + "modified": "2020-06-16 22:54:38.094844", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions",