Merge pull request #11133 from netchampfaris/fix-hub-sync
fix Hub Sync plan
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index 259c0f0..8535365 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -4,7 +4,7 @@
import frappe
from erpnext.hooks import regional_overrides
-__version__ = '9.1.1'
+__version__ = '9.1.2'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js
index 97bbc122..cb52627 100755
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.js
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js
@@ -37,10 +37,10 @@
return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} };
});
- frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => {
- is_online = r && cint(r.is_online)
- frm.toggle_display('offline_pos_section', !is_online);
- frm.toggle_display('print_format_for_online', is_online);
+ frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'use_pos_in_offline_mode', (r) => {
+ is_offline = r && cint(r.use_pos_in_offline_mode)
+ frm.toggle_display('offline_pos_section', is_offline);
+ frm.toggle_display('print_format_for_online', !is_offline);
});
},
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index dd10977..ccf8a84 100644
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -88,7 +88,7 @@
doc.naming_series = pos_profile.get('naming_series') or 'SINV-'
doc.letter_head = pos_profile.get('letter_head') or company_data.default_letter_head
doc.ignore_pricing_rule = pos_profile.get('ignore_pricing_rule') or 0
- doc.apply_discount_on = pos_profile.get('apply_discount_on') if pos_profile.get('apply_discount') else ''
+ doc.apply_discount_on = pos_profile.get('apply_discount_on') or ''
doc.customer_group = pos_profile.get('customer_group') or get_root('Customer Group')
doc.territory = pos_profile.get('territory') or get_root('Territory')
doc.terms = frappe.db.get_value('Terms and Conditions', pos_profile.get('tc_name'), 'terms') or doc.terms or ''
diff --git a/erpnext/docs/assets/img/schools/admission/student-admission.gif b/erpnext/docs/assets/img/schools/admission/student-admission.gif
new file mode 100644
index 0000000..26caea9
--- /dev/null
+++ b/erpnext/docs/assets/img/schools/admission/student-admission.gif
Binary files differ
diff --git a/erpnext/docs/user/manual/en/schools/admission/student_admission.md b/erpnext/docs/user/manual/en/schools/admission/student_admission.md
index eeaa977..7a63fa7 100644
--- a/erpnext/docs/user/manual/en/schools/admission/student_admission.md
+++ b/erpnext/docs/user/manual/en/schools/admission/student_admission.md
@@ -1,13 +1,14 @@
# Student Admission
-The admission process begins with filling the admission form. The Student Admission record enables to intitate your admission process for a given **Academic year**. ERPNext admission module allow you to create an admission record which can be then published on the ERPNext generate website.
+The admission process begins with filling the admission form. The Student Admission record enables to initiate your admission process for a given **Academic year**. ERPNext admission module allows you to create an admission record which can be then published on the ERPNext generate website.
To create a Student Admission record go to :
**Schools** >> **Admissions** >> **Student Admission** >>
-<img class="screenshot" alt="Student Applicant" src="/docs/assets/img/schools/student/student-admission.gif">
+<img class="screenshot" alt="Student Applicant" src="/docs/assets/img/schools/admission/student-admission.gif">
+Once an admission record is created, the age eligibility criteria can be determined for the every program. Similarly, you can also determine the application fee and naming series for every student applicant. If you keep the naming series blank then the default naming series will be applied for every student applicant.
-Once a admission record is created it can be published on the website and the student can apply from the web portal itself.
\ No newline at end of file
+The information provided in the Student Admission records will be used for the validation and creation of the Student Admission records (only if student admission link is filled there)
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index 86b1884..2532ed1 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -183,16 +183,20 @@
var btn_update_status = function(frm, status){
var doc = frm.doc;
- frappe.call({
- method:
- "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status",
- args: {appointmentId: doc.name, status:status},
- callback: function(data){
- if(!data.exc){
- cur_frm.reload_doc();
- }
+ frappe.confirm(__('Are you sure you want to cancel this appointment?'),
+ function() {
+ frappe.call({
+ method:
+ "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status",
+ args: {appointmentId: doc.name, status:status},
+ callback: function(data){
+ if(!data.exc){
+ frm.reload_doc();
+ }
+ }
+ });
}
- });
+ );
};
var btn_invoice_consultation = function(frm){
diff --git a/erpnext/hr/doctype/training_event/training_event.json b/erpnext/hr/doctype/training_event/training_event.json
index f3c0e41..cb8518b 100644
--- a/erpnext/hr/doctype/training_event/training_event.json
+++ b/erpnext/hr/doctype/training_event/training_event.json
@@ -79,6 +79,37 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Conference' || doc.type == 'Exam'",
+ "fieldname": "has_certificate",
+ "fieldtype": "Check",
+ "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": "Has Certificate",
+ "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,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
@@ -139,6 +170,38 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "depends_on": "eval:doc.type == 'Seminar' || doc.type == 'Workshop' || doc.type == 'Exam'",
+ "fieldname": "level",
+ "fieldtype": "Select",
+ "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": "Level",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nBeginner\nExpert\nAdvance",
+ "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,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@@ -715,7 +778,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-10-05 05:57:08.212658",
+ "modified": "2017-10-06 10:59:09.217283",
"modified_by": "Administrator",
"module": "HR",
"name": "Training Event",
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 93a41f3..ead5d33 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -128,7 +128,7 @@
'uom' : item and args['stock_uom'] or '',
'conversion_factor': 1,
'bom_no' : args['bom_no'],
- 'rate' : rate / self.conversion_rate,
+ 'rate' : rate / self.conversion_rate if self.conversion_rate else rate,
'qty' : args.get("qty") or args.get("stock_qty") or 1,
'stock_qty' : args.get("qty") or args.get("stock_qty") or 1,
'base_rate' : rate
@@ -374,7 +374,7 @@
if d.workstation:
if not d.hour_rate:
hour_rate = flt(frappe.db.get_value("Workstation", d.workstation, "hour_rate"))
- d.hour_rate = hour_rate / flt(self.conversion_rate)
+ d.hour_rate = hour_rate / flt(self.conversion_rate) if self.conversion_rate else hour_rate
if d.hour_rate and d.time_in_mins:
d.base_hour_rate = flt(d.hour_rate) * flt(self.conversion_rate)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 8b0f714..9492c11 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -450,3 +450,5 @@
erpnext.patches.v8_9.update_billing_gstin_for_indian_account
erpnext.patches.v9_0.fix_subscription_next_date
erpnext.patches.v9_0.set_schedule_date_for_material_request_and_purchase_order
+erpnext.patches.v9_0.student_admission_childtable_migrate
+erpnext.patches.v9_0.add_healthcare_domain
diff --git a/erpnext/patches/v9_0/add_healthcare_domain.py b/erpnext/patches/v9_0/add_healthcare_domain.py
new file mode 100644
index 0000000..45fceb1
--- /dev/null
+++ b/erpnext/patches/v9_0/add_healthcare_domain.py
@@ -0,0 +1,14 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+
+def execute():
+ domain = _('Healthcare')
+ if not frappe.db.exists('Domain', domain):
+ frappe.get_doc({
+ 'doctype': 'Domain',
+ 'domain': domain
+ }).insert(ignore_permissions=True)
\ No newline at end of file
diff --git a/erpnext/patches/v9_0/student_admission_childtable_migrate.py b/erpnext/patches/v9_0/student_admission_childtable_migrate.py
new file mode 100644
index 0000000..dcbbeeb
--- /dev/null
+++ b/erpnext/patches/v9_0/student_admission_childtable_migrate.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc('schools', 'doctype', 'Student Admission Program')
+ frappe.reload_doctype('Student Admission')
+
+ if "program" not in frappe.db.get_table_columns("Student Admission"):
+ return
+
+ student_admissions = frappe.get_all("Student Admission", fields=["name", "application_fee", \
+ "naming_series_for_student_applicant", "program", "introduction", "eligibility"])
+ for student_admission in student_admissions:
+ doc = frappe.get_doc("Student Admission", student_admission.name)
+ doc.append("program_details", {
+ "program": student_admission.get("program"),
+ "application_fee": student_admission.get("application_fee"),
+ "applicant_naming_series": student_admission.get("naming_series_for_student_applicant"),
+ })
+ if student_admission.eligibility and student_admission.introduction:
+ doc.introduction = student_admission.introduction + "<br><div>" + \
+ student_admission.eligibility + "</div>"
+ doc.flags.ignore_validate = True
+ doc.flags.ignore_mandatory = True
+ doc.save()
diff --git a/erpnext/public/js/communication.js b/erpnext/public/js/communication.js
index 5ce57b6..bb2eaef 100644
--- a/erpnext/public/js/communication.js
+++ b/erpnext/public/js/communication.js
@@ -1,39 +1,45 @@
frappe.ui.form.on("Communication", {
- refresh: function(frm) {
+ refresh: (frm) => {
+ // setup custom Make button only if Communication is Email
+ if(frm.doc.communication_medium == "Email" && frm.doc.sent_or_received == "Received") {
+ frm.events.setup_custom_buttons(frm);
+ }
+ },
+
+ setup_custom_buttons: (frm) => {
+ let confirm_msg = "Are you sure you want to create {0} from this email";
if(frm.doc.reference_doctype !== "Issue") {
- frm.add_custom_button(__("Issue"), function() {
- frappe.confirm("Are you sure you want to create Issue from this email", function(){
+ frm.add_custom_button(__("Issue"), () => {
+ frappe.confirm(__(confirm_msg, [__("Issue")]), () => {
frm.trigger('make_issue_from_communication');
})
}, "Make");
}
if(!in_list(["Lead", "Opportunity"], frm.doc.reference_doctype)) {
- frm.add_custom_button(__("Lead"), function() {
- frappe.confirm("Are you sure you want to create Lead from this email", function(){
+ frm.add_custom_button(__("Lead"), () => {
+ frappe.confirm(__(confirm_msg, [__("Lead")]), () => {
frm.trigger('make_lead_from_communication');
})
}, "Make");
- frm.add_custom_button(__("Opportunity"), function() {
- frappe.confirm("Are you sure you want to create Opportunity from this email", function(){
+ frm.add_custom_button(__("Opportunity"), () => {
+ frappe.confirm(__(confirm_msg, [__("Opportunity")]), () => {
frm.trigger('make_opportunity_from_communication');
})
}, "Make");
}
-
-
frm.page.set_inner_btn_group_as_primary(__("Make"));
},
- make_lead_from_communication: function(frm) {
+ make_lead_from_communication: (frm) => {
return frappe.call({
method: "frappe.email.inbox.make_lead_from_communication",
args: {
communication: frm.doc.name
},
freeze: true,
- callback: function(r) {
+ callback: (r) => {
if(r.message) {
frm.reload_doc()
}
@@ -41,14 +47,14 @@
})
},
- make_issue_from_communication: function(frm) {
+ make_issue_from_communication: (frm) => {
return frappe.call({
method: "frappe.email.inbox.make_issue_from_communication",
args: {
communication: frm.doc.name
},
freeze: true,
- callback: function(r) {
+ callback: (r) => {
if(r.message) {
frm.reload_doc()
}
@@ -56,14 +62,14 @@
})
},
- make_opportunity_from_communication: function(frm) {
+ make_opportunity_from_communication: (frm) => {
return frappe.call({
method: "frappe.email.inbox.make_opportunity_from_communication",
args: {
communication: frm.doc.name
},
freeze: true,
- callback: function(r) {
+ callback: (r) => {
if(r.message) {
frm.reload_doc()
}
diff --git a/erpnext/public/js/pos/pos.html b/erpnext/public/js/pos/pos.html
index 485a945..1d9fd7c 100644
--- a/erpnext/public/js/pos/pos.html
+++ b/erpnext/public/js/pos/pos.html
@@ -37,7 +37,6 @@
<div class="cell price-cell text-right tax-table">
</div>
</div>
- {% if (apply_discount_on) { %}
<div class="pos-list-row discount-amount-area">
<div class="cell"></div>
<div class="cell text-right">{%= __("Discount") %}</div>
@@ -52,7 +51,6 @@
</div>
</div>
</div>
- {% } %}
<div class="pos-list-row grand-total-area collapse-btn" style="border-bottom:1px solid #d1d8dd;">
<div class="cell">
<a class="">
diff --git a/erpnext/schools/doctype/fees/fees.py b/erpnext/schools/doctype/fees/fees.py
index a2fcecc..42b329e 100644
--- a/erpnext/schools/doctype/fees/fees.py
+++ b/erpnext/schools/doctype/fees/fees.py
@@ -50,6 +50,7 @@
select g.email_address
from `tabGuardian` g, `tabStudent Guardian` sg
where g.name = sg.guardian and sg.parent = %s and sg.parenttype = 'Student'
+ and ifnull(g.email_address, '')!=''
""", self.student)
student_email_id = frappe.db.get_value("Student", self.student, "student_email_id")
diff --git a/erpnext/schools/doctype/student_admission/test_student_admission.js b/erpnext/schools/doctype/student_admission/test_student_admission.js
index 0d7c997..3e997ca 100644
--- a/erpnext/schools/doctype/student_admission/test_student_admission.js
+++ b/erpnext/schools/doctype/student_admission/test_student_admission.js
@@ -11,23 +11,29 @@
{admission_start_date: '2016-04-20'},
{admission_end_date: '2016-05-31'},
{title: '2016-17 Admissions'},
- {program: 'Standard Test'},
- {application_fee: 1000},
- {naming_series_for_student_applicant: 'AP'},
+ {application_form_route: 'student-applicant'},
{introduction: 'Test intro'},
- {eligibility: 'Test eligibility'}
+ {program_details: [
+ [
+ {'program': 'Standard Test'},
+ {'application_fee': 1000},
+ {'applicant_naming_series': 'AP'},
+ ]
+ ]}
]);
},
+ () => cur_frm.save(),
() => {
assert.ok(cur_frm.doc.academic_year == '2016-17');
assert.ok(cur_frm.doc.admission_start_date == '2016-04-20');
assert.ok(cur_frm.doc.admission_end_date == '2016-05-31');
assert.ok(cur_frm.doc.title == '2016-17 Admissions');
- assert.ok(cur_frm.doc.program == 'Standard Test', 'Program correctly selected');
- assert.ok(cur_frm.doc.application_fee == 1000);
- assert.ok(cur_frm.doc.naming_series_for_student_applicant == 'AP');
+ assert.ok(cur_frm.doc.application_form_route == 'student-applicant');
assert.ok(cur_frm.doc.introduction == 'Test intro');
- assert.ok(cur_frm.doc.eligibility == 'Test eligibility');
+ assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected');
+ assert.ok(cur_frm.doc.program_details[0].application_fee == 1000);
+ assert.ok(cur_frm.doc.program_details[0].applicant_naming_series == 'AP');
+ assert.ok(cur_frm.doc.route == 'admissions/2016-17-Admissions', "Route successfully set");
},
() => done()
]);
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index 726e80f..c042244 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -222,6 +222,11 @@
}
update_item_in_frm(item, field, value) {
+ if (field == 'qty' && value < 0) {
+ frappe.msgprint(__("Quantity must be positive"));
+ value = item.qty;
+ }
+
if (field) {
frappe.model.set_value(item.doctype, item.name, field, value);
}
@@ -518,7 +523,7 @@
// Update totals
this.$taxes_and_totals.find('.net-total')
- .html(format_currency(this.frm.doc.net_total, currency));
+ .html(format_currency(this.frm.doc.total, currency));
// Update taxes
const taxes_html = this.frm.doc.taxes.map(tax => {
@@ -943,6 +948,7 @@
if (this.search_index[search_term]) {
const items = this.search_index[search_term];
this.render_items(items);
+ this.set_item_in_the_cart(items);
return;
}
} else if (item_group == "All Item Groups") {
@@ -956,19 +962,32 @@
}
this.render_items(items);
- if(serial_no) {
- this.events.update_cart(items[0].item_code,
- 'serial_no', serial_no);
- this.reset_search_field();
- }
- if(batch_no) {
- this.events.update_cart(items[0].item_code,
- 'batch_no', batch_no);
- this.reset_search_field();
- }
+ this.set_item_in_the_cart(items, serial_no, batch_no);
});
}
+ set_item_in_the_cart(items, serial_no, batch_no) {
+ if (serial_no) {
+ this.events.update_cart(items[0].item_code,
+ 'serial_no', serial_no);
+ this.reset_search_field();
+ return;
+ }
+
+ if (batch_no) {
+ this.events.update_cart(items[0].item_code,
+ 'batch_no', batch_no);
+ this.reset_search_field();
+ return;
+ }
+
+ if (items.length === 1) {
+ this.events.update_cart(items[0].item_code,
+ 'qty', '+1');
+ this.reset_search_field();
+ }
+ }
+
reset_search_field() {
this.search_field.set_value('');
this.search_field.$input.trigger("input");