Merge pull request #7221 from bhupennewalkar1337/production
Fix#7085 Issue Items Against Production Order
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index e88e00d..6c93525 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -2,7 +2,7 @@
from __future__ import unicode_literals
import frappe
-__version__ = '7.1.24'
+__version__ = '7.1.25'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
index f1ccd9f..956f15a 100644
--- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
+++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.js
@@ -15,7 +15,7 @@
frm.set_query("bank_account", function() {
return {
"filters": {
- "account_type": "Bank",
+ "account_type": ["in",["Bank","Cash"]],
"is_group": 0
}
};
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index b8404f0..cef56af 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -309,7 +309,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "allow_partial_payment",
+ "fieldname": "allow_delete",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -317,7 +317,7 @@
"in_filter": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Allow Partial Payment",
+ "label": "Allow Delete",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -400,6 +400,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -426,6 +427,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Item Groups",
"length": 0,
"no_copy": 0,
@@ -454,6 +456,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -480,6 +483,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Customer Groups",
"length": 0,
"no_copy": 0,
@@ -1054,7 +1058,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "fa fa-cog",
+ "icon": "icon-cog",
"idx": 1,
"image_view": 0,
"in_create": 0,
@@ -1063,7 +1067,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-11-17 05:19:52.335433",
+ "modified": "2016-12-12 17:02:22.323006",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 3a41851..9928227 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -138,7 +138,7 @@
})
if args.ignore_pricing_rule or not args.item_code:
- if args.name and args.get("pricing_rule"):
+ if frappe.db.exists(args.doctype, args.name) and args.get("pricing_rule"):
item_details = remove_pricing_rule(args, item_details)
return item_details
diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js
index 70db094..b2b6aea 100644
--- a/erpnext/accounts/page/pos/pos.js
+++ b/erpnext/accounts/page/pos/pos.js
@@ -107,51 +107,122 @@
show_unsync_invoice_list: function(){
var me = this;
this.si_docs = this.get_doc_from_localstorage();
-
this.list_dialog = new frappe.ui.Dialog({
title: 'Invoice List'
});
this.list_dialog.show();
this.list_body = this.list_dialog.body;
+ if(me.pos_profile_data["allow_delete"]) {
+ this.list_dialog.set_primary_action(__("Delete"), function() {
+ frappe.confirm(__("Delete permanently?"), function () {
+ me.delete_records();
+ })
+ }).addClass("btn-danger");
+ this.toggle_primary_action();
+ }
+
if(this.si_docs.length > 0){
- $(this.list_body).append('<div class="row list-row list-row-head pos-invoice-list">\
- <div class="col-xs-1">Sr</div>\
- <div class="col-xs-3">Customer</div>\
- <div class="col-xs-2 text-left">Status</div>\
- <div class="col-xs-3 text-right">Paid Amount</div>\
- <div class="col-xs-3 text-right">Grand Total</div>\
- </div>')
-
- $.each(this.si_docs, function(index, data){
- for(key in data) {
- $(frappe.render_template("pos_invoice_list", {
- sr: index + 1,
- name: key,
- customer: data[key].customer,
- paid_amount: format_currency(data[key].paid_amount, me.frm.doc.currency),
- grand_total: format_currency(data[key].grand_total, me.frm.doc.currency),
- data: me.get_doctype_status(data[key])
- })).appendTo($(me.list_body));
- }
- })
-
- $(this.list_body).find('.list-row').click(function() {
- me.name = $(this).attr('invoice-name')
- doc_data = me.get_invoice_doc(me.si_docs)
- if(doc_data){
- me.frm.doc = doc_data[0][me.name];
- me.set_missing_values();
- me.refresh(false);
- me.disable_input_field();
- me.list_dialog.hide();
- }
- })
+ me.render_offline_data();
+ me.dialog_actions()
}else{
$(this.list_body).append(repl('<div class="media-heading">%(message)s</div>', {'message': __("All records are synced.")}))
}
},
+ render_offline_data: function() {
+ var me = this;
+
+ this.removed_items = [];
+ $(this.list_body).empty();
+
+ $(this.list_body).append('<div class="row list-row list-row-head pos-invoice-list">\
+ <div class="col-xs-1"><input class="list-select-all" type="checkbox"></div>\
+ <div class="col-xs-3">Customer</div>\
+ <div class="col-xs-2 text-left">Status</div>\
+ <div class="col-xs-3 text-right">Paid Amount</div>\
+ <div class="col-xs-3 text-right">Grand Total</div>\
+ </div>')
+
+ $.each(this.si_docs, function(index, data){
+ for(key in data) {
+ $(frappe.render_template("pos_invoice_list", {
+ sr: index + 1,
+ name: key,
+ customer: data[key].customer,
+ paid_amount: format_currency(data[key].paid_amount, me.frm.doc.currency),
+ grand_total: format_currency(data[key].grand_total, me.frm.doc.currency),
+ data: me.get_doctype_status(data[key])
+ })).appendTo($(me.list_body));
+ }
+ })
+ },
+
+ dialog_actions: function() {
+ var me = this;
+
+ $(this.list_body).find('.list-column').click(function() {
+ me.name = $(this).parents().attr('invoice-name')
+ me.edit_record();
+ })
+
+ $(this.list_body).find('.list-select-all').click(function() {
+ me.removed_items = [];
+ $(me.list_body).find('.list-delete').prop("checked", $(this).is(":checked"))
+ if($(this).is(":checked")) {
+ $.each(me.si_docs, function(index, data){
+ for(key in data) {
+ me.removed_items.push(key)
+ }
+ })
+ }
+
+ me.toggle_primary_action();
+ })
+
+ $(this.list_body).find('.list-delete').click(function() {
+ me.name = $(this).parent().parent().attr('invoice-name');
+ if($(this).is(":checked")) {
+ me.removed_items.push(me.name);
+ } else {
+ me.removed_items.pop(me.name)
+ }
+
+ me.toggle_primary_action();
+ })
+ },
+
+ edit_record: function() {
+ var me = this;
+
+ doc_data = this.get_invoice_doc(this.si_docs);
+ if(doc_data){
+ this.frm.doc = doc_data[0][this.name];
+ this.set_missing_values();
+ this.refresh(false);
+ this.disable_input_field();
+ this.list_dialog.hide();
+ }
+ },
+
+ delete_records: function() {
+ var me = this;
+ this.remove_doc_from_localstorage()
+ this.update_localstorage();
+ this.render_offline_data();
+ this.dialog_actions();
+ this.toggle_primary_action();
+ },
+
+ toggle_primary_action: function() {
+ var me = this;
+ if(this.removed_items && this.removed_items.length > 0) {
+ $(this.list_dialog.wrapper).find('.btn-danger').show();
+ } else {
+ $(this.list_dialog.wrapper).find('.btn-danger').hide();
+ }
+ },
+
get_doctype_status: function(doc){
if(doc.docstatus == 0) {
return {status: "Draft", indicator: "red"}
@@ -229,7 +300,7 @@
},
save_previous_entry : function(){
- if(this.frm.doc.items.length > 0){
+ if(this.frm.doc.docstatus < 1 && this.frm.doc.items.length > 0){
this.create_invoice()
}
},
@@ -256,7 +327,7 @@
frappe.meta.sync(data)
})
- this.print_template = frappe.render_template("print_template",
+ this.print_template_data = frappe.render_template("print_template",
{content: this.print_template, title:"POS",
base_url: frappe.urllib.get_base_url(), print_css: frappe.boot.print_css})
},
@@ -736,7 +807,7 @@
}, "octicon octfa fa-credit-card");
}else if(this.frm.doc.docstatus == 1) {
this.page.set_primary_action(__("Print"), function() {
- html = frappe.render(me.print_template, me.frm.doc)
+ html = frappe.render(me.print_template_data, me.frm.doc)
me.print_document(html)
})
}else {
@@ -759,7 +830,7 @@
]));
$('.print_doc').click(function(){
- html = frappe.render(me.print_template, me.frm.doc)
+ html = frappe.render(me.print_template_data, me.frm.doc)
me.print_document(html)
})
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index cec01a0..8e59d8f 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -110,7 +110,7 @@
if party_type and party:
cond.append("""gle.party_type = "%s" and gle.party = "%s" """ %
(frappe.db.escape(party_type), frappe.db.escape(party, percent=False)))
-
+
if company:
cond.append("""gle.company = "%s" """ % (frappe.db.escape(company, percent=False)))
@@ -178,7 +178,7 @@
voucher_type, voucher_no, against_voucher_type, against_voucher
FROM `tabGL Entry` gle
WHERE {0}""".format(" and ".join(cond)), as_dict=True)
-
+
count = 0
for gle in entries:
if fieldname not in ('invoiced_amount','payables'):
@@ -196,12 +196,12 @@
WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s
and party = %(party)s and name != %(name)s""".format(select_fields),
{"date": date, "voucher_no": gle.voucher_no, "party": gle.party, "name": gle.name})[0][0]
-
+
outstanding_amount = flt(gle.get(dr_or_cr)) - flt(gle.get(cr_or_dr)) - payment_amount
currency_precision = get_currency_precision() or 2
if abs(flt(outstanding_amount)) > 0.1/10**currency_precision:
count += 1
-
+
return count
@frappe.whitelist()
@@ -209,7 +209,7 @@
if not args:
args = frappe.local.form_dict
args.pop("cmd")
-
+
ac = frappe.new_doc("Account")
if args.get("ignore_permissions"):
@@ -220,7 +220,7 @@
if not ac.parent_account:
ac.parent_account = args.get("parent")
-
+
ac.old_parent = ""
ac.freeze_account = "No"
if cint(ac.get("is_root")):
@@ -252,10 +252,10 @@
Cancel JV, Update aginst document, split if required and resubmit jv
"""
for d in args:
-
- check_if_advance_entry_modified(d)
+
+ check_if_advance_entry_modified(d)
validate_allocated_amount(d)
-
+
# cancel advance entry
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
@@ -289,13 +289,13 @@
else:
party_account_field = "paid_from" if args.party_type == "Customer" else "paid_to"
if args.voucher_detail_no:
- ret = frappe.db.sql("""select t1.name
- from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
+ ret = frappe.db.sql("""select t1.name
+ from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
where
- t1.name = t2.parent and t1.docstatus = 1
+ t1.name = t2.parent and t1.docstatus = 1
and t1.name = %(voucher_no)s and t2.name = %(voucher_detail_no)s
and t1.party_type = %(party_type)s and t1.party = %(party)s and t1.{0} = %(account)s
- and t2.reference_doctype in ("", "Sales Order", "Purchase Order")
+ and t2.reference_doctype in ("", "Sales Order", "Purchase Order")
and t2.allocated_amount = %(unadjusted_amount)s
""".format(party_account_field), args)
else:
@@ -367,7 +367,7 @@
# will work as update after submit
jv_obj.flags.ignore_validate_update_after_submit = True
jv_obj.save(ignore_permissions=True)
-
+
def update_reference_in_payment_entry(d, payment_entry):
reference_details = {
"reference_doctype": d.against_voucher_type,
@@ -377,44 +377,44 @@
"allocated_amount": d.allocated_amount,
"exchange_rate": d.exchange_rate
}
-
+
if d.voucher_detail_no:
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
original_row = existing_row.as_dict().copy()
existing_row.update(reference_details)
-
+
if d.allocated_amount < original_row.allocated_amount:
new_row = payment_entry.append("references")
new_row.docstatus = 1
for field in reference_details.keys():
new_row.set(field, original_row[field])
-
+
new_row.allocated_amount = original_row.allocated_amount - d.allocated_amount
else:
new_row = payment_entry.append("references")
new_row.docstatus = 1
new_row.update(reference_details)
-
+
payment_entry.flags.ignore_validate_update_after_submit = True
payment_entry.setup_party_account_field()
payment_entry.set_missing_values()
payment_entry.set_amounts()
payment_entry.save(ignore_permissions=True)
-
+
def unlink_ref_doc_from_payment_entries(ref_doc):
remove_ref_doc_link_from_jv(ref_doc.doctype, ref_doc.name)
remove_ref_doc_link_from_pe(ref_doc.doctype, ref_doc.name)
-
+
frappe.db.sql("""update `tabGL Entry`
set against_voucher_type=null, against_voucher=null,
modified=%s, modified_by=%s
where against_voucher_type=%s and against_voucher=%s
and voucher_no != ifnull(against_voucher, '')""",
(now(), frappe.session.user, ref_doc.doctype, ref_doc.name))
-
+
if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"):
ref_doc.set("advances", [])
-
+
frappe.db.sql("""delete from `tab{0} Advance` where parent = %s"""
.format(ref_doc.doctype), ref_doc.name)
@@ -430,7 +430,7 @@
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
frappe.msgprint(_("Journal Entries {0} are un-linked".format("\n".join(linked_jv))))
-
+
def remove_ref_doc_link_from_pe(ref_type, ref_no):
linked_pe = frappe.db.sql_list("""select parent from `tabPayment Entry Reference`
where reference_doctype=%s and reference_name=%s and docstatus < 2""", (ref_type, ref_no))
@@ -440,18 +440,18 @@
set allocated_amount=0, modified=%s, modified_by=%s
where reference_doctype=%s and reference_name=%s
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
-
+
for pe in linked_pe:
pe_doc = frappe.get_doc("Payment Entry", pe)
pe_doc.set_total_allocated_amount()
pe_doc.set_unallocated_amount()
pe_doc.clear_unallocated_reference_document_rows()
-
- frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s,
- base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s
- where name=%s""", (pe_doc.total_allocated_amount, pe_doc.base_total_allocated_amount,
+
+ frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s,
+ base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s
+ where name=%s""", (pe_doc.total_allocated_amount, pe_doc.base_total_allocated_amount,
pe_doc.unallocated_amount, now(), frappe.session.user, pe))
-
+
frappe.msgprint(_("Payment Entries {0} are un-linked".format("\n".join(linked_pe))))
@frappe.whitelist()
@@ -562,7 +562,7 @@
from
`tabGL Entry` invoice_gl_entry
where
- party_type = %(party_type)s and party = %(party)s
+ party_type = %(party_type)s and party = %(party)s
and account = %(account)s and {dr_or_cr} > 0
{condition}
and ((voucher_type = 'Journal Entry'
@@ -590,9 +590,9 @@
'outstanding_amount': flt(d.invoice_amount - d.payment_amount, precision),
'due_date': frappe.db.get_value(d.voucher_type, d.voucher_no, "due_date"),
}))
-
+
outstanding_invoices = sorted(outstanding_invoices, key=lambda k: k['due_date'] or getdate(nowdate()))
-
+
return outstanding_invoices
diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py
index 0b21fec..55363ab 100644
--- a/erpnext/config/hr.py
+++ b/erpnext/config/hr.py
@@ -150,12 +150,23 @@
},
{
- "label": _("Tools"),
- "icon": "fa fa-wrench",
+ "label": _("Training"),
"items": [
-
+ {
+ "type": "doctype",
+ "name": "Training Event"
+ },
+ {
+ "type": "doctype",
+ "name": "Training Result"
+ },
+ {
+ "type": "doctype",
+ "name": "Training Feedback"
+ },
]
},
+
{
"label": _("Setup"),
"icon": "fa fa-cog",
diff --git a/erpnext/demo/setup/setup_data.py b/erpnext/demo/setup/setup_data.py
index 278c398..902bbab 100644
--- a/erpnext/demo/setup/setup_data.py
+++ b/erpnext/demo/setup/setup_data.py
@@ -2,7 +2,7 @@
import random, json
import frappe, erpnext
-from frappe.utils import flt, now_datetime, cstr
+from frappe.utils import flt, now_datetime, cstr, random_string
from frappe.utils.make_random import add_random_children, get_random
from erpnext.demo.domains import data
from frappe import _
@@ -14,8 +14,15 @@
setup_holiday_list()
setup_user()
setup_employee()
- setup_salary_structure()
- setup_salary_structure_for_timesheet()
+
+ employees = frappe.get_all('Employee', fields=['name', 'date_of_joining'])
+
+ # monthly salary
+ setup_salary_structure(employees[:5], 0)
+
+ # based on timesheet
+ setup_salary_structure(employees[5:], 1)
+
setup_leave_allocation()
setup_user_roles()
setup_customer()
@@ -29,7 +36,7 @@
setup_account_to_expense_type()
setup_budget()
setup_pos_profile()
-
+
frappe.db.commit()
frappe.clear_cache()
@@ -111,30 +118,44 @@
frappe.db.set_value("HR Settings", None, "emp_created_by", "Naming Series")
frappe.db.commit()
+ for d in frappe.get_all('Salary Component'):
+ salary_component = frappe.get_doc('Salary Component', d.name)
+ salary_component.append('accounts', dict(
+ company=erpnext.get_default_company(),
+ default_account=frappe.get_value('Account', dict(account_name=('like', 'Salary%')))
+ ))
+ salary_component.save()
+
import_json('Employee')
-def setup_salary_structure():
+def setup_salary_structure(employees, salary_slip_based_on_timesheet=0):
f = frappe.get_doc('Fiscal Year', frappe.defaults.get_global_default('fiscal_year'))
ss = frappe.new_doc('Salary Structure')
- ss.name = "Sample Salary Structure - " + str(f.year_start_date)
- for e in frappe.get_all('Employee', fields=['name', 'date_of_joining']):
+ ss.name = "Sample Salary Structure - " + random_string(5)
+ for e in employees:
ss.append('employees', {
'employee': e.name,
'base': random.random() * 10000
})
- if not e.date_of_joining:
- continue
+ ss.from_date = e.date_of_joining if (e.date_of_joining
+ and e.date_of_joining > f.year_start_date) else f.year_start_date
+ ss.to_date = f.year_end_date
+ ss.salary_slip_based_on_timesheet = salary_slip_based_on_timesheet
- ss.from_date = e.date_of_joining if (e.date_of_joining
- and e.date_of_joining > f.year_start_date) else f.year_start_date
- ss.to_date = f.year_end_date
- ss.payment_account = frappe.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
+ if salary_slip_based_on_timesheet:
+ ss.salary_component = 'Basic'
+ ss.hour_rate = flt(random.random() * 10, 2)
+ else:
+ ss.payroll_frequency = 'Monthly'
+
+ ss.payment_account = frappe.get_value('Account',
+ {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
+
ss.append('earnings', {
'salary_component': 'Basic',
"abbr":'B',
- 'condition': 'base > 5000',
'formula': 'base*.2',
'amount_based_on_formula': 1,
"idx": 1
@@ -142,20 +163,14 @@
ss.append('deductions', {
'salary_component': 'Income Tax',
"abbr":'IT',
- 'condition': 'base > 5000',
+ 'condition': 'base > 1000',
'amount': random.random() * 1000,
"idx": 1
})
ss.insert()
-def setup_salary_structure_for_timesheet():
- for e in frappe.get_all('Salary Structure', fields=['name'], filters={'is_active': 'Yes'}, limit=2):
- ss_doc = frappe.get_doc("Salary Structure", e.name)
- ss_doc.salary_slip_based_on_timesheet = 1
- ss_doc.salary_component = 'Basic'
- ss_doc.hour_rate = flt(random.random() * 10, 2)
- ss_doc.save(ignore_permissions=True)
+ return ss
def setup_user_roles():
user = frappe.get_doc('User', 'demo@erpnext.com')
@@ -208,11 +223,11 @@
user = frappe.get_doc('User', 'aromn@example.com')
user.add_roles('Academics User')
frappe.db.set_global('demo_schools_user', user.name)
-
+
#Add Expense Approver
user = frappe.get_doc('User', 'WanMai@example.com')
user.add_roles('Expense Approver')
-
+
def setup_leave_allocation():
year = now_datetime().year
for employee in frappe.get_all('Employee', fields=['name']):
@@ -345,7 +360,7 @@
})
pos.insert()
-
+
def import_json(doctype, submit=False, values=None):
frappe.flags.in_import = True
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
diff --git a/erpnext/demo/user/accounts.py b/erpnext/demo/user/accounts.py
index 392d13d..1a41482 100644
--- a/erpnext/demo/user/accounts.py
+++ b/erpnext/demo/user/accounts.py
@@ -23,25 +23,32 @@
report = "Ordered Items to be Billed"
for so in list(set([r[0] for r in query_report.run(report)["result"]
if r[0]!="Total"]))[:random.randint(1, 5)]:
- si = frappe.get_doc(make_sales_invoice(so))
- si.posting_date = frappe.flags.current_date
- for d in si.get("items"):
- if not d.income_account:
- d.income_account = "Sales - {}".format(frappe.db.get_value('Company', si.company, 'abbr'))
- si.insert()
- si.submit()
- frappe.db.commit()
+ try:
+ si = frappe.get_doc(make_sales_invoice(so))
+ si.posting_date = frappe.flags.current_date
+ for d in si.get("items"):
+ if not d.income_account:
+ d.income_account = "Sales - {}".format(frappe.db.get_value('Company', si.company, 'abbr'))
+ si.insert()
+ si.submit()
+ frappe.db.commit()
+ except frappe.ValidationError:
+ pass
if random.random() <= 0.6:
report = "Received Items to be Billed"
for pr in list(set([r[0] for r in query_report.run(report)["result"]
if r[0]!="Total"]))[:random.randint(1, 5)]:
- pi = frappe.get_doc(make_purchase_invoice(pr))
- pi.posting_date = frappe.flags.current_date
- pi.bill_no = random_string(6)
- pi.insert()
- pi.submit()
- frappe.db.commit()
+ try:
+ pi = frappe.get_doc(make_purchase_invoice(pr))
+ pi.posting_date = frappe.flags.current_date
+ pi.bill_no = random_string(6)
+ pi.insert()
+ pi.submit()
+ frappe.db.commit()
+ except frappe.ValidationError:
+ pass
+
if random.random() < 0.5:
make_payment_entries("Sales Invoice", "Accounts Receivable")
@@ -67,7 +74,7 @@
def make_payment_entries(ref_doctype, report):
outstanding_invoices = list(set([r[3] for r in query_report.run(report,
{"report_date": frappe.flags.current_date })["result"] if r[2]==ref_doctype]))
-
+
# make Payment Entry
for inv in outstanding_invoices[:random.randint(1, 2)]:
pe = get_payment_entry(ref_doctype, inv)
diff --git a/erpnext/demo/user/hr.py b/erpnext/demo/user/hr.py
index 2686987..cfe3119 100644
--- a/erpnext/demo/user/hr.py
+++ b/erpnext/demo/user/hr.py
@@ -1,40 +1,50 @@
from __future__ import unicode_literals
import frappe, erpnext
import random
-from frappe.utils import random_string, add_days, cint
+import datetime
+from frappe.utils import random_string, add_days, get_last_day, getdate
from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
from frappe.utils.make_random import get_random
from erpnext.hr.doctype.expense_claim.expense_claim import get_expense_approver, make_bank_entry
-from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on, OverlapError
+from erpnext.hr.doctype.leave_application.leave_application import (get_leave_balance_on,
+ OverlapError, AttendanceAlreadyMarkedError)
def work():
frappe.set_user(frappe.db.get_global('demo_hr_user'))
year, month = frappe.flags.current_date.strftime("%Y-%m").split("-")
- prev_month = str(cint(month)- 1).zfill(2)
- if month=="01":
- prev_month = "12"
-
mark_attendance()
make_leave_application()
# process payroll
- if not frappe.db.get_value("Salary Slip", {"month": prev_month, "fiscal_year": year}):
+ if not frappe.db.sql('select name from `tabSalary Slip` where month(adddate(start_date, interval 1 month))=month(curdate())'):
+ # process payroll for previous month
process_payroll = frappe.get_doc("Process Payroll", "Process Payroll")
process_payroll.company = frappe.flags.company
- process_payroll.month = prev_month
- process_payroll.fiscal_year = year
- process_payroll.from_date = frappe.flags.current_date
- process_payroll.to_date = add_days(frappe.flags.current_date, random.randint(0, 30))
- process_payroll.reference_number = "DemoRef23"
- process_payroll.reference_date = frappe.flags.current_date
+ process_payroll.payroll_frequency = 'Monthly'
+
+ # select a posting date from the previous month
+ process_payroll.posting_date = get_last_day(getdate(frappe.flags.current_date) - datetime.timedelta(days=10))
process_payroll.payment_account = frappe.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
- process_payroll.submit_salary_slip()
- process_payroll.make_journal_entry()
-
+
+ process_payroll.set_start_end_dates()
+
+ # based on frequency
+ process_payroll.salary_slip_based_on_timesheet = 0
+ process_payroll.create_salary_slips()
+ process_payroll.submit_salary_slips()
+ process_payroll.make_journal_entry(reference_date=frappe.flags.current_date,
+ reference_number=random_string(10))
+
+ process_payroll.salary_slip_based_on_timesheet = 1
+ process_payroll.create_salary_slips()
+ process_payroll.submit_salary_slips()
+ process_payroll.make_journal_entry(reference_date=frappe.flags.current_date,
+ reference_number=random_string(10))
+
if frappe.db.get_global('demo_hr_user'):
make_timesheet_records()
-
+
#expense claim
expense_claim = frappe.new_doc("Expense Claim")
expense_claim.extend('expenses', get_expenses())
@@ -100,7 +110,10 @@
select employee from `tabSalary Structure Employee`
where parent IN %(sal_struct)s""", {"sal_struct": sal_struct}, as_dict=True)
return employees
-
+
+ else:
+ return []
+
def make_timesheet_records():
employees = get_timesheet_based_salary_slip_employee()
for e in employees:
@@ -134,10 +147,10 @@
sales_invoice.insert()
sales_invoice.submit()
frappe.db.commit()
-
+
def make_leave_application():
allocated_leaves = frappe.get_all("Leave Allocation", fields=['employee', 'leave_type'])
-
+
for allocated_leave in allocated_leaves:
leave_balance = get_leave_balance_on(allocated_leave.employee, allocated_leave.leave_type, frappe.flags.current_date,
consider_all_leaves_in_the_allocation_period=True)
@@ -146,7 +159,7 @@
to_date = frappe.flags.current_date
else:
to_date = add_days(frappe.flags.current_date, random.randint(0, leave_balance-1))
-
+
leave_application = frappe.get_doc({
"doctype": "Leave Application",
"employee": allocated_leave.employee,
@@ -159,13 +172,13 @@
leave_application.insert()
leave_application.submit()
frappe.db.commit()
- except (OverlapError):
+ except (OverlapError, AttendanceAlreadyMarkedError):
frappe.db.rollback()
-
+
def mark_attendance():
att_date = frappe.flags.current_date
for employee in frappe.get_all('Employee', fields=['name'], filters = {'status': 'Active'}):
-
+
if not frappe.db.get_value("Attendance", {"employee": employee.name, "att_date": att_date}):
attendance = frappe.get_doc({
"doctype": "Attendance",
@@ -175,11 +188,11 @@
leave = frappe.db.sql("""select name from `tabLeave Application`
where employee = %s and %s between from_date and to_date and status = 'Approved'
and docstatus = 1""", (employee.name, att_date))
-
+
if leave:
attendance.status = "Absent"
else:
attendance.status = "Present"
attendance.save()
- attendance.submit()
+ attendance.submit()
frappe.db.commit()
diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py
index 10df143..ddd36ef 100644
--- a/erpnext/demo/user/sales.py
+++ b/erpnext/demo/user/sales.py
@@ -55,12 +55,13 @@
"enquiry_from": "Customer",
"customer": get_random("Customer"),
"enquiry_type": "Sales",
+ "with_items": 1,
"transaction_date": frappe.flags.current_date,
})
add_random_children(b, "items", rows=4, randomize = {
"qty": (1, 5),
- "item_code": ("Item", {"has_variants": "0", "is_fixed_asset": 0})
+ "item_code": ("Item", {"has_variants": 0, "is_fixed_asset": 0})
}, unique="item_code")
b.insert()
@@ -68,7 +69,7 @@
def make_quotation():
# get open opportunites
- opportunity = get_random("Opportunity", {"status": "Open"})
+ opportunity = get_random("Opportunity", {"status": "Open", "with_items": 1})
if opportunity:
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
diff --git a/erpnext/demo/user/stock.py b/erpnext/demo/user/stock.py
index ea23853..0bd5ce3 100644
--- a/erpnext/demo/user/stock.py
+++ b/erpnext/demo/user/stock.py
@@ -85,7 +85,7 @@
def submit_draft_stock_entries():
from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, \
- DuplicateEntryForProductionOrderError, OperationsNotCompleteError
+ DuplicateEntryForProductionOrderError, OperationsNotCompleteError
# try posting older drafts (if exists)
frappe.db.commit()
@@ -102,7 +102,7 @@
def make_sales_return_records():
for data in frappe.get_all('Delivery Note', fields=["name"], filters={"docstatus": 1}):
- if random.random() < 0.2:
+ if random.random() < 0.1:
try:
dn = make_sales_return(data.name)
dn.insert()
@@ -113,7 +113,7 @@
def make_purchase_return_records():
for data in frappe.get_all('Purchase Receipt', fields=["name"], filters={"docstatus": 1}):
- if random.random() < 0.2:
+ if random.random() < 0.1:
try:
pr = make_purchase_return(data.name)
pr.insert()
diff --git a/erpnext/docs/assets/img/human-resources/training_event.png b/erpnext/docs/assets/img/human-resources/training_event.png
new file mode 100644
index 0000000..04162eb
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/training_event.png
Binary files differ
diff --git a/erpnext/docs/assets/img/human-resources/training_event_employee.png b/erpnext/docs/assets/img/human-resources/training_event_employee.png
new file mode 100644
index 0000000..aea90b4
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/training_event_employee.png
Binary files differ
diff --git a/erpnext/docs/assets/img/human-resources/training_feedback.png b/erpnext/docs/assets/img/human-resources/training_feedback.png
new file mode 100644
index 0000000..84a2ec2
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/training_feedback.png
Binary files differ
diff --git a/erpnext/docs/assets/img/human-resources/training_result.png b/erpnext/docs/assets/img/human-resources/training_result.png
new file mode 100644
index 0000000..36f086c
--- /dev/null
+++ b/erpnext/docs/assets/img/human-resources/training_result.png
Binary files differ
diff --git a/erpnext/docs/user/manual/en/human-resources/index.txt b/erpnext/docs/user/manual/en/human-resources/index.txt
index 2543b4b..5615790 100644
--- a/erpnext/docs/user/manual/en/human-resources/index.txt
+++ b/erpnext/docs/user/manual/en/human-resources/index.txt
@@ -7,6 +7,7 @@
job-applicant
job-opening
offer-letter
+training
tools
human-resources-reports
setup
diff --git a/erpnext/docs/user/manual/en/human-resources/training.md b/erpnext/docs/user/manual/en/human-resources/training.md
new file mode 100644
index 0000000..5d55b6c
--- /dev/null
+++ b/erpnext/docs/user/manual/en/human-resources/training.md
@@ -0,0 +1,28 @@
+### Training Event
+
+Schedule seminars, workshops, conferences etc using Training Event. You can also invite your employees to attend the event using this feature.
+
+<img class="screenshot" alt="Employee" src="{{docs_base_url}}/assets/img/human-resources/training_event.png">
+
+### Inviting Employees for Event
+
+You can invite your employees to attend the event. You can do so by selecting the employees to be invited in the employee table.
+By default the status of the employee will be 'Open'.
+The system shall notify the employee with status 'Open' by sending a email to the office email address of the employee as mentioned in the employee master if you have selected 'Send Email' checkbox.
+The status is changed to 'Invited' when an invitation email is sent to the employee by the system.
+When an Employee confirms his/her presence for the event you can change the status to 'Confirmed'.
+
+<img class="screenshot" alt="Employee" src="{{docs_base_url}}/assets/img/human-resources/training_event_employee.png">
+
+### Training Result
+
+After compleation of the training Employee Wise training results can be stored based on the Feedback received from the Trainer.
+
+<img class="screenshot" alt="Employee" src="{{docs_base_url}}/assets/img/human-resources/training_result.png">
+
+
+### Trainig Feedback
+
+Collect feedback regarding the event from your Employees using Training Feedback.
+
+<img class="screenshot" alt="Employee" src="{{docs_base_url}}/assets/img/human-resources/training_feedback.png">
diff --git a/erpnext/docs/user/manual/en/selling/articles/adding-margin.md b/erpnext/docs/user/manual/en/selling/articles/adding-margin.md
index 2b2fb09..2725fee 100644
--- a/erpnext/docs/user/manual/en/selling/articles/adding-margin.md
+++ b/erpnext/docs/user/manual/en/selling/articles/adding-margin.md
@@ -1,7 +1,7 @@
#Adding Margin
User Can apply the margin on Quotation Item and Sales Order Item using following two options.
-1)Price Rule: With the help of this method user can apply the margin on Quotation and Sales Order based on condition. you can find the section margin on pricing rule where a user has to select the type of margin whether it is Percentage or Amount and rate or amount. The system will apply the margin on quotation item and sales order item if pricing rule is enabled.
+1)Price Rule: With the help of this method user can apply the margin on Quotation and Sales Order based on condition. You can find the section margin on pricing rule where a user has to select the type of margin whether it is Percentage or Amount and Rate or Amount. The system will apply the margin on quotation item and sales order item if pricing rule is enabled.
To setup Pricing Rule, go to:
diff --git a/erpnext/docs/user/manual/en/setting-up/integration-services/ldap-integration.md b/erpnext/docs/user/manual/en/setting-up/integration-services/ldap-integration.md
index 34678a9..e12ed1c 100644
--- a/erpnext/docs/user/manual/en/setting-up/integration-services/ldap-integration.md
+++ b/erpnext/docs/user/manual/en/setting-up/integration-services/ldap-integration.md
@@ -4,16 +4,16 @@
By settings up LDAP service, you able to login to ERPNext account by using LDAP credentials.
-####Step 1: Create Razorpay service
+####Step 1: Create LDAP service
`Explore > Setup > Integration Service`
Make a new Integration Service and select `LDAP` as a service from dropdown then save the document.
After saving a document, click on `LDAP Settings` button, to setup service.
-####Step 2: Setup ldap service
+####Step 2: Setup LDAP service
-To enable ldap service, you need to configure parameters like LDAP Server Url, Organizational Unit, Base Distinguished Name (DN) and Password for Base DN
+To enable ldap service, you need to configure parameters like LDAP Server Url, Organizational Unit, UID, Base Distinguished Name (DN) and Password for Base DN
<img class="screenshot" alt="LDAP Settings" src="{{docs_base_url}}/assets/img/setup/integration-service/ldap_settings.png">
@@ -21,4 +21,4 @@
After setting up credentials on LDAP Settings, go back to LDAP Service record and enable it.
While enabling, it will validate LDAP details and on successful validation, it will enables LDAP login option.
-<img class="screenshot" alt="LOGIN via LDAP" src="{{docs_base_url}}/assets/img/setup/integration-service/login_via_ldap.png">
\ No newline at end of file
+<img class="screenshot" alt="LOGIN via LDAP" src="{{docs_base_url}}/assets/img/setup/integration-service/login_via_ldap.png">
diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.json b/erpnext/hr/doctype/appraisal_template/appraisal_template.json
index 22ee3dd..97402ac 100644
--- a/erpnext/hr/doctype/appraisal_template/appraisal_template.json
+++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.json
@@ -3,16 +3,19 @@
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:kra_title",
+ "beta": 0,
"creation": "2012-07-03 13:30:39",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
+ "editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "kra_title",
"fieldtype": "Data",
"hidden": 0,
@@ -20,6 +23,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Appraisal Template Title",
"length": 0,
"no_copy": 0,
@@ -29,6 +33,7 @@
"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,
@@ -39,6 +44,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
@@ -46,6 +52,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
@@ -56,6 +63,7 @@
"print_hide_if_no_value": 0,
"print_width": "300px",
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -67,6 +75,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "goals",
"fieldtype": "Table",
"hidden": 0,
@@ -74,6 +83,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Goals",
"length": 0,
"no_copy": 0,
@@ -84,6 +94,7 @@
"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,
@@ -93,15 +104,16 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "fa fa-file-text",
+ "icon": "icon-file-text",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-04-13 01:49:21.815151",
+ "modified": "2016-12-13 12:37:56.937023",
"modified_by": "Administrator",
"module": "HR",
"name": "Appraisal Template",
@@ -117,6 +129,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -126,8 +139,30 @@
"share": 1,
"submit": 0,
"write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 0,
+ "delete": 0,
+ "email": 0,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 0,
+ "read": 1,
+ "report": 0,
+ "role": "Employee",
+ "set_user_permissions": 0,
+ "share": 0,
+ "submit": 0,
+ "write": 0
}
],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_order": "DESC",
diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
index b8e70e2..81455f1 100644
--- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
+++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.py
@@ -56,6 +56,7 @@
hour = frappe.utils.nowtime().split(':')[0]
frappe.db.sql('delete from `tabDaily Work Summary`')
frappe.db.sql('delete from `tabEmail Queue`')
+ frappe.db.sql('delete from `tabEmail Queue Recipient`')
frappe.db.sql('delete from `tabCommunication`')
# setup email to trigger at this our
@@ -75,6 +76,6 @@
employees = frappe.get_all('Employee', fields = ['user_id'],
filters=dict(company='_Test Company', status='Active'))
- emails = frappe.get_all('Email Queue', fields=['recipient', 'message', 'message_id'])
+ emails = frappe.db.sql("""select r.recipient, q.message, q.message_id from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r where q.name = r.parent""", as_dict=1)
return settings, employees, emails
\ No newline at end of file
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 4cc2e10..5c836df 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -16,6 +16,7 @@
class OverlapError(frappe.ValidationError): pass
class InvalidLeaveApproverError(frappe.ValidationError): pass
class LeaveApproverIdentityError(frappe.ValidationError): pass
+class AttendanceAlreadyMarkedError(frappe.ValidationError): pass
from frappe.model.document import Document
class LeaveApplication(Document):
@@ -100,16 +101,16 @@
def validate_salary_processed_days(self):
if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
return
-
+
last_processed_pay_slip = frappe.db.sql("""
select start_date, end_date from `tabSalary Slip`
where docstatus = 1 and employee = %s
- and ((%s between start_date and end_date) or (%s between start_date and end_date))
+ and ((%s between start_date and end_date) or (%s between start_date and end_date))
order by modified desc limit 1
""",(self.employee, self.to_date, self.from_date))
if last_processed_pay_slip:
- frappe.throw(_("Salary already processed for period between {0} and {1}, Leave application period cannot be between this date range.").format(formatdate(last_processed_pay_slip[0][0]),
+ frappe.throw(_("Salary already processed for period between {0} and {1}, Leave application period cannot be between this date range.").format(formatdate(last_processed_pay_slip[0][0]),
formatdate(last_processed_pay_slip[0][1])))
@@ -213,13 +214,14 @@
elif self.docstatus==1 and len(leave_approvers) and self.leave_approver != frappe.session.user:
frappe.throw(_("Only the selected Leave Approver can submit this Leave Application"),
LeaveApproverIdentityError)
-
+
def validate_attendance(self):
attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s and (att_date between %s and %s)
and docstatus = 1""",
(self.employee, self.from_date, self.to_date))
if attendance:
- frappe.throw(_("Attendance for employee {0} is already marked for this day").format(self.employee))
+ frappe.throw(_("Attendance for employee {0} is already marked for this day").format(self.employee),
+ AttendanceAlreadyMarkedError)
def notify_employee(self, status):
employee = frappe.get_doc("Employee", self.employee)
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.js b/erpnext/hr/doctype/process_payroll/process_payroll.js
index 3265f88..0ad8cec 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.js
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.js
@@ -3,21 +3,24 @@
frappe.ui.form.on("Process Payroll", {
onload: function(frm) {
- frm.doc.posting_date = frm.doc.start_date = frm.doc.end_date = frappe.datetime.nowdate()
+ frm.doc.posting_date = frappe.datetime.nowdate();
+ frm.doc.start_date = '';
+ frm.doc.end_date = '';
+ frm.doc.payroll_frequency = '';
},
refresh: function(frm) {
frm.disable_save();
},
-
+
payroll_frequency: function(frm) {
frm.trigger("set_start_end_dates");
},
-
+
start_date: function(frm) {
frm.trigger("set_start_end_dates");
},
-
+
end_date: function(frm) {
frm.trigger("set_start_end_dates");
},
@@ -25,20 +28,19 @@
payment_account: function(frm) {
frm.toggle_display(['make_bank_entry'], (frm.doc.payment_account!="" && frm.doc.payment_account!="undefined"));
},
-
+
set_start_end_dates: function(frm) {
if (!frm.doc.salary_slip_based_on_timesheet){
frappe.call({
method:'erpnext.hr.doctype.process_payroll.process_payroll.get_start_end_dates',
args:{
payroll_frequency: frm.doc.payroll_frequency,
- start_date: frm.doc.start_date,
- end_date: frm.doc.end_date
+ start_date: frm.doc.start_date || frm.doc.posting_date
},
callback: function(r){
if (r.message){
frm.set_value('start_date', r.message.start_date);
- frm.set_value('end_date', r.message.end_date);
+ frm.set_value('end_date', r.message.end_date);
}
}
})
@@ -75,7 +77,7 @@
if (r.message)
cur_frm.cscript.display_activity_log(r.message);
}
- return $c('runserverobj', args={'method':'create_sal_slip','docs':doc},callback);
+ return $c('runserverobj', args={'method':'create_salary_slips','docs':doc},callback);
}
cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
@@ -94,7 +96,7 @@
cur_frm.cscript.display_activity_log(r.message);
}
- return $c('runserverobj', args={'method':'submit_salary_slip','docs':doc},callback);
+ return $c('runserverobj', args={'method':'submit_salary_slips','docs':doc},callback);
});
}
@@ -111,15 +113,15 @@
title: __("Bank Transaction Reference"),
fields: [
{
- "label": __("Reference Number"),
+ "label": __("Reference Number"),
"fieldname": "reference_number",
- "fieldtype": "Data",
+ "fieldtype": "Data",
"reqd": 1
},
{
- "label": __("Reference Date"),
+ "label": __("Reference Date"),
"fieldname": "reference_date",
- "fieldtype": "Date",
+ "fieldtype": "Date",
"reqd": 1,
"default": get_today()
}
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.json b/erpnext/hr/doctype/process_payroll/process_payroll.json
index 8e0d395..7d84910 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.json
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.json
@@ -127,7 +127,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "Monthly",
+ "default": "",
"depends_on": "eval:doc.salary_slip_based_on_timesheet == 0",
"fieldname": "payroll_frequency",
"fieldtype": "Select",
@@ -352,7 +352,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "Today",
+ "default": "",
"fieldname": "start_date",
"fieldtype": "Date",
"hidden": 0,
@@ -408,7 +408,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "Today",
+ "default": "",
"fieldname": "end_date",
"fieldtype": "Date",
"hidden": 0,
@@ -722,7 +722,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-11-26 01:14:51.691057",
+ "modified": "2016-12-14 01:48:22.326326",
"modified_by": "Administrator",
"module": "HR",
"name": "Process Payroll",
diff --git a/erpnext/hr/doctype/process_payroll/process_payroll.py b/erpnext/hr/doctype/process_payroll/process_payroll.py
index 97e3876..45030cb 100644
--- a/erpnext/hr/doctype/process_payroll/process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/process_payroll.py
@@ -5,15 +5,11 @@
import frappe
from frappe.utils import cint, flt, nowdate, add_days, getdate
from frappe import _
-import collections
-from collections import defaultdict
-from calendar import monthrange
from erpnext.accounts.utils import get_fiscal_year
from frappe.model.document import Document
class ProcessPayroll(Document):
-
def get_emp_list(self):
"""
Returns list of active employees based on selected criteria
@@ -21,24 +17,33 @@
"""
cond = self.get_filter_condition()
cond += self.get_joining_releiving_condition()
-
-
- struct_cond = ''
+
+
+ condition = ''
if self.payroll_frequency:
- struct_cond = """and payroll_frequency = '%(payroll_frequency)s'""" % {"payroll_frequency": self.payroll_frequency}
-
+ condition = """and payroll_frequency = '%(payroll_frequency)s'""" % {"payroll_frequency": self.payroll_frequency}
+
sal_struct = frappe.db.sql("""
- select name from `tabSalary Structure`
- where docstatus != 2 and is_active = 'Yes' and company = %(company)s and
- ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s""",
+ select
+ name from `tabSalary Structure`
+ where
+ docstatus != 2 and
+ is_active = 'Yes'
+ and company = %(company)s and
+ ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
+ {condition}""".format(condition=condition),
{"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
-
+
if sal_struct:
cond += "and t2.parent IN %(sal_struct)s "
emp_list = frappe.db.sql("""
- select t1.name
- from `tabEmployee` t1, `tabSalary Structure Employee` t2
- where t1.docstatus!=2 and t1.name = t2.employee
+ select
+ t1.name
+ from
+ `tabEmployee` t1, `tabSalary Structure Employee` t2
+ where
+ t1.docstatus!=2
+ and t1.name = t2.employee
%s """% cond, {"sal_struct": sal_struct})
return emp_list
@@ -67,7 +72,7 @@
if not self.get(fieldname):
frappe.throw(_("Please set {0}").format(self.meta.get_label(fieldname)))
- def create_sal_slip(self):
+ def create_salary_slips(self):
"""
Creates salary slip for selected employees if already not created
"""
@@ -77,31 +82,26 @@
ss_list = []
if emp_list:
for emp in emp_list:
- if not frappe.db.sql("""select name from `tabSalary Slip`
- where docstatus!= 2 and employee = %s and start_date >= %s and end_date <= %s and company = %s
+ if not frappe.db.sql("""select
+ name from `tabSalary Slip`
+ where
+ docstatus!= 2 and
+ employee = %s and
+ start_date >= %s and
+ end_date <= %s and
+ company = %s
""", (emp[0], self.start_date, self.end_date, self.company)):
- if self.payroll_frequency == "Monthly" or self.payroll_frequency == '':
- ss = frappe.get_doc({
- "doctype": "Salary Slip",
- "salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
- "employee": emp[0],
- "employee_name": frappe.get_value("Employee", {"name":emp[0]}, "employee_name"),
- "company": self.company,
- "posting_date": self.posting_date,
- "payroll_frequency": self.payroll_frequency
- })
- else:
- ss = frappe.get_doc({
- "doctype": "Salary Slip",
- "salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
- "start_date": self.start_date,
- "end_date": self.end_date,
- "employee": emp[0],
- "employee_name": frappe.get_value("Employee", {"name":emp[0]}, "employee_name"),
- "company": self.company,
- "posting_date": self.posting_date,
- "payroll_frequency": self.payroll_frequency
- })
+ ss = frappe.get_doc({
+ "doctype": "Salary Slip",
+ "salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
+ "payroll_frequency": self.payroll_frequency,
+ "start_date": self.start_date,
+ "end_date": self.end_date,
+ "employee": emp[0],
+ "employee_name": frappe.get_value("Employee", {"name":emp[0]}, "employee_name"),
+ "company": self.company,
+ "posting_date": self.posting_date
+ })
ss.insert()
ss_list.append(ss.name)
return self.create_log(ss_list)
@@ -120,16 +120,16 @@
Returns list of salary slips based on selected criteria
"""
cond = self.get_filter_condition()
-
+
ss_list = frappe.db.sql("""
select t1.name, t1.salary_structure from `tabSalary Slip` t1
- where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s
+ where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s
and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s %s
""" % ('%s', '%s', '%s','%s', cond), (ss_status, self.start_date, self.end_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
return ss_list
- def submit_salary_slip(self):
+ def submit_salary_slips(self):
"""
Submit all salary slips based on selected criteria
"""
@@ -144,12 +144,11 @@
else:
try:
ss_obj.submit()
- except Exception,e:
+ except frappe.ValidationError:
not_submitted_ss.append(ss[0])
return self.create_submit_log(ss_list, not_submitted_ss)
-
def create_submit_log(self, all_ss, not_submitted_ss):
log = ''
if not all_ss:
@@ -167,12 +166,9 @@
log += """
<b>Not Submitted Salary Slips: </b>\
<br><br> %s <br><br> \
- Reason: <br>\
- May be net pay is less than 0 <br>
- May be company email id specified in employee master is not valid. <br> \
- Please mention correct email id in employee master or if you don't want to \
- send mail, uncheck 'Send Email' checkbox. <br>\
- Then try to submit Salary Slip again.
+ Possible reasons: <br>\
+ 1. Net pay is less than 0 <br>
+ 2. Company email id specified in employee master is not valid. <br> \
"""% ('<br>'.join(not_submitted_ss))
return log
@@ -191,42 +187,42 @@
""" % ('%s', '%s', cond), (self.start_date, self.end_date))
return flt(tot[0][0])
-
+
def get_salary_component_account(self, salary_component):
account = frappe.db.get_value("Salary Component Account",
{"parent": salary_component, "company": self.company}, "default_account")
-
+
if not account:
frappe.throw(_("Please set default account in Salary Component {0}")
.format(salary_component))
-
- return account
-
+
+ return account
+
def get_salary_components(self, component_type):
salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
if salary_slips:
salary_components = frappe.db.sql("""select salary_component, amount, parentfield
from `tabSalary Detail` where parentfield = '%s' and parent in (%s)""" %
- (component_type, ', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=True)
+ (component_type, ', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=True)
return salary_components
-
+
def get_salary_component_total(self, component_type = None):
salary_components = self.get_salary_components(component_type)
if salary_components:
component_dict = {}
- for item in salary_components:
+ for item in salary_components:
component_dict[item['salary_component']] = component_dict.get(item['salary_component'], 0) + item['amount']
account_details = self.get_account(component_dict = component_dict)
return account_details
-
+
def get_account(self, component_dict = None):
account_dict = {}
for s, a in component_dict.items():
account = self.get_salary_component_account(s)
- account_dict[account] = account_dict.get(account, 0) + a
+ account_dict[account] = account_dict.get(account, 0) + a
return account_dict
-
-
+
+
def make_journal_entry(self, reference_number = None, reference_date = None):
self.check_permission('write')
earnings = self.get_salary_component_total(component_type = "earnings") or {}
@@ -240,7 +236,7 @@
self.end_date)
journal_entry.company = self.company
journal_entry.posting_date = nowdate()
-
+
account_amt_list = []
adjustment_amt = 0
for acc, amt in earnings.items():
@@ -258,7 +254,7 @@
account_amt_list.append({
"account": self.payment_account,
"credit_in_account_currency": adjustment_amt
- })
+ })
journal_entry.set("accounts", account_amt_list)
journal_entry.cheque_no = reference_number
journal_entry.cheque_date = reference_date
@@ -271,24 +267,60 @@
except Exception, e:
frappe.msgprint(e)
return self.create_jv_log(jv_name)
-
+
def create_jv_log(self, jv_name):
log = "<p>" + _("No submitted Salary Slip found") + "</p>"
if jv_name:
log = "<b>" + _("Journal Entry Submitted") + "</b>\
%s" % '<br>''<a href="#Form/Journal Entry/{0}">{0}</a>'.format(jv_name)
- return log
-
+ return log
+
def update_salary_slip_status(self, jv_name = None):
ss_list = self.get_sal_slip_list(ss_status=1)
for ss in ss_list:
ss_obj = frappe.get_doc("Salary Slip",ss[0])
frappe.db.set_value("Salary Slip", ss_obj.name, "status", "Paid")
- frappe.db.set_value("Salary Slip", ss_obj.name, "journal_entry", jv_name)
+ frappe.db.set_value("Salary Slip", ss_obj.name, "journal_entry", jv_name)
+
+ def set_start_end_dates(self):
+ self.update(get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date))
@frappe.whitelist()
+def get_start_end_dates(payroll_frequency, start_date=None):
+ '''Returns dict of start and end dates for given payroll frequency based on start_date'''
+ if not payroll_frequency:
+ frappe.throw(_("Please set Payroll Frequency first"))
+
+ if payroll_frequency == "Monthly" or payroll_frequency == "Bimonthly":
+ fiscal_year = get_fiscal_year(start_date)[0]
+ month = "%02d" % getdate(start_date).month
+ m = get_month_details(fiscal_year, month)
+ if payroll_frequency == "Bimonthly":
+ if getdate(start_date).day <= 15:
+ start_date = m['month_start_date']
+ end_date = m['month_mid_end_date']
+ else:
+ start_date = m['month_mid_start_date']
+ end_date = m['month_end_date']
+ else:
+ start_date = m['month_start_date']
+ end_date = m['month_end_date']
+
+ if payroll_frequency == "Weekly":
+ end_date = add_days(start_date, 6)
+
+ if payroll_frequency == "Fortnightly":
+ end_date = add_days(start_date, 13)
+
+ if payroll_frequency == "Daily":
+ end_date = start_date
+
+ return frappe._dict({
+ 'start_date': start_date, 'end_date': end_date
+ })
+
def get_month_details(year, month):
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
if ysd:
@@ -312,32 +344,3 @@
})
else:
frappe.throw(_("Fiscal Year {0} not found").format(year))
-
-@frappe.whitelist()
-def get_start_end_dates(payroll_frequency, start_date, end_date):
- if payroll_frequency == "Monthly" or payroll_frequency == "Bimonthly":
- fiscal_year = get_fiscal_year(start_date)[0] or get_fiscal_year(end_date)[0]
- month = "%02d" % getdate(start_date).month or "%02d" % getdate(end_date).month
- m = get_month_details(fiscal_year, month)
- if payroll_frequency == "Bimonthly":
- if getdate(start_date).day <= 15:
- start_date = m['month_start_date']
- end_date = m['month_mid_end_date']
- else:
- start_date = m['month_mid_start_date']
- end_date = m['month_end_date']
- else:
- start_date = m['month_start_date']
- end_date = m['month_end_date']
-
- if payroll_frequency == "Weekly":
- end_date = add_days(start_date, 6)
-
- if payroll_frequency == "Fortnightly":
- end_date = add_days(start_date, 13)
-
- if payroll_frequency == "Daily":
- end_date = start_date
- return frappe._dict({
- 'start_date': start_date, 'end_date': end_date
- })
\ No newline at end of file
diff --git a/erpnext/hr/doctype/process_payroll/test_process_payroll.py b/erpnext/hr/doctype/process_payroll/test_process_payroll.py
index 5167365..2eb1c6b 100644
--- a/erpnext/hr/doctype/process_payroll/test_process_payroll.py
+++ b/erpnext/hr/doctype/process_payroll/test_process_payroll.py
@@ -26,8 +26,8 @@
process_payroll.payment_account = payment_account
process_payroll.posting_date = nowdate()
process_payroll.payroll_frequency = "Monthly"
- process_payroll.create_sal_slip()
- process_payroll.submit_salary_slip()
+ process_payroll.create_salary_slips()
+ process_payroll.submit_salary_slips()
if process_payroll.get_sal_slip_list(ss_status = 1):
r = process_payroll.make_journal_entry(reference_number=random_string(10),reference_date=nowdate())
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json
index 062f41f..e558d73 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.json
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.json
@@ -44,6 +44,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
@@ -421,7 +422,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "Today",
+ "default": "",
"fieldname": "start_date",
"fieldtype": "Date",
"hidden": 0,
@@ -450,7 +451,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "Today",
+ "default": "",
"depends_on": "",
"fieldname": "end_date",
"fieldtype": "Date",
@@ -1361,7 +1362,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-12-08 12:03:31.602913",
+ "modified": "2016-12-14 08:26:31.400930",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index ffd1136..eeec6e8 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -4,18 +4,15 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import add_days, cint, cstr, flt, getdate, nowdate, rounded, date_diff, money_in_words
+from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words
from frappe.model.naming import make_autoname
from frappe import msgprint, _
-from erpnext.accounts.utils import get_fiscal_year
from erpnext.setup.utils import get_company_currency
-from erpnext.hr.doctype.process_payroll.process_payroll import get_month_details, get_start_end_dates
+from erpnext.hr.doctype.process_payroll.process_payroll import get_start_end_dates
from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
from erpnext.utilities.transaction_base import TransactionBase
-from datetime import timedelta
-
class SalarySlip(TransactionBase):
def autoname(self):
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
@@ -36,7 +33,7 @@
company_currency = get_company_currency(self.company)
self.total_in_words = money_in_words(self.rounded_total, company_currency)
-
+
if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"):
max_working_hours = frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet")
if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)):
@@ -46,7 +43,7 @@
def validate_dates(self):
if date_diff(self.end_date, self.start_date) < 0:
frappe.throw(_("To date cannot be before From date"))
-
+
def calculate_component_amounts(self):
if not getattr(self, '_salary_structure_doc', None):
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
@@ -54,17 +51,17 @@
data = self.get_data_for_eval()
for key in ('earnings', 'deductions'):
for struct_row in self._salary_structure_doc.get(key):
- amount = self.eval_condition_and_formula(struct_row, data)
+ amount = self.eval_condition_and_formula(struct_row, data)
if amount:
self.update_component_row(struct_row, amount, key)
-
-
+
+
def update_component_row(self, struct_row, amount, key):
component_row = None
for d in self.get(key):
if d.salary_component == struct_row.salary_component:
component_row = d
-
+
if not component_row:
self.append(key, {
'amount': amount,
@@ -74,12 +71,12 @@
})
else:
component_row.amount = amount
-
+
def eval_condition_and_formula(self, d, data):
try:
if d.condition:
if not eval(d.condition, None, data):
- return None
+ return None
amount = d.amount
if d.amount_based_on_formula:
if d.formula:
@@ -87,23 +84,23 @@
if amount:
data[d.abbr] = amount
return amount
-
+
except NameError as err:
frappe.throw(_("Name error: {0}".format(err)))
except SyntaxError as err:
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
except:
frappe.throw(_("Error in formula or condition"))
- raise
-
+ raise
+
def get_data_for_eval(self):
'''Returns data for evaluating formula'''
data = frappe._dict()
-
+
for d in self._salary_structure_doc.employees:
if d.employee == self.employee:
data.base, data.variable = d.base, d.variable
-
+
data.update(frappe.get_doc("Employee", self.employee).as_dict())
data.update(self.as_dict())
@@ -111,9 +108,9 @@
salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"])
for salary_component in salary_components:
data[salary_component.salary_component_abbr] = 0
-
+
return data
-
+
def get_emp_and_leave_details(self):
'''First time, load all the components from salary structure'''
@@ -148,16 +145,16 @@
})
def get_date_details(self):
- date_details = get_start_end_dates(self.payroll_frequency, self.start_date, self.end_date)
- self.start_date = date_details.start_date
- self.end_date = date_details.end_date
-
+ if not self.end_date:
+ date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date)
+ self.start_date = date_details.start_date
+ self.end_date = date_details.end_date
def check_sal_struct(self, joining_date, relieving_date):
cond = ''
if self.payroll_frequency:
cond = """and payroll_frequency = '%(payroll_frequency)s'""" % {"payroll_frequency": self.payroll_frequency}
-
+
st_name = frappe.db.sql("""select parent from `tabSalary Structure Employee`
where employee=%s
and parent in (select name from `tabSalary Structure`
@@ -165,7 +162,7 @@
and (from_date <= %s or from_date <= %s)
and (to_date is null or to_date >= %s or to_date >= %s) %s)
"""% ('%s', '%s', '%s','%s','%s', cond),(self.employee, self.start_date, joining_date, self.end_date, relieving_date))
-
+
if st_name:
if len(st_name) > 1:
frappe.msgprint(_("Multiple active Salary Structures found for employee {0} for the given dates")
@@ -174,7 +171,7 @@
else:
self.salary_structure = None
frappe.msgprint(_("No active or default Salary Structure found for employee {0} for the given dates")
- .format(self.employee), title=_('Salary Structure Missing'))
+ .format(self.employee), title=_('Salary Structure Missing'))
def pull_sal_struct(self):
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
@@ -185,16 +182,13 @@
self.hour_rate = self._salary_structure_doc.hour_rate
self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
self.add_earning_for_hourly_wages(self._salary_structure_doc.salary_component)
-
-
-
+
def process_salary_structure(self):
'''Calculate salary after salary structure details have been updated'''
self.get_date_details()
self.pull_emp_details()
self.get_leave_details()
self.calculate_net_pay()
-
def add_earning_for_hourly_wages(self, salary_component):
default_type = False
@@ -233,10 +227,10 @@
lwp = actual_lwp
elif lwp != actual_lwp:
frappe.msgprint(_("Leave Without Pay does not match with approved Leave Application records"))
-
+
self.total_working_days = working_days
self.leave_without_pay = lwp
-
+
payment_days = flt(self.get_payment_days(joining_date, relieving_date)) - flt(lwp)
self.payment_days = payment_days > 0 and payment_days or 0
@@ -278,7 +272,7 @@
holidays = [cstr(i) for i in holidays]
return holidays
-
+
def calculate_lwp(self, holidays, working_days):
lwp = 0
holidays = "','".join(holidays)
@@ -297,7 +291,7 @@
""".format(holidays), {"employee": self.employee, "dt": dt})
if leave:
lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
- return lwp
+ return lwp
def check_existing(self):
if not self.salary_slip_based_on_timesheet:
@@ -371,7 +365,7 @@
timesheet.flags.ignore_validate_update_after_submit = True
timesheet.set_status()
timesheet.save()
-
+
def set_status(self, status=None):
'''Get and update status'''
if not status:
@@ -387,7 +381,7 @@
status = "Paid"
elif self.docstatus == 2:
status = "Cancelled"
- return status
+ return status
def unlink_ref_doc_from_salary_slip(ref_no):
linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip_list.js b/erpnext/hr/doctype/salary_slip/salary_slip_list.js
index 17f13d6..33d5bd7 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip_list.js
+++ b/erpnext/hr/doctype/salary_slip/salary_slip_list.js
@@ -1,3 +1,3 @@
frappe.listview_settings['Salary Slip'] = {
- add_fields: ["employee", "employee_name", "fiscal_year", "month"],
+ add_fields: ["employee", "employee_name"],
};
diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
index 4025db7..dfa704a 100644
--- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
@@ -16,7 +16,7 @@
def setUp(self):
make_earning_salary_component(["Basic Salary", "Allowance", "HRA"])
make_deduction_salary_component(["Professional Tax", "TDS"])
-
+
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
frappe.db.sql("delete from `tab%s`" % dt)
@@ -65,7 +65,7 @@
self.assertEquals(ss.deductions[1].amount, 2500)
self.assertEquals(ss.gross_pay, 10500)
self.assertEquals(ss.net_pay, 3000)
-
+
def test_payment_days(self):
no_of_days = self.get_no_of_days()
# Holidays not included in working days
@@ -86,10 +86,13 @@
date_of_joining = getdate(nowdate())
relieving_date = getdate(nowdate())
- frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", date_of_joining)
- frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
- frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
-
+ frappe.db.set_value("Employee", frappe.get_value("Employee",
+ {"employee_name":"test_employee@salary.com"}, "name"), "date_of_joining", date_of_joining)
+ frappe.db.set_value("Employee", frappe.get_value("Employee",
+ {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
+ frappe.db.set_value("Employee", frappe.get_value("Employee",
+ {"employee_name":"test_employee@salary.com"}, "name"), "status", "Active")
+
ss = frappe.get_doc("Salary Slip",
self.make_employee_salary_slip("test_employee@salary.com", "Monthly"))
@@ -101,7 +104,7 @@
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", relieving_date)
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Left")
ss.save()
-
+
self.assertEquals(ss.total_working_days, no_of_days[0])
self.assertEquals(ss.payment_days, getdate(relieving_date).day)
@@ -190,7 +193,7 @@
"from_date": fiscal_year[1],
"to_date": fiscal_year[2],
"weekly_off": "Sunday"
- }).insert()
+ }).insert()
holiday_list.get_weekly_off_dates()
holiday_list.save()
@@ -198,12 +201,11 @@
employee = frappe.db.get_value("Employee", {"user_id": user})
salary_structure = make_salary_structure(payroll_frequency + " Salary Structure Test for Salary Slip", payroll_frequency, employee)
salary_slip = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})})
-
+
if not salary_slip:
salary_slip = make_salary_slip(salary_structure, employee = employee)
salary_slip.employee_name = frappe.get_value("Employee", {"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name")
salary_slip.payroll_frequency = payroll_frequency
- salary_slip.start_date = nowdate()
salary_slip.posting_date = nowdate()
salary_slip.insert()
# salary_slip.submit()
@@ -220,9 +222,10 @@
def get_no_of_days(self):
no_of_days_in_month = calendar.monthrange(getdate(nowdate()).year,
- getdate(nowdate()).month)
+ getdate(nowdate()).month)
no_of_holidays_in_month = len([1 for i in calendar.monthcalendar(getdate(nowdate()).year,
- getdate(nowdate()).month) if i[6] != 0])
+ getdate(nowdate()).month) if i[6] != 0])
+
return [no_of_days_in_month[1], no_of_holidays_in_month]
@@ -280,7 +283,7 @@
}
]
-def get_earnings_component():
+def get_earnings_component():
return [
{
"salary_component": 'Basic Salary',
@@ -310,8 +313,8 @@
"idx": 4
},
]
-
-def get_deductions_component():
+
+def get_deductions_component():
return [
{
"salary_component": 'Professional Tax',
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.json b/erpnext/hr/doctype/salary_structure/salary_structure.json
index 5c0a635..8db8e93 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.json
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.json
@@ -100,7 +100,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "",
+ "default": "Monthly",
"depends_on": "eval:(!doc.salary_slip_based_on_timesheet)",
"fieldname": "payroll_frequency",
"fieldtype": "Select",
@@ -894,7 +894,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-12-07 14:57:22.083825",
+ "modified": "2016-12-14 02:02:10.848614",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure",
diff --git a/erpnext/hr/doctype/training_event/__init__.py b/erpnext/hr/doctype/training_event/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/training_event/__init__.py
diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py
new file mode 100644
index 0000000..03416ee
--- /dev/null
+++ b/erpnext/hr/doctype/training_event/test_training_event.py
@@ -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('Training Event')
+
+class TestTrainingEvent(unittest.TestCase):
+ pass
diff --git a/erpnext/hr/doctype/training_event/training_event.js b/erpnext/hr/doctype/training_event/training_event.js
new file mode 100644
index 0000000..ebe0c79
--- /dev/null
+++ b/erpnext/hr/doctype/training_event/training_event.js
@@ -0,0 +1,21 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Training Event', {
+ refresh: function(frm) {
+ if(!frm.doc.__islocal) {
+ frm.add_custom_button(__("Training Result"), function() {
+ frappe.route_options = {
+ training_event: frm.doc.name
+ }
+ frappe.set_route("List", "Training Result");
+ });
+ frm.add_custom_button(__("Training Feedback"), function() {
+ frappe.route_options = {
+ training_event: frm.doc.name
+ }
+ frappe.set_route("List", "Training Feedback");
+ });
+ }
+ }
+});
diff --git a/erpnext/hr/doctype/training_event/training_event.json b/erpnext/hr/doctype/training_event/training_event.json
new file mode 100644
index 0000000..168943d
--- /dev/null
+++ b/erpnext/hr/doctype/training_event/training_event.json
@@ -0,0 +1,679 @@
+{
+ "allow_copy": 0,
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "field:event_name",
+ "beta": 0,
+ "creation": "2016-08-08 04:53:58.355206",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "event_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": "Event 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": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "event_status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Event Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Scheduled\nCompleted\nCancelled",
+ "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": "type",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Type",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Seminar\nTheory\nWorkshop\nConference\nExam\nInternet",
+ "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_4",
+ "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": "trainer_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": "Trainer 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": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "trainer_email",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Trainer Email",
+ "length": 0,
+ "no_copy": 0,
+ "options": "",
+ "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_7",
+ "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": "supplier",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Supplier",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Supplier",
+ "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": "contact_number",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Contact Number",
+ "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": "section_break_9",
+ "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": "course",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "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": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "location",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Location",
+ "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": "column_break_12",
+ "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": "start_time",
+ "fieldtype": "Datetime",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Start Time",
+ "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": "end_time",
+ "fieldtype": "Datetime",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "End Time",
+ "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": "section_break_15",
+ "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": "introduction",
+ "fieldtype": "Text Editor",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Introduction",
+ "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": "section_break_18",
+ "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": "Attendees",
+ "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,
+ "description": "Will send an email about the event to employees with status 'Open'",
+ "fieldname": "send_email",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Send Email",
+ "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": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "employees",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employees",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Training Event Employee",
+ "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": "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": "Training Event",
+ "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 11:46:25.712779",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Training Event",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 1,
+ "apply_user_permissions": 0,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 1,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "search_fields": "event_name",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "event_name",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/training_event/training_event.py b/erpnext/hr/doctype/training_event/training_event.py
new file mode 100644
index 0000000..c9be3dd
--- /dev/null
+++ b/erpnext/hr/doctype/training_event/training_event.py
@@ -0,0 +1,27 @@
+# -*- 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 TrainingEvent(Document):
+ def validate(self):
+ if self.event_status == "Scheduled":
+ self.invite_employee()
+
+ def on_update_after_submit(self):
+ if self.event_status == "Scheduled" and self.send_email:
+ self.invite_employee()
+
+ def invite_employee(self):
+ subject = _("""You are invited for to attend {0} - {1} scheduled from {2} to {3} at {4}."""\
+ .format(self.type, self.event_name, self.start_time, self.end_time, self.location))
+
+ for emp in self.employees:
+ if emp.status== "Open":
+ frappe.sendmail(frappe.db.get_value("Employee", emp.employee, "company_email"), \
+ subject=subject, content= self.introduction)
+ emp.status= "Invited"
\ No newline at end of file
diff --git a/erpnext/hr/doctype/training_event_employee/__init__.py b/erpnext/hr/doctype/training_event_employee/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/training_event_employee/__init__.py
diff --git a/erpnext/hr/doctype/training_event_employee/training_event_employee.json b/erpnext/hr/doctype/training_event_employee/training_event_employee.json
new file mode 100644
index 0000000..575d0e7
--- /dev/null
+++ b/erpnext/hr/doctype/training_event_employee/training_event_employee.json
@@ -0,0 +1,152 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-08-08 05:33:39.965305",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Employee",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Employee",
+ "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": "employee_name",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employee Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "employee.employee_name",
+ "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_3",
+ "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": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "Open",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 1,
+ "options": "Open\nInvited\nConfirmed\nAttended\nWithdrawn",
+ "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 11:43:40.996578",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Training Event Employee",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/training_event_employee/training_event_employee.py b/erpnext/hr/doctype/training_event_employee/training_event_employee.py
new file mode 100644
index 0000000..234e958
--- /dev/null
+++ b/erpnext/hr/doctype/training_event_employee/training_event_employee.py
@@ -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 TrainingEventEmployee(Document):
+ pass
diff --git a/erpnext/hr/doctype/training_feedback/__init__.py b/erpnext/hr/doctype/training_feedback/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/training_feedback/__init__.py
diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.py b/erpnext/hr/doctype/training_feedback/test_training_feedback.py
new file mode 100644
index 0000000..3455998
--- /dev/null
+++ b/erpnext/hr/doctype/training_feedback/test_training_feedback.py
@@ -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('Training Feedback')
+
+class TestTrainingFeedback(unittest.TestCase):
+ pass
diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.js b/erpnext/hr/doctype/training_feedback/training_feedback.js
new file mode 100644
index 0000000..0dea098
--- /dev/null
+++ b/erpnext/hr/doctype/training_feedback/training_feedback.js
@@ -0,0 +1,10 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Training Feedback', {
+ onload: function(frm) {
+ frm.add_fetch("training_event", "course", "course");
+ frm.add_fetch("training_event", "event_name", "event_name");
+ frm.add_fetch("training_event", "trainer_name", "trainer_name");
+ }
+});
\ No newline at end of file
diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.json b/erpnext/hr/doctype/training_feedback/training_feedback.json
new file mode 100644
index 0000000..932d4a7
--- /dev/null
+++ b/erpnext/hr/doctype/training_feedback/training_feedback.json
@@ -0,0 +1,354 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "Feedback.####",
+ "beta": 0,
+ "creation": "2016-08-08 06:35:34.158568",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 1,
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employee",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Employee",
+ "permlevel": 0,
+ "precision": "",
+ "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,
+ "columns": 0,
+ "fieldname": "employee_name",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employee Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "employee.employee_name",
+ "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,
+ "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": 0,
+ "label": "Course",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Course",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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_3",
+ "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,
+ "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": "training_event",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Training Event",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Training Event",
+ "permlevel": 0,
+ "precision": "",
+ "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,
+ "columns": 0,
+ "fieldname": "event_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Event Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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": "trainer_name",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Trainer Name",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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_6",
+ "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,
+ "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": "feedback",
+ "fieldtype": "Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Feedback",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "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,
+ "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": "Training Feedback",
+ "permlevel": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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-11-04 08:53:24.731178",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Training Feedback",
+ "name_case": "",
+ "owner": "Administrator",
+ "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": "HR Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 1,
+ "delete": 0,
+ "email": 1,
+ "export": 1,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Employee",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "employee_name",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.py b/erpnext/hr/doctype/training_feedback/training_feedback.py
new file mode 100644
index 0000000..2a0403b
--- /dev/null
+++ b/erpnext/hr/doctype/training_feedback/training_feedback.py
@@ -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 TrainingFeedback(Document):
+ pass
diff --git a/erpnext/hr/doctype/training_result/__init__.py b/erpnext/hr/doctype/training_result/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/training_result/__init__.py
diff --git a/erpnext/hr/doctype/training_result/test_training_result.py b/erpnext/hr/doctype/training_result/test_training_result.py
new file mode 100644
index 0000000..29ed2a0
--- /dev/null
+++ b/erpnext/hr/doctype/training_result/test_training_result.py
@@ -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('Training Result')
+
+class TestTrainingResult(unittest.TestCase):
+ pass
diff --git a/erpnext/hr/doctype/training_result/training_result.js b/erpnext/hr/doctype/training_result/training_result.js
new file mode 100644
index 0000000..3a3d70a
--- /dev/null
+++ b/erpnext/hr/doctype/training_result/training_result.js
@@ -0,0 +1,32 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Training Result', {
+ onload: function(frm) {
+ if (frm.doc.training_event) {
+ frm.trigger("training_event");
+ }
+ },
+
+ training_event: function(frm) {
+ if (frm.doc.training_event) {
+ frappe.call({
+ method: "erpnext.hr.doctype.training_result.training_result.get_employees",
+ args: {
+ "training_event": frm.doc.training_event
+ },
+ callback: function(r) {
+ frm.set_value("employees" ,"");
+ if (r.message) {
+ $.each(r.message, function(i, d) {
+ var row = frappe.model.add_child(cur_frm.doc, "Training Result Employee", "employees");
+ row.employee = d.employee;
+ row.employee_name = d.employee_name;
+ });
+ }
+ refresh_field("employees");
+ }
+ });
+ }
+ }
+});
diff --git a/erpnext/hr/doctype/training_result/training_result.json b/erpnext/hr/doctype/training_result/training_result.json
new file mode 100644
index 0000000..52b9a16
--- /dev/null
+++ b/erpnext/hr/doctype/training_result/training_result.json
@@ -0,0 +1,171 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 1,
+ "autoname": "TRES.#####",
+ "beta": 0,
+ "creation": "2016-11-04 02:13:48.407576",
+ "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": "training_event",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Training Event",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Training Event",
+ "permlevel": 0,
+ "precision": "",
+ "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": 1
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "section_break_3",
+ "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,
+ "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": "employees",
+ "fieldtype": "Table",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employees",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Training Result Employee",
+ "permlevel": 0,
+ "precision": "",
+ "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,
+ "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": "Training Result",
+ "permlevel": 0,
+ "print_hide": 1,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "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-11-04 08:53:48.597031",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Training Result",
+ "name_case": "",
+ "owner": "Administrator",
+ "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": "HR Manager",
+ "set_user_permissions": 0,
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "training_event",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/training_result/training_result.py b/erpnext/hr/doctype/training_result/training_result.py
new file mode 100644
index 0000000..16b76a7
--- /dev/null
+++ b/erpnext/hr/doctype/training_result/training_result.py
@@ -0,0 +1,23 @@
+# -*- 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 TrainingResult(Document):
+ def on_submit(self):
+ self.send_result()
+
+ def send_result(self):
+ for emp in self.employees:
+ message = "Thank You for attending {0}. You grade is {1}".format(self.training_event, emp.grade)
+ frappe.sendmail(frappe.db.get_value("Employee", emp.employee, "company_email"), \
+ subject=_("{0} Results".format(self.training_event)), \
+ content=message)
+
+@frappe.whitelist()
+def get_employees(training_event):
+ return frappe.get_doc("Training Event", training_event).employees
diff --git a/erpnext/hr/doctype/training_result_employee/__init__.py b/erpnext/hr/doctype/training_result_employee/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/hr/doctype/training_result_employee/__init__.py
diff --git a/erpnext/hr/doctype/training_result_employee/training_result_employee.json b/erpnext/hr/doctype/training_result_employee/training_result_employee.json
new file mode 100644
index 0000000..477a8e6
--- /dev/null
+++ b/erpnext/hr/doctype/training_result_employee/training_result_employee.json
@@ -0,0 +1,254 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-11-04 02:39:12.825569",
+ "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": "employee",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Employee",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Employee",
+ "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,
+ "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,
+ "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": "employee_name",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Employee Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "employee.employee_name",
+ "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,
+ "columns": 0,
+ "depends_on": "",
+ "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,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "hours",
+ "fieldtype": "Float",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Hours",
+ "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": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "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": 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_7",
+ "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,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 1,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "comments",
+ "fieldtype": "Text",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 0,
+ "label": "Comments",
+ "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
+ }
+ ],
+ "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-11-04 03:31:08.624394",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Training Result Employee",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/training_result_employee/training_result_employee.py b/erpnext/hr/doctype/training_result_employee/training_result_employee.py
new file mode 100644
index 0000000..54e2a18
--- /dev/null
+++ b/erpnext/hr/doctype/training_result_employee/training_result_employee.py
@@ -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 TrainingResultEmployee(Document):
+ pass
diff --git a/erpnext/projects/doctype/activity_type/activity_type.json b/erpnext/projects/doctype/activity_type/activity_type.json
index fb6d1b2..65a0fa1 100644
--- a/erpnext/projects/doctype/activity_type/activity_type.json
+++ b/erpnext/projects/doctype/activity_type/activity_type.json
@@ -15,6 +15,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "activity_type",
"fieldtype": "Data",
"hidden": 0,
@@ -22,6 +23,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Activity Type",
"length": 0,
"no_copy": 0,
@@ -29,6 +31,7 @@
"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,
@@ -39,6 +42,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "costing_rate",
"fieldtype": "Currency",
"hidden": 0,
@@ -46,6 +50,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Default Costing Rate",
"length": 0,
"no_copy": 0,
@@ -54,6 +59,7 @@
"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,
@@ -64,6 +70,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
@@ -71,6 +78,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -78,6 +86,7 @@
"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,
@@ -88,6 +97,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "billing_rate",
"fieldtype": "Currency",
"hidden": 0,
@@ -95,6 +105,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Default Billing Rate",
"length": 0,
"no_copy": 0,
@@ -103,6 +114,7 @@
"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,
@@ -112,7 +124,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "fa fa-flag",
+ "icon": "icon-flag",
"idx": 1,
"image_view": 0,
"in_create": 0,
@@ -121,7 +133,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-08-22 08:53:43.680463",
+ "modified": "2016-12-13 12:38:18.218618",
"modified_by": "Administrator",
"module": "Projects",
"name": "Activity Type",
@@ -137,6 +149,7 @@
"export": 1,
"if_owner": 0,
"import": 1,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -157,6 +170,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -166,6 +180,27 @@
"share": 1,
"submit": 0,
"write": 1
+ },
+ {
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 0,
+ "delete": 0,
+ "email": 0,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 0,
+ "read": 1,
+ "report": 0,
+ "role": "Employee",
+ "set_user_permissions": 0,
+ "share": 0,
+ "submit": 0,
+ "write": 0
}
],
"quick_entry": 1,
diff --git a/erpnext/public/js/pos/pos_invoice_list.html b/erpnext/public/js/pos/pos_invoice_list.html
index 6eecfe3..67110a7 100644
--- a/erpnext/public/js/pos/pos_invoice_list.html
+++ b/erpnext/public/js/pos/pos_invoice_list.html
@@ -1,7 +1,7 @@
<div class="row list-row pos-invoice-list" invoice-name = "{{name}}">
- <div class="col-xs-1">{%= sr %}</div>
- <div class="col-xs-3">{%= customer %}</div>
- <div class="col-xs-2 text-left"><span class="indicator {{data.indicator}}">{{ data.status }}</span></div>
- <div class="col-xs-3 text-right">{%= paid_amount %}</div>
- <div class="col-xs-3 text-right">{%= grand_total %}</div>
+ <div class="col-xs-1"><input class="list-delete" type="checkbox"></div>
+ <div class="list-column col-xs-3">{%= customer %}</div>
+ <div class="list-column col-xs-2 text-left"><span class="indicator {{data.indicator}}">{{ data.status }}</span></div>
+ <div class="list-column col-xs-3 text-right">{%= paid_amount %}</div>
+ <div class="list-column col-xs-3 text-right">{%= grand_total %}</div>
</div>
diff --git a/erpnext/schools/doctype/course/course.json b/erpnext/schools/doctype/course/course.json
index cd8362a..6fada19 100644
--- a/erpnext/schools/doctype/course/course.json
+++ b/erpnext/schools/doctype/course/course.json
@@ -219,7 +219,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-11-07 05:28:55.089882",
+ "modified": "2016-08-08 05:26:26.442635",
"modified_by": "Administrator",
"module": "Schools",
"name": "Course",
@@ -246,6 +246,26 @@
"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,
diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.py b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
index 7f1a43c..ab892fb 100644
--- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
@@ -7,15 +7,17 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import get_datetime, get_datetime_str, formatdate
+from frappe.utils import get_datetime_str, formatdate, nowdate
class CurrencyExchange(Document):
- def autoname(self):
- self.name = formatdate(get_datetime_str(self.date),"yyyy-MM-dd") + "-" + self.from_currency + "-" + self.to_currency
- #self.name = self.date + "-" + self.from_currency + "-" + self.to_currency
+ def autoname(self):
+ if not self.date:
+ self.date = nowdate()
+ self.name = '{0}-{1}-{2}'.format(formatdate(get_datetime_str(self.date), "yyyy-MM-dd"),
+ self.from_currency, self.to_currency)
- def validate(self):
- self.validate_value("exchange_rate", ">", 0)
+ def validate(self):
+ self.validate_value("exchange_rate", ">", 0)
- if self.from_currency == self.to_currency:
- frappe.throw(_("From Currency and To Currency cannot be same"))
+ if self.from_currency == self.to_currency:
+ frappe.throw(_("From Currency and To Currency cannot be same"))
diff --git a/erpnext/setup/setup_wizard/install_fixtures.py b/erpnext/setup/setup_wizard/install_fixtures.py
index a80e6d9..1589b3b 100644
--- a/erpnext/setup/setup_wizard/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/install_fixtures.py
@@ -31,8 +31,8 @@
'is_group': 0, 'parent_item_group': _('All Item Groups') },
# salary component
- {'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax')},
- {'doctype': 'Salary Component', 'salary_component': _('Basic'), 'description': _('Basic')},
+ {'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax'), 'type': 'Deduction'},
+ {'doctype': 'Salary Component', 'salary_component': _('Basic'), 'description': _('Basic'), 'type': 'Earning'},
# expense claim type
{'doctype': 'Expense Claim Type', 'name': _('Calls'), 'expense_type': _('Calls')},
@@ -186,9 +186,6 @@
{'doctype': "Print Heading", 'print_heading': _("Credit Note")},
{'doctype': "Print Heading", 'print_heading': _("Debit Note")},
-
- {"doctype": "Salary Component", "salary_component": _("Basic")},
- {"doctype": "Salary Component", "salary_component": _("Income Tax")},
]
from erpnext.setup.setup_wizard.industry_type import get_industry_types
diff --git a/erpnext/utilities/doctype/address/address.py b/erpnext/utilities/doctype/address/address.py
index 1d215cc..2952531 100644
--- a/erpnext/utilities/doctype/address/address.py
+++ b/erpnext/utilities/doctype/address/address.py
@@ -10,6 +10,7 @@
from frappe.model.document import Document
from jinja2 import TemplateSyntaxError
from frappe.utils.user import is_website_user
+from frappe.model.naming import make_autoname
class Address(Document):
def __setup__(self):
@@ -21,7 +22,10 @@
or self.supplier or self.sales_partner or self.lead
if self.address_title:
- self.name = cstr(self.address_title).strip() + "-" + cstr(self.address_type).strip()
+ self.name = (cstr(self.address_title).strip() + "-" + cstr(self.address_type).strip())
+ if frappe.db.exists("Address", self.name):
+ self.name = make_autoname(cstr(self.address_title).strip() + "-" +
+ cstr(self.address_type).strip() + "-.#")
else:
throw(_("Address Title is mandatory."))