Merge branch 'develop' into remove-nonprofit
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 5229d87..9b3b3aa 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -439,7 +439,6 @@
self.paid_amount = 0
def set_account_for_mode_of_payment(self):
- self.payments = [d for d in self.payments if d.amount or d.base_amount or d.default]
for pay in self.payments:
if not pay.account:
pay.account = get_bank_cash_account(pay.mode_of_payment, self.company).get("account")
diff --git a/erpnext/e_commerce/product_data_engine/query.py b/erpnext/e_commerce/product_data_engine/query.py
index 007bf8b..cfc3c7b 100644
--- a/erpnext/e_commerce/product_data_engine/query.py
+++ b/erpnext/e_commerce/product_data_engine/query.py
@@ -264,7 +264,7 @@
customer = get_customer(silent=True)
if customer:
quotation = frappe.get_all("Quotation", fields=["name"], filters=
- {"party_name": customer, "order_type": "Shopping Cart", "docstatus": 0},
+ {"party_name": customer, "contact_email": frappe.session.user, "order_type": "Shopping Cart", "docstatus": 0},
order_by="modified desc", limit_page_length=1)
if quotation:
items = frappe.get_all(
@@ -298,4 +298,4 @@
# slice results manually
result[:self.page_length]
- return result
\ No newline at end of file
+ return result
diff --git a/erpnext/e_commerce/shopping_cart/cart.py b/erpnext/e_commerce/shopping_cart/cart.py
index 458cf69..372aed0 100644
--- a/erpnext/e_commerce/shopping_cart/cart.py
+++ b/erpnext/e_commerce/shopping_cart/cart.py
@@ -310,7 +310,7 @@
party = get_party()
quotation = frappe.get_all("Quotation", fields=["name"], filters=
- {"party_name": party.name, "order_type": "Shopping Cart", "docstatus": 0},
+ {"party_name": party.name, "contact_email": frappe.session.user, "order_type": "Shopping Cart", "docstatus": 0},
order_by="modified desc", limit_page_length=1)
if quotation:
diff --git a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
index 6be8c94..37b3672 100644
--- a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
+++ b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
@@ -57,13 +57,19 @@
return quotation
def test_get_cart_customer(self):
- self.login_as_customer()
+ def validate_quotation():
+ # test if quotation with customer is fetched
+ quotation = _get_cart_quotation()
+ self.assertEqual(quotation.quotation_to, "Customer")
+ self.assertEqual(quotation.party_name, "_Test Customer")
+ self.assertEqual(quotation.contact_email, frappe.session.user)
+ return quotation
- # test if quotation with customer is fetched
- quotation = _get_cart_quotation()
- self.assertEqual(quotation.quotation_to, "Customer")
- self.assertEqual(quotation.party_name, "_Test Customer")
- self.assertEqual(quotation.contact_email, frappe.session.user)
+ self.login_as_customer("test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer")
+ validate_quotation()
+
+ self.login_as_customer()
+ quotation = validate_quotation()
return quotation
@@ -254,10 +260,9 @@
self.create_user_if_not_exists("test_cart_user@example.com")
frappe.set_user("test_cart_user@example.com")
- def login_as_customer(self):
- self.create_user_if_not_exists("test_contact_customer@example.com",
- "_Test Contact For _Test Customer")
- frappe.set_user("test_contact_customer@example.com")
+ def login_as_customer(self, email="test_contact_customer@example.com", name="_Test Contact For _Test Customer"):
+ self.create_user_if_not_exists(email, name)
+ frappe.set_user(email)
def clear_existing_quotations(self):
quotations = frappe.get_all("Quotation", filters={
diff --git a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js
index 68e7780..4526585 100644
--- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js
+++ b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.js
@@ -3,6 +3,10 @@
frappe.provide("education");
frappe.ui.form.on('Student Attendance Tool', {
+ setup: (frm) => {
+ frm.students_area = $('<div>')
+ .appendTo(frm.fields_dict.students_html.wrapper);
+ },
onload: function(frm) {
frm.set_query("student_group", function() {
return {
@@ -34,6 +38,7 @@
student_group: function(frm) {
if ((frm.doc.student_group && frm.doc.date) || frm.doc.course_schedule) {
+ frm.students_area.find('.student-attendance-checks').html(`<div style='padding: 2rem 0'>Fetching...</div>`);
var method = "erpnext.education.doctype.student_attendance_tool.student_attendance_tool.get_student_attendance_records";
frappe.call({
@@ -62,10 +67,6 @@
},
get_students: function(frm, students) {
- if (!frm.students_area) {
- frm.students_area = $('<div>')
- .appendTo(frm.fields_dict.students_html.wrapper);
- }
students = students || [];
frm.students_editor = new education.StudentsEditor(frm, frm.students_area, students);
}
@@ -163,16 +164,26 @@
);
});
- var htmls = students.map(function(student) {
- return frappe.render_template("student_button", {
- student: student.student,
- student_name: student.student_name,
- group_roll_number: student.group_roll_number,
- status: student.status
- })
- });
+ // make html grid of students
+ let student_html = '';
+ for (let student of students) {
+ student_html += `<div class="col-sm-3">
+ <div class="checkbox">
+ <label>
+ <input
+ type="checkbox"
+ data-group_roll_number="${student.group_roll_number}"
+ data-student="${student.student}"
+ data-student-name="${student.student_name}"
+ class="students-check"
+ ${student.status==='Present' ? 'checked' : ''}>
+ ${student.group_roll_number} - ${student.student_name}
+ </label>
+ </div>
+ </div>`;
+ }
- $(htmls.join("")).appendTo(me.wrapper);
+ $(`<div class='student-attendance-checks'>${student_html}</div>`).appendTo(me.wrapper);
}
show_empty_state() {
diff --git a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
index 7deb6b1..92bb20c 100644
--- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
+++ b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py
@@ -24,24 +24,24 @@
student_list = frappe.get_all("Student Group Student", fields=["student", "student_name", "group_roll_number"],
filters={"parent": student_group, "active": 1}, order_by= "group_roll_number")
- table = frappe.qb.DocType("Student Attendance")
+ StudentAttendance = frappe.qb.DocType("Student Attendance")
if course_schedule:
student_attendance_list = (
- frappe.qb.from_(table)
- .select(table.student, table.status)
+ frappe.qb.from_(StudentAttendance)
+ .select(StudentAttendance.student, StudentAttendance.status)
.where(
- (table.course_schedule == course_schedule)
+ (StudentAttendance.course_schedule == course_schedule)
)
).run(as_dict=True)
else:
student_attendance_list = (
- frappe.qb.from_(table)
- .select(table.student, table.status)
+ frappe.qb.from_(StudentAttendance)
+ .select(StudentAttendance.student, StudentAttendance.status)
.where(
- (table.student_group == student_group)
- & (table.date == date)
- & (table.course_schedule == "") | (table.course_schedule.isnull())
+ (StudentAttendance.student_group == student_group)
+ & (StudentAttendance.date == date)
+ & ((StudentAttendance.course_schedule == "") | (StudentAttendance.course_schedule.isnull()))
)
).run(as_dict=True)
diff --git a/erpnext/hr/doctype/department/department.py b/erpnext/hr/doctype/department/department.py
index ed0bfcf..71300c4 100644
--- a/erpnext/hr/doctype/department/department.py
+++ b/erpnext/hr/doctype/department/department.py
@@ -32,7 +32,7 @@
return new
def on_update(self):
- if not frappe.local.flags.ignore_update_nsm:
+ if not (frappe.local.flags.ignore_update_nsm or frappe.flags.in_setup_wizard):
super(Department, self).on_update()
def on_trash(self):
diff --git a/erpnext/hr/doctype/department/test_records.json b/erpnext/hr/doctype/department/test_records.json
index 654925e..e3421f2 100644
--- a/erpnext/hr/doctype/department/test_records.json
+++ b/erpnext/hr/doctype/department/test_records.json
@@ -1,4 +1,4 @@
[
- {"doctype":"Department", "department_name":"_Test Department", "company": "_Test Company"},
- {"doctype":"Department", "department_name":"_Test Department 1", "company": "_Test Company"}
+ {"doctype":"Department", "department_name":"_Test Department", "company": "_Test Company", "parent_department": "All Departments"},
+ {"doctype":"Department", "department_name":"_Test Department 1", "company": "_Test Company", "parent_department": "All Departments"}
]
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index d88e10a..2359815 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -9,6 +9,7 @@
get_sales_orders,
get_warehouse_list,
)
+from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
@@ -466,26 +467,29 @@
bom = make_bom(item=item, raw_materials=raw_materials)
# Create Production Plan
- pln = create_production_plan(item_code=bom.item, planned_qty=10)
+ pln = create_production_plan(item_code=bom.item, planned_qty=5)
# All the created Work Orders
wo_list = []
- # Create and Submit 1st Work Order for 5 qty
- create_work_order(item, pln, 5)
+ # Create and Submit 1st Work Order for 3 qty
+ create_work_order(item, pln, 3)
+ pln.reload()
+ self.assertEqual(pln.po_items[0].ordered_qty, 3)
+
+ # Create and Submit 2nd Work Order for 2 qty
+ create_work_order(item, pln, 2)
pln.reload()
self.assertEqual(pln.po_items[0].ordered_qty, 5)
- # Create and Submit 2nd Work Order for 3 qty
- create_work_order(item, pln, 3)
- pln.reload()
- self.assertEqual(pln.po_items[0].ordered_qty, 8)
+ # Overproduction
+ self.assertRaises(OverProductionError, create_work_order, item=item, pln=pln, qty=2)
# Cancel 1st Work Order
wo1 = frappe.get_doc("Work Order", wo_list[0])
wo1.cancel()
pln.reload()
- self.assertEqual(pln.po_items[0].ordered_qty, 3)
+ self.assertEqual(pln.po_items[0].ordered_qty, 2)
# Cancel 2nd Work Order
wo2 = frappe.get_doc("Work Order", wo_list[1])
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index ed6a029..e1120c7 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -636,6 +636,21 @@
if not self.qty > 0:
frappe.throw(_("Quantity to Manufacture must be greater than 0."))
+ if self.production_plan and self.production_plan_item:
+ qty_dict = frappe.db.get_value("Production Plan Item", self.production_plan_item, ["planned_qty", "ordered_qty"], as_dict=1)
+
+ allowance_qty =flt(frappe.db.get_single_value("Manufacturing Settings",
+ "overproduction_percentage_for_work_order"))/100 * qty_dict.get("planned_qty", 0)
+
+ max_qty = qty_dict.get("planned_qty", 0) + allowance_qty - qty_dict.get("ordered_qty", 0)
+
+ if max_qty < 1:
+ frappe.throw(_("Cannot produce more item for {0}")
+ .format(self.production_item), OverProductionError)
+ elif self.qty > max_qty:
+ frappe.throw(_("Cannot produce more than {0} items for {1}")
+ .format(max_qty, self.production_item), OverProductionError)
+
def validate_transfer_against(self):
if not self.docstatus == 1:
# let user configure operations until they're ready to submit
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index ae8c0c8..00373a6 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -2285,13 +2285,17 @@
}
coupon_code() {
- var me = this;
- frappe.run_serially([
- () => this.frm.doc.ignore_pricing_rule=1,
- () => me.ignore_pricing_rule(),
- () => this.frm.doc.ignore_pricing_rule=0,
- () => me.apply_pricing_rule()
- ]);
+ if (this.frm.doc.coupon_code || this.frm._last_coupon_code) {
+ // reset pricing rules if coupon code is set or is unset
+ const _ignore_pricing_rule = this.frm.doc.ignore_pricing_rule;
+ return frappe.run_serially([
+ () => this.frm.doc.ignore_pricing_rule=1,
+ () => this.frm.trigger('ignore_pricing_rule'),
+ () => this.frm.doc.ignore_pricing_rule=_ignore_pricing_rule,
+ () => this.frm.trigger('apply_pricing_rule'),
+ () => this.frm._last_coupon_code = this.frm.doc.coupon_code
+ ]);
+ }
}
};
diff --git a/erpnext/public/js/education/student_button.html b/erpnext/public/js/education/student_button.html
deleted file mode 100644
index b64c73a..0000000
--- a/erpnext/public/js/education/student_button.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<div class="col-sm-3">
- <div class="checkbox">
- <label>
- <input
- type="checkbox"
- data-group_roll_number="{{group_roll_number}}"
- data-student="{{student}}"
- data-student-name="{{student_name}}"
- class="students-check"
- {% if status === "Present" %}
- checked
- {% endif %}
- >
- {{ group_roll_number }} - {{ student_name }}
- </label>
- </div>
-</div>
diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
index b3a68b3..8409e78 100644
--- a/erpnext/public/js/erpnext.bundle.js
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -16,7 +16,6 @@
import "./utils/item_quick_entry";
import "./utils/customer_quick_entry";
import "./utils/supplier_quick_entry";
-import "./education/student_button.html";
import "./education/assessment_result_tool.html";
import "./call_popup/call_popup";
import "./utils/dimension_tree_filter";
diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
index 831626a..a585aa6 100644
--- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
+++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js
@@ -304,12 +304,13 @@
}
get_child_nodes(node_id) {
+ let me = this;
return new Promise(resolve => {
frappe.call({
- method: this.method,
+ method: me.method,
args: {
parent: node_id,
- company: this.company
+ company: me.company
}
}).then(r => resolve(r.message));
});
@@ -350,12 +351,13 @@
}
get_all_nodes() {
+ let me = this;
return new Promise(resolve => {
frappe.call({
method: 'erpnext.utilities.hierarchy_chart.get_all_nodes',
args: {
- method: this.method,
- company: this.company
+ method: me.method,
+ company: me.company
},
callback: (r) => {
resolve(r.message);
@@ -427,8 +429,8 @@
add_connector(parent_id, child_id) {
// using pure javascript for better performance
- const parent_node = document.querySelector(`#${parent_id}`);
- const child_node = document.querySelector(`#${child_id}`);
+ const parent_node = document.getElementById(`${parent_id}`);
+ const child_node = document.getElementById(`${child_id}`);
let path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
index 0a8ba78..52236e7 100644
--- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
+++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js
@@ -235,7 +235,7 @@
let me = this;
return new Promise(resolve => {
frappe.call({
- method: this.method,
+ method: me.method,
args: {
parent: node_id,
company: me.company,
@@ -286,8 +286,8 @@
}
add_connector(parent_id, child_id) {
- const parent_node = document.querySelector(`#${parent_id}`);
- const child_node = document.querySelector(`#${child_id}`);
+ const parent_node = document.getElementById(`${parent_id}`);
+ const child_node = document.getElementById(`${child_id}`);
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
@@ -518,7 +518,8 @@
level.nextAll('li').remove();
let node_object = this.nodes[node.id];
- let current_node = level.find(`#${node.id}`).detach();
+ let current_node = level.find(`[id="${node.id}"]`).detach();
+
current_node.removeClass('active-child active-path');
node_object.expanded = 0;
diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js
index e746ce9..83b69ae 100644
--- a/erpnext/public/js/setup_wizard.js
+++ b/erpnext/public/js/setup_wizard.js
@@ -78,11 +78,11 @@
slide.get_input("company_name").on("change", function () {
var parts = slide.get_input("company_name").val().split(" ");
var abbr = $.map(parts, function (p) { return p ? p.substr(0, 1) : null }).join("");
- slide.get_field("company_abbr").set_value(abbr.slice(0, 5).toUpperCase());
+ slide.get_field("company_abbr").set_value(abbr.slice(0, 10).toUpperCase());
}).val(frappe.boot.sysdefaults.company_name || "").trigger("change");
slide.get_input("company_abbr").on("change", function () {
- if (slide.get_input("company_abbr").val().length > 5) {
+ if (slide.get_input("company_abbr").val().length > 10) {
frappe.msgprint(__("Company Abbreviation cannot have more than 5 characters"));
slide.get_field("company_abbr").set_value("");
}
@@ -96,7 +96,7 @@
if (!this.values.company_abbr) {
return false;
}
- if (this.values.company_abbr.length > 5) {
+ if (this.values.company_abbr.length > 10) {
return false;
}
return true;
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 57f4a6a..12b10bb 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -111,7 +111,7 @@
def add_print_formats():
frappe.reload_doc("regional", "print_format", "gst_tax_invoice")
- frappe.reload_doc("accounts", "print_format", "gst_pos_invoice")
+ frappe.reload_doc("selling", "print_format", "gst_pos_invoice")
frappe.reload_doc("accounts", "print_format", "GST E-Invoice")
frappe.db.set_value("Print Format", "GST POS Invoice", "disabled", 0)
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 4d75e6e..1e9f6d7 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -170,17 +170,20 @@
});
frappe.ui.form.on('POS Invoice', 'coupon_code', (frm) => {
- if (!frm.doc.ignore_pricing_rule) {
- if (frm.doc.coupon_code) {
- frappe.run_serially([
- () => frm.doc.ignore_pricing_rule=1,
- () => frm.trigger('ignore_pricing_rule'),
- () => frm.doc.ignore_pricing_rule=0,
- () => frm.trigger('apply_pricing_rule'),
- () => frm.save(),
- () => this.update_totals_section(frm.doc)
- ]);
- }
+ if (!frm.doc.ignore_pricing_rule && frm.doc.coupon_code) {
+ frappe.run_serially([
+ () => frm.doc.ignore_pricing_rule=1,
+ () => frm.trigger('ignore_pricing_rule'),
+ () => frm.doc.ignore_pricing_rule=0,
+ () => frm.trigger('apply_pricing_rule'),
+ () => frm.save(),
+ () => this.update_totals_section(frm.doc)
+ ]);
+ } else if (frm.doc.ignore_pricing_rule && frm.doc.coupon_code) {
+ frappe.show_alert({
+ message: __("Ignore Pricing Rule is enabled. Cannot apply coupon code."),
+ indicator: "orange"
+ });
}
});
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 5c9da3a..324ca7a 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -629,6 +629,12 @@
frm.events.set_serial_no(frm, cdt, cdn, () => {
frm.events.get_warehouse_details(frm, cdt, cdn);
});
+
+ // set allow_zero_valuation_rate to 0 if s_warehouse is selected.
+ let item = frappe.get_doc(cdt, cdn);
+ if (item.s_warehouse) {
+ item.allow_zero_valuation_rate = 0;
+ }
},
t_warehouse: function(frm, cdt, cdn) {
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index df65706..83aed90 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -1,7 +1,7 @@
{
"actions": [],
"autoname": "hash",
- "creation": "2013-03-29 18:22:12",
+ "creation": "2022-02-05 00:17:49.860824",
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 1,
@@ -340,13 +340,13 @@
"label": "More Information"
},
{
- "allow_on_submit": 1,
"default": "0",
"fieldname": "allow_zero_valuation_rate",
"fieldtype": "Check",
"label": "Allow Zero Valuation Rate",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "read_only_depends_on": "eval:doc.s_warehouse"
},
{
"allow_on_submit": 1,
@@ -556,12 +556,14 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2021-06-22 16:47:11.268975",
+ "modified": "2022-02-26 00:51:24.963653",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry Detail",
+ "naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
- "sort_order": "ASC"
+ "sort_order": "ASC",
+ "states": []
}
\ No newline at end of file
diff --git a/erpnext/tests/utils.py b/erpnext/tests/utils.py
index 2bd7e9e..40c95eb 100644
--- a/erpnext/tests/utils.py
+++ b/erpnext/tests/utils.py
@@ -66,6 +66,20 @@
contact.add_phone("+91 0000000000", is_primary_phone=True)
contact.insert()
+ contact_two = frappe.get_doc({
+ "doctype": 'Contact',
+ "first_name": "_Test Contact 2 for _Test Customer",
+ "links": [
+ {
+ "link_doctype": "Customer",
+ "link_name": "_Test Customer"
+ }
+ ]
+ })
+ contact_two.add_email("test_contact_two_customer@example.com", is_primary=True)
+ contact_two.add_phone("+92 0000000000", is_primary_phone=True)
+ contact_two.insert()
+
@contextmanager
def change_settings(doctype, settings_dict):