Merge branch 'develop' into fleet-moved-to-hr

This commit is contained in:
Nabin Hait 2017-01-17 11:05:39 +05:30 committed by GitHub
commit b064cf39c2
65 changed files with 2965 additions and 915 deletions

View File

@ -24,3 +24,18 @@ def get_default_currency():
company = get_default_company() company = get_default_company()
if company: if company:
return frappe.db.get_value('Company', company, 'default_currency') return frappe.db.get_value('Company', company, 'default_currency')
def set_perpetual_inventory(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.auto_accounting_for_stock = enable
accounts_settings.save()
def encode_company_abbr(name, company):
'''Returns name encoded with company abbreviation'''
company_abbr = frappe.db.get_value("Company", company, "abbr")
parts = name.rsplit(" - ", 1)
if parts[-1].lower() != company_abbr.lower():
parts.append(company_abbr)
return " - ".join([parts[0], company_abbr])

View File

@ -3,11 +3,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, cint from frappe.utils import cint, fmt_money
from frappe import throw, _ from frappe import throw, _
from frappe.model.document import Document from frappe.model.document import Document
class RootNotEditable(frappe.ValidationError): pass class RootNotEditable(frappe.ValidationError): pass
class BalanceMismatchError(frappe.ValidationError): pass
class Account(Document): class Account(Document):
nsm_parent_field = 'parent_account' nsm_parent_field = 'parent_account'
@ -162,23 +163,38 @@ class Account(Document):
throw(_("Report Type is mandatory")) throw(_("Report Type is mandatory"))
def validate_warehouse_account(self): def validate_warehouse_account(self):
'''If perpetual inventory is set, and warehouse is linked,
the account balance and stock balance as of now must always match.
'''
from erpnext.accounts.utils import get_balance_on
from erpnext.stock.utils import get_stock_value_on
if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): if not cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
return return
if self.account_type == "Stock" and not cint(self.is_group): if self.account_type == "Stock":
if not self.warehouse: if self.is_group == 0 and not self.warehouse:
throw(_("Warehouse is mandatory")) frappe.throw(_("Warehouse is mandatory for non group Accounts of type Stock"))
old_warehouse = cstr(frappe.db.get_value("Account", self.name, "warehouse")) if self.warehouse:
if old_warehouse != cstr(self.warehouse): # company must be same
if old_warehouse and frappe.db.exists("Warehouse", old_warehouse): if frappe.get_value('Warehouse', self.warehouse, 'company') != self.company:
self.validate_warehouse(old_warehouse) frappe.throw(_("Warehouse company must be same as Account company"))
if self.warehouse:
self.validate_warehouse(self.warehouse) # balance must be same
stock_balance = get_stock_value_on(self.warehouse)
if self.is_new():
account_balance = 0.0
else:
account_balance = get_balance_on(self.name)
if account_balance != stock_balance:
frappe.throw(_('Account balance ({0}) and stock value ({1}) must be same')\
.format(fmt_money(account_balance, self.account_currency),
fmt_money(stock_balance, self.account_currency)))
elif self.warehouse: elif self.warehouse:
self.warehouse = None self.warehouse = None
def validate_warehouse(self, warehouse): def validate_warehouse(self, warehouse):
lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"])

View File

@ -171,7 +171,7 @@ class TestJournalEntry(unittest.TestCase):
}) })
jv.submit() jv.submit()
def test_clear_blank_rows(self): def test_clear_blank_rows(self):
je = make_journal_entry("_Test Bank - _TC", "_Test Account Stock Expenses - _TC", 100, save=False) je = make_journal_entry("_Test Bank - _TC", "_Test Account Stock Expenses - _TC", 100, save=False)
je.append("accounts", { je.append("accounts", {
@ -180,11 +180,11 @@ class TestJournalEntry(unittest.TestCase):
"credit_in_account_currency": 0, "credit_in_account_currency": 0,
"exchange_rate": 1 "exchange_rate": 1
}) })
self.assertEqual(len(je.get("accounts")), 3) self.assertEqual(len(je.get("accounts")), 3)
je.save() je.save()
self.assertEqual(len(je.get("accounts")), 2) self.assertEqual(len(je.get("accounts")), 2)
def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None): def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None):
if not cost_center: if not cost_center:

View File

@ -228,15 +228,6 @@ def get_data():
"_doctype": "Student Applicant", "_doctype": "Student Applicant",
"type": "list" "type": "list"
}, },
{
"module_name": "Assessment",
"color": "#8a70be",
"icon": "fa fa-file-text-alt",
"label": _("Assessment"),
"link": "List/Assessment",
"_doctype": "Assessment",
"type": "list"
},
{ {
"module_name": "Fees", "module_name": "Fees",
"color": "#83C21E", "color": "#83C21E",

View File

@ -112,7 +112,7 @@ def get_data():
"items": [ "items": [
{ {
"type": "doctype", "type": "doctype",
"name": "Assessment" "name": "Assessment Plan"
}, },
{ {
"type": "doctype", "type": "doctype",
@ -120,7 +120,19 @@ def get_data():
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Grading Structure" "name": "Assessment Result"
},
{
"type": "doctype",
"name": "Grading Scale"
},
{
"type": "doctype",
"name": "Evaluation Criteria"
},
{
"type": "doctype",
"name": "Assessment Result Tool"
} }
] ]
}, },

View File

@ -113,7 +113,7 @@ class AccountsController(TransactionBase):
date_field = "transaction_date" date_field = "transaction_date"
if date_field and self.get(date_field): if date_field and self.get(date_field):
validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company, validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company,
self.meta.get_label(date_field), self) self.meta.get_label(date_field), self)
def validate_due_date(self): def validate_due_date(self):
@ -131,7 +131,7 @@ class AccountsController(TransactionBase):
transaction_date = self.posting_date transaction_date = self.posting_date
else: else:
transaction_date = self.transaction_date transaction_date = self.transaction_date
if self.meta.get_field("currency"): if self.meta.get_field("currency"):
# price list part # price list part
fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \ fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \
@ -144,7 +144,7 @@ class AccountsController(TransactionBase):
self.plc_conversion_rate = 1.0 self.plc_conversion_rate = 1.0
elif not self.plc_conversion_rate: elif not self.plc_conversion_rate:
self.plc_conversion_rate = get_exchange_rate(self.price_list_currency, self.plc_conversion_rate = get_exchange_rate(self.price_list_currency,
self.company_currency, transaction_date) self.company_currency, transaction_date)
# currency # currency
@ -570,7 +570,7 @@ class AccountsController(TransactionBase):
elif asset.status in ("Scrapped", "Cancelled", "Sold"): elif asset.status in ("Scrapped", "Cancelled", "Sold"):
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}") frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}")
.format(d.idx, d.asset, asset.status)) .format(d.idx, d.asset, asset.status))
def delink_advance_entries(self, linked_doc_name): def delink_advance_entries(self, linked_doc_name):
total_allocated_amount = 0 total_allocated_amount = 0
for adv in self.advances: for adv in self.advances:
@ -583,7 +583,7 @@ class AccountsController(TransactionBase):
if consider_for_total_advance: if consider_for_total_advance:
total_allocated_amount += flt(adv.allocated_amount, adv.precision("allocated_amount")) total_allocated_amount += flt(adv.allocated_amount, adv.precision("allocated_amount"))
frappe.db.set_value(self.doctype, self.name, "total_advance", frappe.db.set_value(self.doctype, self.name, "total_advance",
total_allocated_amount, update_modified=False) total_allocated_amount, update_modified=False)
def group_similar_items(self): def group_similar_items(self):
@ -711,7 +711,7 @@ def get_advance_journal_entries(party_type, party, party_account, amount_field,
.format(order_doctype, order_condition)) .format(order_doctype, order_condition))
reference_condition = " and (" + " or ".join(conditions) + ")" if conditions else "" reference_condition = " and (" + " or ".join(conditions) + ")" if conditions else ""
journal_entries = frappe.db.sql(""" journal_entries = frappe.db.sql("""
select select
"Journal Entry" as reference_type, t1.name as reference_name, "Journal Entry" as reference_type, t1.name as reference_name,
@ -771,8 +771,8 @@ def get_advance_payment_entries(party_type, party, party_account,
def update_invoice_status(): def update_invoice_status():
# Daily update the status of the invoices # Daily update the status of the invoices
frappe.db.sql(""" update `tabSales Invoice` set status = 'Overdue' frappe.db.sql(""" update `tabSales Invoice` set status = 'Overdue'
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""") where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")
frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue' frappe.db.sql(""" update `tabPurchase Invoice` set status = 'Overdue'
where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""") where due_date < CURDATE() and docstatus = 1 and outstanding_amount > 0""")

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cint, flt, cstr, now from frappe.utils import cint, flt, cstr
from frappe import msgprint, _ from frappe import msgprint, _
import frappe.defaults import frappe.defaults
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year
@ -14,7 +14,7 @@ class StockController(AccountsController):
def validate(self): def validate(self):
super(StockController, self).validate() super(StockController, self).validate()
self.validate_inspection() self.validate_inspection()
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
if self.docstatus == 2: if self.docstatus == 2:
delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name) delete_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
@ -43,16 +43,16 @@ class StockController(AccountsController):
gl_list = [] gl_list = []
warehouse_with_no_account = [] warehouse_with_no_account = []
for item_row in voucher_details: for item_row in voucher_details:
sle_list = sle_map.get(item_row.name) sle_list = sle_map.get(item_row.name)
if sle_list: if sle_list:
for sle in sle_list: for sle in sle_list:
if warehouse_account.get(sle.warehouse): if warehouse_account.get(sle.warehouse):
# from warehouse account # from warehouse account
self.check_expense_account(item_row) self.check_expense_account(item_row)
if not sle.stock_value_difference: if not sle.stock_value_difference:
self.validate_negative_stock(sle) self.validate_negative_stock(sle)
@ -80,16 +80,16 @@ class StockController(AccountsController):
for wh in warehouse_with_no_account: for wh in warehouse_with_no_account:
if frappe.db.get_value("Warehouse", wh, "company"): if frappe.db.get_value("Warehouse", wh, "company"):
frappe.throw(_("Warehouse {0} is not linked to any account, please create/link the corresponding (Asset) account for the warehouse.").format(wh)) frappe.throw(_("Warehouse {0} is not linked to any account, please create/link the corresponding (Asset) account for the warehouse.").format(wh))
msgprint(_("No accounting entries for the following warehouses") + ": \n" + msgprint(_("No accounting entries for the following warehouses") + ": \n" +
"\n".join(warehouse_with_no_account)) "\n".join(warehouse_with_no_account))
return process_gl_map(gl_list) return process_gl_map(gl_list)
def validate_negative_stock(self, sle): def validate_negative_stock(self, sle):
if sle.qty_after_transaction < 0 and sle.actual_qty < 0: if sle.qty_after_transaction < 0 and sle.actual_qty < 0:
frappe.throw(_("For the Item {0}, valuation rate not found for warehouse {1}. To be able to do accounting entries (for booking expenses), we need valuation rate for item {2}. Please create an incoming stock transaction, on or before {3} {4}, and then try submiting {5}") frappe.throw(_("For the Item {0}, valuation rate not found for warehouse {1}. To be able to do accounting entries (for booking expenses), we need valuation rate for item {2}. Please create an incoming stock transaction, on or before {3} {4}, and then try submiting {5}")
.format(sle.item_code, sle.warehouse, .format(sle.item_code, sle.warehouse,
sle.item_code, sle.posting_date, sle.posting_time, self.name)) sle.item_code, sle.posting_date, sle.posting_time, self.name))
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map): def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
@ -248,7 +248,7 @@ class StockController(AccountsController):
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0 incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
return incoming_rate return incoming_rate
def validate_warehouse(self): def validate_warehouse(self):
from erpnext.stock.utils import validate_warehouse_company from erpnext.stock.utils import validate_warehouse_company
@ -257,7 +257,7 @@ class StockController(AccountsController):
for w in warehouses: for w in warehouses:
validate_warehouse_company(w, self.company) validate_warehouse_company(w, self.company)
def update_billing_percentage(self, update_modified=True): def update_billing_percentage(self, update_modified=True):
self._update_percent_field({ self._update_percent_field({
"target_dt": self.doctype + " Item", "target_dt": self.doctype + " Item",
@ -271,21 +271,21 @@ class StockController(AccountsController):
def validate_inspection(self): def validate_inspection(self):
'''Checks if quality inspection is set for Items that require inspection. '''Checks if quality inspection is set for Items that require inspection.
On submit, throw an exception''' On submit, throw an exception'''
inspection_required_fieldname = None inspection_required_fieldname = None
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
inspection_required_fieldname = "inspection_required_before_purchase" inspection_required_fieldname = "inspection_required_before_purchase"
elif self.doctype in ["Delivery Note", "Sales Invoice"]: elif self.doctype in ["Delivery Note", "Sales Invoice"]:
inspection_required_fieldname = "inspection_required_before_delivery" inspection_required_fieldname = "inspection_required_before_delivery"
if not inspection_required_fieldname or \ if not inspection_required_fieldname or \
(self.doctype in ["Sales Invoice", "Purchase Invoice"] and not self.update_stock): (self.doctype in ["Sales Invoice", "Purchase Invoice"] and not self.update_stock):
return return
for d in self.get('items'): for d in self.get('items'):
if (frappe.db.get_value("Item", d.item_code, inspection_required_fieldname) if (frappe.db.get_value("Item", d.item_code, inspection_required_fieldname)
and not d.quality_inspection): and not d.quality_inspection):
frappe.msgprint(_("Quality Inspection required for Item {0}").format(d.item_code)) frappe.msgprint(_("Quality Inspection required for Item {0}").format(d.item_code))
if self.docstatus==1: if self.docstatus==1:
raise frappe.ValidationError raise frappe.ValidationError
@ -301,7 +301,7 @@ def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for
future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items) future_stock_vouchers = get_future_stock_vouchers(posting_date, posting_time, for_warehouses, for_items)
gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date) gle = get_voucherwise_gl_entries(future_stock_vouchers, posting_date)
for voucher_type, voucher_no in future_stock_vouchers: for voucher_type, voucher_no in future_stock_vouchers:
existing_gle = gle.get((voucher_type, voucher_no), []) existing_gle = gle.get((voucher_type, voucher_no), [])
voucher_obj = frappe.get_doc(voucher_type, voucher_no) voucher_obj = frappe.get_doc(voucher_type, voucher_no)
@ -364,14 +364,19 @@ def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
return gl_entries return gl_entries
def get_warehouse_account(): def get_warehouse_account():
if not frappe.flags.warehouse_account_map: if not frappe.flags.warehouse_account_map or frappe.flags.in_test:
warehouse_account = frappe._dict() warehouse_account = frappe._dict()
for d in frappe.db.sql("""select warehouse, name, account_currency from tabAccount for d in frappe.db.sql("""select
where account_type = 'Stock' and (warehouse is not null and warehouse != '' warehouse, name, account_currency
and is_group != 1) and is_group=0 """, as_dict=1): from
warehouse_account.setdefault(d.warehouse, d) tabAccount
where
account_type = 'Stock'
and (warehouse is not null and warehouse != '')
and is_group=0 """, as_dict=1):
warehouse_account.setdefault(d.warehouse, d)
frappe.flags.warehouse_account_map = warehouse_account frappe.flags.warehouse_account_map = warehouse_account
return frappe.flags.warehouse_account_map return frappe.flags.warehouse_account_map

View File

@ -108,8 +108,6 @@ portal_menu_items = [
{"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role":"Customer"}, {"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role":"Customer"},
{"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"}, {"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"},
{"title": _("Announcements"), "route": "/announcement", "reference_doctype": "Announcement"}, {"title": _("Announcements"), "route": "/announcement", "reference_doctype": "Announcement"},
{"title": _("Courses"), "route": "/course", "reference_doctype": "Course", "role":"Student"},
{"title": _("Assessment Schedule"), "route": "/assessment", "reference_doctype": "Assessment", "role":"Student"},
{"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"} {"title": _("Fees"), "route": "/fees", "reference_doctype": "Fees", "role":"Student"}
] ]

View File

@ -360,6 +360,7 @@ erpnext.patches.v7_1.set_sales_person_status
erpnext.patches.v7_1.repost_stock_for_deleted_bins_for_merging_items erpnext.patches.v7_1.repost_stock_for_deleted_bins_for_merging_items
execute:frappe.delete_doc('Desktop Icon', {'module_name': 'Profit and Loss Statment'}) execute:frappe.delete_doc('Desktop Icon', {'module_name': 'Profit and Loss Statment'})
erpnext.patches.v7_2.update_website_for_variant erpnext.patches.v7_2.update_website_for_variant
erpnext.patches.v7_2.update_assessment_modules
erpnext.patches.v7_2.update_doctype_status erpnext.patches.v7_2.update_doctype_status
erpnext.patches.v7_2.update_salary_slips erpnext.patches.v7_2.update_salary_slips
erpnext.patches.v7_2.delete_fleet_management_module_def erpnext.patches.v7_2.delete_fleet_management_module_def

View File

@ -0,0 +1,31 @@
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
#Rename Grading Structure to Grading Scale
frappe.rename_doc("DocType", "Grading Structure", "Grading Scale", force=True)
frappe.rename_doc("DocType", "Grade Interval", "Grading Scale Interval", force=True)
frappe.reload_doc("schools", "doctype", "grading_scale_interval")
rename_field("Grading Scale Interval", "to_score", "threshold")
frappe.rename_doc("DocType", "Assessment", "Assessment Plan", force=True)
#Rename Assessment Results
frappe.reload_doc("schools", "doctype", "assessment_plan")
rename_field("Assessment Plan", "grading_structure", "grading_scale")
frappe.reload_doc("schools", "doctype", "assessment_result")
for assessment in frappe.get_all("Assessment Plan", fields=["name", "grading_scale"]):
for stud_result in frappe.db.sql("select * from `tabAssessment Result` where parent= %s", assessment.name, as_dict=True):
if stud_result.result:
assessment_result = frappe.new_doc("Assessment Result")
assessment_result.student = stud_result.student
assessment_result.student_name = stud_result.student_name
assessment_result.assessment_plan = assessment.name
assessment_result.grading_scale = assessment.grading_scale
assessment_result.total_score = stud_result.result
assessment_result.flags.ignore_validate = True
assessment_result.save()
frappe.db.sql("""delete from `tabAssessment Result` where parent != '' or parent is not null""")

View File

@ -28,7 +28,8 @@
"public/js/templates/item_selector.html", "public/js/templates/item_selector.html",
"public/js/utils/item_selector.js", "public/js/utils/item_selector.js",
"public/js/help_links.js", "public/js/help_links.js",
"public/js/schools/student_button.html" "public/js/schools/student_button.html",
"public/js/schools/assessment_result_tool.html"
], ],
"js/item-dashboard.min.js": [ "js/item-dashboard.min.js": [
"stock/dashboard/item_dashboard.html", "stock/dashboard/item_dashboard.html",

View File

@ -212,3 +212,25 @@ body[data-route="pos"] .modal-dialog {
margin: 15px; margin: 15px;
width: 130px; width: 130px;
} }
.frappe-control[data-fieldname='result_html'] {
overflow: scroll;
}
.assessment-result-tool {
table-layout: fixed;
}
.assessment-result-tool input {
width: 100%;
border: 0;
outline: none;
text-align: right;
}
.assessment-result-tool th {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.assessment-result-tool .total-score,
.assessment-result-tool .grade,
.assessment-result-tool .score {
text-align: right;
}

View File

@ -0,0 +1,44 @@
<table class="table table-bordered assessment-result-tool">
<thead>
<tr>
<th style="width: 100px" rowspan="2">Student</th>
<th style="width: 200px" rowspan="2">Student Name</th>
{% for c in criterias %}
<th class="score" style="width: 100px">{{ c.evaluation_criteria }}</th>
{% endfor %}
<th class="score" style="width: 100px">Total Marks</th>
<!--criteria-->
</tr>
<tr>
{% for c in criterias %}
<th class="score" style="width: 100px">{{ c.maximum_score }}</th>
{% endfor %}
<th class="score" style="width: 100px">{{max_total_score}}</th>
</tr>
</thead>
<tbody>
{% for s in students %}
<tr
{% if(s.assessment_details) { %} class="text-muted" {% } %}
data-student="{{s.student}}">
<td>{{ s.student }}</td>
<td>{{ s.student_name }}</td>
{% for c in criterias %}
<td>
<input type="text"
data-max-score="{{c.maximum_score}}"
data-criteria="{{c.evaluation_criteria}}"
data-student="{{s.student}}"
{% if(s.assessment_details) { %}
disabled
value="{{s.assessment_details[c.evaluation_criteria]}}"
{% } %}/>
</td>
{% endfor %}
<td data-student="{{s.student}}" class="total-score">
{% if(s.assessment_details) { %} {{s.assessment_details.total_score}} {% } %}
</td>
</tr>
{% endfor %}
</tbody>
</table>

View File

@ -257,3 +257,28 @@ body[data-route="pos"] .modal-dialog {
margin: 15px; margin: 15px;
width: 130px; width: 130px;
} }
// assessment tool
.frappe-control[data-fieldname='result_html'] {
overflow: scroll;
}
.assessment-result-tool {
table-layout: fixed;
input {
width: 100%;
border: 0;
outline: none;
text-align: right;
}
th {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.total-score, .grade, .score {
text-align: right;
}
}

View File

@ -7,7 +7,7 @@ import frappe
import json import json
from frappe import _ from frappe import _
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from frappe.utils import flt from frappe.utils import flt, cstr
@frappe.whitelist() @frappe.whitelist()
def enroll_student(source_name): def enroll_student(source_name):
@ -84,7 +84,7 @@ def make_attendance_records(student, student_name, status, course_schedule=None,
@frappe.whitelist() @frappe.whitelist()
def get_student_batch_students(student_batch): def get_student_batch_students(student_batch):
"""Returns List of student, student_name in Student Batch. """Returns List of student, student_name, idx in Student Batch.
:param student_batch: Student Batch. :param student_batch: Student Batch.
""" """
@ -163,3 +163,92 @@ def get_course_schedule_events(start, end, filters=None):
}, as_dict=True, update={"allDay": 0}) }, as_dict=True, update={"allDay": 0})
return data return data
@frappe.whitelist()
def get_evaluation_criterias(course):
"""Returns Evaluation Criterias and their Weightage from Course Master.
:param Course: Course
"""
return frappe.get_list("Course Evaluation Criteria", \
fields=["evaluation_criteria", "weightage"], filters={"parent": course}, order_by= "idx")
@frappe.whitelist()
def get_assessment_students(assessment_plan, student_group=None, student_batch=None):
student_list = []
if student_group:
student_list = get_student_group_students(student_group)
elif student_batch:
student_list = get_student_batch_students(student_batch)
for i, student in enumerate(student_list):
result = get_result(student.student, assessment_plan)
if result:
student_result = {}
for d in result.details:
student_result.update({d.evaluation_criteria: cstr(d.score) + " ("+ d.grade + ")"})
student_result.update({"total_score": cstr(result.total_score) + " (" + result.grade + ")"})
student.update({'assessment_details': student_result})
else:
student.update({'assessment_details': None})
return student_list
@frappe.whitelist()
def get_assessment_details(assessment_plan):
"""Returns Evaluation Criteria and Maximum Score from Assessment Plan Master.
:param Assessment Plan: Assessment Plan
"""
return frappe.get_list("Assessment Evaluation Criteria", \
fields=["evaluation_criteria", "maximum_score"], filters={"parent": assessment_plan}, order_by= "idx")
@frappe.whitelist()
def get_result(student, assessment_plan):
"""Returns Submitted Result of given student for specified Assessment Plan
:param Student: Student
:param Assessment Plan: Assessment Plan
"""
results = frappe.get_all("Assessment Result", filters={"student": student, "assessment_plan": assessment_plan, "docstatus": 1})
if results:
return frappe.get_doc("Assessment Result", results[0])
else:
return None
@frappe.whitelist()
def get_grade(grading_scale, percentage):
"""Returns Grade based on the Grading Scale and Score.
:param Grading Scale: Grading Scale
: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}):
grading_scale_intervals.update({d.threshold:d.grade_code})
intervals = sorted(grading_scale_intervals.keys(), key=float, reverse=True)
for interval in intervals:
if flt(percentage) >= interval:
grade = grading_scale_intervals.get(interval)
break
else:
grade = ""
return grade
@frappe.whitelist()
def mark_assessment_result(student, assessment_plan, scores):
student_score = json.loads(scores)
details = []
for s in student_score.keys():
details.append({
"evaluation_criteria": s,
"score": flt(student_score[s])
})
assessment_result = frappe.new_doc("Assessment Result")
assessment_result.update({
"student": student,
"student_name": frappe.db.get_value("Student", student, "title"),
"assessment_plan": assessment_plan,
"details": details
})
assessment_result.save()
assessment_result.submit()
return assessment_result

View File

@ -1,51 +0,0 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
cur_frm.add_fetch("student_group", "course", "course");
cur_frm.add_fetch("examiner", "instructor_name", "examiner_name");
cur_frm.add_fetch("supervisor", "instructor_name", "supervisor_name");
cur_frm.add_fetch("student", "title", "student_name");
frappe.ui.form.on("Assessment", {
student_group: function(frm) {
frm.set_value("results", "");
if (frm.doc.student_group) {
frappe.call({
method: "erpnext.schools.api.get_student_group_students",
args: {
"student_group": frm.doc.student_group
},
callback: function(r) {
if (r.message) {
$.each(r.message, function(i, d) {
var row = frappe.model.add_child(cur_frm.doc, "Assessment Result", "results");
row.student = d.student;
row.student_name = d.student_name;
});
}
refresh_field("results");
}
});
}
}
});
frappe.ui.form.on("Assessment Result", {
result: function(frm, cdt, cdn) {
if (frm.doc.grading_structure) {
var assessment_result = locals[cdt][cdn];
frappe.call({
method: "erpnext.schools.doctype.assessment.assessment.get_grade",
args: {
grading_structure: frm.doc.grading_structure,
result: assessment_result.result
},
callback: function(r) {
if (r.message) {
frappe.model.set_value(cdt, cdn, 'grade', r.message);
}
}
});
}
}
});

View File

@ -1,73 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
from frappe.model.document import Document
import frappe
from frappe import _
class Assessment(Document):
def validate(self):
self.validate_overlap()
def validate_overlap(self):
"""Validates overlap for Student Group/Student Batch, Instructor, Room"""
from erpnext.schools.utils import validate_overlap_for
#Validate overlapping course schedules.
if self.student_batch:
validate_overlap_for(self, "Course Schedule", "student_batch")
if self.student_group:
validate_overlap_for(self, "Course Schedule", "student_group")
validate_overlap_for(self, "Course Schedule", "instructor")
validate_overlap_for(self, "Course Schedule", "room")
#validate overlapping assessment schedules.
if self.student_batch:
validate_overlap_for(self, "Assessment", "student_batch")
if self.student_group:
validate_overlap_for(self, "Assessment", "student_group")
validate_overlap_for(self, "Assessment", "room")
validate_overlap_for(self, "Assessment", "supervisor", self.instructor)
def get_assessment_list(doctype, txt, filters, limit_start, limit_page_length=20):
user = frappe.session.user
student = frappe.db.sql("select name from `tabStudent` where student_email_id= %s", user)
if student:
return frappe. db.sql('''select course, schedule_date, from_time, to_time, sgs.name from `tabAssessment` as assessment,
`tabStudent Group Student` as sgs where assessment.student_group = sgs.parent and sgs.student = %s and assessment.docstatus=1
order by assessment.name asc limit {0} , {1}'''
.format(limit_start, limit_page_length), student, as_dict = True)
def get_list_context(context=None):
return {
"show_sidebar": True,
'no_breadcrumbs': True,
"title": _("Assessment Schedule"),
"get_list": get_assessment_list,
"row_template": "templates/includes/assessment/assessment_row.html"
}
@frappe.whitelist()
def get_grade(grading_structure, result):
grade = frappe.db.sql("""select gi.from_score, gi.to_score, gi.grade_code, gi.grade_description
from `tabGrading Structure` as gs, `tabGrade Interval` as gi
where gs.name = gi.parent and gs.name = %(grading_structure)s and gi.from_score <= %(result)s
and gi.to_score >= %(result)s""".format(),
{
"grading_structure":grading_structure,
"result": result
},
as_dict=True)
return grade[0].grade_code if grade else ""
def validate_grade(score, grade):
pass

View File

@ -0,0 +1,124 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2016-12-14 17:20:27.738226",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "evaluation_criteria",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Evaluation Criteria",
"length": 0,
"no_copy": 0,
"options": "Evaluation Criteria",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "maximum_score",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Maximum Score",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-12-14 17:31:11.950549",
"modified_by": "Administrator",
"module": "Schools",
"name": "Assessment Evaluation Criteria",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class AssessmentEvaluationCriteria(Document):
pass

View File

@ -0,0 +1,36 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
cur_frm.add_fetch("student_group", "course", "course");
cur_frm.add_fetch("student_group", "student_batch", "student_batch");
cur_frm.add_fetch("examiner", "instructor_name", "examiner_name");
cur_frm.add_fetch("supervisor", "instructor_name", "supervisor_name");
frappe.ui.form.on("Assessment Plan", {
course: function(frm) {
if (frm.doc.course && frm.doc.maximum_assessment_score) {
frappe.call({
method: "erpnext.schools.api.get_evaluation_criterias",
args: {
course: frm.doc.course
},
callback: function(r) {
if (r.message) {
frm.doc.evaluation_criterias = [];
$.each(r.message, function(i, d) {
var row = frappe.model.add_child(frm.doc, "Assessment Evaluation Criteria", "evaluation_criterias");
row.evaluation_criteria = d.evaluation_criteria;
row.maximum_score = d.weightage / 100 * frm.doc.maximum_assessment_score;
});
}
refresh_field("evaluation_criterias");
}
});
}
},
maximum_assessment_score: function(frm) {
frm.trigger("course");
}
});

View File

@ -68,6 +68,175 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "assessment_group",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Assessment Group",
"length": 0,
"no_copy": 0,
"options": "Assessment Group",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "course",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Course",
"length": 0,
"no_copy": 0,
"options": "Course",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "maximum_assessment_score",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Maximum Assessment Score",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "grading_scale",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Grading Scale",
"length": 0,
"no_copy": 0,
"options": "Grading Scale",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_10",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -97,6 +266,33 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -130,234 +326,6 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "grading_structure",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Grading Structure",
"length": 0,
"no_copy": 0,
"options": "Grading Structure",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "course",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Course",
"length": 0,
"no_copy": 0,
"options": "Course",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "assessment_group",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Assessment Group",
"length": 0,
"no_copy": 0,
"options": "Assessment Group",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "supervisor",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Supervisor",
"length": 0,
"no_copy": 0,
"options": "Instructor",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "supervisor_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Supervisor Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": "examiner",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Examiner",
"length": 0,
"no_copy": 0,
"options": "Instructor",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "examiner_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Examiner Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": 1,
"collapsible_depends_on": "", "collapsible_depends_on": "",
"columns": 0, "columns": 0,
"depends_on": "", "depends_on": "",
@ -442,6 +410,63 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "examiner",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Examiner",
"length": 0,
"no_copy": 0,
"options": "Instructor",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "examiner_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Examiner Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@ -528,9 +553,66 @@
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 1, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "section_break_11", "fieldname": "supervisor",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Supervisor",
"length": 0,
"no_copy": 0,
"options": "Instructor",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "supervisor_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Supervisor Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": "section_break_20",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
@ -538,7 +620,6 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "Results",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
@ -558,7 +639,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "results", "fieldname": "evaluation_criterias",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
@ -566,10 +647,10 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0, "in_standard_filter": 0,
"label": "results", "label": "Evaluation Criterias",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"options": "Assessment Result", "options": "Assessment Evaluation Criteria",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@ -577,7 +658,7 @@
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0, "remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
@ -598,7 +679,7 @@
"label": "Amended From", "label": "Amended From",
"length": 0, "length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Assessment", "options": "Assessment Plan",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
@ -622,10 +703,10 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2016-11-16 13:05:54.953750", "modified": "2017-01-05 12:15:33.183100",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Schools", "module": "Schools",
"name": "Assessment", "name": "Assessment Plan",
"name_case": "", "name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
@ -656,5 +737,6 @@
"read_only_onload": 0, "read_only_onload": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"track_changes": 0,
"track_seen": 0 "track_seen": 0
} }

View File

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
from frappe.model.document import Document
import frappe
from frappe import _
class AssessmentPlan(Document):
def validate(self):
if not (self.student_batch or self.student_group):
frappe.throw(_("Please select Student Group or Student Batch"))
self.validate_student_batch()
self.validate_overlap()
def validate_overlap(self):
"""Validates overlap for Student Group/Student Batch, Instructor, Room"""
from erpnext.schools.utils import validate_overlap_for
#Validate overlapping course schedules.
if self.student_batch:
validate_overlap_for(self, "Course Schedule", "student_batch")
if self.student_group:
validate_overlap_for(self, "Course Schedule", "student_group")
validate_overlap_for(self, "Course Schedule", "instructor")
validate_overlap_for(self, "Course Schedule", "room")
#validate overlapping assessment schedules.
if self.student_batch:
validate_overlap_for(self, "Assessment Plan", "student_batch")
if self.student_group:
validate_overlap_for(self, "Assessment Plan", "student_group")
validate_overlap_for(self, "Assessment Plan", "room")
validate_overlap_for(self, "Assessment Plan", "supervisor", self.supervisor)
def validate_student_batch(self):
if self.student_group:
self.student_batch = frappe.db.get_value("Student Group", self.student_group, "student_batch")

View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Assessment Plan')
class TestAssessmentPlan(unittest.TestCase):
pass

View File

@ -0,0 +1,51 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
cur_frm.add_fetch("student", "title", "student_name");
cur_frm.add_fetch("assessment_plan", "grading_scale", "grading_scale");
cur_frm.add_fetch("assessment_plan", "maximum_assessment_score", "maximum_score");
frappe.ui.form.on("Assessment Result", {
assessment_plan: function(frm) {
frappe.call({
method: "erpnext.schools.api.get_assessment_details",
args: {
assessment_plan: frm.doc.assessment_plan
},
callback: function(r) {
if (r.message) {
frm.doc.details = [];
$.each(r.message, function(i, d) {
var row = frappe.model.add_child(frm.doc, "Assessment Result Detail", "details");
row.evaluation_criteria = d.evaluation_criteria;
row.maximum_score = d.maximum_score;
});
}
refresh_field("details");
}
});
}
});
frappe.ui.form.on("Assessment Result Detail", {
score: function(frm, cdt, cdn) {
var d = locals[cdt][cdn];
if (d.score >= d.maximum_score) {
frappe.throw(_("Score cannot be greater than Maximum Score"));
}
else {
frappe.call({
method: "erpnext.schools.api.get_grade",
args: {
grading_scale: frm.doc.grading_scale,
percentage: ((d.score/d.maximum_score) * 100)
},
callback: function(r) {
if (r.message) {
frappe.model.set_value(cdt, cdn, "grade", r.message);
}
}
});
}
}
});

View File

@ -2,6 +2,7 @@
"allow_copy": 0, "allow_copy": 0,
"allow_import": 0, "allow_import": 0,
"allow_rename": 0, "allow_rename": 0,
"autoname": "RES.######",
"beta": 0, "beta": 0,
"creation": "2015-11-13 17:18:06.468332", "creation": "2015-11-13 17:18:06.468332",
"custom": 0, "custom": 0,
@ -9,18 +10,21 @@
"doctype": "DocType", "doctype": "DocType",
"document_type": "", "document_type": "",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "student", "fieldname": "student",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 0,
"in_standard_filter": 0,
"label": "Student", "label": "Student",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -30,6 +34,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
@ -40,6 +45,7 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "student_name", "fieldname": "student_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -47,6 +53,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Student Name", "label": "Student Name",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -55,6 +62,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -65,6 +73,7 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "column_break_3", "fieldname": "column_break_3",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -72,6 +81,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0,
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
@ -79,6 +89,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -89,21 +100,54 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "result", "columns": 0,
"fieldtype": "Data", "fieldname": "assessment_plan",
"fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Result", "in_standard_filter": 0,
"label": "Assessment Plan",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"options": "Assessment Plan",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "grading_scale",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Grading Scale",
"length": 0,
"no_copy": 0,
"options": "Grading Scale",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -114,6 +158,174 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fieldname": "details",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Details",
"length": 0,
"no_copy": 0,
"options": "Assessment Result Detail",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "maximum_score",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Maximum Score",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": "total_score",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Total Score",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "grade", "fieldname": "grade",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -121,6 +333,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Grade", "label": "Grade",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -129,6 +342,35 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 1, "read_only": 1,
"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": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Assessment Result",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -142,21 +384,45 @@
"image_view": 0, "image_view": 0,
"in_create": 0, "in_create": 0,
"in_dialog": 0, "in_dialog": 0,
"is_submittable": 0, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2016-08-27 12:15:01.923000", "modified": "2017-01-04 16:56:33.868949",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Schools", "module": "Schools",
"name": "Assessment Result", "name": "Assessment Result",
"name_case": "", "name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [
{
"amend": 1,
"apply_user_permissions": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0, "quick_entry": 0,
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"title_field": "student_name",
"track_changes": 0,
"track_seen": 0 "track_seen": 0
} }

View File

@ -4,7 +4,33 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _
from frappe.utils import flt
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.schools.api import get_grade
from erpnext.schools.api import get_assessment_details
class AssessmentResult(Document): class AssessmentResult(Document):
pass def validate(self):
self.grading_scale = frappe.db.get_value("Assessment Plan", self.assessment_plan, "grading_scale")
self.validate_maximum_score()
self.validate_grade()
def validate_maximum_score(self):
self.maximum_score = frappe.db.get_value("Assessment Plan", self.assessment_plan, "maximum_assessment_score")
assessment_details = get_assessment_details(self.assessment_plan)
max_scores = {}
for d in assessment_details:
max_scores.update({d.evaluation_criteria: d.maximum_score})
for d in self.details:
d.maximum_score = max_scores.get(d.evaluation_criteria)
if d.score > d.maximum_score:
frappe.throw(_("Score cannot be greater than Maximum Score"))
def validate_grade(self):
self.total_score = 0.0
for d in self.details:
d.grade = get_grade(self.grading_scale, (flt(d.score)/d.maximum_score)*100)
self.total_score += d.score
self.grade = get_grade(self.grading_scale, (self.total_score/self.maximum_score)*100)

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
from erpnext.schools.api import get_grade
# test_records = frappe.get_test_records('Assessment Result')
class TestAssessmentResult(unittest.TestCase):
def test_grade(self):
grade = get_grade("_Test Grading Scale", 80)
self.assertEquals("A", grade)
grade = get_grade("_Test Grading Scale", 70)
self.assertEquals("B", grade)

View File

@ -0,0 +1,180 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2016-12-14 17:44:35.583123",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 4,
"fieldname": "evaluation_criteria",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Evaluation Criteria",
"length": 0,
"no_copy": 0,
"options": "Evaluation Criteria",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "maximum_score",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Maximum Score",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "score",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Score",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 2,
"fieldname": "grade",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Grade",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-12-27 16:18:12.022257",
"modified_by": "Administrator",
"module": "Schools",
"name": "Assessment Result Detail",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class AssessmentResultDetail(Document):
pass

View File

@ -0,0 +1,105 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
cur_frm.add_fetch("assessment_plan", "student_group", "student_group");
cur_frm.add_fetch("assessment_plan", "student_batch", "student_batch");
frappe.ui.form.on('Assessment Result Tool', {
refresh: function(frm) {
frm.disable_save();
frm.page.clear_indicator();
},
assessment_plan: function(frm) {
if(!(frm.doc.student_batch || frm.doc.student_group)) return;
frappe.call({
method: "erpnext.schools.api.get_assessment_students",
args: {
"assessment_plan": frm.doc.assessment_plan,
"student_batch": frm.doc.student_batch,
"student_group": frm.doc.student_group
},
callback: function(r) {
frm.events.render_table(frm, r.message);
}
});
},
render_table: function(frm, students) {
$(frm.fields_dict.result_html.wrapper).empty();
var assessment_plan = frm.doc.assessment_plan;
var student_scores = {};
students.forEach(function(stu) {
student_scores[stu.student] = {}
});
frappe.call({
method: "erpnext.schools.api.get_assessment_details",
args: {
assessment_plan: assessment_plan
},
callback: function(r) {
var criterias = r.message;
var max_total_score = 0;
criterias.forEach(function(c) {
max_total_score += c.maximum_score
});
var result_table = $(frappe.render_template('assessment_result_tool', {
frm: frm,
students: students,
criterias: criterias,
max_total_score: max_total_score
}));
result_table.appendTo(frm.fields_dict.result_html.wrapper)
result_table.on('change', 'input', function(e) {
var $input = $(e.target);
var max_score = $input.data().maxScore;
var student = $input.data().student;
var criteria = $input.data().criteria;
var value = $input.val();
if(value < 0) {
$input.val(0);
value = 0;
}
if(value > max_score) {
$input.val(max_score);
value = max_score;
}
student_scores[student][criteria] = value;
if(Object.keys(student_scores[student]).length == criterias.length) {
frappe.call(({
method: "erpnext.schools.api.mark_assessment_result",
args: {
"student": student,
"assessment_plan": assessment_plan,
"scores": student_scores[student]
},
callback: function(r) {
var doc = r.message;
var student = doc.student;
result_table.find(`[data-student=${student}].total-score`)
.html(doc.total_score + ' ('+ doc.grade + ')');
var details = doc.details;
result_table.find(`tr[data-student=${student}]`).addClass('text-muted');
result_table.find(`input[data-student=${student}]`).each(function(el, input) {
var $input = $(input);
var criteria = $input.data().criteria;
var value = $input.val();
var grade = details.find(function(d) {
return d.evaluation_criteria === criteria;
}).grade;
$input.val(`${value} (${grade})`);
$input.attr('disabled', true);
});
}
}))
}
});
}
});
},
});

View File

@ -0,0 +1,232 @@
{
"allow_copy": 1,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-01-05 12:27:48.951036",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "assessment_plan",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Assessment Plan",
"length": 0,
"no_copy": 0,
"options": "Assessment Plan",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student_group",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Student Group",
"length": 0,
"no_copy": 0,
"options": "Student Group",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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": "student_batch",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Student Batch",
"length": 0,
"no_copy": 0,
"options": "Student Batch",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"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,
"depends_on": "assessment_plan",
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "result_html",
"fieldtype": "HTML",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Result HTML",
"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
}
],
"hide_heading": 1,
"hide_toolbar": 1,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-01-05 15:45:59.338722",
"modified_by": "Administrator",
"module": "Schools",
"name": "Assessment Result Tool",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class AssessmentResultTool(Document):
pass

View File

@ -21,11 +21,11 @@ frappe.ui.form.on("Course", "refresh", function(frm) {
frappe.set_route("List", "Course Schedule"); frappe.set_route("List", "Course Schedule");
}); });
frm.add_custom_button(__("Assessment"), function() { frm.add_custom_button(__("Assessment Plan"), function() {
frappe.route_options = { frappe.route_options = {
course: frm.doc.name course: frm.doc.name
} }
frappe.set_route("List", "Assessment"); frappe.set_route("List", "Assessment Plan");
}); });
} }
}); });

View File

@ -1,278 +1,362 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 1, "allow_rename": 1,
"autoname": "field:course_name", "autoname": "field:course_name",
"beta": 0, "beta": 0,
"creation": "2015-09-07 12:39:55.181893", "creation": "2015-09-07 12:39:55.181893",
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "", "document_type": "",
"editable_grid": 0, "editable_grid": 0,
"engine": "InnoDB", "engine": "InnoDB",
"fields": [ "fields": [{
{ "allow_on_submit": 0,
"allow_on_submit": 0, "bold": 0,
"bold": 0, "collapsible": 0,
"collapsible": 0, "columns": 0,
"columns": 0, "fieldname": "course_name",
"fieldname": "course_name", "fieldtype": "Data",
"fieldtype": "Data", "hidden": 0,
"hidden": 0, "ignore_user_permissions": 0,
"ignore_user_permissions": 0, "ignore_xss_filter": 0,
"ignore_xss_filter": 0, "in_filter": 0,
"in_filter": 0, "in_list_view": 0,
"in_list_view": 0, "in_standard_filter": 0,
"in_standard_filter": 0, "label": "Course Name",
"label": "Course Name", "length": 0,
"length": 0, "no_copy": 0,
"no_copy": 0, "permlevel": 0,
"permlevel": 0, "precision": "",
"precision": "", "print_hide": 0,
"print_hide": 0, "print_hide_if_no_value": 0,
"print_hide_if_no_value": 0, "read_only": 0,
"read_only": 0, "remember_last_selected_value": 0,
"remember_last_selected_value": 0, "report_hide": 0,
"report_hide": 0, "reqd": 1,
"reqd": 1, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0, "unique": 0
"unique": 0 },
}, {
{ "allow_on_submit": 0,
"allow_on_submit": 0, "bold": 0,
"bold": 0, "collapsible": 0,
"collapsible": 0, "columns": 0,
"columns": 0, "fieldname": "course_code",
"fieldname": "course_code", "fieldtype": "Data",
"fieldtype": "Data", "hidden": 0,
"hidden": 0, "ignore_user_permissions": 0,
"ignore_user_permissions": 0, "ignore_xss_filter": 0,
"ignore_xss_filter": 0, "in_filter": 0,
"in_filter": 0, "in_list_view": 1,
"in_list_view": 1, "in_standard_filter": 0,
"in_standard_filter": 0, "label": "Course Code",
"label": "Course Code", "length": 0,
"length": 0, "no_copy": 0,
"no_copy": 0, "permlevel": 0,
"permlevel": 0, "precision": "",
"precision": "", "print_hide": 0,
"print_hide": 0, "print_hide_if_no_value": 0,
"print_hide_if_no_value": 0, "read_only": 0,
"read_only": 0, "remember_last_selected_value": 0,
"remember_last_selected_value": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 0, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0, "unique": 0
"unique": 0 },
}, {
{ "allow_on_submit": 0,
"allow_on_submit": 0, "bold": 0,
"bold": 0, "collapsible": 0,
"collapsible": 0, "columns": 0,
"columns": 0, "fieldname": "column_break_3",
"fieldname": "column_break_3", "fieldtype": "Column Break",
"fieldtype": "Column Break", "hidden": 0,
"hidden": 0, "ignore_user_permissions": 0,
"ignore_user_permissions": 0, "ignore_xss_filter": 0,
"ignore_xss_filter": 0, "in_filter": 0,
"in_filter": 0, "in_list_view": 0,
"in_list_view": 0, "in_standard_filter": 0,
"in_standard_filter": 0, "length": 0,
"length": 0, "no_copy": 0,
"no_copy": 0, "permlevel": 0,
"permlevel": 0, "precision": "",
"precision": "", "print_hide": 0,
"print_hide": 0, "print_hide_if_no_value": 0,
"print_hide_if_no_value": 0, "read_only": 0,
"read_only": 0, "remember_last_selected_value": 0,
"remember_last_selected_value": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 0, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0, "unique": 0
"unique": 0 },
}, {
{ "allow_on_submit": 0,
"allow_on_submit": 0, "bold": 0,
"bold": 0, "collapsible": 0,
"collapsible": 0, "columns": 0,
"columns": 0, "fieldname": "course_abbreviation",
"fieldname": "course_abbreviation", "fieldtype": "Data",
"fieldtype": "Data", "hidden": 0,
"hidden": 0, "ignore_user_permissions": 0,
"ignore_user_permissions": 0, "ignore_xss_filter": 0,
"ignore_xss_filter": 0, "in_filter": 0,
"in_filter": 0, "in_list_view": 0,
"in_list_view": 0, "in_standard_filter": 0,
"in_standard_filter": 0, "label": "Course Abbreviation",
"label": "Course Abbreviation", "length": 0,
"length": 0, "no_copy": 0,
"no_copy": 0, "permlevel": 0,
"permlevel": 0, "precision": "",
"precision": "", "print_hide": 0,
"print_hide": 0, "print_hide_if_no_value": 0,
"print_hide_if_no_value": 0, "read_only": 0,
"read_only": 0, "remember_last_selected_value": 0,
"remember_last_selected_value": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 0, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0, "unique": 0
"unique": 0 },
}, {
{ "allow_on_submit": 0,
"allow_on_submit": 0, "bold": 0,
"bold": 0, "collapsible": 0,
"collapsible": 0, "columns": 0,
"columns": 0, "fieldname": "department",
"fieldname": "department", "fieldtype": "Link",
"fieldtype": "Link", "hidden": 0,
"hidden": 0, "ignore_user_permissions": 0,
"ignore_user_permissions": 0, "ignore_xss_filter": 0,
"ignore_xss_filter": 0, "in_filter": 0,
"in_filter": 0, "in_list_view": 1,
"in_list_view": 1, "in_standard_filter": 1,
"in_standard_filter": 1, "label": "Department",
"label": "Department", "length": 0,
"length": 0, "no_copy": 0,
"no_copy": 0, "options": "Department",
"options": "Department", "permlevel": 0,
"permlevel": 0, "precision": "",
"precision": "", "print_hide": 0,
"print_hide": 0, "print_hide_if_no_value": 0,
"print_hide_if_no_value": 0, "read_only": 0,
"read_only": 0, "remember_last_selected_value": 0,
"remember_last_selected_value": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 0, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0, "unique": 0
"unique": 0 },
}, {
{ "allow_on_submit": 0,
"allow_on_submit": 0, "bold": 0,
"bold": 0, "collapsible": 0,
"collapsible": 0, "columns": 0,
"columns": 0, "fieldname": "section_break_6",
"fieldname": "section_break_6", "fieldtype": "Section Break",
"fieldtype": "Section Break", "hidden": 0,
"hidden": 0, "ignore_user_permissions": 0,
"ignore_user_permissions": 0, "ignore_xss_filter": 0,
"ignore_xss_filter": 0, "in_filter": 0,
"in_filter": 0, "in_list_view": 0,
"in_list_view": 0, "in_standard_filter": 0,
"in_standard_filter": 0, "length": 0,
"length": 0, "no_copy": 0,
"no_copy": 0, "permlevel": 0,
"permlevel": 0, "precision": "",
"precision": "", "print_hide": 0,
"print_hide": 0, "print_hide_if_no_value": 0,
"print_hide_if_no_value": 0, "read_only": 0,
"read_only": 0, "remember_last_selected_value": 0,
"remember_last_selected_value": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 0, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0, "unique": 0
"unique": 0 },
}, {
{ "allow_on_submit": 0,
"allow_on_submit": 0, "bold": 0,
"bold": 0, "collapsible": 0,
"collapsible": 0, "columns": 0,
"columns": 0, "fieldname": "course_intro",
"fieldname": "course_intro", "fieldtype": "Text Editor",
"fieldtype": "Text Editor", "hidden": 0,
"hidden": 0, "ignore_user_permissions": 0,
"ignore_user_permissions": 0, "ignore_xss_filter": 0,
"ignore_xss_filter": 0, "in_filter": 0,
"in_filter": 0, "in_list_view": 0,
"in_list_view": 0, "in_standard_filter": 0,
"in_standard_filter": 0, "label": "Course Intro",
"label": "Course Intro", "length": 0,
"length": 0, "no_copy": 0,
"no_copy": 0, "permlevel": 0,
"permlevel": 0, "precision": "",
"precision": "", "print_hide": 0,
"print_hide": 0, "print_hide_if_no_value": 0,
"print_hide_if_no_value": 0, "read_only": 0,
"read_only": 0, "remember_last_selected_value": 0,
"remember_last_selected_value": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 0, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0, "unique": 0
"unique": 0 },
} {
], "allow_on_submit": 0,
"hide_heading": 0, "bold": 0,
"hide_toolbar": 0, "collapsible": 0,
"idx": 0, "columns": 0,
"image_view": 0, "fieldname": "assessment",
"in_create": 0, "fieldtype": "Section Break",
"in_dialog": 0, "hidden": 0,
"is_submittable": 0, "ignore_user_permissions": 0,
"issingle": 0, "ignore_xss_filter": 0,
"istable": 0, "in_filter": 0,
"max_attachments": 0, "in_list_view": 0,
"menu_index": 0, "in_standard_filter": 0,
"modified": "2016-08-08 05:26:26.442635", "label": "Assessment",
"modified_by": "Administrator", "length": 0,
"module": "Schools", "no_copy": 0,
"name": "Course", "permlevel": 0,
"name_case": "", "precision": "",
"owner": "Administrator", "print_hide": 0,
"permissions": [ "print_hide_if_no_value": 0,
{ "read_only": 0,
"amend": 0, "remember_last_selected_value": 0,
"apply_user_permissions": 0, "report_hide": 0,
"cancel": 0, "reqd": 0,
"create": 1, "search_index": 0,
"delete": 1, "set_only_once": 0,
"email": 1, "unique": 0
"export": 1, },
"if_owner": 0, {
"import": 0, "allow_on_submit": 0,
"is_custom": 0, "bold": 0,
"permlevel": 0, "collapsible": 0,
"print": 1, "columns": 0,
"read": 1, "fieldname": "default_grading_scale",
"report": 1, "fieldtype": "Link",
"role": "Academics User", "hidden": 0,
"set_user_permissions": 0, "ignore_user_permissions": 0,
"share": 1, "ignore_xss_filter": 0,
"submit": 0, "in_filter": 0,
"write": 1 "in_list_view": 0,
}, "in_standard_filter": 0,
{ "label": "Default Grading Scale",
"amend": 0, "length": 0,
"apply_user_permissions": 0, "no_copy": 0,
"cancel": 0, "options": "Grading Scale",
"create": 1, "permlevel": 0,
"delete": 1, "precision": "",
"email": 1, "print_hide": 0,
"export": 1, "print_hide_if_no_value": 0,
"if_owner": 0, "read_only": 0,
"import": 0, "remember_last_selected_value": 0,
"permlevel": 0, "report_hide": 0,
"print": 1, "reqd": 0,
"read": 1, "search_index": 0,
"report": 1, "set_only_once": 0,
"role": "HR Manager", "unique": 0
"set_user_permissions": 0, },
"share": 1, {
"submit": 0, "allow_on_submit": 0,
"write": 1 "bold": 0,
} "collapsible": 0,
], "columns": 0,
"quick_entry": 0, "fieldname": "evaluation_criterias",
"read_only": 0, "fieldtype": "Table",
"read_only_onload": 0, "hidden": 0,
"search_fields": "department", "ignore_user_permissions": 0,
"sort_field": "modified", "ignore_xss_filter": 0,
"sort_order": "DESC", "in_filter": 0,
"track_seen": 0 "in_list_view": 0,
"in_standard_filter": 0,
"label": "Evaluation Criterias",
"length": 0,
"no_copy": 0,
"options": "Course Evaluation Criteria",
"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
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2016-12-14 16:48:16.642813",
"modified_by": "Administrator",
"module": "Schools",
"name": "Course",
"name_case": "",
"owner": "Administrator",
"permissions": [{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"search_fields": "department",
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
} }

View File

@ -8,21 +8,13 @@ from frappe.model.document import Document
from frappe import _ from frappe import _
class Course(Document): class Course(Document):
pass def validate(self):
self.validate_evaluation_criterias()
def get_sg_list(doctype, txt, filters, limit_start, limit_page_length=20):
user = frappe.session.user def validate_evaluation_criterias(self):
student = frappe.db.sql("select name from `tabStudent` where student_email_id= %s", user) if self.evaluation_criterias:
if student: total_weightage = 0
return frappe.db.sql('''select course, academic_term, academic_year, SG.name from `tabStudent Group` for criteria in self.evaluation_criterias:
as SG, `tabStudent Group Student` as SGS where SG.name = SGS.parent and SGS.student = %s total_weightage += criteria.weightage
order by SG.name asc limit {0} , {1}'''.format(limit_start, limit_page_length), student, as_dict=True) if total_weightage != 100:
frappe.throw(_("Total Weightage of all Evaluation Criterias must be 100%"))
def get_list_context(context=None):
return {
"show_sidebar": True,
'no_breadcrumbs': True,
"title": _("Courses"),
"get_list": get_sg_list,
"row_template": "templates/includes/course/course_row.html"
}

View File

@ -0,0 +1,124 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "",
"beta": 0,
"creation": "2016-12-14 16:46:46.786353",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "evaluation_criteria",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Evaluation Criteria",
"length": 0,
"no_copy": 0,
"options": "Evaluation Criteria",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "weightage",
"fieldtype": "Percent",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Weightage",
"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
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-12-14 16:59:17.353023",
"modified_by": "Administrator",
"module": "Schools",
"name": "Course Evaluation Criteria",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class CourseEvaluationCriteria(Document):
pass

View File

@ -55,11 +55,11 @@ class CourseSchedule(Document):
#validate overlapping assessment schedules. #validate overlapping assessment schedules.
if self.student_batch: if self.student_batch:
validate_overlap_for(self, "Assessment", "student_batch") validate_overlap_for(self, "Assessment Plan", "student_batch")
if self.student_group: if self.student_group:
validate_overlap_for(self, "Assessment", "student_group") validate_overlap_for(self, "Assessment Plan", "student_group")
validate_overlap_for(self, "Assessment", "room") validate_overlap_for(self, "Assessment Plan", "room")
validate_overlap_for(self, "Assessment", "supervisor", self.instructor) validate_overlap_for(self, "Assessment Plan", "supervisor", self.instructor)

View File

@ -0,0 +1,8 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Evaluation Criteria', {
refresh: function(frm) {
}
});

View File

@ -0,0 +1,89 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "field:evaluation_criteria",
"beta": 0,
"creation": "2016-12-14 16:40:15.144115",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "evaluation_criteria",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Evaluation Criteria",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-12-14 16:40:36.351183",
"modified_by": "Administrator",
"module": "Schools",
"name": "Evaluation Criteria",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class EvaluationCriteria(Document):
pass

View File

@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Evaluation Criteria')
class TestEvaluationCriteria(unittest.TestCase):
pass

View File

@ -0,0 +1,8 @@
[
{
"evaluation_criteria": "_Test Evaluation Criteria"
},
{
"evaluation_criteria": "_Test Evaluation Criteria 1"
}
]

View File

@ -9,11 +9,13 @@
"doctype": "DocType", "doctype": "DocType",
"document_type": "", "document_type": "",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "grade_code", "fieldname": "grade_code",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -21,6 +23,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Grade Code", "label": "Grade Code",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -29,6 +32,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
@ -39,39 +43,16 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "from_score", "columns": 0,
"fieldtype": "Float", "fieldname": "min_score",
"fieldtype": "Percent",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "From Score", "in_standard_filter": 0,
"length": 0, "label": "Min Score",
"no_copy": 0,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "to_score",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "To Score",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
@ -79,6 +60,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
@ -89,6 +71,7 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "grade_description", "fieldname": "grade_description",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"hidden": 0, "hidden": 0,
@ -96,6 +79,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Grade Description", "label": "Grade Description",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -104,6 +88,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -121,7 +106,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2016-08-27 15:45:04.657328", "modified": "2016-12-14 12:54:56.902465",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Schools", "module": "Schools",
"name": "Grade Interval", "name": "Grade Interval",

View File

@ -0,0 +1,8 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Grading Scale', {
refresh: function(frm) {
}
});

View File

@ -0,0 +1,203 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "field:grading_scale_name",
"beta": 0,
"creation": "2016-08-26 03:06:53.922972",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "grading_scale_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Grading Scale Name",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Description",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "grading_intervals_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Grading Scale Intervals",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "intervals",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Intervals",
"length": 0,
"no_copy": 0,
"options": "Grading Scale Interval",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Grading Scale",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 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": "2016-12-14 14:35:22.907023",
"modified_by": "Administrator",
"module": "Schools",
"name": "Grading Scale",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "",
"track_seen": 0
}

View File

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
class GradingScale(Document):
def validate(self):
thresholds = []
for d in self.intervals:
if d.threshold in thresholds:
frappe.throw(_("Treshold {0}% appears more than once.".format(d.threshold)))
else:
thresholds.append(d.threshold)
if 0 not in thresholds:
frappe.throw(_("Please define grade for treshold 0%"))

View File

@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
# test_records = frappe.get_test_records('Assessment') # test_records = frappe.get_test_records('Grading Scale')
class TestAssessment(unittest.TestCase): class TestGradingScale(unittest.TestCase):
pass pass

View File

@ -0,0 +1,19 @@
[
{
"grading_scale_name": "_Test Grading Scale",
"intervals": [
{
"grade_code": "A",
"threshold": 75
},
{
"grade_code": "B",
"threshold": 50
},
{
"grade_code": "C",
"threshold": 0
}
]
}
]

View File

@ -0,0 +1,124 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2016-08-26 03:11:09.591049",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "grade_code",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Grade Code",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "threshold",
"fieldtype": "Percent",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Threshold",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "1",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "grade_description",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Grade Description",
"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
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-01-04 15:27:56.729286",
"modified_by": "Administrator",
"module": "Schools",
"name": "Grading Scale Interval",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class GradingScaleInterval(Document):
pass

View File

@ -10,11 +10,13 @@
"doctype": "DocType", "doctype": "DocType",
"document_type": "", "document_type": "",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "grading_system_name", "fieldname": "grading_system_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -22,6 +24,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Grading System Name", "label": "Grading System Name",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -30,6 +33,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
@ -40,6 +44,34 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "description", "fieldname": "description",
"fieldtype": "Text", "fieldtype": "Text",
"hidden": 0, "hidden": 0,
@ -47,6 +79,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 0,
"label": "Description", "label": "Description",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -55,6 +88,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -65,30 +99,7 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "column_break_3", "columns": 0,
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "grading_intervals_section", "fieldname": "grading_intervals_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -96,6 +107,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0,
"label": "Grading Intervals", "label": "Grading Intervals",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -104,6 +116,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@ -114,6 +127,7 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0,
"fieldname": "grade_intervals", "fieldname": "grade_intervals",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
@ -121,6 +135,7 @@
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"in_standard_filter": 0,
"label": "Grade Intervals", "label": "Grade Intervals",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 0,
@ -130,6 +145,7 @@
"print_hide": 0, "print_hide": 0,
"print_hide_if_no_value": 0, "print_hide_if_no_value": 0,
"read_only": 0, "read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
@ -147,7 +163,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2016-08-27 14:20:50.709823", "modified": "2016-12-14 12:35:39.690256",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Schools", "module": "Schools",
"name": "Grading Structure", "name": "Grading Structure",
@ -164,6 +180,7 @@
"export": 1, "export": 1,
"if_owner": 0, "if_owner": 0,
"import": 0, "import": 0,
"is_custom": 0,
"permlevel": 0, "permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,

View File

@ -10,7 +10,7 @@ def get_data():
'items': ['Student Log', 'Student Batch', 'Student Group', 'Program Enrollment'] 'items': ['Student Log', 'Student Batch', 'Student Group', 'Program Enrollment']
}, },
{ {
'items': ['Fees', 'Assessment', 'Student Attendance', 'Student Leave Application'] 'items': ['Fees', 'Assessment Result', 'Student Attendance', 'Student Leave Application']
} }
] ]
} }

View File

@ -10,11 +10,11 @@ frappe.ui.form.on("Student Group", {
frappe.set_route("List", "Course Schedule"); frappe.set_route("List", "Course Schedule");
}); });
frm.add_custom_button(__("Assessment"), function() { frm.add_custom_button(__("Assessment Plan"), function() {
frappe.route_options = { frappe.route_options = {
student_group: frm.doc.name student_group: frm.doc.name
} }
frappe.set_route("List", "Assessment"); frappe.set_route("List", "Assessment Plan");
}); });
} }
}, },

View File

@ -8,6 +8,7 @@ import frappe
import frappe.defaults import frappe.defaults
from frappe.utils import cint, flt, cstr, today from frappe.utils import cint, flt, cstr, today
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
from erpnext import set_perpetual_inventory
class TestPurchaseReceipt(unittest.TestCase): class TestPurchaseReceipt(unittest.TestCase):
def test_make_purchase_invoice(self): def test_make_purchase_invoice(self):
@ -26,10 +27,10 @@ class TestPurchaseReceipt(unittest.TestCase):
def test_purchase_receipt_no_gl_entry(self): def test_purchase_receipt_no_gl_entry(self):
set_perpetual_inventory(0) set_perpetual_inventory(0)
existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item", existing_bin_stock_value = frappe.db.get_value("Bin", {"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC"}, "stock_value") "warehouse": "_Test Warehouse - _TC"}, "stock_value")
pr = make_purchase_receipt() pr = make_purchase_receipt()
stock_value_difference = frappe.db.get_value("Stock Ledger Entry", stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
@ -78,22 +79,22 @@ class TestPurchaseReceipt(unittest.TestCase):
def test_subcontracting(self): def test_subcontracting(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100) make_stock_entry(item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100)
make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC", make_stock_entry(item_code="_Test Item Home Desktop 100", target="_Test Warehouse 1 - _TC",
qty=100, basic_rate=100) qty=100, basic_rate=100)
pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes") pr = make_purchase_receipt(item_code="_Test FG Item", qty=10, rate=500, is_subcontracted="Yes")
self.assertEquals(len(pr.get("supplied_items")), 2) self.assertEquals(len(pr.get("supplied_items")), 2)
rm_supp_cost = sum([d.amount for d in pr.get("supplied_items")]) rm_supp_cost = sum([d.amount for d in pr.get("supplied_items")])
self.assertEquals(pr.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2)) self.assertEquals(pr.get("items")[0].rm_supp_cost, flt(rm_supp_cost, 2))
def test_serial_no_supplier(self): def test_serial_no_supplier(self):
pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1) pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
self.assertEquals(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "supplier"), self.assertEquals(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "supplier"),
pr.supplier) pr.supplier)
pr.cancel() pr.cancel()
self.assertFalse(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "warehouse")) self.assertFalse(frappe.db.get_value("Serial No", pr.get("items")[0].serial_no, "warehouse"))
@ -118,21 +119,21 @@ class TestPurchaseReceipt(unittest.TestCase):
for serial_no in rejected_serial_nos: for serial_no in rejected_serial_nos:
self.assertEquals(frappe.db.get_value("Serial No", serial_no, "warehouse"), self.assertEquals(frappe.db.get_value("Serial No", serial_no, "warehouse"),
pr.get("items")[0].rejected_warehouse) pr.get("items")[0].rejected_warehouse)
def test_purchase_return(self): def test_purchase_return(self):
set_perpetual_inventory() set_perpetual_inventory()
pr = make_purchase_receipt() pr = make_purchase_receipt()
return_pr = make_purchase_receipt(is_return=1, return_against=pr.name, qty=-2) return_pr = make_purchase_receipt(is_return=1, return_against=pr.name, qty=-2)
# check sle # check sle
outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt", outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
"voucher_no": return_pr.name}, "outgoing_rate") "voucher_no": return_pr.name}, "outgoing_rate")
self.assertEqual(outgoing_rate, 50) self.assertEqual(outgoing_rate, 50)
# check gl entries for return # check gl entries for return
gl_entries = get_gl_entries("Purchase Receipt", return_pr.name) gl_entries = get_gl_entries("Purchase Receipt", return_pr.name)
@ -146,7 +147,7 @@ class TestPurchaseReceipt(unittest.TestCase):
for gle in gl_entries: for gle in gl_entries:
self.assertEquals(expected_values[gle.account][0], gle.debit) self.assertEquals(expected_values[gle.account][0], gle.debit)
self.assertEquals(expected_values[gle.account][1], gle.credit) self.assertEquals(expected_values[gle.account][1], gle.credit)
set_perpetual_inventory(0) set_perpetual_inventory(0)
def test_purchase_return_for_rejected_qty(self): def test_purchase_return_for_rejected_qty(self):
@ -158,7 +159,7 @@ class TestPurchaseReceipt(unittest.TestCase):
actual_qty = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt", actual_qty = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
"voucher_no": return_pr.name, 'warehouse': return_pr.items[0].rejected_warehouse}, "actual_qty") "voucher_no": return_pr.name, 'warehouse': return_pr.items[0].rejected_warehouse}, "actual_qty")
self.assertEqual(actual_qty, -2) self.assertEqual(actual_qty, -2)
set_perpetual_inventory(0) set_perpetual_inventory(0)
@ -168,87 +169,82 @@ class TestPurchaseReceipt(unittest.TestCase):
serial_no = frappe.get_doc("Serial No", serial_no) serial_no = frappe.get_doc("Serial No", serial_no)
for field, value in field_values.items(): for field, value in field_values.items():
self.assertEquals(cstr(serial_no.get(field)), value) self.assertEquals(cstr(serial_no.get(field)), value)
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1) pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1)
serial_no = get_serial_nos(pr.get("items")[0].serial_no)[0] serial_no = get_serial_nos(pr.get("items")[0].serial_no)[0]
_check_serial_no_values(serial_no, { _check_serial_no_values(serial_no, {
"warehouse": "_Test Warehouse - _TC", "warehouse": "_Test Warehouse - _TC",
"purchase_document_no": pr.name "purchase_document_no": pr.name
}) })
return_pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=-1, return_pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=-1,
is_return=1, return_against=pr.name, serial_no=serial_no) is_return=1, return_against=pr.name, serial_no=serial_no)
_check_serial_no_values(serial_no, { _check_serial_no_values(serial_no, {
"warehouse": "", "warehouse": "",
"purchase_document_no": pr.name, "purchase_document_no": pr.name,
"delivery_document_no": return_pr.name "delivery_document_no": return_pr.name
}) })
def test_closed_purchase_receipt(self): def test_closed_purchase_receipt(self):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_purchase_receipt_status from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_purchase_receipt_status
pr = make_purchase_receipt(do_not_submit=True) pr = make_purchase_receipt(do_not_submit=True)
pr.submit() pr.submit()
update_purchase_receipt_status(pr.name, "Closed") update_purchase_receipt_status(pr.name, "Closed")
self.assertEquals(frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed") self.assertEquals(frappe.db.get_value("Purchase Receipt", pr.name, "status"), "Closed")
def test_pr_billing_status(self): def test_pr_billing_status(self):
# PO -> PR1 -> PI and PO -> PI and PO -> PR2 # PO -> PR1 -> PI and PO -> PI and PO -> PR2
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.buying.doctype.purchase_order.purchase_order \ from erpnext.buying.doctype.purchase_order.purchase_order \
import make_purchase_receipt, make_purchase_invoice as make_purchase_invoice_from_po import make_purchase_receipt, make_purchase_invoice as make_purchase_invoice_from_po
po = create_purchase_order() po = create_purchase_order()
pr1 = make_purchase_receipt(po.name) pr1 = make_purchase_receipt(po.name)
pr1.posting_date = today() pr1.posting_date = today()
pr1.posting_time = "10:00" pr1.posting_time = "10:00"
pr1.get("items")[0].received_qty = 2 pr1.get("items")[0].received_qty = 2
pr1.get("items")[0].qty = 2 pr1.get("items")[0].qty = 2
pr1.submit() pr1.submit()
pi1 = make_purchase_invoice(pr1.name) pi1 = make_purchase_invoice(pr1.name)
pi1.submit() pi1.submit()
pr1.load_from_db() pr1.load_from_db()
self.assertEqual(pr1.per_billed, 100) self.assertEqual(pr1.per_billed, 100)
pi2 = make_purchase_invoice_from_po(po.name) pi2 = make_purchase_invoice_from_po(po.name)
pi2.get("items")[0].qty = 4 pi2.get("items")[0].qty = 4
pi2.submit() pi2.submit()
pr2 = make_purchase_receipt(po.name) pr2 = make_purchase_receipt(po.name)
pr2.posting_date = today() pr2.posting_date = today()
pr2.posting_time = "08:00" pr2.posting_time = "08:00"
pr2.get("items")[0].received_qty = 5 pr2.get("items")[0].received_qty = 5
pr2.get("items")[0].qty = 5 pr2.get("items")[0].qty = 5
pr2.submit() pr2.submit()
pr1.load_from_db() pr1.load_from_db()
self.assertEqual(pr1.get("items")[0].billed_amt, 1000) self.assertEqual(pr1.get("items")[0].billed_amt, 1000)
self.assertEqual(pr1.per_billed, 100) self.assertEqual(pr1.per_billed, 100)
self.assertEqual(pr1.status, "Completed") self.assertEqual(pr1.status, "Completed")
self.assertEqual(pr2.get("items")[0].billed_amt, 2000) self.assertEqual(pr2.get("items")[0].billed_amt, 2000)
self.assertEqual(pr2.per_billed, 80) self.assertEqual(pr2.per_billed, 80)
self.assertEqual(pr2.status, "To Bill") self.assertEqual(pr2.status, "To Bill")
def get_gl_entries(voucher_type, voucher_no): def get_gl_entries(voucher_type, voucher_no):
return frappe.db.sql("""select account, debit, credit return frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type=%s and voucher_no=%s from `tabGL Entry` where voucher_type=%s and voucher_no=%s
order by account desc""", (voucher_type, voucher_no), as_dict=1) order by account desc""", (voucher_type, voucher_no), as_dict=1)
def set_perpetual_inventory(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.auto_accounting_for_stock = enable
accounts_settings.save()
def make_purchase_receipt(**args): def make_purchase_receipt(**args):
pr = frappe.new_doc("Purchase Receipt") pr = frappe.new_doc("Purchase Receipt")
args = frappe._dict(args) args = frappe._dict(args)

View File

@ -4,59 +4,64 @@ from __future__ import unicode_literals
from frappe.model.rename_doc import rename_doc from frappe.model.rename_doc import rename_doc
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from frappe.utils import cint from frappe.utils import cint
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory from erpnext import set_perpetual_inventory
from frappe.test_runner import make_test_records
import frappe import frappe
import unittest import unittest
test_records = frappe.get_test_records('Warehouse') test_records = frappe.get_test_records('Warehouse')
class TestWarehouse(unittest.TestCase): class TestWarehouse(unittest.TestCase):
def setUp(self):
if not frappe.get_value('Item', '_Test Item'):
make_test_records('Item')
def test_parent_warehouse(self): def test_parent_warehouse(self):
parent_warehouse = frappe.get_doc("Warehouse", "_Test Warehouse Group - _TC") parent_warehouse = frappe.get_doc("Warehouse", "_Test Warehouse Group - _TC")
self.assertEquals(parent_warehouse.is_group, 1) self.assertEquals(parent_warehouse.is_group, 1)
def test_warehouse_hierarchy(self): def test_warehouse_hierarchy(self):
p_warehouse = frappe.get_doc("Warehouse", "_Test Warehouse Group - _TC") p_warehouse = frappe.get_doc("Warehouse", "_Test Warehouse Group - _TC")
child_warehouses = frappe.db.sql("""select name, is_group, parent_warehouse from `tabWarehouse` wh child_warehouses = frappe.db.sql("""select name, is_group, parent_warehouse from `tabWarehouse` wh
where wh.lft > %s and wh.rgt < %s""", (p_warehouse.lft, p_warehouse.rgt), as_dict=1) where wh.lft > %s and wh.rgt < %s""", (p_warehouse.lft, p_warehouse.rgt), as_dict=1)
for child_warehouse in child_warehouses: for child_warehouse in child_warehouses:
self.assertEquals(p_warehouse.name, child_warehouse.parent_warehouse) self.assertEquals(p_warehouse.name, child_warehouse.parent_warehouse)
self.assertEquals(child_warehouse.is_group, 0) self.assertEquals(child_warehouse.is_group, 0)
def test_warehouse_renaming(self): def test_warehouse_renaming(self):
set_perpetual_inventory(1) set_perpetual_inventory(1)
create_warehouse("Test Warehouse for Renaming 1") create_warehouse("Test Warehouse for Renaming 1")
self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 1 - _TC")) self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 1 - _TC"))
self.assertTrue(frappe.db.get_value("Account", self.assertTrue(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Renaming 1 - _TC"})) filters={"warehouse": "Test Warehouse for Renaming 1 - _TC"}))
# Rename with abbr # Rename with abbr
if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - _TC"): if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 2 - _TC"):
frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC") frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC")
rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC") rename_doc("Warehouse", "Test Warehouse for Renaming 1 - _TC", "Test Warehouse for Renaming 2 - _TC")
self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 2 - _TC")) self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 2 - _TC"))
self.assertTrue(frappe.db.get_value("Account", self.assertTrue(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Renaming 2 - _TC"})) filters={"warehouse": "Test Warehouse for Renaming 2 - _TC"}))
self.assertFalse(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Renaming 1 - _TC"}))
# Rename without abbr # Rename without abbr
if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - _TC"): if frappe.db.exists("Warehouse", "Test Warehouse for Renaming 3 - _TC"):
frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC") frappe.delete_doc("Warehouse", "Test Warehouse for Renaming 3 - _TC")
rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3") rename_doc("Warehouse", "Test Warehouse for Renaming 2 - _TC", "Test Warehouse for Renaming 3")
self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 3 - _TC")) self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Renaming 3 - _TC"))
self.assertTrue(frappe.db.get_value("Account", self.assertTrue(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Renaming 3 - _TC"})) filters={"warehouse": "Test Warehouse for Renaming 3 - _TC"}))
set_perpetual_inventory(0)
def test_warehouse_merging(self): def test_warehouse_merging(self):
set_perpetual_inventory(1) set_perpetual_inventory(1)
create_warehouse("Test Warehouse for Merging 1") create_warehouse("Test Warehouse for Merging 1")
create_warehouse("Test Warehouse for Merging 2") create_warehouse("Test Warehouse for Merging 2")
@ -64,31 +69,29 @@ class TestWarehouse(unittest.TestCase):
qty=1, rate=100) qty=1, rate=100)
make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 2 - _TC", make_stock_entry(item_code="_Test Item", target="Test Warehouse for Merging 2 - _TC",
qty=1, rate=100) qty=1, rate=100)
existing_bin_qty = ( existing_bin_qty = (
cint(frappe.db.get_value("Bin", cint(frappe.db.get_value("Bin",
{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 1 - _TC"}, "actual_qty")) {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 1 - _TC"}, "actual_qty"))
+ cint(frappe.db.get_value("Bin", + cint(frappe.db.get_value("Bin",
{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty")) {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty"))
) )
rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC", rename_doc("Warehouse", "Test Warehouse for Merging 1 - _TC",
"Test Warehouse for Merging 2 - _TC", merge=True) "Test Warehouse for Merging 2 - _TC", merge=True)
self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - _TC")) self.assertFalse(frappe.db.exists("Warehouse", "Test Warehouse for Merging 1 - _TC"))
bin_qty = frappe.db.get_value("Bin", bin_qty = frappe.db.get_value("Bin",
{"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty") {"item_code": "_Test Item", "warehouse": "Test Warehouse for Merging 2 - _TC"}, "actual_qty")
self.assertEqual(bin_qty, existing_bin_qty) self.assertEqual(bin_qty, existing_bin_qty)
self.assertFalse(frappe.db.exists("Account", "Test Warehouse for Merging 1 - _TC")) self.assertFalse(frappe.db.exists("Account", "Test Warehouse for Merging 1 - _TC"))
self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Merging 2 - _TC")) self.assertTrue(frappe.db.exists("Account", "Test Warehouse for Merging 2 - _TC"))
self.assertTrue(frappe.db.get_value("Account", self.assertTrue(frappe.db.get_value("Account",
filters={"warehouse": "Test Warehouse for Merging 2 - _TC"})) filters={"warehouse": "Test Warehouse for Merging 2 - _TC"}))
set_perpetual_inventory(0)
def create_warehouse(warehouse_name): def create_warehouse(warehouse_name):
if not frappe.db.exists("Warehouse", warehouse_name + " - _TC"): if not frappe.db.exists("Warehouse", warehouse_name + " - _TC"):
w = frappe.new_doc("Warehouse") w = frappe.new_doc("Warehouse")
@ -96,5 +99,7 @@ def create_warehouse(warehouse_name):
w.parent_warehouse = "_Test Warehouse Group - _TC" w.parent_warehouse = "_Test Warehouse Group - _TC"
w.company = "_Test Company" w.company = "_Test Company"
w.save() w.save()
if not frappe.get_value('Account', dict(warehouse=warehouse_name + ' - _TC')):
print 'Warehouse {0} not linked'.format(warehouse_name)

View File

@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe, erpnext
from frappe.utils import cint, validate_email_add from frappe.utils import cint, validate_email_add
from frappe import throw, msgprint, _ from frappe import throw, msgprint, _
from frappe.utils.nestedset import NestedSet from frappe.utils.nestedset import NestedSet
@ -53,6 +53,8 @@ class Warehouse(NestedSet):
self.update_nsm_model() self.update_nsm_model()
def create_account_head(self): def create_account_head(self):
'''Create new account head if there is no account linked to this Warehouse'''
from erpnext.accounts.doctype.account.account import BalanceMismatchError
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")): if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")):
if not self.get_account(): if not self.get_account():
if self.get("__islocal") or not frappe.db.get_value( if self.get("__islocal") or not frappe.db.get_value(
@ -76,10 +78,12 @@ class Warehouse(NestedSet):
ac_doc.insert() ac_doc.insert()
msgprint(_("Account head {0} created").format(ac_doc.name), indicator='green', alert=True) msgprint(_("Account head {0} created").format(ac_doc.name), indicator='green', alert=True)
except frappe.DuplicateEntryError, e: except frappe.DuplicateEntryError:
if not (e.args and e.args[0]=='Account'): msgprint(_("Please create an Account for this Warehouse and link it. This cannot be done automatically as an account with name {0} already exists").format(frappe.bold(self.name)),
# if this is not due to creation of Account indicator='orange')
raise
except BalanceMismatchError:
msgprint(_("Cannot automatically create Account as there is already stock balance in the Account. You must create a matching account before you can make an entry on this warehouse"))
def validate_parent_account(self): def validate_parent_account(self):
if not self.company: if not self.company:
@ -111,7 +115,7 @@ class Warehouse(NestedSet):
else: else:
frappe.db.sql("delete from `tabBin` where name = %s", d['name']) frappe.db.sql("delete from `tabBin` where name = %s", d['name'])
warehouse_account = self.get_account(self.name) warehouse_account = self.get_account()
if warehouse_account: if warehouse_account:
frappe.delete_doc("Account", warehouse_account) frappe.delete_doc("Account", warehouse_account)
@ -131,10 +135,9 @@ class Warehouse(NestedSet):
return frappe.db.sql("""select name from `tabWarehouse` return frappe.db.sql("""select name from `tabWarehouse`
where parent_warehouse = %s""", self.name) where parent_warehouse = %s""", self.name)
def before_rename(self, olddn, newdn, merge=False): def before_rename(self, old_name, new_name, merge=False):
# Add company abbr if not provided # Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr new_warehouse = erpnext.encode_company_abbr(new_name, self.company)
new_warehouse = get_name_with_abbr(newdn, self.company)
if merge: if merge:
if not frappe.db.exists("Warehouse", new_warehouse): if not frappe.db.exists("Warehouse", new_warehouse):
@ -143,64 +146,54 @@ class Warehouse(NestedSet):
if self.company != frappe.db.get_value("Warehouse", new_warehouse, "company"): if self.company != frappe.db.get_value("Warehouse", new_warehouse, "company"):
frappe.throw(_("Both Warehouse must belong to same Company")) frappe.throw(_("Both Warehouse must belong to same Company"))
self.rename_account_for(olddn, new_warehouse, merge) self.rename_account_for(old_name, new_warehouse, merge)
return new_warehouse return new_warehouse
def rename_account_for(self, olddn, newdn, merge): def rename_account_for(self, old_name, new_name, merge):
if self.is_group: old_account_name = frappe.get_value('Account', dict(warehouse=old_name))
old_account = self.get_account()
else:
old_account = self.get_account(olddn)
if old_account: if old_account_name:
new_account = None
if not merge: if not merge:
if old_account == self.add_abbr_if_missing(olddn): # old account name is same as old name, so rename the account too
new_account = frappe.rename_doc("Account", old_account, newdn) if old_account_name == erpnext.encode_company_abbr(old_name, self.company):
frappe.rename_doc("Account", old_account_name, new_name)
else: else:
existing_new_account = self.get_account(newdn) # merge
new_account = frappe.rename_doc("Account", old_account, target_account = frappe.get_value('Account', dict(warehouse=new_name))
existing_new_account or newdn, merge=True if existing_new_account else False) if target_account:
# target warehouse has account, merge into target account
frappe.rename_doc("Account", old_account_name,
target_account, merge=True)
else:
# target warehouse does not have account, use this account
frappe.rename_doc("Account", old_account_name,
new_name, merge=False)
frappe.db.set_value("Account", new_account or old_account, "warehouse", newdn) # rename link
frappe.db.set_value('Account', new_name, 'warehouse', new_name)
def add_abbr_if_missing(self, dn): def get_account(self):
from erpnext.setup.doctype.company.company import get_name_with_abbr return frappe.get_value('Account', dict(warehouse=self.name))
return get_name_with_abbr(dn, self.company)
def get_account(self, warehouse=None): def after_rename(self, old_name, new_name, merge=False):
filters = {
"account_type": "Stock",
"company": self.company,
"is_group": self.is_group
}
if warehouse:
filters.update({"warehouse": warehouse})
else:
filters.update({"account_name": self.warehouse_name})
return frappe.db.get_value("Account", filters)
def after_rename(self, olddn, newdn, merge=False):
if merge: if merge:
self.recalculate_bin_qty(newdn) self.recalculate_bin_qty(new_name)
def recalculate_bin_qty(self, newdn): def recalculate_bin_qty(self, new_name):
from erpnext.stock.stock_balance import repost_stock from erpnext.stock.stock_balance import repost_stock
frappe.db.auto_commit_on_many_writes = 1 frappe.db.auto_commit_on_many_writes = 1
existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock") existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
repost_stock_for_items = frappe.db.sql_list("""select distinct item_code repost_stock_for_items = frappe.db.sql_list("""select distinct item_code
from tabBin where warehouse=%s""", newdn) from tabBin where warehouse=%s""", new_name)
# Delete all existing bins to avoid duplicate bins for the same item and warehouse # Delete all existing bins to avoid duplicate bins for the same item and warehouse
frappe.db.sql("delete from `tabBin` where warehouse=%s", newdn) frappe.db.sql("delete from `tabBin` where warehouse=%s", new_name)
for item_code in repost_stock_for_items: for item_code in repost_stock_for_items:
repost_stock(item_code, newdn) repost_stock(item_code, new_name)
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock) frappe.db.set_value("Stock Settings", None, "allow_negative_stock", existing_allow_negative_stock)
frappe.db.auto_commit_on_many_writes = 0 frappe.db.auto_commit_on_many_writes = 0
@ -231,7 +224,7 @@ class Warehouse(NestedSet):
if self.check_if_sle_exists(): if self.check_if_sle_exists():
throw(_("Warehouses with existing transaction can not be converted to group.")) throw(_("Warehouses with existing transaction can not be converted to group."))
else: else:
account_name = self.get_account(self.name) account_name = self.get_account()
if account_name: if account_name:
doc = frappe.get_doc("Account", account_name) doc = frappe.get_doc("Account", account_name)
doc.flags.exclude_account_type_check = True doc.flags.exclude_account_type_check = True