Merge branch 'master' into develop
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index eb7f91d..641b9b6 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -2,7 +2,7 @@
from __future__ import unicode_literals
import frappe
-__version__ = '8.0.40'
+__version__ = '8.0.41'
def get_default_company(user=None):
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 3fbde29..0e4edcc 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -299,6 +299,7 @@
this.calculate_write_off_amount()
}else {
this.frm.set_value("change_amount", 0.0)
+ this.frm.set_value("base_change_amount", 0.0)
}
this.frm.refresh_fields();
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index cede8f4..42327b9 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -19,10 +19,10 @@
["Converted", "has_customer"],
],
"Opportunity": [
- ["Quotation", "has_active_quotation"],
- ["Converted", "has_ordered_quotation"],
["Lost", "eval:self.status=='Lost'"],
["Lost", "has_lost_quotation"],
+ ["Quotation", "has_active_quotation"],
+ ["Converted", "has_ordered_quotation"],
["Closed", "eval:self.status=='Closed'"]
],
"Quotation": [
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 822d50b..50f6b61 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -440,16 +440,16 @@
self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
- flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
- if self.doc.doctype == "Sales Invoice":
+ if self.doc.doctype == "Sales Invoice":
self.doc.round_floats_in(self.doc, ["paid_amount"])
- paid_amount = self.doc.paid_amount \
- if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
-
- change_amount = self.doc.change_amount \
- if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
-
self.calculate_write_off_amount()
self.calculate_change_amount()
+
+ paid_amount = self.doc.paid_amount \
+ if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
+
+ change_amount = self.doc.change_amount \
+ if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) +
flt(change_amount), self.doc.precision("outstanding_amount"))
@@ -475,7 +475,9 @@
def calculate_change_amount(self):
self.doc.change_amount = 0.0
self.doc.base_change_amount = 0.0
- if self.doc.paid_amount > self.doc.grand_total:
+ if self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
+ and any([d.type == "Cash" for d in self.doc.payments]):
+
self.doc.change_amount = flt(self.doc.paid_amount - self.doc.grand_total +
self.doc.write_off_amount, self.doc.precision("change_amount"))
diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js
index 974b23e..0e14c85 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.js
+++ b/erpnext/crm/doctype/opportunity/opportunity.js
@@ -35,6 +35,7 @@
var doc = frm.doc;
frm.events.enquiry_from(frm);
frm.trigger('set_contact_link');
+ erpnext.toggle_naming_series();
if(!doc.__islocal && doc.status!=="Lost") {
if(doc.with_items){
@@ -53,6 +54,20 @@
frm.add_custom_button(__('Lost'),
cur_frm.cscript['Declare Opportunity Lost']);
}
+ },
+
+ if(!frm.doc.__islocal && frm.perm[0].write && frm.doc.docstatus==0) {
+ if(frm.doc.status==="Open") {
+ frm.add_custom_button(__("Close"), function() {
+ frm.set_value("status", "Closed");
+ frm.save();
+ });
+ } else {
+ frm.add_custom_button(__("Reopen"), function() {
+ frm.set_value("status", "Open");
+ frm.save();
+ });
+ }
}
},
@@ -122,25 +137,6 @@
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
-cur_frm.cscript.refresh = function(doc, cdt, cdn) {
- erpnext.toggle_naming_series();
-
- var frm = cur_frm;
- if(!doc.__islocal && frm.perm[0].write && doc.docstatus==0) {
- if(frm.doc.status==="Open") {
- frm.add_custom_button(__("Close"), function() {
- frm.set_value("status", "Closed");
- frm.save();
- });
- } else {
- frm.add_custom_button(__("Reopen"), function() {
- frm.set_value("status", "Open");
- frm.save();
- });
- }
- }
-}
-
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
if(doc.enquiry_from == 'Lead' && doc.lead)
cur_frm.cscript.lead(doc, cdt, cdn);
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index eebf464..58f26fe 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -31,7 +31,6 @@
if not self.enquiry_from:
frappe.throw(_("Opportunity From field is mandatory"))
- self.set_status()
self.validate_item_details()
self.validate_uom_is_integer("uom", "qty")
self.validate_lead_cust()
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index af4dd3b..d64409d 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -178,7 +178,8 @@
"erpnext.hr.doctype.employee.employee.send_birthday_reminders",
"erpnext.projects.doctype.task.task.set_tasks_as_overdue",
"erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries",
- 'erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary'
+ "erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary",
+ "erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status"
]
}
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index 01dc341..1cd7257 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -11,6 +11,7 @@
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
from erpnext.controllers.accounts_controller import AccountsController
+from frappe.utils.csvutils import getlink
class InvalidExpenseApproverError(frappe.ValidationError): pass
@@ -146,7 +147,7 @@
frappe.throw(_("Cost center is required to book an expense claim"))
if not self.payable_account:
- frappe.throw(_("Please set default payable account in the employee {0}").format(self.employee))
+ frappe.throw(_("Please set default payable account for the company {0}").format(getlink("Company",self.company)))
if self.is_paid:
if not self.mode_of_payment:
diff --git a/erpnext/hr/doctype/training_event/training_event.json b/erpnext/hr/doctype/training_event/training_event.json
index 1f79d23..03b58b4 100644
--- a/erpnext/hr/doctype/training_event/training_event.json
+++ b/erpnext/hr/doctype/training_event/training_event.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:event_name",
@@ -12,6 +13,7 @@
"editable_grid": 1,
"fields": [
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -27,7 +29,7 @@
"in_standard_filter": 0,
"label": "Event Name",
"length": 0,
- "no_copy": 0,
+ "no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -41,6 +43,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -71,6 +74,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -99,6 +103,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -129,6 +134,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -157,6 +163,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -186,6 +193,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -216,6 +224,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -244,6 +253,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -274,6 +284,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -303,6 +314,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -331,6 +343,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -361,6 +374,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -390,6 +404,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -418,6 +433,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -447,6 +463,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -476,6 +493,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -504,6 +522,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -533,6 +552,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -562,6 +582,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -592,6 +613,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -622,6 +644,7 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -651,17 +674,17 @@
"unique": 0
}
],
+ "has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
- "in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-02-17 16:51:35.141403",
+ "modified": "2017-05-29 06:13:38.411039",
"modified_by": "Administrator",
"module": "HR",
"name": "Training Event",
diff --git a/erpnext/hr/doctype/training_result/training_result.py b/erpnext/hr/doctype/training_result/training_result.py
index 16b76a7..36c3cb9 100644
--- a/erpnext/hr/doctype/training_result/training_result.py
+++ b/erpnext/hr/doctype/training_result/training_result.py
@@ -13,7 +13,9 @@
def send_result(self):
for emp in self.employees:
- message = "Thank You for attending {0}. You grade is {1}".format(self.training_event, emp.grade)
+ message = "Thank You for attending {0}.".format(self.training_event)
+ if emp.grade:
+ message = message + "Your grade: {0}".format(emp.grade)
frappe.sendmail(frappe.db.get_value("Employee", emp.employee, "company_email"), \
subject=_("{0} Results".format(self.training_event)), \
content=message)
diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py
index ff1082d..68da336 100644
--- a/erpnext/manufacturing/doctype/production_order/production_order.py
+++ b/erpnext/manufacturing/doctype/production_order/production_order.py
@@ -20,6 +20,7 @@
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
from erpnext.stock.utils import get_bin
+from frappe.utils.csvutils import getlink
class OverProductionError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass
@@ -311,7 +312,7 @@
if timesheet and timesheet.get("time_logs"):
timesheet.save()
- timesheets.append(timesheet.name)
+ timesheets.append(getlink("Timesheet", timesheet.name))
self.planned_end_date = self.operations[-1].planned_end_time
if timesheets:
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index 154f0d0..8844a48 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -368,7 +368,8 @@
conditions = get_conditions(filters)
return frappe.db.sql("""select `tabTimesheet Detail`.name as name,
`tabTimesheet Detail`.docstatus as status, `tabTimesheet Detail`.parent as parent,
- from_time as start_date, hours, activity_type, project, to_time as end_date,
+ from_time as start_date, hours, activity_type,
+ `tabTimesheet Detail`.project, to_time as end_date,
CONCAT(`tabTimesheet Detail`.parent, ' (', ROUND(hours,2),' hrs)') as title
from `tabTimesheet Detail`, `tabTimesheet`
where `tabTimesheet Detail`.parent = `tabTimesheet`.name
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 849275f..dc1db35 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -604,11 +604,18 @@
calculate_change_amount: function(){
this.frm.doc.change_amount = 0.0;
- if(this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return){
- this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total +
- this.frm.doc.write_off_amount, precision("change_amount"));
- this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount - this.frm.doc.base_grand_total +
- this.frm.doc.base_write_off_amount, precision("base_change_amount"));
+ this.frm.doc.base_change_amount = 0.0;
+ if(this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return) {
+ var payment_types = $.map(cur_frm.doc.payments, function(d) { return d.type });
+ if (in_list(payment_types, 'Cash')) {
+ this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total +
+ this.frm.doc.write_off_amount, precision("change_amount"));
+
+ this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount -
+ this.frm.doc.base_grand_total + this.frm.doc.base_write_off_amount,
+ precision("base_change_amount"));
+
+ }
}
},
diff --git a/erpnext/schools/doctype/student_group/student_group.py b/erpnext/schools/doctype/student_group/student_group.py
index e861421..9cdf9c7 100644
--- a/erpnext/schools/doctype/student_group/student_group.py
+++ b/erpnext/schools/doctype/student_group/student_group.py
@@ -14,7 +14,7 @@
self.validate_strength()
if frappe.defaults.get_defaults().student_validation_setting:
self.validate_students()
- self.validate_roll_no()
+ self.validate_and_set_child_table_fields()
validate_duplicate_student(self.students)
def validate_mandatory_fields(self):
@@ -38,9 +38,16 @@
if not frappe.db.get_value("Student", d.student, "enabled") and d.active:
frappe.throw(_("{0} - {1} is inactive student".format(d.group_roll_number, d.student_name)))
- def validate_roll_no(self):
+ def validate_and_set_child_table_fields(self):
+ roll_numbers = [d.group_roll_number for d in self.students if d.group_roll_number]
+ max_roll_no = max(roll_numbers) if roll_numbers else 0
roll_no_list = []
for d in self.students:
+ if not d.student_name:
+ d.student_name = frappe.db.get_value("Student", d.student, "title")
+ if not d.group_roll_number:
+ max_roll_no += 1
+ d.group_roll_number = max_roll_no
if d.group_roll_number in roll_no_list:
frappe.throw(_("Duplicate roll number for student {0}".format(d.student_name)))
else:
diff --git a/erpnext/schools/doctype/student_group/test_student_group.py b/erpnext/schools/doctype/student_group/test_student_group.py
index 6b599aa..18a6b14 100644
--- a/erpnext/schools/doctype/student_group/test_student_group.py
+++ b/erpnext/schools/doctype/student_group/test_student_group.py
@@ -5,8 +5,23 @@
import frappe
import unittest
-
-# test_records = frappe.get_test_records('Student Group')
+from frappe.utils.make_random import get_random
class TestStudentGroup(unittest.TestCase):
- pass
+ def test_student_roll_no(self):
+ doc = frappe.get_doc({
+ "doctype": "Student Group",
+ "student_group_name": "_Test Student Group R",
+ "group_based_on": "Activity"
+ }).insert()
+
+ student_list = []
+ while len(student_list) < 3:
+ s = get_random("Student")
+ if s not in student_list:
+ student_list.append(s)
+
+ doc.extend("students", [{"student":d} for d in student_list])
+ doc.save()
+ self.assertEquals(max([d.group_roll_number for d in doc.students]), 3)
+
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index bc9b893..03d4d73 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -324,3 +324,12 @@
update_rejected_serial_nos = False
if accepted_serial_nos_updated:
break
+
+def update_maintenance_status():
+ serial_nos = frappe.db.sql('''select name from `tabSerial No` where (amc_expiry_date<%s or
+ warranty_expiry_date<%s) and maintenance_status not in ('Out of Warranty', 'Out of AMC')''',
+ (nowdate(), nowdate()))
+ for serial_no in serial_nos:
+ doc = frappe.get_doc("Serial No", serial_no[0])
+ doc.set_maintenance_status()
+ frappe.db.set_value('Serial No', doc.name, 'maintenance_status', doc.maintenance_status)