mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-26 00:14:50 +00:00
[fix] demo and process payroll for demo (#7224)
* [fix] demo and process payroll for demo * [fix] set payroll dates
This commit is contained in:
@@ -110,7 +110,7 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
|
|||||||
if party_type and party:
|
if party_type and party:
|
||||||
cond.append("""gle.party_type = "%s" and gle.party = "%s" """ %
|
cond.append("""gle.party_type = "%s" and gle.party = "%s" """ %
|
||||||
(frappe.db.escape(party_type), frappe.db.escape(party, percent=False)))
|
(frappe.db.escape(party_type), frappe.db.escape(party, percent=False)))
|
||||||
|
|
||||||
if company:
|
if company:
|
||||||
cond.append("""gle.company = "%s" """ % (frappe.db.escape(company, percent=False)))
|
cond.append("""gle.company = "%s" """ % (frappe.db.escape(company, percent=False)))
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ def get_count_on(account, fieldname, date):
|
|||||||
voucher_type, voucher_no, against_voucher_type, against_voucher
|
voucher_type, voucher_no, against_voucher_type, against_voucher
|
||||||
FROM `tabGL Entry` gle
|
FROM `tabGL Entry` gle
|
||||||
WHERE {0}""".format(" and ".join(cond)), as_dict=True)
|
WHERE {0}""".format(" and ".join(cond)), as_dict=True)
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
for gle in entries:
|
for gle in entries:
|
||||||
if fieldname not in ('invoiced_amount','payables'):
|
if fieldname not in ('invoiced_amount','payables'):
|
||||||
@@ -196,12 +196,12 @@ def get_count_on(account, fieldname, date):
|
|||||||
WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s
|
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),
|
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]
|
{"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
|
outstanding_amount = flt(gle.get(dr_or_cr)) - flt(gle.get(cr_or_dr)) - payment_amount
|
||||||
currency_precision = get_currency_precision() or 2
|
currency_precision = get_currency_precision() or 2
|
||||||
if abs(flt(outstanding_amount)) > 0.1/10**currency_precision:
|
if abs(flt(outstanding_amount)) > 0.1/10**currency_precision:
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
return count
|
return count
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -209,7 +209,7 @@ def add_ac(args=None):
|
|||||||
if not args:
|
if not args:
|
||||||
args = frappe.local.form_dict
|
args = frappe.local.form_dict
|
||||||
args.pop("cmd")
|
args.pop("cmd")
|
||||||
|
|
||||||
ac = frappe.new_doc("Account")
|
ac = frappe.new_doc("Account")
|
||||||
|
|
||||||
if args.get("ignore_permissions"):
|
if args.get("ignore_permissions"):
|
||||||
@@ -220,7 +220,7 @@ def add_ac(args=None):
|
|||||||
|
|
||||||
if not ac.parent_account:
|
if not ac.parent_account:
|
||||||
ac.parent_account = args.get("parent")
|
ac.parent_account = args.get("parent")
|
||||||
|
|
||||||
ac.old_parent = ""
|
ac.old_parent = ""
|
||||||
ac.freeze_account = "No"
|
ac.freeze_account = "No"
|
||||||
if cint(ac.get("is_root")):
|
if cint(ac.get("is_root")):
|
||||||
@@ -252,10 +252,10 @@ def reconcile_against_document(args):
|
|||||||
Cancel JV, Update aginst document, split if required and resubmit jv
|
Cancel JV, Update aginst document, split if required and resubmit jv
|
||||||
"""
|
"""
|
||||||
for d in args:
|
for d in args:
|
||||||
|
|
||||||
check_if_advance_entry_modified(d)
|
check_if_advance_entry_modified(d)
|
||||||
validate_allocated_amount(d)
|
validate_allocated_amount(d)
|
||||||
|
|
||||||
# cancel advance entry
|
# cancel advance entry
|
||||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||||
|
|
||||||
@@ -289,13 +289,13 @@ def check_if_advance_entry_modified(args):
|
|||||||
else:
|
else:
|
||||||
party_account_field = "paid_from" if args.party_type == "Customer" else "paid_to"
|
party_account_field = "paid_from" if args.party_type == "Customer" else "paid_to"
|
||||||
if args.voucher_detail_no:
|
if args.voucher_detail_no:
|
||||||
ret = frappe.db.sql("""select t1.name
|
ret = frappe.db.sql("""select t1.name
|
||||||
from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
|
from `tabPayment Entry` t1, `tabPayment Entry Reference` t2
|
||||||
where
|
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.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 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
|
and t2.allocated_amount = %(unadjusted_amount)s
|
||||||
""".format(party_account_field), args)
|
""".format(party_account_field), args)
|
||||||
else:
|
else:
|
||||||
@@ -367,7 +367,7 @@ def update_reference_in_journal_entry(d, jv_obj):
|
|||||||
# will work as update after submit
|
# will work as update after submit
|
||||||
jv_obj.flags.ignore_validate_update_after_submit = True
|
jv_obj.flags.ignore_validate_update_after_submit = True
|
||||||
jv_obj.save(ignore_permissions=True)
|
jv_obj.save(ignore_permissions=True)
|
||||||
|
|
||||||
def update_reference_in_payment_entry(d, payment_entry):
|
def update_reference_in_payment_entry(d, payment_entry):
|
||||||
reference_details = {
|
reference_details = {
|
||||||
"reference_doctype": d.against_voucher_type,
|
"reference_doctype": d.against_voucher_type,
|
||||||
@@ -377,44 +377,44 @@ def update_reference_in_payment_entry(d, payment_entry):
|
|||||||
"allocated_amount": d.allocated_amount,
|
"allocated_amount": d.allocated_amount,
|
||||||
"exchange_rate": d.exchange_rate
|
"exchange_rate": d.exchange_rate
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.voucher_detail_no:
|
if d.voucher_detail_no:
|
||||||
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
|
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
|
||||||
original_row = existing_row.as_dict().copy()
|
original_row = existing_row.as_dict().copy()
|
||||||
existing_row.update(reference_details)
|
existing_row.update(reference_details)
|
||||||
|
|
||||||
if d.allocated_amount < original_row.allocated_amount:
|
if d.allocated_amount < original_row.allocated_amount:
|
||||||
new_row = payment_entry.append("references")
|
new_row = payment_entry.append("references")
|
||||||
new_row.docstatus = 1
|
new_row.docstatus = 1
|
||||||
for field in reference_details.keys():
|
for field in reference_details.keys():
|
||||||
new_row.set(field, original_row[field])
|
new_row.set(field, original_row[field])
|
||||||
|
|
||||||
new_row.allocated_amount = original_row.allocated_amount - d.allocated_amount
|
new_row.allocated_amount = original_row.allocated_amount - d.allocated_amount
|
||||||
else:
|
else:
|
||||||
new_row = payment_entry.append("references")
|
new_row = payment_entry.append("references")
|
||||||
new_row.docstatus = 1
|
new_row.docstatus = 1
|
||||||
new_row.update(reference_details)
|
new_row.update(reference_details)
|
||||||
|
|
||||||
payment_entry.flags.ignore_validate_update_after_submit = True
|
payment_entry.flags.ignore_validate_update_after_submit = True
|
||||||
payment_entry.setup_party_account_field()
|
payment_entry.setup_party_account_field()
|
||||||
payment_entry.set_missing_values()
|
payment_entry.set_missing_values()
|
||||||
payment_entry.set_amounts()
|
payment_entry.set_amounts()
|
||||||
payment_entry.save(ignore_permissions=True)
|
payment_entry.save(ignore_permissions=True)
|
||||||
|
|
||||||
def unlink_ref_doc_from_payment_entries(ref_doc):
|
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_jv(ref_doc.doctype, ref_doc.name)
|
||||||
remove_ref_doc_link_from_pe(ref_doc.doctype, ref_doc.name)
|
remove_ref_doc_link_from_pe(ref_doc.doctype, ref_doc.name)
|
||||||
|
|
||||||
frappe.db.sql("""update `tabGL Entry`
|
frappe.db.sql("""update `tabGL Entry`
|
||||||
set against_voucher_type=null, against_voucher=null,
|
set against_voucher_type=null, against_voucher=null,
|
||||||
modified=%s, modified_by=%s
|
modified=%s, modified_by=%s
|
||||||
where against_voucher_type=%s and against_voucher=%s
|
where against_voucher_type=%s and against_voucher=%s
|
||||||
and voucher_no != ifnull(against_voucher, '')""",
|
and voucher_no != ifnull(against_voucher, '')""",
|
||||||
(now(), frappe.session.user, ref_doc.doctype, ref_doc.name))
|
(now(), frappe.session.user, ref_doc.doctype, ref_doc.name))
|
||||||
|
|
||||||
if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"):
|
if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||||
ref_doc.set("advances", [])
|
ref_doc.set("advances", [])
|
||||||
|
|
||||||
frappe.db.sql("""delete from `tab{0} Advance` where parent = %s"""
|
frappe.db.sql("""delete from `tab{0} Advance` where parent = %s"""
|
||||||
.format(ref_doc.doctype), ref_doc.name)
|
.format(ref_doc.doctype), ref_doc.name)
|
||||||
|
|
||||||
@@ -430,7 +430,7 @@ def remove_ref_doc_link_from_jv(ref_type, ref_no):
|
|||||||
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
|
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
|
||||||
|
|
||||||
frappe.msgprint(_("Journal Entries {0} are un-linked".format("\n".join(linked_jv))))
|
frappe.msgprint(_("Journal Entries {0} are un-linked".format("\n".join(linked_jv))))
|
||||||
|
|
||||||
def remove_ref_doc_link_from_pe(ref_type, ref_no):
|
def remove_ref_doc_link_from_pe(ref_type, ref_no):
|
||||||
linked_pe = frappe.db.sql_list("""select parent from `tabPayment Entry Reference`
|
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))
|
where reference_doctype=%s and reference_name=%s and docstatus < 2""", (ref_type, ref_no))
|
||||||
@@ -440,18 +440,18 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no):
|
|||||||
set allocated_amount=0, modified=%s, modified_by=%s
|
set allocated_amount=0, modified=%s, modified_by=%s
|
||||||
where reference_doctype=%s and reference_name=%s
|
where reference_doctype=%s and reference_name=%s
|
||||||
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
|
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
|
||||||
|
|
||||||
for pe in linked_pe:
|
for pe in linked_pe:
|
||||||
pe_doc = frappe.get_doc("Payment Entry", pe)
|
pe_doc = frappe.get_doc("Payment Entry", pe)
|
||||||
pe_doc.set_total_allocated_amount()
|
pe_doc.set_total_allocated_amount()
|
||||||
pe_doc.set_unallocated_amount()
|
pe_doc.set_unallocated_amount()
|
||||||
pe_doc.clear_unallocated_reference_document_rows()
|
pe_doc.clear_unallocated_reference_document_rows()
|
||||||
|
|
||||||
frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s,
|
frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s,
|
||||||
base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%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,
|
where name=%s""", (pe_doc.total_allocated_amount, pe_doc.base_total_allocated_amount,
|
||||||
pe_doc.unallocated_amount, now(), frappe.session.user, pe))
|
pe_doc.unallocated_amount, now(), frappe.session.user, pe))
|
||||||
|
|
||||||
frappe.msgprint(_("Payment Entries {0} are un-linked".format("\n".join(linked_pe))))
|
frappe.msgprint(_("Payment Entries {0} are un-linked".format("\n".join(linked_pe))))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -562,7 +562,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
from
|
from
|
||||||
`tabGL Entry` invoice_gl_entry
|
`tabGL Entry` invoice_gl_entry
|
||||||
where
|
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
|
and account = %(account)s and {dr_or_cr} > 0
|
||||||
{condition}
|
{condition}
|
||||||
and ((voucher_type = 'Journal Entry'
|
and ((voucher_type = 'Journal Entry'
|
||||||
@@ -590,9 +590,9 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
|
|||||||
'outstanding_amount': flt(d.invoice_amount - d.payment_amount, precision),
|
'outstanding_amount': flt(d.invoice_amount - d.payment_amount, precision),
|
||||||
'due_date': frappe.db.get_value(d.voucher_type, d.voucher_no, "due_date"),
|
'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()))
|
outstanding_invoices = sorted(outstanding_invoices, key=lambda k: k['due_date'] or getdate(nowdate()))
|
||||||
|
|
||||||
return outstanding_invoices
|
return outstanding_invoices
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import random, json
|
import random, json
|
||||||
import frappe, erpnext
|
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 frappe.utils.make_random import add_random_children, get_random
|
||||||
from erpnext.demo.domains import data
|
from erpnext.demo.domains import data
|
||||||
from frappe import _
|
from frappe import _
|
||||||
@@ -14,8 +14,15 @@ def setup(domain):
|
|||||||
setup_holiday_list()
|
setup_holiday_list()
|
||||||
setup_user()
|
setup_user()
|
||||||
setup_employee()
|
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_leave_allocation()
|
||||||
setup_user_roles()
|
setup_user_roles()
|
||||||
setup_customer()
|
setup_customer()
|
||||||
@@ -29,7 +36,7 @@ def setup(domain):
|
|||||||
setup_account_to_expense_type()
|
setup_account_to_expense_type()
|
||||||
setup_budget()
|
setup_budget()
|
||||||
setup_pos_profile()
|
setup_pos_profile()
|
||||||
|
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
frappe.clear_cache()
|
frappe.clear_cache()
|
||||||
|
|
||||||
@@ -111,30 +118,44 @@ def setup_employee():
|
|||||||
frappe.db.set_value("HR Settings", None, "emp_created_by", "Naming Series")
|
frappe.db.set_value("HR Settings", None, "emp_created_by", "Naming Series")
|
||||||
frappe.db.commit()
|
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')
|
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'))
|
f = frappe.get_doc('Fiscal Year', frappe.defaults.get_global_default('fiscal_year'))
|
||||||
|
|
||||||
ss = frappe.new_doc('Salary Structure')
|
ss = frappe.new_doc('Salary Structure')
|
||||||
ss.name = "Sample Salary Structure - " + str(f.year_start_date)
|
ss.name = "Sample Salary Structure - " + random_string(5)
|
||||||
for e in frappe.get_all('Employee', fields=['name', 'date_of_joining']):
|
for e in employees:
|
||||||
ss.append('employees', {
|
ss.append('employees', {
|
||||||
'employee': e.name,
|
'employee': e.name,
|
||||||
'base': random.random() * 10000
|
'base': random.random() * 10000
|
||||||
})
|
})
|
||||||
|
|
||||||
if not e.date_of_joining:
|
ss.from_date = e.date_of_joining if (e.date_of_joining
|
||||||
continue
|
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
|
||||||
|
|
||||||
|
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.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")
|
|
||||||
ss.append('earnings', {
|
ss.append('earnings', {
|
||||||
'salary_component': 'Basic',
|
'salary_component': 'Basic',
|
||||||
"abbr":'B',
|
"abbr":'B',
|
||||||
'condition': 'base > 5000',
|
|
||||||
'formula': 'base*.2',
|
'formula': 'base*.2',
|
||||||
'amount_based_on_formula': 1,
|
'amount_based_on_formula': 1,
|
||||||
"idx": 1
|
"idx": 1
|
||||||
@@ -142,20 +163,14 @@ def setup_salary_structure():
|
|||||||
ss.append('deductions', {
|
ss.append('deductions', {
|
||||||
'salary_component': 'Income Tax',
|
'salary_component': 'Income Tax',
|
||||||
"abbr":'IT',
|
"abbr":'IT',
|
||||||
'condition': 'base > 5000',
|
'condition': 'base > 1000',
|
||||||
'amount': random.random() * 1000,
|
'amount': random.random() * 1000,
|
||||||
"idx": 1
|
"idx": 1
|
||||||
})
|
})
|
||||||
|
|
||||||
ss.insert()
|
ss.insert()
|
||||||
|
|
||||||
def setup_salary_structure_for_timesheet():
|
return ss
|
||||||
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)
|
|
||||||
|
|
||||||
def setup_user_roles():
|
def setup_user_roles():
|
||||||
user = frappe.get_doc('User', 'demo@erpnext.com')
|
user = frappe.get_doc('User', 'demo@erpnext.com')
|
||||||
@@ -208,11 +223,11 @@ def setup_user_roles():
|
|||||||
user = frappe.get_doc('User', 'aromn@example.com')
|
user = frappe.get_doc('User', 'aromn@example.com')
|
||||||
user.add_roles('Academics User')
|
user.add_roles('Academics User')
|
||||||
frappe.db.set_global('demo_schools_user', user.name)
|
frappe.db.set_global('demo_schools_user', user.name)
|
||||||
|
|
||||||
#Add Expense Approver
|
#Add Expense Approver
|
||||||
user = frappe.get_doc('User', 'WanMai@example.com')
|
user = frappe.get_doc('User', 'WanMai@example.com')
|
||||||
user.add_roles('Expense Approver')
|
user.add_roles('Expense Approver')
|
||||||
|
|
||||||
def setup_leave_allocation():
|
def setup_leave_allocation():
|
||||||
year = now_datetime().year
|
year = now_datetime().year
|
||||||
for employee in frappe.get_all('Employee', fields=['name']):
|
for employee in frappe.get_all('Employee', fields=['name']):
|
||||||
@@ -345,7 +360,7 @@ def setup_pos_profile():
|
|||||||
})
|
})
|
||||||
|
|
||||||
pos.insert()
|
pos.insert()
|
||||||
|
|
||||||
def import_json(doctype, submit=False, values=None):
|
def import_json(doctype, submit=False, values=None):
|
||||||
frappe.flags.in_import = True
|
frappe.flags.in_import = True
|
||||||
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
|
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
|
||||||
|
|||||||
@@ -23,25 +23,32 @@ def work():
|
|||||||
report = "Ordered Items to be Billed"
|
report = "Ordered Items to be Billed"
|
||||||
for so in list(set([r[0] for r in query_report.run(report)["result"]
|
for so in list(set([r[0] for r in query_report.run(report)["result"]
|
||||||
if r[0]!="Total"]))[:random.randint(1, 5)]:
|
if r[0]!="Total"]))[:random.randint(1, 5)]:
|
||||||
si = frappe.get_doc(make_sales_invoice(so))
|
try:
|
||||||
si.posting_date = frappe.flags.current_date
|
si = frappe.get_doc(make_sales_invoice(so))
|
||||||
for d in si.get("items"):
|
si.posting_date = frappe.flags.current_date
|
||||||
if not d.income_account:
|
for d in si.get("items"):
|
||||||
d.income_account = "Sales - {}".format(frappe.db.get_value('Company', si.company, 'abbr'))
|
if not d.income_account:
|
||||||
si.insert()
|
d.income_account = "Sales - {}".format(frappe.db.get_value('Company', si.company, 'abbr'))
|
||||||
si.submit()
|
si.insert()
|
||||||
frappe.db.commit()
|
si.submit()
|
||||||
|
frappe.db.commit()
|
||||||
|
except frappe.ValidationError:
|
||||||
|
pass
|
||||||
|
|
||||||
if random.random() <= 0.6:
|
if random.random() <= 0.6:
|
||||||
report = "Received Items to be Billed"
|
report = "Received Items to be Billed"
|
||||||
for pr in list(set([r[0] for r in query_report.run(report)["result"]
|
for pr in list(set([r[0] for r in query_report.run(report)["result"]
|
||||||
if r[0]!="Total"]))[:random.randint(1, 5)]:
|
if r[0]!="Total"]))[:random.randint(1, 5)]:
|
||||||
pi = frappe.get_doc(make_purchase_invoice(pr))
|
try:
|
||||||
pi.posting_date = frappe.flags.current_date
|
pi = frappe.get_doc(make_purchase_invoice(pr))
|
||||||
pi.bill_no = random_string(6)
|
pi.posting_date = frappe.flags.current_date
|
||||||
pi.insert()
|
pi.bill_no = random_string(6)
|
||||||
pi.submit()
|
pi.insert()
|
||||||
frappe.db.commit()
|
pi.submit()
|
||||||
|
frappe.db.commit()
|
||||||
|
except frappe.ValidationError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
if random.random() < 0.5:
|
if random.random() < 0.5:
|
||||||
make_payment_entries("Sales Invoice", "Accounts Receivable")
|
make_payment_entries("Sales Invoice", "Accounts Receivable")
|
||||||
@@ -67,7 +74,7 @@ def work():
|
|||||||
def make_payment_entries(ref_doctype, report):
|
def make_payment_entries(ref_doctype, report):
|
||||||
outstanding_invoices = list(set([r[3] for r in query_report.run(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]))
|
{"report_date": frappe.flags.current_date })["result"] if r[2]==ref_doctype]))
|
||||||
|
|
||||||
# make Payment Entry
|
# make Payment Entry
|
||||||
for inv in outstanding_invoices[:random.randint(1, 2)]:
|
for inv in outstanding_invoices[:random.randint(1, 2)]:
|
||||||
pe = get_payment_entry(ref_doctype, inv)
|
pe = get_payment_entry(ref_doctype, inv)
|
||||||
|
|||||||
@@ -1,40 +1,50 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe, erpnext
|
import frappe, erpnext
|
||||||
import random
|
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.test_timesheet import make_timesheet
|
||||||
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
|
from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_sales_invoice
|
||||||
from frappe.utils.make_random import get_random
|
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.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():
|
def work():
|
||||||
frappe.set_user(frappe.db.get_global('demo_hr_user'))
|
frappe.set_user(frappe.db.get_global('demo_hr_user'))
|
||||||
year, month = frappe.flags.current_date.strftime("%Y-%m").split("-")
|
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()
|
mark_attendance()
|
||||||
make_leave_application()
|
make_leave_application()
|
||||||
|
|
||||||
# process payroll
|
# 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 = frappe.get_doc("Process Payroll", "Process Payroll")
|
||||||
process_payroll.company = frappe.flags.company
|
process_payroll.company = frappe.flags.company
|
||||||
process_payroll.month = prev_month
|
process_payroll.payroll_frequency = 'Monthly'
|
||||||
process_payroll.fiscal_year = year
|
|
||||||
process_payroll.from_date = frappe.flags.current_date
|
# select a posting date from the previous month
|
||||||
process_payroll.to_date = add_days(frappe.flags.current_date, random.randint(0, 30))
|
process_payroll.posting_date = get_last_day(getdate(frappe.flags.current_date) - datetime.timedelta(days=10))
|
||||||
process_payroll.reference_number = "DemoRef23"
|
|
||||||
process_payroll.reference_date = frappe.flags.current_date
|
|
||||||
process_payroll.payment_account = frappe.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name")
|
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'):
|
if frappe.db.get_global('demo_hr_user'):
|
||||||
make_timesheet_records()
|
make_timesheet_records()
|
||||||
|
|
||||||
#expense claim
|
#expense claim
|
||||||
expense_claim = frappe.new_doc("Expense Claim")
|
expense_claim = frappe.new_doc("Expense Claim")
|
||||||
expense_claim.extend('expenses', get_expenses())
|
expense_claim.extend('expenses', get_expenses())
|
||||||
@@ -100,7 +110,10 @@ def get_timesheet_based_salary_slip_employee():
|
|||||||
select employee from `tabSalary Structure Employee`
|
select employee from `tabSalary Structure Employee`
|
||||||
where parent IN %(sal_struct)s""", {"sal_struct": sal_struct}, as_dict=True)
|
where parent IN %(sal_struct)s""", {"sal_struct": sal_struct}, as_dict=True)
|
||||||
return employees
|
return employees
|
||||||
|
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
|
||||||
def make_timesheet_records():
|
def make_timesheet_records():
|
||||||
employees = get_timesheet_based_salary_slip_employee()
|
employees = get_timesheet_based_salary_slip_employee()
|
||||||
for e in employees:
|
for e in employees:
|
||||||
@@ -134,10 +147,10 @@ def make_sales_invoice_for_timesheet(name):
|
|||||||
sales_invoice.insert()
|
sales_invoice.insert()
|
||||||
sales_invoice.submit()
|
sales_invoice.submit()
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|
||||||
def make_leave_application():
|
def make_leave_application():
|
||||||
allocated_leaves = frappe.get_all("Leave Allocation", fields=['employee', 'leave_type'])
|
allocated_leaves = frappe.get_all("Leave Allocation", fields=['employee', 'leave_type'])
|
||||||
|
|
||||||
for allocated_leave in allocated_leaves:
|
for allocated_leave in allocated_leaves:
|
||||||
leave_balance = get_leave_balance_on(allocated_leave.employee, allocated_leave.leave_type, frappe.flags.current_date,
|
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)
|
consider_all_leaves_in_the_allocation_period=True)
|
||||||
@@ -146,7 +159,7 @@ def make_leave_application():
|
|||||||
to_date = frappe.flags.current_date
|
to_date = frappe.flags.current_date
|
||||||
else:
|
else:
|
||||||
to_date = add_days(frappe.flags.current_date, random.randint(0, leave_balance-1))
|
to_date = add_days(frappe.flags.current_date, random.randint(0, leave_balance-1))
|
||||||
|
|
||||||
leave_application = frappe.get_doc({
|
leave_application = frappe.get_doc({
|
||||||
"doctype": "Leave Application",
|
"doctype": "Leave Application",
|
||||||
"employee": allocated_leave.employee,
|
"employee": allocated_leave.employee,
|
||||||
@@ -159,13 +172,13 @@ def make_leave_application():
|
|||||||
leave_application.insert()
|
leave_application.insert()
|
||||||
leave_application.submit()
|
leave_application.submit()
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
except (OverlapError):
|
except (OverlapError, AttendanceAlreadyMarkedError):
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
|
|
||||||
def mark_attendance():
|
def mark_attendance():
|
||||||
att_date = frappe.flags.current_date
|
att_date = frappe.flags.current_date
|
||||||
for employee in frappe.get_all('Employee', fields=['name'], filters = {'status': 'Active'}):
|
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}):
|
if not frappe.db.get_value("Attendance", {"employee": employee.name, "att_date": att_date}):
|
||||||
attendance = frappe.get_doc({
|
attendance = frappe.get_doc({
|
||||||
"doctype": "Attendance",
|
"doctype": "Attendance",
|
||||||
@@ -175,11 +188,11 @@ def mark_attendance():
|
|||||||
leave = frappe.db.sql("""select name from `tabLeave Application`
|
leave = frappe.db.sql("""select name from `tabLeave Application`
|
||||||
where employee = %s and %s between from_date and to_date and status = 'Approved'
|
where employee = %s and %s between from_date and to_date and status = 'Approved'
|
||||||
and docstatus = 1""", (employee.name, att_date))
|
and docstatus = 1""", (employee.name, att_date))
|
||||||
|
|
||||||
if leave:
|
if leave:
|
||||||
attendance.status = "Absent"
|
attendance.status = "Absent"
|
||||||
else:
|
else:
|
||||||
attendance.status = "Present"
|
attendance.status = "Present"
|
||||||
attendance.save()
|
attendance.save()
|
||||||
attendance.submit()
|
attendance.submit()
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
|
|||||||
@@ -55,12 +55,13 @@ def make_opportunity():
|
|||||||
"enquiry_from": "Customer",
|
"enquiry_from": "Customer",
|
||||||
"customer": get_random("Customer"),
|
"customer": get_random("Customer"),
|
||||||
"enquiry_type": "Sales",
|
"enquiry_type": "Sales",
|
||||||
|
"with_items": 1,
|
||||||
"transaction_date": frappe.flags.current_date,
|
"transaction_date": frappe.flags.current_date,
|
||||||
})
|
})
|
||||||
|
|
||||||
add_random_children(b, "items", rows=4, randomize = {
|
add_random_children(b, "items", rows=4, randomize = {
|
||||||
"qty": (1, 5),
|
"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")
|
}, unique="item_code")
|
||||||
|
|
||||||
b.insert()
|
b.insert()
|
||||||
@@ -68,7 +69,7 @@ def make_opportunity():
|
|||||||
|
|
||||||
def make_quotation():
|
def make_quotation():
|
||||||
# get open opportunites
|
# get open opportunites
|
||||||
opportunity = get_random("Opportunity", {"status": "Open"})
|
opportunity = get_random("Opportunity", {"status": "Open", "with_items": 1})
|
||||||
|
|
||||||
if opportunity:
|
if opportunity:
|
||||||
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
|
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ def make_stock_reconciliation():
|
|||||||
|
|
||||||
def submit_draft_stock_entries():
|
def submit_draft_stock_entries():
|
||||||
from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, \
|
from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, \
|
||||||
DuplicateEntryForProductionOrderError, OperationsNotCompleteError
|
DuplicateEntryForProductionOrderError, OperationsNotCompleteError
|
||||||
|
|
||||||
# try posting older drafts (if exists)
|
# try posting older drafts (if exists)
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
@@ -102,7 +102,7 @@ def submit_draft_stock_entries():
|
|||||||
|
|
||||||
def make_sales_return_records():
|
def make_sales_return_records():
|
||||||
for data in frappe.get_all('Delivery Note', fields=["name"], filters={"docstatus": 1}):
|
for data in frappe.get_all('Delivery Note', fields=["name"], filters={"docstatus": 1}):
|
||||||
if random.random() < 0.2:
|
if random.random() < 0.1:
|
||||||
try:
|
try:
|
||||||
dn = make_sales_return(data.name)
|
dn = make_sales_return(data.name)
|
||||||
dn.insert()
|
dn.insert()
|
||||||
@@ -113,7 +113,7 @@ def make_sales_return_records():
|
|||||||
|
|
||||||
def make_purchase_return_records():
|
def make_purchase_return_records():
|
||||||
for data in frappe.get_all('Purchase Receipt', fields=["name"], filters={"docstatus": 1}):
|
for data in frappe.get_all('Purchase Receipt', fields=["name"], filters={"docstatus": 1}):
|
||||||
if random.random() < 0.2:
|
if random.random() < 0.1:
|
||||||
try:
|
try:
|
||||||
pr = make_purchase_return(data.name)
|
pr = make_purchase_return(data.name)
|
||||||
pr.insert()
|
pr.insert()
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ class LeaveDayBlockedError(frappe.ValidationError): pass
|
|||||||
class OverlapError(frappe.ValidationError): pass
|
class OverlapError(frappe.ValidationError): pass
|
||||||
class InvalidLeaveApproverError(frappe.ValidationError): pass
|
class InvalidLeaveApproverError(frappe.ValidationError): pass
|
||||||
class LeaveApproverIdentityError(frappe.ValidationError): pass
|
class LeaveApproverIdentityError(frappe.ValidationError): pass
|
||||||
|
class AttendanceAlreadyMarkedError(frappe.ValidationError): pass
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
class LeaveApplication(Document):
|
class LeaveApplication(Document):
|
||||||
@@ -100,16 +101,16 @@ class LeaveApplication(Document):
|
|||||||
def validate_salary_processed_days(self):
|
def validate_salary_processed_days(self):
|
||||||
if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
|
if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
|
||||||
return
|
return
|
||||||
|
|
||||||
last_processed_pay_slip = frappe.db.sql("""
|
last_processed_pay_slip = frappe.db.sql("""
|
||||||
select start_date, end_date from `tabSalary Slip`
|
select start_date, end_date from `tabSalary Slip`
|
||||||
where docstatus = 1 and employee = %s
|
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
|
order by modified desc limit 1
|
||||||
""",(self.employee, self.to_date, self.from_date))
|
""",(self.employee, self.to_date, self.from_date))
|
||||||
|
|
||||||
if last_processed_pay_slip:
|
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])))
|
formatdate(last_processed_pay_slip[0][1])))
|
||||||
|
|
||||||
|
|
||||||
@@ -213,13 +214,14 @@ class LeaveApplication(Document):
|
|||||||
elif self.docstatus==1 and len(leave_approvers) and self.leave_approver != frappe.session.user:
|
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"),
|
frappe.throw(_("Only the selected Leave Approver can submit this Leave Application"),
|
||||||
LeaveApproverIdentityError)
|
LeaveApproverIdentityError)
|
||||||
|
|
||||||
def validate_attendance(self):
|
def validate_attendance(self):
|
||||||
attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s and (att_date between %s and %s)
|
attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s and (att_date between %s and %s)
|
||||||
and docstatus = 1""",
|
and docstatus = 1""",
|
||||||
(self.employee, self.from_date, self.to_date))
|
(self.employee, self.from_date, self.to_date))
|
||||||
if attendance:
|
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):
|
def notify_employee(self, status):
|
||||||
employee = frappe.get_doc("Employee", self.employee)
|
employee = frappe.get_doc("Employee", self.employee)
|
||||||
|
|||||||
@@ -3,21 +3,24 @@
|
|||||||
|
|
||||||
frappe.ui.form.on("Process Payroll", {
|
frappe.ui.form.on("Process Payroll", {
|
||||||
onload: function(frm) {
|
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) {
|
refresh: function(frm) {
|
||||||
frm.disable_save();
|
frm.disable_save();
|
||||||
},
|
},
|
||||||
|
|
||||||
payroll_frequency: function(frm) {
|
payroll_frequency: function(frm) {
|
||||||
frm.trigger("set_start_end_dates");
|
frm.trigger("set_start_end_dates");
|
||||||
},
|
},
|
||||||
|
|
||||||
start_date: function(frm) {
|
start_date: function(frm) {
|
||||||
frm.trigger("set_start_end_dates");
|
frm.trigger("set_start_end_dates");
|
||||||
},
|
},
|
||||||
|
|
||||||
end_date: function(frm) {
|
end_date: function(frm) {
|
||||||
frm.trigger("set_start_end_dates");
|
frm.trigger("set_start_end_dates");
|
||||||
},
|
},
|
||||||
@@ -25,20 +28,19 @@ frappe.ui.form.on("Process Payroll", {
|
|||||||
payment_account: function(frm) {
|
payment_account: function(frm) {
|
||||||
frm.toggle_display(['make_bank_entry'], (frm.doc.payment_account!="" && frm.doc.payment_account!="undefined"));
|
frm.toggle_display(['make_bank_entry'], (frm.doc.payment_account!="" && frm.doc.payment_account!="undefined"));
|
||||||
},
|
},
|
||||||
|
|
||||||
set_start_end_dates: function(frm) {
|
set_start_end_dates: function(frm) {
|
||||||
if (!frm.doc.salary_slip_based_on_timesheet){
|
if (!frm.doc.salary_slip_based_on_timesheet){
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method:'erpnext.hr.doctype.process_payroll.process_payroll.get_start_end_dates',
|
method:'erpnext.hr.doctype.process_payroll.process_payroll.get_start_end_dates',
|
||||||
args:{
|
args:{
|
||||||
payroll_frequency: frm.doc.payroll_frequency,
|
payroll_frequency: frm.doc.payroll_frequency,
|
||||||
start_date: frm.doc.start_date,
|
start_date: frm.doc.start_date || frm.doc.posting_date
|
||||||
end_date: frm.doc.end_date
|
|
||||||
},
|
},
|
||||||
callback: function(r){
|
callback: function(r){
|
||||||
if (r.message){
|
if (r.message){
|
||||||
frm.set_value('start_date', r.message.start_date);
|
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 @@ cur_frm.cscript.create_salary_slip = function(doc, cdt, cdn) {
|
|||||||
if (r.message)
|
if (r.message)
|
||||||
cur_frm.cscript.display_activity_log(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) {
|
cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
|
||||||
@@ -94,7 +96,7 @@ cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
|
|||||||
cur_frm.cscript.display_activity_log(r.message);
|
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 @@ cur_frm.cscript.reference_entry = function(doc,cdt,cdn){
|
|||||||
title: __("Bank Transaction Reference"),
|
title: __("Bank Transaction Reference"),
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
"label": __("Reference Number"),
|
"label": __("Reference Number"),
|
||||||
"fieldname": "reference_number",
|
"fieldname": "reference_number",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": __("Reference Date"),
|
"label": __("Reference Date"),
|
||||||
"fieldname": "reference_date",
|
"fieldname": "reference_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"default": get_today()
|
"default": get_today()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,7 +127,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Monthly",
|
"default": "",
|
||||||
"depends_on": "eval:doc.salary_slip_based_on_timesheet == 0",
|
"depends_on": "eval:doc.salary_slip_based_on_timesheet == 0",
|
||||||
"fieldname": "payroll_frequency",
|
"fieldname": "payroll_frequency",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@@ -352,7 +352,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Today",
|
"default": "",
|
||||||
"fieldname": "start_date",
|
"fieldname": "start_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -408,7 +408,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Today",
|
"default": "",
|
||||||
"fieldname": "end_date",
|
"fieldname": "end_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -722,7 +722,7 @@
|
|||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-11-26 01:14:51.691057",
|
"modified": "2016-12-14 01:48:22.326326",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Process Payroll",
|
"name": "Process Payroll",
|
||||||
|
|||||||
@@ -5,15 +5,11 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import cint, flt, nowdate, add_days, getdate
|
from frappe.utils import cint, flt, nowdate, add_days, getdate
|
||||||
from frappe import _
|
from frappe import _
|
||||||
import collections
|
|
||||||
from collections import defaultdict
|
|
||||||
from calendar import monthrange
|
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class ProcessPayroll(Document):
|
class ProcessPayroll(Document):
|
||||||
|
|
||||||
def get_emp_list(self):
|
def get_emp_list(self):
|
||||||
"""
|
"""
|
||||||
Returns list of active employees based on selected criteria
|
Returns list of active employees based on selected criteria
|
||||||
@@ -21,24 +17,33 @@ class ProcessPayroll(Document):
|
|||||||
"""
|
"""
|
||||||
cond = self.get_filter_condition()
|
cond = self.get_filter_condition()
|
||||||
cond += self.get_joining_releiving_condition()
|
cond += self.get_joining_releiving_condition()
|
||||||
|
|
||||||
|
|
||||||
struct_cond = ''
|
condition = ''
|
||||||
if self.payroll_frequency:
|
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("""
|
sal_struct = frappe.db.sql("""
|
||||||
select name from `tabSalary Structure`
|
select
|
||||||
where docstatus != 2 and is_active = 'Yes' and company = %(company)s and
|
name from `tabSalary Structure`
|
||||||
ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s""",
|
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})
|
{"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
|
||||||
|
|
||||||
if sal_struct:
|
if sal_struct:
|
||||||
cond += "and t2.parent IN %(sal_struct)s "
|
cond += "and t2.parent IN %(sal_struct)s "
|
||||||
emp_list = frappe.db.sql("""
|
emp_list = frappe.db.sql("""
|
||||||
select t1.name
|
select
|
||||||
from `tabEmployee` t1, `tabSalary Structure Employee` t2
|
t1.name
|
||||||
where t1.docstatus!=2 and t1.name = t2.employee
|
from
|
||||||
|
`tabEmployee` t1, `tabSalary Structure Employee` t2
|
||||||
|
where
|
||||||
|
t1.docstatus!=2
|
||||||
|
and t1.name = t2.employee
|
||||||
%s """% cond, {"sal_struct": sal_struct})
|
%s """% cond, {"sal_struct": sal_struct})
|
||||||
return emp_list
|
return emp_list
|
||||||
|
|
||||||
@@ -67,7 +72,7 @@ class ProcessPayroll(Document):
|
|||||||
if not self.get(fieldname):
|
if not self.get(fieldname):
|
||||||
frappe.throw(_("Please set {0}").format(self.meta.get_label(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
|
Creates salary slip for selected employees if already not created
|
||||||
"""
|
"""
|
||||||
@@ -77,31 +82,26 @@ class ProcessPayroll(Document):
|
|||||||
ss_list = []
|
ss_list = []
|
||||||
if emp_list:
|
if emp_list:
|
||||||
for emp in emp_list:
|
for emp in emp_list:
|
||||||
if not frappe.db.sql("""select name from `tabSalary Slip`
|
if not frappe.db.sql("""select
|
||||||
where docstatus!= 2 and employee = %s and start_date >= %s and end_date <= %s and company = %s
|
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)):
|
""", (emp[0], self.start_date, self.end_date, self.company)):
|
||||||
if self.payroll_frequency == "Monthly" or self.payroll_frequency == '':
|
ss = frappe.get_doc({
|
||||||
ss = frappe.get_doc({
|
"doctype": "Salary Slip",
|
||||||
"doctype": "Salary Slip",
|
"salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
|
||||||
"salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
|
"payroll_frequency": self.payroll_frequency,
|
||||||
"employee": emp[0],
|
"start_date": self.start_date,
|
||||||
"employee_name": frappe.get_value("Employee", {"name":emp[0]}, "employee_name"),
|
"end_date": self.end_date,
|
||||||
"company": self.company,
|
"employee": emp[0],
|
||||||
"posting_date": self.posting_date,
|
"employee_name": frappe.get_value("Employee", {"name":emp[0]}, "employee_name"),
|
||||||
"payroll_frequency": self.payroll_frequency
|
"company": self.company,
|
||||||
})
|
"posting_date": self.posting_date
|
||||||
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.insert()
|
ss.insert()
|
||||||
ss_list.append(ss.name)
|
ss_list.append(ss.name)
|
||||||
return self.create_log(ss_list)
|
return self.create_log(ss_list)
|
||||||
@@ -120,16 +120,16 @@ class ProcessPayroll(Document):
|
|||||||
Returns list of salary slips based on selected criteria
|
Returns list of salary slips based on selected criteria
|
||||||
"""
|
"""
|
||||||
cond = self.get_filter_condition()
|
cond = self.get_filter_condition()
|
||||||
|
|
||||||
ss_list = frappe.db.sql("""
|
ss_list = frappe.db.sql("""
|
||||||
select t1.name, t1.salary_structure from `tabSalary Slip` t1
|
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
|
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)
|
""" % ('%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
|
return ss_list
|
||||||
|
|
||||||
|
|
||||||
def submit_salary_slip(self):
|
def submit_salary_slips(self):
|
||||||
"""
|
"""
|
||||||
Submit all salary slips based on selected criteria
|
Submit all salary slips based on selected criteria
|
||||||
"""
|
"""
|
||||||
@@ -144,12 +144,11 @@ class ProcessPayroll(Document):
|
|||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
ss_obj.submit()
|
ss_obj.submit()
|
||||||
except Exception,e:
|
except frappe.ValidationError:
|
||||||
not_submitted_ss.append(ss[0])
|
not_submitted_ss.append(ss[0])
|
||||||
|
|
||||||
return self.create_submit_log(ss_list, not_submitted_ss)
|
return self.create_submit_log(ss_list, not_submitted_ss)
|
||||||
|
|
||||||
|
|
||||||
def create_submit_log(self, all_ss, not_submitted_ss):
|
def create_submit_log(self, all_ss, not_submitted_ss):
|
||||||
log = ''
|
log = ''
|
||||||
if not all_ss:
|
if not all_ss:
|
||||||
@@ -167,12 +166,9 @@ class ProcessPayroll(Document):
|
|||||||
log += """
|
log += """
|
||||||
<b>Not Submitted Salary Slips: </b>\
|
<b>Not Submitted Salary Slips: </b>\
|
||||||
<br><br> %s <br><br> \
|
<br><br> %s <br><br> \
|
||||||
Reason: <br>\
|
Possible reasons: <br>\
|
||||||
May be net pay is less than 0 <br>
|
1. Net pay is less than 0 <br>
|
||||||
May be company email id specified in employee master is not valid. <br> \
|
2. 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.
|
|
||||||
"""% ('<br>'.join(not_submitted_ss))
|
"""% ('<br>'.join(not_submitted_ss))
|
||||||
return log
|
return log
|
||||||
|
|
||||||
@@ -191,42 +187,42 @@ class ProcessPayroll(Document):
|
|||||||
""" % ('%s', '%s', cond), (self.start_date, self.end_date))
|
""" % ('%s', '%s', cond), (self.start_date, self.end_date))
|
||||||
|
|
||||||
return flt(tot[0][0])
|
return flt(tot[0][0])
|
||||||
|
|
||||||
def get_salary_component_account(self, salary_component):
|
def get_salary_component_account(self, salary_component):
|
||||||
account = frappe.db.get_value("Salary Component Account",
|
account = frappe.db.get_value("Salary Component Account",
|
||||||
{"parent": salary_component, "company": self.company}, "default_account")
|
{"parent": salary_component, "company": self.company}, "default_account")
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
frappe.throw(_("Please set default account in Salary Component {0}")
|
frappe.throw(_("Please set default account in Salary Component {0}")
|
||||||
.format(salary_component))
|
.format(salary_component))
|
||||||
|
|
||||||
return account
|
return account
|
||||||
|
|
||||||
def get_salary_components(self, component_type):
|
def get_salary_components(self, component_type):
|
||||||
salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
|
salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
|
||||||
if salary_slips:
|
if salary_slips:
|
||||||
salary_components = frappe.db.sql("""select salary_component, amount, parentfield
|
salary_components = frappe.db.sql("""select salary_component, amount, parentfield
|
||||||
from `tabSalary Detail` where parentfield = '%s' and parent in (%s)""" %
|
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
|
return salary_components
|
||||||
|
|
||||||
def get_salary_component_total(self, component_type = None):
|
def get_salary_component_total(self, component_type = None):
|
||||||
salary_components = self.get_salary_components(component_type)
|
salary_components = self.get_salary_components(component_type)
|
||||||
if salary_components:
|
if salary_components:
|
||||||
component_dict = {}
|
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']
|
component_dict[item['salary_component']] = component_dict.get(item['salary_component'], 0) + item['amount']
|
||||||
account_details = self.get_account(component_dict = component_dict)
|
account_details = self.get_account(component_dict = component_dict)
|
||||||
return account_details
|
return account_details
|
||||||
|
|
||||||
def get_account(self, component_dict = None):
|
def get_account(self, component_dict = None):
|
||||||
account_dict = {}
|
account_dict = {}
|
||||||
for s, a in component_dict.items():
|
for s, a in component_dict.items():
|
||||||
account = self.get_salary_component_account(s)
|
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
|
return account_dict
|
||||||
|
|
||||||
|
|
||||||
def make_journal_entry(self, reference_number = None, reference_date = None):
|
def make_journal_entry(self, reference_number = None, reference_date = None):
|
||||||
self.check_permission('write')
|
self.check_permission('write')
|
||||||
earnings = self.get_salary_component_total(component_type = "earnings") or {}
|
earnings = self.get_salary_component_total(component_type = "earnings") or {}
|
||||||
@@ -240,7 +236,7 @@ class ProcessPayroll(Document):
|
|||||||
self.end_date)
|
self.end_date)
|
||||||
journal_entry.company = self.company
|
journal_entry.company = self.company
|
||||||
journal_entry.posting_date = nowdate()
|
journal_entry.posting_date = nowdate()
|
||||||
|
|
||||||
account_amt_list = []
|
account_amt_list = []
|
||||||
adjustment_amt = 0
|
adjustment_amt = 0
|
||||||
for acc, amt in earnings.items():
|
for acc, amt in earnings.items():
|
||||||
@@ -258,7 +254,7 @@ class ProcessPayroll(Document):
|
|||||||
account_amt_list.append({
|
account_amt_list.append({
|
||||||
"account": self.payment_account,
|
"account": self.payment_account,
|
||||||
"credit_in_account_currency": adjustment_amt
|
"credit_in_account_currency": adjustment_amt
|
||||||
})
|
})
|
||||||
journal_entry.set("accounts", account_amt_list)
|
journal_entry.set("accounts", account_amt_list)
|
||||||
journal_entry.cheque_no = reference_number
|
journal_entry.cheque_no = reference_number
|
||||||
journal_entry.cheque_date = reference_date
|
journal_entry.cheque_date = reference_date
|
||||||
@@ -271,24 +267,60 @@ class ProcessPayroll(Document):
|
|||||||
except Exception, e:
|
except Exception, e:
|
||||||
frappe.msgprint(e)
|
frappe.msgprint(e)
|
||||||
return self.create_jv_log(jv_name)
|
return self.create_jv_log(jv_name)
|
||||||
|
|
||||||
|
|
||||||
def create_jv_log(self, jv_name):
|
def create_jv_log(self, jv_name):
|
||||||
log = "<p>" + _("No submitted Salary Slip found") + "</p>"
|
log = "<p>" + _("No submitted Salary Slip found") + "</p>"
|
||||||
if jv_name:
|
if jv_name:
|
||||||
log = "<b>" + _("Journal Entry Submitted") + "</b>\
|
log = "<b>" + _("Journal Entry Submitted") + "</b>\
|
||||||
%s" % '<br>''<a href="#Form/Journal Entry/{0}">{0}</a>'.format(jv_name)
|
%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):
|
def update_salary_slip_status(self, jv_name = None):
|
||||||
ss_list = self.get_sal_slip_list(ss_status=1)
|
ss_list = self.get_sal_slip_list(ss_status=1)
|
||||||
for ss in ss_list:
|
for ss in ss_list:
|
||||||
ss_obj = frappe.get_doc("Salary Slip",ss[0])
|
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, "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()
|
@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):
|
def get_month_details(year, month):
|
||||||
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
|
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
|
||||||
if ysd:
|
if ysd:
|
||||||
@@ -312,32 +344,3 @@ def get_month_details(year, month):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("Fiscal Year {0} not found").format(year))
|
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
|
|
||||||
})
|
|
||||||
@@ -26,8 +26,8 @@ class TestProcessPayroll(unittest.TestCase):
|
|||||||
process_payroll.payment_account = payment_account
|
process_payroll.payment_account = payment_account
|
||||||
process_payroll.posting_date = nowdate()
|
process_payroll.posting_date = nowdate()
|
||||||
process_payroll.payroll_frequency = "Monthly"
|
process_payroll.payroll_frequency = "Monthly"
|
||||||
process_payroll.create_sal_slip()
|
process_payroll.create_salary_slips()
|
||||||
process_payroll.submit_salary_slip()
|
process_payroll.submit_salary_slips()
|
||||||
if process_payroll.get_sal_slip_list(ss_status = 1):
|
if process_payroll.get_sal_slip_list(ss_status = 1):
|
||||||
r = process_payroll.make_journal_entry(reference_number=random_string(10),reference_date=nowdate())
|
r = process_payroll.make_journal_entry(reference_number=random_string(10),reference_date=nowdate())
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
|
"default": "Today",
|
||||||
"fieldname": "posting_date",
|
"fieldname": "posting_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -421,7 +422,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Today",
|
"default": "",
|
||||||
"fieldname": "start_date",
|
"fieldname": "start_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@@ -450,7 +451,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "Today",
|
"default": "",
|
||||||
"depends_on": "",
|
"depends_on": "",
|
||||||
"fieldname": "end_date",
|
"fieldname": "end_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
@@ -1361,7 +1362,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-12-08 12:03:31.602913",
|
"modified": "2016-12-14 08:26:31.400930",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Slip",
|
"name": "Salary Slip",
|
||||||
|
|||||||
@@ -4,18 +4,15 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
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.model.naming import make_autoname
|
||||||
|
|
||||||
from frappe import msgprint, _
|
from frappe import msgprint, _
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
|
||||||
from erpnext.setup.utils import get_company_currency
|
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.hr.doctype.employee.employee import get_holiday_list_for_employee
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
|
||||||
from datetime import timedelta
|
|
||||||
|
|
||||||
class SalarySlip(TransactionBase):
|
class SalarySlip(TransactionBase):
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
|
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
|
||||||
@@ -36,7 +33,7 @@ class SalarySlip(TransactionBase):
|
|||||||
|
|
||||||
company_currency = get_company_currency(self.company)
|
company_currency = get_company_currency(self.company)
|
||||||
self.total_in_words = money_in_words(self.rounded_total, company_currency)
|
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"):
|
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")
|
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)):
|
if self.salary_slip_based_on_timesheet and (self.total_working_hours > int(max_working_hours)):
|
||||||
@@ -46,7 +43,7 @@ class SalarySlip(TransactionBase):
|
|||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
if date_diff(self.end_date, self.start_date) < 0:
|
if date_diff(self.end_date, self.start_date) < 0:
|
||||||
frappe.throw(_("To date cannot be before From date"))
|
frappe.throw(_("To date cannot be before From date"))
|
||||||
|
|
||||||
def calculate_component_amounts(self):
|
def calculate_component_amounts(self):
|
||||||
if not getattr(self, '_salary_structure_doc', None):
|
if not getattr(self, '_salary_structure_doc', None):
|
||||||
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
|
self._salary_structure_doc = frappe.get_doc('Salary Structure', self.salary_structure)
|
||||||
@@ -54,17 +51,17 @@ class SalarySlip(TransactionBase):
|
|||||||
data = self.get_data_for_eval()
|
data = self.get_data_for_eval()
|
||||||
for key in ('earnings', 'deductions'):
|
for key in ('earnings', 'deductions'):
|
||||||
for struct_row in self._salary_structure_doc.get(key):
|
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:
|
if amount:
|
||||||
self.update_component_row(struct_row, amount, key)
|
self.update_component_row(struct_row, amount, key)
|
||||||
|
|
||||||
|
|
||||||
def update_component_row(self, struct_row, amount, key):
|
def update_component_row(self, struct_row, amount, key):
|
||||||
component_row = None
|
component_row = None
|
||||||
for d in self.get(key):
|
for d in self.get(key):
|
||||||
if d.salary_component == struct_row.salary_component:
|
if d.salary_component == struct_row.salary_component:
|
||||||
component_row = d
|
component_row = d
|
||||||
|
|
||||||
if not component_row:
|
if not component_row:
|
||||||
self.append(key, {
|
self.append(key, {
|
||||||
'amount': amount,
|
'amount': amount,
|
||||||
@@ -74,12 +71,12 @@ class SalarySlip(TransactionBase):
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
component_row.amount = amount
|
component_row.amount = amount
|
||||||
|
|
||||||
def eval_condition_and_formula(self, d, data):
|
def eval_condition_and_formula(self, d, data):
|
||||||
try:
|
try:
|
||||||
if d.condition:
|
if d.condition:
|
||||||
if not eval(d.condition, None, data):
|
if not eval(d.condition, None, data):
|
||||||
return None
|
return None
|
||||||
amount = d.amount
|
amount = d.amount
|
||||||
if d.amount_based_on_formula:
|
if d.amount_based_on_formula:
|
||||||
if d.formula:
|
if d.formula:
|
||||||
@@ -87,23 +84,23 @@ class SalarySlip(TransactionBase):
|
|||||||
if amount:
|
if amount:
|
||||||
data[d.abbr] = amount
|
data[d.abbr] = amount
|
||||||
return amount
|
return amount
|
||||||
|
|
||||||
except NameError as err:
|
except NameError as err:
|
||||||
frappe.throw(_("Name error: {0}".format(err)))
|
frappe.throw(_("Name error: {0}".format(err)))
|
||||||
except SyntaxError as err:
|
except SyntaxError as err:
|
||||||
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
|
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
|
||||||
except:
|
except:
|
||||||
frappe.throw(_("Error in formula or condition"))
|
frappe.throw(_("Error in formula or condition"))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def get_data_for_eval(self):
|
def get_data_for_eval(self):
|
||||||
'''Returns data for evaluating formula'''
|
'''Returns data for evaluating formula'''
|
||||||
data = frappe._dict()
|
data = frappe._dict()
|
||||||
|
|
||||||
for d in self._salary_structure_doc.employees:
|
for d in self._salary_structure_doc.employees:
|
||||||
if d.employee == self.employee:
|
if d.employee == self.employee:
|
||||||
data.base, data.variable = d.base, d.variable
|
data.base, data.variable = d.base, d.variable
|
||||||
|
|
||||||
data.update(frappe.get_doc("Employee", self.employee).as_dict())
|
data.update(frappe.get_doc("Employee", self.employee).as_dict())
|
||||||
data.update(self.as_dict())
|
data.update(self.as_dict())
|
||||||
|
|
||||||
@@ -111,9 +108,9 @@ class SalarySlip(TransactionBase):
|
|||||||
salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"])
|
salary_components = frappe.get_all("Salary Component", fields=["salary_component_abbr"])
|
||||||
for salary_component in salary_components:
|
for salary_component in salary_components:
|
||||||
data[salary_component.salary_component_abbr] = 0
|
data[salary_component.salary_component_abbr] = 0
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_emp_and_leave_details(self):
|
def get_emp_and_leave_details(self):
|
||||||
'''First time, load all the components from salary structure'''
|
'''First time, load all the components from salary structure'''
|
||||||
@@ -148,16 +145,16 @@ class SalarySlip(TransactionBase):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def get_date_details(self):
|
def get_date_details(self):
|
||||||
date_details = get_start_end_dates(self.payroll_frequency, self.start_date, self.end_date)
|
if not self.end_date:
|
||||||
self.start_date = date_details.start_date
|
date_details = get_start_end_dates(self.payroll_frequency, self.start_date or self.posting_date)
|
||||||
self.end_date = date_details.end_date
|
self.start_date = date_details.start_date
|
||||||
|
self.end_date = date_details.end_date
|
||||||
|
|
||||||
def check_sal_struct(self, joining_date, relieving_date):
|
def check_sal_struct(self, joining_date, relieving_date):
|
||||||
cond = ''
|
cond = ''
|
||||||
if self.payroll_frequency:
|
if self.payroll_frequency:
|
||||||
cond = """and payroll_frequency = '%(payroll_frequency)s'""" % {"payroll_frequency": 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`
|
st_name = frappe.db.sql("""select parent from `tabSalary Structure Employee`
|
||||||
where employee=%s
|
where employee=%s
|
||||||
and parent in (select name from `tabSalary Structure`
|
and parent in (select name from `tabSalary Structure`
|
||||||
@@ -165,7 +162,7 @@ class SalarySlip(TransactionBase):
|
|||||||
and (from_date <= %s or from_date <= %s)
|
and (from_date <= %s or from_date <= %s)
|
||||||
and (to_date is null or to_date >= %s or to_date >= %s) %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))
|
"""% ('%s', '%s', '%s','%s','%s', cond),(self.employee, self.start_date, joining_date, self.end_date, relieving_date))
|
||||||
|
|
||||||
if st_name:
|
if st_name:
|
||||||
if len(st_name) > 1:
|
if len(st_name) > 1:
|
||||||
frappe.msgprint(_("Multiple active Salary Structures found for employee {0} for the given dates")
|
frappe.msgprint(_("Multiple active Salary Structures found for employee {0} for the given dates")
|
||||||
@@ -174,7 +171,7 @@ class SalarySlip(TransactionBase):
|
|||||||
else:
|
else:
|
||||||
self.salary_structure = None
|
self.salary_structure = None
|
||||||
frappe.msgprint(_("No active or default Salary Structure found for employee {0} for the given dates")
|
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):
|
def pull_sal_struct(self):
|
||||||
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
|
||||||
@@ -185,16 +182,13 @@ class SalarySlip(TransactionBase):
|
|||||||
self.hour_rate = self._salary_structure_doc.hour_rate
|
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.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)
|
self.add_earning_for_hourly_wages(self._salary_structure_doc.salary_component)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def process_salary_structure(self):
|
def process_salary_structure(self):
|
||||||
'''Calculate salary after salary structure details have been updated'''
|
'''Calculate salary after salary structure details have been updated'''
|
||||||
self.get_date_details()
|
self.get_date_details()
|
||||||
self.pull_emp_details()
|
self.pull_emp_details()
|
||||||
self.get_leave_details()
|
self.get_leave_details()
|
||||||
self.calculate_net_pay()
|
self.calculate_net_pay()
|
||||||
|
|
||||||
|
|
||||||
def add_earning_for_hourly_wages(self, salary_component):
|
def add_earning_for_hourly_wages(self, salary_component):
|
||||||
default_type = False
|
default_type = False
|
||||||
@@ -233,10 +227,10 @@ class SalarySlip(TransactionBase):
|
|||||||
lwp = actual_lwp
|
lwp = actual_lwp
|
||||||
elif lwp != actual_lwp:
|
elif lwp != actual_lwp:
|
||||||
frappe.msgprint(_("Leave Without Pay does not match with approved Leave Application records"))
|
frappe.msgprint(_("Leave Without Pay does not match with approved Leave Application records"))
|
||||||
|
|
||||||
self.total_working_days = working_days
|
self.total_working_days = working_days
|
||||||
self.leave_without_pay = lwp
|
self.leave_without_pay = lwp
|
||||||
|
|
||||||
payment_days = flt(self.get_payment_days(joining_date, relieving_date)) - flt(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
|
self.payment_days = payment_days > 0 and payment_days or 0
|
||||||
|
|
||||||
@@ -278,7 +272,7 @@ class SalarySlip(TransactionBase):
|
|||||||
holidays = [cstr(i) for i in holidays]
|
holidays = [cstr(i) for i in holidays]
|
||||||
|
|
||||||
return holidays
|
return holidays
|
||||||
|
|
||||||
def calculate_lwp(self, holidays, working_days):
|
def calculate_lwp(self, holidays, working_days):
|
||||||
lwp = 0
|
lwp = 0
|
||||||
holidays = "','".join(holidays)
|
holidays = "','".join(holidays)
|
||||||
@@ -297,7 +291,7 @@ class SalarySlip(TransactionBase):
|
|||||||
""".format(holidays), {"employee": self.employee, "dt": dt})
|
""".format(holidays), {"employee": self.employee, "dt": dt})
|
||||||
if leave:
|
if leave:
|
||||||
lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
|
lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
|
||||||
return lwp
|
return lwp
|
||||||
|
|
||||||
def check_existing(self):
|
def check_existing(self):
|
||||||
if not self.salary_slip_based_on_timesheet:
|
if not self.salary_slip_based_on_timesheet:
|
||||||
@@ -371,7 +365,7 @@ class SalarySlip(TransactionBase):
|
|||||||
timesheet.flags.ignore_validate_update_after_submit = True
|
timesheet.flags.ignore_validate_update_after_submit = True
|
||||||
timesheet.set_status()
|
timesheet.set_status()
|
||||||
timesheet.save()
|
timesheet.save()
|
||||||
|
|
||||||
def set_status(self, status=None):
|
def set_status(self, status=None):
|
||||||
'''Get and update status'''
|
'''Get and update status'''
|
||||||
if not status:
|
if not status:
|
||||||
@@ -387,7 +381,7 @@ class SalarySlip(TransactionBase):
|
|||||||
status = "Paid"
|
status = "Paid"
|
||||||
elif self.docstatus == 2:
|
elif self.docstatus == 2:
|
||||||
status = "Cancelled"
|
status = "Cancelled"
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def unlink_ref_doc_from_salary_slip(ref_no):
|
def unlink_ref_doc_from_salary_slip(ref_no):
|
||||||
linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`
|
linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
frappe.listview_settings['Salary Slip'] = {
|
frappe.listview_settings['Salary Slip'] = {
|
||||||
add_fields: ["employee", "employee_name", "fiscal_year", "month"],
|
add_fields: ["employee", "employee_name"],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
make_earning_salary_component(["Basic Salary", "Allowance", "HRA"])
|
make_earning_salary_component(["Basic Salary", "Allowance", "HRA"])
|
||||||
make_deduction_salary_component(["Professional Tax", "TDS"])
|
make_deduction_salary_component(["Professional Tax", "TDS"])
|
||||||
|
|
||||||
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
|
for dt in ["Leave Application", "Leave Allocation", "Salary Slip"]:
|
||||||
frappe.db.sql("delete from `tab%s`" % dt)
|
frappe.db.sql("delete from `tab%s`" % dt)
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
self.assertEquals(ss.deductions[1].amount, 2500)
|
self.assertEquals(ss.deductions[1].amount, 2500)
|
||||||
self.assertEquals(ss.gross_pay, 10500)
|
self.assertEquals(ss.gross_pay, 10500)
|
||||||
self.assertEquals(ss.net_pay, 3000)
|
self.assertEquals(ss.net_pay, 3000)
|
||||||
|
|
||||||
def test_payment_days(self):
|
def test_payment_days(self):
|
||||||
no_of_days = self.get_no_of_days()
|
no_of_days = self.get_no_of_days()
|
||||||
# Holidays not included in working days
|
# Holidays not included in working days
|
||||||
@@ -86,10 +86,13 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
date_of_joining = getdate(nowdate())
|
date_of_joining = getdate(nowdate())
|
||||||
relieving_date = 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",
|
||||||
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "relieving_date", None)
|
{"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"), "status", "Active")
|
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",
|
ss = frappe.get_doc("Salary Slip",
|
||||||
self.make_employee_salary_slip("test_employee@salary.com", "Monthly"))
|
self.make_employee_salary_slip("test_employee@salary.com", "Monthly"))
|
||||||
|
|
||||||
@@ -101,7 +104,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
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"), "relieving_date", relieving_date)
|
||||||
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Left")
|
frappe.db.set_value("Employee", frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}, "name"), "status", "Left")
|
||||||
ss.save()
|
ss.save()
|
||||||
|
|
||||||
self.assertEquals(ss.total_working_days, no_of_days[0])
|
self.assertEquals(ss.total_working_days, no_of_days[0])
|
||||||
self.assertEquals(ss.payment_days, getdate(relieving_date).day)
|
self.assertEquals(ss.payment_days, getdate(relieving_date).day)
|
||||||
|
|
||||||
@@ -190,7 +193,7 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
"from_date": fiscal_year[1],
|
"from_date": fiscal_year[1],
|
||||||
"to_date": fiscal_year[2],
|
"to_date": fiscal_year[2],
|
||||||
"weekly_off": "Sunday"
|
"weekly_off": "Sunday"
|
||||||
}).insert()
|
}).insert()
|
||||||
holiday_list.get_weekly_off_dates()
|
holiday_list.get_weekly_off_dates()
|
||||||
holiday_list.save()
|
holiday_list.save()
|
||||||
|
|
||||||
@@ -198,12 +201,11 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
employee = frappe.db.get_value("Employee", {"user_id": user})
|
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_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})})
|
salary_slip = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})})
|
||||||
|
|
||||||
if not salary_slip:
|
if not salary_slip:
|
||||||
salary_slip = make_salary_slip(salary_structure, employee = employee)
|
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.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.payroll_frequency = payroll_frequency
|
||||||
salary_slip.start_date = nowdate()
|
|
||||||
salary_slip.posting_date = nowdate()
|
salary_slip.posting_date = nowdate()
|
||||||
salary_slip.insert()
|
salary_slip.insert()
|
||||||
# salary_slip.submit()
|
# salary_slip.submit()
|
||||||
@@ -220,9 +222,10 @@ class TestSalarySlip(unittest.TestCase):
|
|||||||
|
|
||||||
def get_no_of_days(self):
|
def get_no_of_days(self):
|
||||||
no_of_days_in_month = calendar.monthrange(getdate(nowdate()).year,
|
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,
|
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]
|
return [no_of_days_in_month[1], no_of_holidays_in_month]
|
||||||
|
|
||||||
|
|
||||||
@@ -280,7 +283,7 @@ def get_employee_details(employee):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_earnings_component():
|
def get_earnings_component():
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"salary_component": 'Basic Salary',
|
"salary_component": 'Basic Salary',
|
||||||
@@ -310,8 +313,8 @@ def get_earnings_component():
|
|||||||
"idx": 4
|
"idx": 4
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_deductions_component():
|
def get_deductions_component():
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"salary_component": 'Professional Tax',
|
"salary_component": 'Professional Tax',
|
||||||
|
|||||||
@@ -100,7 +100,7 @@
|
|||||||
"bold": 0,
|
"bold": 0,
|
||||||
"collapsible": 0,
|
"collapsible": 0,
|
||||||
"columns": 0,
|
"columns": 0,
|
||||||
"default": "",
|
"default": "Monthly",
|
||||||
"depends_on": "eval:(!doc.salary_slip_based_on_timesheet)",
|
"depends_on": "eval:(!doc.salary_slip_based_on_timesheet)",
|
||||||
"fieldname": "payroll_frequency",
|
"fieldname": "payroll_frequency",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
@@ -894,7 +894,7 @@
|
|||||||
"issingle": 0,
|
"issingle": 0,
|
||||||
"istable": 0,
|
"istable": 0,
|
||||||
"max_attachments": 0,
|
"max_attachments": 0,
|
||||||
"modified": "2016-12-07 14:57:22.083825",
|
"modified": "2016-12-14 02:02:10.848614",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Salary Structure",
|
"name": "Salary Structure",
|
||||||
|
|||||||
@@ -7,15 +7,17 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
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):
|
class CurrencyExchange(Document):
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
self.name = formatdate(get_datetime_str(self.date),"yyyy-MM-dd") + "-" + self.from_currency + "-" + self.to_currency
|
if not self.date:
|
||||||
#self.name = self.date + "-" + self.from_currency + "-" + self.to_currency
|
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):
|
def validate(self):
|
||||||
self.validate_value("exchange_rate", ">", 0)
|
self.validate_value("exchange_rate", ">", 0)
|
||||||
|
|
||||||
if self.from_currency == self.to_currency:
|
if self.from_currency == self.to_currency:
|
||||||
frappe.throw(_("From Currency and To Currency cannot be same"))
|
frappe.throw(_("From Currency and To Currency cannot be same"))
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ def install(country=None):
|
|||||||
'is_group': 0, 'parent_item_group': _('All Item Groups') },
|
'is_group': 0, 'parent_item_group': _('All Item Groups') },
|
||||||
|
|
||||||
# salary component
|
# salary component
|
||||||
{'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax')},
|
{'doctype': 'Salary Component', 'salary_component': _('Income Tax'), 'description': _('Income Tax'), 'type': 'Deduction'},
|
||||||
{'doctype': 'Salary Component', 'salary_component': _('Basic'), 'description': _('Basic')},
|
{'doctype': 'Salary Component', 'salary_component': _('Basic'), 'description': _('Basic'), 'type': 'Earning'},
|
||||||
|
|
||||||
# expense claim type
|
# expense claim type
|
||||||
{'doctype': 'Expense Claim Type', 'name': _('Calls'), 'expense_type': _('Calls')},
|
{'doctype': 'Expense Claim Type', 'name': _('Calls'), 'expense_type': _('Calls')},
|
||||||
@@ -186,9 +186,6 @@ def install(country=None):
|
|||||||
|
|
||||||
{'doctype': "Print Heading", 'print_heading': _("Credit Note")},
|
{'doctype': "Print Heading", 'print_heading': _("Credit Note")},
|
||||||
{'doctype': "Print Heading", 'print_heading': _("Debit 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
|
from erpnext.setup.setup_wizard.industry_type import get_industry_types
|
||||||
|
|||||||
Reference in New Issue
Block a user