Fixed merge conflict

This commit is contained in:
Nabin Hait
2016-10-17 16:42:49 +05:30
21 changed files with 738 additions and 195 deletions

View File

@@ -1,6 +1,62 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Process Payroll", {
refresh: function(frm) {
frm.disable_save();
frm.trigger("toggle_fields");
frm.trigger("set_month_dates");
},
month: function(frm) {
frm.trigger("set_month_dates");
},
fiscal_year: function(frm) {
frm.trigger("set_month_dates");
},
salary_slip_based_on_timesheet: function(frm) {
frm.trigger("toggle_fields")
},
toggle_fields: function(frm) {
frm.toggle_display(['from_date','to_date'],
cint(frm.doc.salary_slip_based_on_timesheet)==1);
frm.toggle_display(['fiscal_year', 'month'],
cint(frm.doc.salary_slip_based_on_timesheet)==0);
},
set_month_dates: function(frm) {
if (!frm.doc.salary_slip_based_on_timesheet){
frappe.call({
method:'erpnext.hr.doctype.process_payroll.process_payroll.get_month_details',
args:{
year: frm.doc.fiscal_year,
month: frm.doc.month
},
callback: function(r){
if (r.message){
frm.set_value('from_date', r.message.month_start_date);
frm.set_value('to_date', r.message.month_end_date);
}
}
})
}
}
})
cur_frm.cscript.onload = function(doc,cdt,cdn){
if(!doc.month) {
var today=new Date();
month = (today.getMonth()+01).toString();
if(month.length>1) doc.month = month;
else doc.month = '0'+month;
}
if(!doc.fiscal_year) doc.fiscal_year = sys_defaults['fiscal_year'];
refresh_many(['month', 'fiscal_year']);
}
cur_frm.cscript.display_activity_log = function(msg) {
if(!cur_frm.ss_html)
cur_frm.ss_html = $a(cur_frm.fields_dict['activity_log'].wrapper,'div');
@@ -26,7 +82,7 @@ cur_frm.cscript.create_salary_slip = function(doc, cdt, cdn) {
cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
cur_frm.cscript.display_activity_log("");
frappe.confirm(__("Do you really want to Submit all Salary Slip for month {0} and year {1}", [doc.month, doc.fiscal_year]), function() {
frappe.confirm(__("Do you really want to Submit all Salary Slip for Account {0} from {1} to {2}", [doc.payment_account, doc.from_date, doc.to_date]), function() {
// clear all in locals
if(locals["Salary Slip"]) {
$.each(locals["Salary Slip"], function(name, d) {
@@ -44,22 +100,45 @@ cur_frm.cscript.submit_salary_slip = function(doc, cdt, cdn) {
}
cur_frm.cscript.make_bank_entry = function(doc,cdt,cdn){
if(doc.company && doc.month && doc.fiscal_year){
cur_frm.cscript.make_jv(doc, cdt, cdn);
if(doc.company && doc.from_date && doc.to_date){
return cur_frm.cscript.reference_entry(doc,cdt,cdn);
} else {
msgprint(__("Company, Month and Fiscal Year is mandatory"));
msgprint(__("Company, From Date and To Date is mandatory"));
}
}
cur_frm.cscript.make_jv = function(doc, dt, dn) {
return $c_obj(doc, 'make_journal_entry', '', function(r) {
var doc = frappe.model.sync(r.message)[0];
frappe.set_route("Form", doc.doctype, doc.name);
cur_frm.cscript.reference_entry = function(doc,cdt,cdn){
var dialog = new frappe.ui.Dialog({
title: __("Bank Transaction Reference"),
fields: [
{
"label": __("Reference Number"),
"fieldname": "reference_number",
"fieldtype": "Data",
"reqd": 1
},
{
"label": __("Reference Date"),
"fieldname": "reference_date",
"fieldtype": "Date",
"reqd": 1,
"default": get_today()
}
]
});
}
frappe.ui.form.on("Process Payroll", {
refresh: function(frm) {
frm.disable_save();
}
})
dialog.set_primary_action(__("Make"), function() {
args = dialog.get_values();
if(!args) return;
dialog.hide();
return frappe.call({
doc: cur_frm.doc,
method: "make_journal_entry",
args: {"reference_number": args.reference_number, "reference_date":args.reference_date},
callback: function(r) {
if (r.message)
cur_frm.cscript.display_activity_log(r.message);
}
});
});
dialog.show();
}

View File

@@ -8,11 +8,13 @@
"docstatus": 0,
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break0",
"fieldtype": "Section Break",
"hidden": 0,
@@ -37,6 +39,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"hidden": 0,
@@ -61,6 +64,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@@ -86,6 +90,59 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Posting Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "50%"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "branch",
"fieldtype": "Link",
"hidden": 0,
@@ -111,30 +168,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "50%"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "department",
"fieldtype": "Link",
"hidden": 0,
@@ -160,6 +194,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "designation",
"fieldtype": "Link",
"hidden": 0,
@@ -185,6 +220,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
@@ -209,9 +245,10 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "salary_slip_based_on_timesheet",
"fieldtype": "Check",
"hidden": 1,
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
@@ -234,14 +271,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "select_payroll_year_and_month",
"columns": 0,
"fieldname": "select_payroll_period",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Select Payroll Year and Month",
"label": "Select Payroll Period",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -259,6 +297,33 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "From",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "fiscal_year",
"fieldtype": "Link",
"hidden": 0,
@@ -284,6 +349,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
@@ -308,6 +374,33 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_date",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "To",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "month",
"fieldtype": "Select",
"hidden": 0,
@@ -333,32 +426,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Posting Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "process_payroll",
"fieldtype": "Section Break",
"hidden": 0,
@@ -384,6 +452,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break2",
"fieldtype": "Column Break",
"hidden": 0,
@@ -408,6 +477,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Creates salary slip for above mentioned criteria.",
"fieldname": "create_salary_slip",
"fieldtype": "Button",
@@ -433,6 +503,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break3",
"fieldtype": "Column Break",
"hidden": 0,
@@ -457,6 +528,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Submit all salary slips for the above selected criteria",
"fieldname": "submit_salary_slip",
"fieldtype": "Button",
@@ -482,16 +554,19 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break4",
"fieldtype": "Column Break",
"columns": 0,
"fieldname": "account",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -499,13 +574,42 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "25%"
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Select Payment Account to make Bank Entry",
"fieldname": "payment_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Payment Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "payment_account",
"description": "Create Bank Entry for the total salary paid for the above selected criteria",
"fieldname": "make_bank_entry",
"fieldtype": "Button",
@@ -531,6 +635,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break2",
"fieldtype": "Section Break",
"hidden": 0,
@@ -554,6 +659,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "activity_log",
"fieldtype": "HTML",
"hidden": 0,
@@ -586,7 +692,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2016-09-19 15:12:54.090381",
"modified": "2016-09-28 05:43:26.472928",
"modified_by": "Administrator",
"module": "HR",
"name": "Process Payroll",

View File

@@ -5,12 +5,13 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import cint, flt, nowdate
from frappe import _
import collections
from collections import defaultdict
from frappe.model.document import Document
class ProcessPayroll(Document):
def get_emp_list(self):
"""
Returns list of active employees based on selected criteria
@@ -21,19 +22,20 @@ class ProcessPayroll(Document):
sal_struct = frappe.db.sql("""
select name from `tabSalary Structure`
where docstatus != 2 and
ifnull(salary_slip_based_on_timesheet,0) = 0""")
where docstatus != 2 and payment_account = '%(payment_account)s' and
ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s"""%
{"payment_account": self.payment_account, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
if sal_struct:
cond += "and t2.parent IN %(sal_struct)s "
emp_list = frappe.db.sql("""
select t1.name
from `tabEmployee` t1, `tabSalary Structure Employee` t2
where t1.docstatus!=2 and t1.name = t2.employee
%s """% cond, {"sal_struct": sal_struct})
emp_list = frappe.db.sql("""
select t1.name
from `tabEmployee` t1, `tabSalary Structure Employee` t2
where t1.docstatus!=2 and t1.name = t2.employee
%s """% cond, {"sal_struct": sal_struct})
return emp_list
return emp_list
def get_filter_condition(self):
@@ -48,16 +50,15 @@ class ProcessPayroll(Document):
def get_joining_releiving_condition(self):
m = get_month_details(self.fiscal_year, self.month)
cond = """
and ifnull(t1.date_of_joining, '0000-00-00') <= '%(month_end_date)s'
and ifnull(t1.relieving_date, '2199-12-31') >= '%(month_start_date)s'
""" % m
and ifnull(t1.date_of_joining, '0000-00-00') <= '%(from_date)s'
and ifnull(t1.relieving_date, '2199-12-31') >= '%(to_date)s'
""" % {"from_date": self.from_date, "to_date": self.to_date}
return cond
def check_mandatory(self):
for f in ['company', 'month', 'fiscal_year']:
for f in ['company', 'from_date', 'to_date']:
if not self.get(f):
frappe.throw(_("Please set {0}").format(f))
@@ -65,27 +66,37 @@ class ProcessPayroll(Document):
"""
Creates salary slip for selected employees if already not created
"""
self.check_permission('write')
emp_list = self.get_emp_list()
ss_list = []
for emp in emp_list:
if not frappe.db.sql("""select name from `tabSalary Slip`
where docstatus!= 2 and employee = %s and month = %s and fiscal_year = %s and company = %s
""", (emp[0], self.month, self.fiscal_year, self.company)):
ss = frappe.get_doc({
"doctype": "Salary Slip",
"salary_slip_based_on_timesheet": 0,
"fiscal_year": self.fiscal_year,
"employee": emp[0],
"month": self.month,
"company": self.company,
"posting_date": self.posting_date,
})
ss.insert()
ss_list.append(ss.name)
if emp_list:
for emp in emp_list:
if not frappe.db.sql("""select name from `tabSalary Slip`
where docstatus!= 2 and employee = %s and start_date >= %s and end_date <= %s and company = %s
""", (emp[0], self.from_date, self.to_date, self.company)):
if self.salary_slip_based_on_timesheet:
ss = frappe.get_doc({
"doctype": "Salary Slip",
"salary_slip_based_on_timesheet": self.salary_slip_based_on_timesheet,
"start_date": self.from_date,
"end_date": self.to_date,
"employee": emp[0],
"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,
"fiscal_year": self.fiscal_year,
"month": self.month,
"employee": emp[0],
"company": self.company,
"posting_date": self.posting_date
})
ss.insert()
ss_list.append(ss.name)
return self.create_log(ss_list)
@@ -97,16 +108,17 @@ class ProcessPayroll(Document):
return log
def get_sal_slip_list(self):
def get_sal_slip_list(self, ss_status, as_dict=False):
"""
Returns list of salary slips based on selected criteria
which are not submitted
"""
cond = self.get_filter_condition()
ss_list = frappe.db.sql("""
select t1.name from `tabSalary Slip` t1
where t1.docstatus = 0 and month = %s and fiscal_year = %s %s
""" % ('%s', '%s', cond), (self.month, self.fiscal_year))
select t1.name, t1.salary_structure from `tabSalary Slip` t1
where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s
and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s %s
""" % ('%s', '%s', '%s','%s', cond), (ss_status, self.from_date, self.to_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
return ss_list
@@ -116,16 +128,17 @@ class ProcessPayroll(Document):
"""
self.check_permission('write')
ss_list = self.get_sal_slip_list()
ss_list = self.get_sal_slip_list(ss_status=0)
not_submitted_ss = []
for ss in ss_list:
ss_obj = frappe.get_doc("Salary Slip",ss[0])
try:
ss_obj.submit()
except Exception,e:
if ss_obj.net_pay<0:
not_submitted_ss.append(ss[0])
frappe.msgprint(e)
continue
else:
try:
ss_obj.submit()
except Exception,e:
not_submitted_ss.append(ss[0])
return self.create_submit_log(ss_list, not_submitted_ss)
@@ -148,6 +161,7 @@ class ProcessPayroll(Document):
<b>Not Submitted Salary Slips: </b>\
<br><br> %s <br><br> \
Reason: <br>\
May be net pay is less than 0 <br>
May be company email id specified in employee master is not valid. <br> \
Please mention correct email id in employee master or if you don't want to \
send mail, uncheck 'Send Email' checkbox. <br>\
@@ -166,38 +180,105 @@ class ProcessPayroll(Document):
cond = self.get_filter_condition()
tot = frappe.db.sql("""
select sum(rounded_total) from `tabSalary Slip` t1
where t1.docstatus = 1 and month = %s and fiscal_year = %s %s
""" % ('%s', '%s', cond), (self.month, self.fiscal_year))
where t1.docstatus = 1 and start_date >= %s and end_date <= %s %s
""" % ('%s', '%s', cond), (self.from_date, self.to_date))
return flt(tot[0][0])
def make_journal_entry(self, salary_account = None):
def get_salary_component_account(self, salary_component):
account = frappe.db.get_value("Salary Component Account",
{"parent": salary_component, "company": self.company}, "default_account")
if not account:
frappe.throw(_("Please set default account in Salary Component {0}")
.format(salary_component))
return account
def get_salary_components(self, component_type):
salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
if salary_slips:
salary_components = frappe.db.sql("""select salary_component, amount, parentfield
from `tabSalary Detail` where parentfield = '%s' and parent in (%s)""" %
(component_type, ', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=True)
return salary_components
def get_salary_component_total(self, component_type = None):
salary_components = self.get_salary_components(component_type)
if salary_components:
component_dict = {}
for item in salary_components:
component_dict[item['salary_component']] = component_dict.get(item['salary_component'], 0) + item['amount']
account_details = self.get_account(component_dict = component_dict)
return account_details
def get_account(self, component_dict = None):
account_dict = {}
for s, a in component_dict.items():
account = self.get_salary_component_account(s)
account_dict[account] = account_dict.get(account, 0) + a
return account_dict
def make_journal_entry(self, reference_number = None, reference_date = None):
self.check_permission('write')
earnings = self.get_salary_component_total(component_type = "earnings")
deductions = self.get_salary_component_total(component_type = "deductions")
jv_name = ""
amount = self.get_total_salary()
default_bank_account = frappe.db.get_value("Company", self.company,
"default_bank_account")
if earnings or deductions:
journal_entry = frappe.new_doc('Journal Entry')
journal_entry.voucher_type = 'Bank Entry'
journal_entry.user_remark = _('Payment of salary from {0} to {1}').format(self.from_date,
self.to_date)
journal_entry.company = self.company
journal_entry.posting_date = nowdate()
account_amt_list = []
adjustment_amt = 0
for acc, amt in earnings.items():
adjustment_amt = adjustment_amt+amt
account_amt_list.append({
"account": acc,
"debit_in_account_currency": amt
})
for acc, amt in deductions.items():
adjustment_amt = adjustment_amt-amt
account_amt_list.append({
"account": acc,
"credit_in_account_currency": amt
})
account_amt_list.append({
"account": self.payment_account,
"credit_in_account_currency": adjustment_amt
})
journal_entry.set("accounts", account_amt_list)
journal_entry.cheque_no = reference_number
journal_entry.cheque_date = reference_date
journal_entry.save()
try:
journal_entry.submit()
jv_name = journal_entry.name
self.update_salary_slip_status(jv_name = jv_name)
except Exception, e:
frappe.msgprint(e)
return self.create_jv_log(jv_name)
journal_entry = frappe.new_doc('Journal Entry')
journal_entry.voucher_type = 'Bank Entry'
journal_entry.user_remark = _('Payment of salary for the month {0} and year {1}').format(self.month,
self.fiscal_year)
journal_entry.fiscal_year = self.fiscal_year
journal_entry.company = self.company
journal_entry.posting_date = nowdate()
journal_entry.set("accounts", [
{
"account": salary_account,
"debit_in_account_currency": amount
},
{
"account": default_bank_account,
"credit_in_account_currency": amount
},
])
def create_jv_log(self, jv_name):
log = "<p>" + _("No submitted Salary Slip found") + "</p>"
if jv_name:
log = "<b>" + _("Journal Entry Submitted") + "</b>\
%s" % '<br>''<a href="#Form/Journal Entry/{0}">{0}</a>'.format(jv_name)
return log
def update_salary_slip_status(self, jv_name = None):
ss_list = self.get_sal_slip_list(ss_status=1)
for ss in ss_list:
ss_obj = frappe.get_doc("Salary Slip",ss[0])
frappe.db.set_value("Salary Slip", ss_obj.name, "status", "Paid")
frappe.db.set_value("Salary Slip", ss_obj.name, "journal_entry", jv_name)
return journal_entry.as_dict()
@frappe.whitelist()
def get_month_details(year, month):

View File

@@ -0,0 +1,27 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import unittest
import frappe
import erpnext
from frappe.utils import flt, add_months, cint, nowdate, getdate, add_days, random_string
from frappe.utils.make_random import get_random
class TestProcessPayroll(unittest.TestCase):
def test_process_payroll(self):
month = "11"
fiscal_year = "_Test Fiscal Year 2016"
payment_account = frappe.get_all("Account")[0].name
if not frappe.db.get_value("Salary Slip", {"fiscal_year": fiscal_year, "month": month}):
process_payroll = frappe.get_doc("Process Payroll", "Process Payroll")
process_payroll.company = erpnext.get_default_company()
process_payroll.month = month
process_payroll.fiscal_year = fiscal_year
process_payroll.from_date = "2016-11-01"
process_payroll.to_date = "2016-11-30"
process_payroll.payment_account = payment_account
process_payroll.create_sal_slip()
process_payroll.submit_salary_slip()
if process_payroll.get_sal_slip_list(ss_status = 1):
r = process_payroll.make_journal_entry(reference_number=random_string(10),reference_date=nowdate())

View File

@@ -65,6 +65,33 @@
"unique": 0,
"width": "120px"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Type",
"length": 0,
"no_copy": 0,
"options": "Earning\nDeduction",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -91,6 +118,31 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -117,33 +169,6 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Type",
"length": 0,
"no_copy": 0,
"options": "Earning\nDeduction",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
@@ -157,7 +182,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-08-29 05:33:22.495594",
"modified": "2016-08-31 08:08:47.359578",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Component",

View File

@@ -229,6 +229,59 @@
"unique": 0,
"width": "50%"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"options": "Draft\nSubmitted\nPaid\nCancelled",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "journal_entry",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Journal Entry",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@@ -1244,7 +1297,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-09-24 05:16:54.102341",
"modified": "2016-09-25 04:29:14.432800",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",

View File

@@ -20,6 +20,7 @@ class SalarySlip(TransactionBase):
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
def validate(self):
self.status = self.get_status()
self.validate_dates()
self.check_existing()
self.set_month_dates()
@@ -340,11 +341,16 @@ class SalarySlip(TransactionBase):
self.precision("net_pay") if disable_rounded_total else 0)
def on_submit(self):
self.update_status(self.name)
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")):
self.email_salary_slip()
if self.net_pay < 0:
frappe.throw(_("Net Pay cannot be less than 0"))
else:
self.set_status()
self.update_status(self.name)
if(frappe.db.get_single_value("HR Settings", "email_salary_slip_to_employee")):
self.email_salary_slip()
def on_cancel(self):
self.set_status()
self.update_status()
def email_salary_slip(self):
@@ -365,3 +371,29 @@ class SalarySlip(TransactionBase):
timesheet.flags.ignore_validate_update_after_submit = True
timesheet.set_status()
timesheet.save()
def set_status(self, status=None):
'''Get and update status'''
if not status:
status = self.get_status()
self.db_set("status", status)
def get_status(self):
if self.docstatus == 0:
status = "Draft"
elif self.docstatus == 1:
status = "Submitted"
if self.journal_entry:
status = "Paid"
elif self.docstatus == 2:
status = "Cancelled"
return status
def unlink_ref_doc_from_salary_slip(ref_no):
linked_ss = frappe.db.sql_list("""select name from `tabSalary Slip`
where journal_entry=%s and docstatus < 2""", (ref_no))
if linked_ss:
for ss in linked_ss:
ss_doc = frappe.get_doc("Salary Slip", ss)
frappe.db.set_value("Salary Slip", ss_doc.name, "status", "Submitted")
frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "")

View File

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import unittest
import frappe
import erpnext
from frappe.utils.make_random import get_random
from frappe.utils import today, now_datetime, getdate, cstr, add_years, nowdate
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
from erpnext.hr.doctype.leave_application.test_leave_application import make_allocation_record
@@ -203,7 +204,8 @@ def make_salary_structure(sal_struct):
"from_date": nowdate(),
"employees": get_employee_details(),
"earnings": get_earnings_component(),
"deductions": get_deductions_component()
"deductions": get_deductions_component(),
"payment_account": get_random("Account")
}).insert()
return sal_struct

View File

@@ -1,5 +1,6 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
{% include "erpnext/public/js/controllers/accounts.js" %}
cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('company', 'default_letter_head', 'letter_head');

View File

@@ -687,6 +687,111 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "account",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "mode_of_payment",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Mode of Payment",
"length": 0,
"no_copy": 0,
"options": "Mode of Payment",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_28",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "payment_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Payment Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
@@ -700,7 +805,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-08-23 12:49:41.258649",
"modified": "2016-09-06 05:40:18.311581",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure",

View File

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
import unittest
import erpnext
from frappe.utils.make_random import get_random
from frappe.utils import nowdate, add_days, add_years
from erpnext.hr.doctype.salary_structure.salary_structure import make_salary_slip
# test_records = frappe.get_test_records('Salary Structure')
@@ -85,11 +86,11 @@ class TestSalaryStructure(unittest.TestCase):
if not sal_slip:
sal_slip = make_salary_slip_from_salary_structure(employee=frappe.get_value("Employee", {"employee_name":"test_employee@salary.com"}))
self.assertEquals(sal_slip.get("salary_structure"), 'Salary Structure Sample')
self.assertEquals(sal_slip.get("earnings")[0].amount, 0)
self.assertEquals(sal_slip.get("deductions")[0].amount, 0)
self.assertEquals(sal_slip.get("deductions")[1].amount, 0)
self.assertEquals(sal_slip.get("total_deduction"), 0)
self.assertEquals(sal_slip.get("net_pay"), 0)
self.assertEquals(sal_slip.get("earnings")[0].amount, 5000)
self.assertEquals(sal_slip.get("deductions")[0].amount, 5000)
self.assertEquals(sal_slip.get("deductions")[1].amount, 2500)
self.assertEquals(sal_slip.get("total_deduction"), 7500)
self.assertEquals(sal_slip.get("net_pay"), 7500)
def make_salary_slip_from_salary_structure(employee):
@@ -112,7 +113,8 @@ def make_salary_structure(sal_struct):
"from_date": nowdate(),
"employees": get_employee_details(),
"earnings": get_earnings_component(),
"deductions": get_deductions_component()
"deductions": get_deductions_component(),
"payment_account": frappe.get_all("Account")[0].name
}).insert()
return sal_struct
@@ -124,7 +126,7 @@ def get_employee_details():
"idx": 1
},
{"employee": frappe.get_value("Employee", {"employee_name":"test_employee_2@salary.com"}, "name"),
"base": 2100,
"base": 15000,
"variable": 100,
"idx": 2
}
@@ -149,7 +151,7 @@ def get_earnings_component():
{
"salary_component": 'HRA',
"abbr":'H',
"amount": 3000,
"amount": 10000,
"idx": 3
},
{
@@ -173,6 +175,7 @@ def get_deductions_component():
{
"salary_component": 'TDS',
"abbr":'T',
"condition": 'employment_type!="Intern"',
"formula": 'base*.5',
"idx": 2
},