Merge branch 'develop' into crm-contact-duplication-develop
diff --git a/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json b/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
index 7de9ae1..02e30c3 100644
--- a/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
+++ b/erpnext/accounts/form_tour/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json
@@ -2,15 +2,17 @@
"creation": "2021-08-24 12:28:18.044902",
"docstatus": 0,
"doctype": "Form Tour",
+ "first_document": 0,
"idx": 0,
+ "include_name_field": 0,
"is_standard": 1,
- "modified": "2021-08-24 12:28:18.044902",
+ "modified": "2022-01-18 18:32:17.102330",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Taxes and Charges Template",
"owner": "Administrator",
"reference_doctype": "Sales Taxes and Charges Template",
- "save_on_complete": 0,
+ "save_on_complete": 1,
"steps": [
{
"description": "A name by which you will identify this template. You can change this later.",
diff --git a/erpnext/accounts/module_onboarding/accounts/accounts.json b/erpnext/accounts/module_onboarding/accounts/accounts.json
index 2e0ab43..aa7cdf7 100644
--- a/erpnext/accounts/module_onboarding/accounts/accounts.json
+++ b/erpnext/accounts/module_onboarding/accounts/accounts.json
@@ -13,16 +13,13 @@
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts",
"idx": 0,
"is_complete": 0,
- "modified": "2021-08-13 11:59:35.690443",
+ "modified": "2022-01-18 18:35:52.326688",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts",
"owner": "Administrator",
"steps": [
{
- "step": "Company"
- },
- {
"step": "Chart of Accounts"
},
{
diff --git a/erpnext/accounts/onboarding_step/company/company.json b/erpnext/accounts/onboarding_step/company/company.json
deleted file mode 100644
index 4992e4d..0000000
--- a/erpnext/accounts/onboarding_step/company/company.json
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "action": "Go to Page",
- "action_label": "Let's Review your Company",
- "creation": "2021-06-29 14:47:42.497318",
- "description": "# Company\n\nIn ERPNext, you can also create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company. \n",
- "docstatus": 0,
- "doctype": "Onboarding Step",
- "idx": 0,
- "is_complete": 0,
- "is_single": 0,
- "is_skipped": 0,
- "modified": "2021-08-13 11:43:35.767341",
- "modified_by": "Administrator",
- "name": "Company",
- "owner": "Administrator",
- "path": "app/company",
- "reference_document": "Company",
- "show_form_tour": 0,
- "show_full_form": 0,
- "title": "Review Company",
- "validate_action": 1
-}
\ No newline at end of file
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index 2ffae1a..07d928c 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -1,7 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
-
import frappe
from frappe import _, throw
from frappe.utils import add_days, cint, cstr, date_diff, formatdate, getdate
@@ -306,13 +305,18 @@
return schedule.name
@frappe.whitelist()
-def update_serial_nos(s_id):
- serial_nos = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'serial_no')
+def get_serial_nos_from_schedule(item_code, schedule=None):
+ serial_nos = []
+ if schedule:
+ serial_nos = frappe.db.get_value('Maintenance Schedule Item', {
+ 'parent': schedule,
+ 'item_code': item_code
+ }, 'serial_no')
+
if serial_nos:
serial_nos = get_serial_nos(serial_nos)
- return serial_nos
- else:
- return False
+
+ return serial_nos
@frappe.whitelist()
def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=None):
@@ -320,12 +324,9 @@
def update_status_and_detail(source, target, parent):
target.maintenance_type = "Scheduled"
- target.maintenance_schedule = source.name
target.maintenance_schedule_detail = s_id
- def update_sales_and_serial(source, target, parent):
- sales_person = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'sales_person')
- target.service_person = sales_person
+ def update_serial(source, target, parent):
serial_nos = get_serial_nos(target.serial_no)
if len(serial_nos) == 1:
target.serial_no = serial_nos[0]
@@ -346,7 +347,10 @@
"Maintenance Schedule Item": {
"doctype": "Maintenance Visit Purpose",
"condition": lambda doc: doc.item_name == item_name,
- "postprocess": update_sales_and_serial
+ "field_map": {
+ "sales_person": "service_person"
+ },
+ "postprocess": update_serial
}
}, target_doc)
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
index 5017126..4d3c3f4 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py
@@ -7,8 +7,11 @@
from frappe.utils.data import add_days, formatdate, today
from erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule import (
+ get_serial_nos_from_schedule,
make_maintenance_visit,
)
+from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
# test_records = frappe.get_test_records('Maintenance Schedule')
@@ -80,6 +83,42 @@
#checks if visit status is back updated in schedule
self.assertTrue(ms.schedules[1].completion_status, "Partially Completed")
+ def test_serial_no_filters(self):
+ # Without serial no. set in schedule -> returns None
+ item_code = "_Test Serial Item"
+ make_serial_item_with_serial(item_code)
+ ms = make_maintenance_schedule(item_code=item_code)
+ ms.submit()
+
+ s_item = ms.schedules[0]
+ mv = make_maintenance_visit(source_name=ms.name, item_name=item_code, s_id=s_item.name)
+ mvi = mv.purposes[0]
+ serial_nos = get_serial_nos_from_schedule(mvi.item_name, ms.name)
+ self.assertEqual(serial_nos, None)
+
+ # With serial no. set in schedule -> returns serial nos.
+ make_serial_item_with_serial(item_code)
+ ms = make_maintenance_schedule(item_code=item_code, serial_no="TEST001, TEST002")
+ ms.submit()
+
+ s_item = ms.schedules[0]
+ mv = make_maintenance_visit(source_name=ms.name, item_name=item_code, s_id=s_item.name)
+ mvi = mv.purposes[0]
+ serial_nos = get_serial_nos_from_schedule(mvi.item_name, ms.name)
+ self.assertEqual(serial_nos, ["TEST001", "TEST002"])
+
+ frappe.db.rollback()
+
+def make_serial_item_with_serial(item_code):
+ serial_item_doc = create_item(item_code, is_stock_item=1)
+ if not serial_item_doc.has_serial_no or not serial_item_doc.serial_no_series:
+ serial_item_doc.has_serial_no = 1
+ serial_item_doc.serial_no_series = "TEST.###"
+ serial_item_doc.save(ignore_permissions=True)
+ active_serials = frappe.db.get_all('Serial No', {"status": "Active", "item_code": item_code})
+ if len(active_serials) < 2:
+ make_serialized_item(item_code=item_code)
+
def get_events(ms):
return frappe.get_all("Event Participants", filters={
"reference_doctype": ms.doctype,
@@ -87,17 +126,18 @@
"parenttype": "Event"
})
-def make_maintenance_schedule():
+def make_maintenance_schedule(**args):
ms = frappe.new_doc("Maintenance Schedule")
ms.company = "_Test Company"
ms.customer = "_Test Customer"
ms.transaction_date = today()
ms.append("items", {
- "item_code": "_Test Item",
+ "item_code": args.get("item_code") or "_Test Item",
"start_date": today(),
"periodicity": "Weekly",
"no_of_visits": 4,
+ "serial_no": args.get("serial_no"),
"sales_person": "Sales Team",
})
ms.insert(ignore_permissions=True)
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
index d2197a6..72686e7 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
@@ -2,52 +2,54 @@
// License: GNU General Public License v3. See license.txt
frappe.provide("erpnext.maintenance");
-var serial_nos = [];
frappe.ui.form.on('Maintenance Visit', {
- refresh: function (frm) {
- //filters for serial_no based on item_code
- frm.set_query('serial_no', 'purposes', function (frm, cdt, cdn) {
- let item = locals[cdt][cdn];
- if (serial_nos) {
- return {
- filters: {
- 'item_code': item.item_code,
- 'name': ["in", serial_nos]
- }
- };
- } else {
- return {
- filters: {
- 'item_code': item.item_code
- }
- };
- }
- });
- },
setup: function (frm) {
frm.set_query('contact_person', erpnext.queries.contact_query);
frm.set_query('customer_address', erpnext.queries.address_query);
frm.set_query('customer', erpnext.queries.customer);
},
- onload: function (frm, cdt, cdn) {
- let item = locals[cdt][cdn];
+ onload: function (frm) {
+ // filters for serial no based on item code
if (frm.doc.maintenance_type === "Scheduled") {
- const schedule_id = item.purposes[0].prevdoc_detail_docname || frm.doc.maintenance_schedule_detail;
+ let item_code = frm.doc.purposes[0].item_code;
frappe.call({
- method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.update_serial_nos",
+ method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.get_serial_nos_from_schedule",
args: {
- s_id: schedule_id
- },
- callback: function (r) {
- serial_nos = r.message;
+ schedule: frm.doc.maintenance_schedule,
+ item_code: item_code
}
+ }).then((r) => {
+ let serial_nos = r.message;
+ frm.set_query('serial_no', 'purposes', () => {
+ if (serial_nos.length > 0) {
+ return {
+ filters: {
+ 'item_code': item_code,
+ 'name': ["in", serial_nos]
+ }
+ };
+ }
+ return {
+ filters: {
+ 'item_code': item_code
+ }
+ };
+ });
+ });
+ } else {
+ frm.set_query('serial_no', 'purposes', (frm, cdt, cdn) => {
+ let row = locals[cdt][cdn];
+ return {
+ filters: {
+ 'item_code': row.item_code
+ }
+ };
});
}
if (!frm.doc.status) {
frm.set_value({ status: 'Draft' });
}
if (frm.doc.__islocal) {
- frm.doc.maintenance_type == 'Unscheduled' && frm.clear_table("purposes");
frm.set_value({ mntc_date: frappe.datetime.get_today() });
}
},
@@ -60,7 +62,6 @@
contact_person: function (frm) {
erpnext.utils.get_contact_details(frm);
}
-
})
// TODO commonify this code
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
index ec32239..4a6aa0a 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json
@@ -179,8 +179,7 @@
"label": "Purposes",
"oldfieldname": "maintenance_visit_details",
"oldfieldtype": "Table",
- "options": "Maintenance Visit Purpose",
- "reqd": 1
+ "options": "Maintenance Visit Purpose"
},
{
"fieldname": "more_info",
@@ -294,10 +293,11 @@
"idx": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-05-27 16:06:17.352572",
+ "modified": "2021-12-17 03:10:27.608112",
"modified_by": "Administrator",
"module": "Maintenance",
"name": "Maintenance Visit",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
index 5a87b16..d5d8753 100644
--- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
+++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py
@@ -18,6 +18,10 @@
if d.serial_no and not frappe.db.exists("Serial No", d.serial_no):
frappe.throw(_("Serial No {0} does not exist").format(d.serial_no))
+ def validate_purpose_table(self):
+ if not self.purposes:
+ frappe.throw(_("Add Items in the Purpose Table"), title="Purposes Required")
+
def validate_maintenance_date(self):
if self.maintenance_type == "Scheduled" and self.maintenance_schedule_detail:
item_ref = frappe.db.get_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'item_reference')
@@ -29,6 +33,7 @@
def validate(self):
self.validate_serial_no()
self.validate_maintenance_date()
+ self.validate_purpose_table()
def update_completion_status(self):
if self.maintenance_schedule_detail:
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index fa62b7f..1f83c4f 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -313,6 +313,7 @@
erpnext.patches.v13_0.update_category_in_ltds_certificate
erpnext.patches.v13_0.create_pan_field_for_india #2
erpnext.patches.v14_0.delete_hub_doctypes
+erpnext.patches.v13_0.update_maintenance_schedule_field_in_visit
erpnext.patches.v13_0.create_ksa_vat_custom_fields # 07-01-2022
erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
erpnext.patches.v14_0.migrate_crm_settings
diff --git a/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py b/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py
new file mode 100644
index 0000000..450c00e
--- /dev/null
+++ b/erpnext/patches/v13_0/update_maintenance_schedule_field_in_visit.py
@@ -0,0 +1,22 @@
+
+import frappe
+
+
+def execute():
+ # Updates the Maintenance Schedule link to fetch serial nos
+ from frappe.query_builder.functions import Coalesce
+ mvp = frappe.qb.DocType('Maintenance Visit Purpose')
+ mv = frappe.qb.DocType('Maintenance Visit')
+
+ frappe.qb.update(
+ mv
+ ).join(
+ mvp
+ ).on(mvp.parent == mv.name).set(
+ mv.maintenance_schedule,
+ Coalesce(mvp.prevdoc_docname, '')
+ ).where(
+ (mv.maintenance_type == "Scheduled")
+ & (mvp.prevdoc_docname.notnull())
+ & (mv.docstatus < 2)
+ ).run(as_dict=1)