Merge branch 'hotfix'
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index f4876bd..029d066 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '10.0.14'
+__version__ = '10.0.15'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 07853d0..28d51ce 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -96,7 +96,7 @@
if not self.credit_to:
self.credit_to = get_party_account("Supplier", self.supplier, self.company)
if not self.due_date:
- self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier)
+ self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company)
super(PurchaseInvoice, self).set_missing_values(for_validate)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index f6d43c7..b6c8405 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -237,7 +237,7 @@
if not self.debit_to:
self.debit_to = get_party_account("Customer", self.customer, self.company)
if not self.due_date and self.customer:
- self.due_date = get_due_date(self.posting_date, "Customer", self.customer)
+ self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
super(SalesInvoice, self).set_missing_values(for_validate)
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 7bccfe8..5237a71 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -51,7 +51,7 @@
set_other_values(out, party, party_type)
set_price_list(out, party, party_type, price_list)
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
- out["payment_terms_template"] = get_pyt_term_template(party.name, party_type)
+ out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
if not out.get("currency"):
out["currency"] = currency
@@ -164,7 +164,7 @@
out = {
party_type.lower(): party,
account_fieldname : account,
- "due_date": get_due_date(posting_date, party_type, party)
+ "due_date": get_due_date(posting_date, party_type, party, company)
}
return out
@@ -267,12 +267,12 @@
@frappe.whitelist()
-def get_due_date(posting_date, party_type, party):
+def get_due_date(posting_date, party_type, party, company=None):
"""Get due date from `Payment Terms Template`"""
due_date = None
if posting_date and party:
due_date = posting_date
- template_name = get_pyt_term_template(party, party_type)
+ template_name = get_pyt_term_template(party, party_type, company)
if template_name:
due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
else:
@@ -305,12 +305,11 @@
return due_date
-
-def validate_due_date(posting_date, due_date, party_type, party):
+def validate_due_date(posting_date, due_date, party_type, party, company=None):
if getdate(due_date) < getdate(posting_date):
frappe.throw(_("Due Date cannot be before Posting Date"))
else:
- default_due_date = get_due_date(posting_date, party_type, party)
+ default_due_date = get_due_date(posting_date, party_type, party, company)
if not default_due_date:
return
@@ -360,14 +359,32 @@
@frappe.whitelist()
-def get_pyt_term_template(party_name, party_type):
+def get_pyt_term_template(party_name, party_type, company=None):
+ if party_type not in ("Customer", "Supplier"):
+ return
+
template = None
- if party_type in ('Customer', 'Supplier'):
- template = frappe.db.get_value(party_type, party_name, fieldname='payment_terms')
+ if party_type == 'Customer':
+ customer = frappe.db.get_value("Customer", party_name,
+ fieldname=['payment_terms', "customer_group"], as_dict=1)
+ template = customer.payment_terms
+
+ if not template and customer.customer_group:
+ template = frappe.db.get_value("Customer Group",
+ customer.customer_group, fieldname='payment_terms')
+ else:
+ supplier = frappe.db.get_value("Supplier", party_name,
+ fieldname=['payment_terms', "supplier_type"], as_dict=1)
+ template = supplier.payment_terms
+
+ if not template and supplier.supplier_type:
+ template = frappe.db.get_value("Supplier Type", supplier.supplier_type, fieldname='payment_terms')
+
+ if not template and company:
+ template = frappe.db.get_value("Company", company, fieldname='payment_terms')
return template
-
def validate_party_frozen_disabled(party_type, party_name):
if party_type and party_name:
if party_type in ("Customer", "Supplier"):
diff --git a/erpnext/buying/doctype/supplier/test_records.json b/erpnext/buying/doctype/supplier/test_records.json
index 7479f55..370ce8d 100644
--- a/erpnext/buying/doctype/supplier/test_records.json
+++ b/erpnext/buying/doctype/supplier/test_records.json
@@ -9,7 +9,6 @@
"doctype": "Supplier",
"supplier_name": "_Test Supplier P",
"supplier_type": "_Test Supplier Type",
- "credit_days_based_on": "Fixed Days"
},
{
"doctype": "Supplier",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index c7b5206..b3672cb 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -135,9 +135,9 @@
if not self.due_date:
frappe.throw(_("Due Date is mandatory"))
- validate_due_date(self.posting_date, self.due_date, "Customer", self.customer)
+ validate_due_date(self.posting_date, self.due_date, "Customer", self.customer, self.company)
elif self.doctype == "Purchase Invoice":
- validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier)
+ validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company)
def set_price_list_currency(self, buying_or_selling):
if self.meta.get_field("posting_date"):
diff --git a/erpnext/education/api.py b/erpnext/education/api.py
index ea4da69..99fb36e 100644
--- a/erpnext/education/api.py
+++ b/erpnext/education/api.py
@@ -269,7 +269,10 @@
:param Percentage: Score Percentage Percentage
"""
grading_scale_intervals = {}
- for d in frappe.get_all("Grading Scale Interval", fields=["grade_code", "threshold"], filters={"parent": grading_scale}):
+ if not hasattr(frappe.local, 'grading_scale'):
+ grading_scale = frappe.get_all("Grading Scale Interval", fields=["grade_code", "threshold"], filters={"parent": grading_scale})
+ frappe.local.grading_scale = grading_scale
+ for d in frappe.local.grading_scale:
grading_scale_intervals.update({d.threshold:d.grade_code})
intervals = sorted(grading_scale_intervals.keys(), key=float, reverse=True)
for interval in intervals:
diff --git a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py b/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
index e666a74..eadc0de 100644
--- a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
+++ b/erpnext/education/doctype/assessment_criteria/assessment_criteria.py
@@ -6,5 +6,9 @@
import frappe
from frappe.model.document import Document
+STD_CRITERIA = ["total", "total score", "total grade", "maximum score", "score", "grade"]
+
class AssessmentCriteria(Document):
- pass
+ def validate(self):
+ if self.assessment_criteria.lower() in STD_CRITERIA:
+ frappe.throw("Can't create standard criteria. Please rename the criteria")
\ No newline at end of file
diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.html b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.html
index 60300b0..e46a5e7 100644
--- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.html
+++ b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.html
@@ -6,9 +6,15 @@
{% } %}
<h4 class="text-center">{%= __("Assessment Report") %}</h4>
<hr>
+<h5 class="text-center">{%= __("Academic Year: ") %} {%= filters.academic_year %} </h5>
+{% if (filters.academic_term){ %}
+<h5 class="text-center">{%= __("Academic Term: ") %} {%= filters.academic_term %} </h5>
+{% } %}
<h5 class="text-center">{%= __("Course Code: ") %} {%= filters.course %}</h5>
<h5 class="text-center">{%= __("Assessment Group: ") %} {%= filters.assessment_group %}</h5>
-<h5 class="text-center">{%= __("Assessment Plan: ") %} {%= data_to_be_printed[0]["assessment_plan"] %} </h5>
+{% if (filters.student_group){ %}
+<h5 class="text-center">{%= __("Student Group: ") %} {%= filters.student_group %} </h5>
+{% } %}
<hr>
<table class="table table-bordered">
diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.js b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.js
index 42b19eb..8c42d48 100644
--- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.js
+++ b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.js
@@ -4,18 +4,17 @@
frappe.query_reports["Course wise Assessment Report"] = {
"filters": [
{
- "fieldname":"assessment_group",
- "label": __("Assessment Group"),
+ "fieldname":"academic_year",
+ "label": __("Academic Year"),
"fieldtype": "Link",
- "options": "Assessment Group",
- "reqd": 1,
- "get_query": function() {
- return{
- filters: {
- 'is_group': 0
- }
- };
- }
+ "options": "Academic Year",
+ "reqd": 1
+ },
+ {
+ "fieldname":"academic_term",
+ "label": __("Academic Term"),
+ "fieldtype": "Link",
+ "options": "Academic Term"
},
{
"fieldname":"course",
@@ -29,6 +28,13 @@
"label": __("Student Group"),
"fieldtype": "Link",
"options": "Student Group"
+ },
+ {
+ "fieldname":"assessment_group",
+ "label": __("Assessment Group"),
+ "fieldtype": "Link",
+ "options": "Assessment Group",
+ "reqd": 1
}
]
};
diff --git a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py
index ff17238..a50ad7b 100644
--- a/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py
+++ b/erpnext/education/report/course_wise_assessment_report/course_wise_assessment_report.py
@@ -5,129 +5,189 @@
import frappe
from frappe import _
from frappe.utils import flt
-from collections import defaultdict
+from collections import defaultdict, OrderedDict
from erpnext.education.api import get_grade
def execute(filters=None):
- data = []
-
+ data, chart, grades = [], [], []
args = frappe._dict()
+ grade_wise_analysis = defaultdict(dict)
+
+ args["academic_year"] = filters.get("academic_year")
+ args["course"] = filters.get("course")
args["assessment_group"] = filters.get("assessment_group")
+
+ args["academic_term"] = filters.get("academic_term")
+ args["student_group"] = filters.get("student_group")
+
if args["assessment_group"] == "All Assessment Groups":
frappe.throw(_("Please select the assessment group other than 'All Assessment Groups'"))
- args["course"] = filters.get("course")
- args["student_group"] = filters.get("student_group")
+ returned_values = get_formatted_result(args, get_assessment_criteria=True)
+ student_dict = returned_values["student_details"]
+ result_dict = returned_values["assessment_result"]
+ assessment_criteria_dict = returned_values["assessment_criteria"]
+
+ for student in result_dict:
+ student_row = {}
+ student_row["student"] = student
+ student_row["student_name"] = student_dict[student]
+ for criteria in assessment_criteria_dict:
+ scrub_criteria = frappe.scrub(criteria)
+ if criteria in result_dict[student][args.course][args.assessment_group]:
+ student_row[scrub_criteria] = result_dict[student][args.course][args.assessment_group][criteria]["grade"]
+ student_row[scrub_criteria + "_score"] = result_dict[student][args.course][args.assessment_group][criteria]["score"]
+
+ # create the list of possible grades
+ if student_row[scrub_criteria] not in grades:
+ grades.append(student_row[scrub_criteria])
+
+ # create the dict of for gradewise analysis
+ if student_row[scrub_criteria] not in grade_wise_analysis[criteria]:
+ grade_wise_analysis[criteria][student_row[scrub_criteria]] = 1
+ else:
+ grade_wise_analysis[criteria][student_row[scrub_criteria]] += 1
+ else:
+ student_row[frappe.scrub(criteria)] = ""
+ student_row[frappe.scrub(criteria)+ "_score"] = ""
+ data.append(student_row)
+
+ assessment_criteria_list = [d for d in assessment_criteria_dict]
+ columns = get_column(assessment_criteria_dict)
+ chart = get_chart_data(grades, assessment_criteria_list, grade_wise_analysis)
+
+ return columns, data, None, chart
- # find all assessment plan and related details linked with the given filters
- def get_assessment_details():
- if args["student_group"]:
- cond = "and ap.student_group=%(student_group)s"
+def get_formatted_result(args, get_assessment_criteria=False, get_course=False):
+ cond, cond1, cond2, cond3, cond4 = " ", " ", " ", " ", " "
+ args_list = [args.academic_year]
+
+ if args.course:
+ cond = " and ar.course=%s"
+ args_list.append(args.course)
+
+ if args.academic_term:
+ cond1 = " and ar.academic_term=%s"
+ args_list.append(args.academic_term)
+
+ if args.student_group:
+ cond2 = " and ar.student_group=%s"
+ args_list.append(args.student_group)
+
+ create_total_dict = False
+ group_type = frappe.get_value("Assessment Group", args.assessment_group, "is_group")
+ if group_type:
+ from frappe.desk.treeview import get_children
+ assessment_groups = [d.get("value") for d in get_children("Assessment Group",
+ args.assessment_group) if d.get("value") and not d.get("expandable")]
+ cond3 = " and ar.assessment_group in (%s)"%(', '.join(['%s']*len(assessment_groups)))
+ else:
+ assessment_groups = [args.assessment_group]
+ cond3 = " and ar.assessment_group=%s"
+ args_list += assessment_groups
+
+ if args.students:
+ cond4 = " and ar.student in (%s)"%(', '.join(['%s']*len(args.students)))
+ args_list += args.students
+
+ assessment_result = frappe.db.sql('''
+ SELECT
+ ar.student, ar.student_name, ar.academic_year, ar.academic_term, ar.program, ar.course,
+ ar.assessment_plan, ar.grading_scale, ar.assessment_group, ar.student_group,
+ ard.assessment_criteria, ard.maximum_score, ard.grade, ard.score
+ FROM
+ `tabAssessment Result` ar, `tabAssessment Result Detail` ard
+ WHERE
+ ar.name=ard.parent and ar.docstatus=1 and ar.academic_year=%s {0} {1} {2} {3} {4}
+ ORDER BY
+ ard.assessment_criteria'''.format(cond, cond1, cond2, cond3, cond4),
+ tuple(args_list), as_dict=1)
+
+ # create the nested dictionary structure as given below:
+ # <variable_name>.<student_name>.<course>.<assessment_group>.<assessment_criteria>.<grade/score/max_score>
+ # "Total Score" -> assessment criteria used for totaling and args.assessment_group -> for totaling all the assesments
+
+ student_details = {}
+ formatted_assessment_result = defaultdict(dict)
+ assessment_criteria_dict = OrderedDict()
+ course_dict = OrderedDict()
+ total_maximum_score = None
+ if not (len(assessment_groups) == 1 and assessment_groups[0] == args.assessment_group):
+ create_total_dict = True
+
+ # add the score for a given score and recalculate the grades
+ def add_score_and_recalculate_grade(result, assessment_group, assessment_criteria):
+ formatted_assessment_result[result.student][result.course][assessment_group]\
+ [assessment_criteria]["maximum_score"] += result.maximum_score
+ formatted_assessment_result[result.student][result.course][assessment_group]\
+ [assessment_criteria]["score"] += result.score
+ tmp_grade = get_grade(result.grading_scale, ((formatted_assessment_result[result.student][result.course]
+ [assessment_group][assessment_criteria]["score"])/(formatted_assessment_result[result.student]
+ [result.course][assessment_group][assessment_criteria]["maximum_score"]))*100)
+ formatted_assessment_result[result.student][result.course][assessment_group]\
+ [assessment_criteria]["grade"] = tmp_grade
+
+ # create the assessment criteria "Total Score" with the sum of all the scores of the assessment criteria in a given assessment group
+ def add_total_score(result, assessment_group):
+ if "Total Score" not in formatted_assessment_result[result.student][result.course][assessment_group]:
+ formatted_assessment_result[result.student][result.course][assessment_group]["Total Score"] = frappe._dict({
+ "assessment_criteria": "Total Score", "maximum_score": result.maximum_score, "score": result.score, "grade": result.grade})
else:
- cond = ''
+ add_score_and_recalculate_grade(result, assessment_group, "Total Score")
- assessment_plan = frappe.db.sql('''
- select
- ap.name, ap.student_group, ap.grading_scale, apc.assessment_criteria, apc.maximum_score as max_score
- from
- `tabAssessment Plan` ap, `tabAssessment Plan Criteria` apc
- where
- ap.assessment_group=%(assessment_group)s and ap.course=%(course)s and
- ap.name=apc.parent and ap.docstatus=1 {0}
- order by
- apc.assessment_criteria'''.format(cond), (args), as_dict=1)
+ for result in assessment_result:
+ if result.student not in student_details:
+ student_details[result.student] = result.student_name
- assessment_plan_list = list(set([d["name"] for d in assessment_plan]))
- if not assessment_plan_list:
- frappe.throw(_("No assessment plan linked with this assessment group"))
+ assessment_criteria_details = frappe._dict({"assessment_criteria": result.assessment_criteria,
+ "maximum_score": result.maximum_score, "score": result.score, "grade": result.grade})
- assessment_criteria_list = list(set([(d["assessment_criteria"],d["max_score"]) for d in assessment_plan]))
- student_group_list = list(set([d["student_group"] for d in assessment_plan]))
- total_maximum_score = flt(sum([flt(d[1]) for d in assessment_criteria_list]))
- grading_scale = assessment_plan[0]["grading_scale"]
+ if not formatted_assessment_result[result.student]:
+ formatted_assessment_result[result.student] = defaultdict(dict)
+ if not formatted_assessment_result[result.student][result.course]:
+ formatted_assessment_result[result.student][result.course] = defaultdict(dict)
- return assessment_plan_list, assessment_criteria_list, total_maximum_score, grading_scale, student_group_list
+ if not create_total_dict:
+ formatted_assessment_result[result.student][result.course][result.assessment_group]\
+ [result.assessment_criteria] = assessment_criteria_details
+ add_total_score(result, result.assessment_group)
+
+ # create the total of all the assessment groups criteria-wise
+ elif create_total_dict:
+ if not formatted_assessment_result[result.student][result.course][args.assessment_group]:
+ formatted_assessment_result[result.student][result.course][args.assessment_group] = defaultdict(dict)
+ formatted_assessment_result[result.student][result.course][args.assessment_group]\
+ [result.assessment_criteria] = assessment_criteria_details
+ elif result.assessment_criteria not in formatted_assessment_result[result.student][result.course][args.assessment_group]:
+ formatted_assessment_result[result.student][result.course][args.assessment_group]\
+ [result.assessment_criteria] = assessment_criteria_details
+ elif result.assessment_criteria in formatted_assessment_result[result.student][result.course][args.assessment_group]:
+ add_score_and_recalculate_grade(result, args.assessment_group, result.assessment_criteria)
+
+ add_total_score(result, args.assessment_group)
+
+ total_maximum_score = formatted_assessment_result[result.student][result.course][args.assessment_group]\
+ ["Total Score"]["maximum_score"]
+ if get_assessment_criteria:
+ assessment_criteria_dict[result.assessment_criteria] = formatted_assessment_result[result.student][result.course]\
+ [args.assessment_group][result.assessment_criteria]["maximum_score"]
+ if get_course:
+ course_dict[result.course] = total_maximum_score
+
+ if get_assessment_criteria and total_maximum_score:
+ assessment_criteria_dict["Total Score"] = total_maximum_score
+
+ return {
+ "student_details": student_details,
+ "assessment_result": formatted_assessment_result,
+ "assessment_criteria": assessment_criteria_dict,
+ "course_dict": course_dict
+ }
- # get all the result and make a dict map student as the key and value as dict of result
- def get_result_map():
- result_dict = defaultdict(dict)
- kounter = defaultdict(dict)
- assessment_result = frappe.db.sql('''select ar.student, ard.assessment_criteria, ard.grade, ard.score
- from `tabAssessment Result` ar, `tabAssessment Result Detail` ard
- where ar.assessment_plan in (%s) and ar.name=ard.parent and ar.docstatus=1
- order by ard.assessment_criteria''' %', '.join(['%s']*len(assessment_plan_list)),
- tuple(assessment_plan_list), as_dict=1)
-
- for result in assessment_result:
- if "total_score" in result_dict[result.student]:
- total_score = result_dict[result.student]["total_score"] + result.score
- else:
- total_score = result.score
- total = get_grade(grading_scale, (total_score/total_maximum_score)*100)
-
- if result.grade in kounter[result.assessment_criteria]:
- kounter[result.assessment_criteria][result.grade] += 1
- else:
- kounter[result.assessment_criteria].update({result.grade: 1})
-
- if "Total" not in kounter:
- kounter["Total"] = {}
-
- if "total" in result_dict[result.student]:
- prev_grade = result_dict[result.student]["total"]
- prev_grade_count = kounter["Total"].get(prev_grade) - 1
- kounter["Total"].update({prev_grade: prev_grade_count})
- latest_grade_count = kounter["Total"].get(total)+1 if kounter["Total"].get(total) else 1
- kounter["Total"].update({total: latest_grade_count})
-
- result_dict[result.student].update({
- frappe.scrub(result.assessment_criteria): result.grade,
- frappe.scrub(result.assessment_criteria)+"_score": result.score,
- "total_score": total_score,
- "total": total
- })
-
- return result_dict, kounter
-
- # make data from the result dict
- def get_data():
- student_list = frappe.db.sql('''select sgs.student, sgs.student_name
- from `tabStudent Group` sg, `tabStudent Group Student` sgs
- where sg.name = sgs.parent and sg.name in (%s)
- order by sgs.group_roll_number asc''' %', '.join(['%s']*len(student_group_list)),
- tuple(student_group_list), as_dict=1)
-
- for student in student_list:
- student.update(result_dict[student.student])
- return student_list
-
-
- # get chart data
- def get_chart():
- grading_scale = frappe.db.get_value("Assessment Plan", list(assessment_plan_list)[0], "grading_scale")
- grades = frappe.db.sql_list('''select grade_code from `tabGrading Scale Interval` where parent=%s''',
- (grading_scale))
- criteria_list = [d[0] for d in assessment_criteria_list] + ["Total"]
- return get_chart_data(grades, criteria_list, kounter)
-
-
- assessment_plan_list, assessment_criteria_list, total_maximum_score, grading_scale,\
- student_group_list = get_assessment_details()
- result_dict, kounter = get_result_map()
- data = get_data()
-
- columns = get_column(assessment_criteria_list, total_maximum_score)
- chart = get_chart()
- data_to_be_printed = [{
- "assessment_plan": ", ".join(assessment_plan_list)
- }]
-
- return columns, data, None, chart, data_to_be_printed
-
-def get_column(assessment_criteria, total_maximum_score):
+def get_column(assessment_criteria):
columns = [{
"fieldname": "student",
"label": _("Student ID"),
@@ -143,40 +203,28 @@
}]
for d in assessment_criteria:
columns.append({
- "fieldname": frappe.scrub(d[0]),
- "label": d[0],
+ "fieldname": frappe.scrub(d),
+ "label": d,
"fieldtype": "Data",
"width": 110
})
columns.append({
- "fieldname": frappe.scrub(d[0]) +"_score",
- "label": "Score(" + str(int(d[1])) + ")",
+ "fieldname": frappe.scrub(d) +"_score",
+ "label": "Score(" + str(int(assessment_criteria[d])) + ")",
"fieldtype": "Float",
"width": 100
})
- columns += [{
- "fieldname": "total",
- "label": "Total",
- "fieldtype": "Data",
- "width": 100
- },
- {
- "fieldname": "total_score",
- "label": "Total Score("+ str(int(total_maximum_score)) + ")",
- "fieldtype": "Float",
- "width": 110
- }]
-
return columns
-def get_chart_data(grades, assessment_criteria_list, kounter):
+
+def get_chart_data(grades, criteria_list, kounter):
grades = sorted(grades)
datasets = []
for grade in grades:
tmp = frappe._dict({"values":[], "title": grade})
- for criteria in assessment_criteria_list:
+ for criteria in criteria_list:
if grade in kounter[criteria]:
tmp["values"].append(kounter[criteria][grade])
else:
@@ -185,7 +233,7 @@
return {
"data": {
- "labels": assessment_criteria_list,
+ "labels": criteria_list,
"datasets": datasets
},
"type": 'bar',
diff --git a/erpnext/education/report/final_assessment_grades/__init__.py b/erpnext/education/report/final_assessment_grades/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/education/report/final_assessment_grades/__init__.py
diff --git a/erpnext/education/report/final_assessment_grades/final_assessment_grades.js b/erpnext/education/report/final_assessment_grades/final_assessment_grades.js
new file mode 100644
index 0000000..ba0a42f
--- /dev/null
+++ b/erpnext/education/report/final_assessment_grades/final_assessment_grades.js
@@ -0,0 +1,38 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Final Assessment Grades"] = {
+ "filters": [
+ {
+ "fieldname":"academic_year",
+ "label": __("Academic Year"),
+ "fieldtype": "Link",
+ "options": "Academic Year",
+ "reqd": 1
+ },
+ {
+ "fieldname":"student_group",
+ "label": __("Student Group"),
+ "fieldtype": "Link",
+ "options": "Student Group",
+ "reqd": 1,
+ "get_query": function() {
+ return{
+ filters: {
+ "group_based_on": "Batch",
+ "academic_year": frappe.query_report_filters_by_name.academic_year.value
+ }
+ };
+ }
+ },
+ {
+ "fieldname":"assessment_group",
+ "label": __("Assessment Group"),
+ "fieldtype": "Link",
+ "options": "Assessment Group",
+ "reqd": 1
+ }
+
+ ]
+}
diff --git a/erpnext/education/report/final_assessment_grades/final_assessment_grades.json b/erpnext/education/report/final_assessment_grades/final_assessment_grades.json
new file mode 100644
index 0000000..1efbb6e
--- /dev/null
+++ b/erpnext/education/report/final_assessment_grades/final_assessment_grades.json
@@ -0,0 +1,20 @@
+{
+ "add_total_row": 0,
+ "apply_user_permissions": 1,
+ "creation": "2018-01-22 17:04:43.412054",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "Shishuvan Secondary School",
+ "modified": "2018-01-22 17:04:43.412054",
+ "modified_by": "Administrator",
+ "module": "Education",
+ "name": "Final Assessment Grades",
+ "owner": "Administrator",
+ "ref_doctype": "Assessment Result",
+ "report_name": "Final Assessment Grades",
+ "report_type": "Script Report",
+ "roles": []
+}
\ No newline at end of file
diff --git a/erpnext/education/report/final_assessment_grades/final_assessment_grades.py b/erpnext/education/report/final_assessment_grades/final_assessment_grades.py
new file mode 100644
index 0000000..efc9aff
--- /dev/null
+++ b/erpnext/education/report/final_assessment_grades/final_assessment_grades.py
@@ -0,0 +1,85 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from collections import defaultdict
+
+from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import get_formatted_result
+from erpnext.education.report.course_wise_assessment_report.course_wise_assessment_report import get_chart_data
+
+
+def execute(filters=None):
+ columns, data, grades = [], [], []
+ args = frappe._dict()
+ course_wise_analysis = defaultdict(dict)
+
+ args["academic_year"] = filters.get("academic_year")
+ assessment_group = args["assessment_group"] = filters.get("assessment_group")
+
+ student_group = filters.get("student_group")
+ args.students = frappe.db.sql_list("select student from `tabStudent Group Student` where parent=%s", (student_group))
+
+ values = get_formatted_result(args, get_course=True)
+ student_details = values.get("student_details")
+ assessment_result = values.get("assessment_result")
+ course_dict = values.get("course_dict")
+
+ for student in args.students:
+ student_row = {}
+ student_row["student"] = student
+ student_row["student_name"] = student_details[student]
+ for course in course_dict:
+ scrub_course = frappe.scrub(course)
+ if assessment_group in assessment_result[student][course]:
+ student_row["grade_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["grade"]
+ student_row["score_" + scrub_course] = assessment_result[student][course][assessment_group]["Total Score"]["score"]
+
+ # create the list of possible grades
+ if student_row["grade_" + scrub_course] not in grades:
+ grades.append(student_row["grade_" + scrub_course])
+
+ # create the dict of for gradewise analysis
+ if student_row["grade_" + scrub_course] not in course_wise_analysis[course]:
+ course_wise_analysis[course][student_row["grade_" + scrub_course]] = 1
+ else:
+ course_wise_analysis[course][student_row["grade_" + scrub_course]] += 1
+
+ data.append(student_row)
+
+ course_list = [d for d in course_dict]
+ columns = get_column(course_dict)
+ chart = get_chart_data(grades, course_list, course_wise_analysis)
+ return columns, data, None, chart
+
+
+def get_column(course_dict):
+ columns = [{
+ "fieldname": "student",
+ "label": _("Student ID"),
+ "fieldtype": "Link",
+ "options": "Student",
+ "width": 90
+ },
+ {
+ "fieldname": "student_name",
+ "label": _("Student Name"),
+ "fieldtype": "Data",
+ "width": 160
+ }]
+ for course in course_dict:
+ columns.append({
+ "fieldname": "grade_" + frappe.scrub(course),
+ "label": course,
+ "fieldtype": "Data",
+ "width": 110
+ })
+ columns.append({
+ "fieldname": "score_" + frappe.scrub(course),
+ "label": "Score(" + str(course_dict[course]) + ")",
+ "fieldtype": "Float",
+ "width": 100
+ })
+
+ return columns
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 8fa382c..3a18efc 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -183,7 +183,6 @@
execute:frappe.db.sql("""delete from `tabProject Task`""")
erpnext.patches.v5_0.update_item_desc_in_invoice
erpnext.patches.v5_1.fix_against_account
-erpnext.patches.v5_1.fix_credit_days_based_on
execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force=True)
erpnext.patches.v5_1.rename_roles
erpnext.patches.v5_1.default_bom
@@ -487,3 +486,4 @@
erpnext.patches.v10_0.set_numeric_ranges_in_template_if_blank
erpnext.patches.v10_0.update_assessment_plan
erpnext.patches.v10_0.update_assessment_result
+erpnext.patches.v10_0.set_default_payment_terms_based_on_company
\ No newline at end of file
diff --git a/erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py b/erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py
new file mode 100644
index 0000000..1b59633
--- /dev/null
+++ b/erpnext/patches/v10_0/set_default_payment_terms_based_on_company.py
@@ -0,0 +1,37 @@
+from __future__ import unicode_literals
+import frappe
+from erpnext.patches.v8_10.change_default_customer_credit_days import make_payment_term, make_template
+
+def execute():
+ for dt in ("Company", "Customer Group"):
+ frappe.reload_doc("setup", "doctype", frappe.scrub(dt))
+
+ credit_records = frappe.db.sql("""
+ SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name`
+ from `tab{0}`
+ where
+ ((credit_days_based_on='Fixed Days' or credit_days_based_on is null) and credit_days is not null)
+ or credit_days_based_on='Last Day of the Next Month'
+ """.format(dt), as_dict=1)
+
+ for d in credit_records:
+ template = create_payment_terms_template(d)
+
+ frappe.db.sql("""
+ update `tab{0}`
+ set `payment_terms` = %s
+ where name = %s
+ """.format(dt), (template, d.name))
+
+def create_payment_terms_template(data):
+ if data.credit_days_based_on == "Fixed Days":
+ pyt_template_name = 'Default Payment Term - N{0}'.format(data.credit_days)
+ else:
+ pyt_template_name = 'Default Payment Term - EO2M'
+
+ if not frappe.db.exists("Payment Terms Template", pyt_template_name):
+ payment_term = make_payment_term(data.credit_days, data.credit_days_based_on)
+ template = make_template(payment_term)
+ else:
+ template = frappe.get_doc("Payment Terms Template", pyt_template_name)
+ return template
\ No newline at end of file
diff --git a/erpnext/patches/v5_1/fix_credit_days_based_on.py b/erpnext/patches/v5_1/fix_credit_days_based_on.py
deleted file mode 100644
index 6df19f2..0000000
--- a/erpnext/patches/v5_1/fix_credit_days_based_on.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from __future__ import unicode_literals
-
-import frappe
-
-def execute():
- for dt in ("Customer", "Customer Group", "Company"):
- frappe.reload_doctype(dt, force=True)
- frappe.db.sql("""update `tab{0}` set credit_days_based_on='Fixed Days'
- where ifnull(credit_days, 0) > 0""".format(dt))
diff --git a/erpnext/patches/v8_10/change_default_customer_credit_days.py b/erpnext/patches/v8_10/change_default_customer_credit_days.py
index 640c8aa..eddafb5 100644
--- a/erpnext/patches/v8_10/change_default_customer_credit_days.py
+++ b/erpnext/patches/v8_10/change_default_customer_credit_days.py
@@ -17,7 +17,8 @@
SELECT DISTINCT `credit_days`, `credit_days_based_on`, `name`
from `tab{0}`
where
- (credit_days_based_on='Fixed Days' and credit_days is not null)
+ ((credit_days_based_on='Fixed Days' or credit_days_based_on is null)
+ and credit_days is not null)
or credit_days_based_on='Last Day of the Next Month'
""".format(doctype))
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index d06a76a..31488de 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -523,7 +523,8 @@
},
callback: function(r, rt) {
if(r.message) {
- me.frm.set_value("due_date", r.message);
+ me.frm.doc.due_date = r.message;
+ refresh_field("due_date");
frappe.ui.form.trigger(me.frm.doc.doctype, "currency");
me.recalculate_terms();
}
@@ -538,7 +539,7 @@
due_date: function() {
// due_date is to be changed, payment terms template and/or payment schedule must
// be removed as due_date is automatically changed based on payment terms
- if (this.frm.doc.due_date) {
+ if (this.frm.doc.due_date && !this.frm.updating_party_details) {
if (this.frm.doc.payment_terms_template ||
(this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length)) {
var message1 = "";
@@ -555,11 +556,9 @@
if (message1.length !== 0) message2 = " and " + message2;
final_message = final_message + message2;
}
-
frappe.msgprint(final_message);
}
-
- }
+ }
},
recalculate_terms: function() {
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 9603290..92734cc 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -43,7 +43,7 @@
frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'}
- frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal);
+ frm.toggle_display(['address_html','contact_html','primary_contact_detail'], !frm.doc.__islocal);
if(!frm.doc.__islocal) {
frappe.contacts.render_address_and_contact(frm);
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index 3d174b7..5ca2618 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -340,7 +340,8 @@
conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
this._super(doc, cdt, cdn, dont_fetch_price_list_rate);
- if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
+ if(frappe.meta.get_docfield(cdt, "stock_qty", cdn) &&
+ in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) {
this.set_batch_number(cdt, cdn);
}
},
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 9824c70..d444417 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -1186,6 +1186,35 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fieldname": "column_break_26",
+ "fieldtype": "Column Break",
+ "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,
+ "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,
"depends_on": "eval:!doc.__islocal",
"fieldname": "credit_limit",
"fieldtype": "Currency",
@@ -1219,8 +1248,9 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "column_break_26",
- "fieldtype": "Column Break",
+ "depends_on": "",
+ "fieldname": "payment_terms",
+ "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1228,8 +1258,10 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
+ "label": "Default Payment Terms Template",
"length": 0,
"no_copy": 0,
+ "options": "Payment Terms Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -1248,69 +1280,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "credit_days_based_on",
- "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": "Credit Days Based On",
- "length": 0,
- "no_copy": 0,
- "options": "\nFixed Days\nLast Day of the Next Month",
- "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,
- "depends_on": "eval:(!doc.__islocal && doc.credit_days_based_on=='Fixed Days')",
- "fieldname": "credit_days",
- "fieldtype": "Int",
- "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": "Credit Days",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "credit_days",
- "oldfieldtype": "Int",
- "permlevel": 0,
- "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,
"depends_on": "eval:!doc.__islocal",
"fieldname": "auto_accounting_for_stock_settings",
"fieldtype": "Section Break",
@@ -2052,7 +2021,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2017-12-07 18:40:24.646920",
+ "modified": "2018-01-29 12:40:24.646920",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
diff --git a/erpnext/setup/doctype/customer_group/customer_group.json b/erpnext/setup/doctype/customer_group/customer_group.json
index 316f11b..062a49a 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.json
+++ b/erpnext/setup/doctype/customer_group/customer_group.json
@@ -162,12 +162,14 @@
"unique": 0
},
{
+ "allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "credit_days_based_on",
- "fieldtype": "Select",
+ "depends_on": "",
+ "fieldname": "payment_terms",
+ "fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -175,10 +177,10 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Credit Days Based On",
+ "label": "Default Payment Terms Template",
"length": 0,
"no_copy": 0,
- "options": "\nFixed Days\nLast Day of the Next Month",
+ "options": "Payment Terms Template",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -196,35 +198,6 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "depends_on": "eval:doc.credit_days_based_on=='Fixed Days'",
- "fieldname": "credit_days",
- "fieldtype": "Int",
- "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": "Credit Days",
- "length": 0,
- "no_copy": 0,
- "permlevel": 1,
- "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_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "credit_limit",
"fieldtype": "Currency",
"hidden": 0,
@@ -411,7 +384,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-02-20 13:25:31.549874",
+ "modified": "2018-01-29 13:25:31.549874",
"modified_by": "Administrator",
"module": "Setup",
"name": "Customer Group",
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index 3c334c4..4a52526 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -75,7 +75,7 @@
this.content.find('.more').addClass('hidden');
}
- // If not any stock in any warehouses provide a message to end user
+ // If not any stock in any warehouses provide a message to end user
if (context.data.length > 0) {
$(frappe.render_template('item_dashboard_list', context)).appendTo(this.result);
} else {
@@ -86,6 +86,7 @@
get_item_dashboard_data: function(data, max_count, show_item) {
if(!max_count) max_count = 0;
if(!data) data = [];
+
data.forEach(function(d) {
d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production;
d.pending_qty = 0;
@@ -97,9 +98,16 @@
max_count = Math.max(d.actual_or_pending, d.actual_qty,
d.total_reserved, max_count);
});
+
+ var can_write = 0;
+ if(frappe.boot.user.can_write.indexOf("Stock Entry")>=0){
+ can_write = 1;
+ }
+
return {
data: data,
max_count: max_count,
+ can_write:can_write,
show_item: show_item || false
}
}
@@ -187,4 +195,4 @@
frappe.set_route('Form', doc.doctype, doc.name);
})
});
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/dashboard/item_dashboard_list.html b/erpnext/stock/dashboard/item_dashboard_list.html
index 63f2480..e0b3431 100644
--- a/erpnext/stock/dashboard/item_dashboard_list.html
+++ b/erpnext/stock/dashboard/item_dashboard_list.html
@@ -39,6 +39,7 @@
</span>
</span>
</div>
+ {% if can_write %}
<div class="col-sm-2 text-right" style="margin-top: 8px;">
{% if d.actual_qty %}
<button class="btn btn-default btn-xs btn-move"
@@ -52,6 +53,7 @@
data-item="{{ d.item_code }}"
data-rate="{{ d.valuation_rate }}">{{ __("Add") }}</a>
</div>
+ {% endif %}
</div>
</div>
{% endfor %}
diff --git a/erpnext/stock/doctype/batch/batch_list.js b/erpnext/stock/doctype/batch/batch_list.js
index 2138fa1..7ee81aa 100644
--- a/erpnext/stock/doctype/batch/batch_list.js
+++ b/erpnext/stock/doctype/batch/batch_list.js
@@ -1,7 +1,7 @@
frappe.listview_settings['Batch'] = {
add_fields: ["item", "expiry_date"],
get_indicator: function(doc) {
- if(doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date) <= 0) {
+ if(doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) {
return [__("Expired"), "red", "expiry_date,>=,Today"]
} else if(doc.expiry_date) {
return [__("Not Expired"), "green", "expiry_date,<,Today"]
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 6a6c667..7d5af5f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -288,7 +288,7 @@
# get basic rate
if not d.bom_no:
- if not flt(d.basic_rate) or d.s_warehouse or force:
+ if (not flt(d.basic_rate) and not d.allow_zero_valuation_rate) or d.s_warehouse or force:
basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d))
if basic_rate > 0:
d.basic_rate = basic_rate
@@ -299,7 +299,8 @@
# get scrap items basic rate
if d.bom_no:
- if not flt(d.basic_rate) and getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse:
+ if not flt(d.basic_rate) and not d.allow_zero_valuation_rate and \
+ getattr(self, "pro_doc", frappe._dict()).scrap_warehouse == d.t_warehouse:
basic_rate = flt(get_incoming_rate(args), self.precision("basic_rate", d))
if basic_rate > 0:
d.basic_rate = basic_rate
diff --git a/erpnext/utilities/user_progress_utils.py b/erpnext/utilities/user_progress_utils.py
index 18d38c0..58a03e3 100644
--- a/erpnext/utilities/user_progress_utils.py
+++ b/erpnext/utilities/user_progress_utils.py
@@ -40,12 +40,12 @@
@frappe.whitelist()
def create_letterhead(args_data):
args = json.loads(args_data)
- letterhead = args.get("letterhead").encode('utf-8')
+ letterhead = args.get("letterhead")
if letterhead:
try:
frappe.get_doc({
"doctype":"Letter Head",
- "content":"""<div><img src="{0}" style='max-width: 100%%;'><br></div>""".format(letterhead),
+ "content":"""<div><img src="{0}" style='max-width: 100%%;'><br></div>""".format(letterhead.encode('utf-8')),
"letter_head_name": _("Standard"),
"is_default": 1
}).insert()