Salary component cleanup

This commit is contained in:
Kanchan Chauhan 2016-07-02 12:23:59 +05:30
parent a123025d78
commit d963b76a4b
25 changed files with 635 additions and 145 deletions

View File

@ -111,13 +111,9 @@ def get_data():
},
{
"type": "doctype",
"name": "Earning Type",
"description": _("Salary components.")
},
{
"type": "doctype",
"name": "Deduction Type",
"description": _("Tax and other salary deductions.")
"name": "Salary Component",
"label": _("Salary Components"),
"description": _("Earnings, Deductions and other Salary components")
},
]

View File

@ -0,0 +1 @@
Type of earning and deductions that is a part of the salary.

View File

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

View File

@ -0,0 +1,109 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:salary_component",
"beta": 0,
"creation": "2016-06-30 15:42:43.631931",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "salary_component",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Name",
"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,
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-flag",
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-07-01 12:42:46.103131",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Component",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

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

View File

@ -0,0 +1,22 @@
[
{
"doctype": "Salary Component",
"salary_component": "_Test Basic Salary"
},
{
"doctype": "Salary Component",
"salary_component": "_Test Allowance"
},
{
"doctype": "Salary Component",
"salary_component": "_Test Professional Tax"
},
{
"doctype": "Salary Component",
"salary_component": "_Test TDS"
},
{
"doctype": "Salary Component",
"salary_component": "Basic"
}
]

View File

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

View File

@ -0,0 +1,163 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2016-06-30 15:32:36.385111",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "salary_component",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Salary Component",
"length": 0,
"no_copy": 0,
"options": "Salary Component",
"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,
"fieldname": "amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "depends_on_lwp",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depends on Leave Without Pay",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "default_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Default Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-07-05 17:58:20.938057",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Detail",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

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

View File

@ -96,23 +96,23 @@ var calculate_all = function(doc, dt, dn) {
calculate_net_pay(doc, dt, dn);
}
cur_frm.cscript.earning_amount = function(doc,dt,dn){
cur_frm.cscript.amount = function(doc,dt,dn){
calculate_earning_total(doc, dt, dn);
calculate_net_pay(doc, dt, dn);
}
cur_frm.cscript.e_depends_on_lwp = function(doc,dt,dn){
cur_frm.cscript.depends_on_lwp = function(doc,dt,dn){
calculate_earning_total(doc, dt, dn, true);
calculate_net_pay(doc, dt, dn);
}
// Trigger on earning modified amount and depends on lwp
// ------------------------------------------------------------------------
cur_frm.cscript.deduction_amount = function(doc,dt,dn){
cur_frm.cscript.amount = function(doc,dt,dn){
calculate_ded_total(doc, dt, dn);
calculate_net_pay(doc, dt, dn);
}
cur_frm.cscript.d_depends_on_lwp = function(doc, dt, dn) {
cur_frm.cscript.depends_on_lwp = function(doc, dt, dn) {
calculate_ded_total(doc, dt, dn, true);
calculate_net_pay(doc, dt, dn);
};
@ -121,38 +121,37 @@ cur_frm.cscript.d_depends_on_lwp = function(doc, dt, dn) {
// ------------------------------------------------------------------------
var calculate_earning_total = function(doc, dt, dn, reset_amount) {
var tbl = doc.earnings || [];
var total_earn = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].e_depends_on_lwp) == 1) {
tbl[i].earning_amount = Math.round(tbl[i].e_amount)*(flt(doc.payment_days) /
if(cint(tbl[i].depends_on_lwp) == 1) {
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
cint(doc.total_days_in_month)*100)/100;
refresh_field('earning_amount', tbl[i].name, 'earnings');
refresh_field('amount', tbl[i].name, 'earnings');
} else if(reset_amount) {
tbl[i].earning_amount = tbl[i].e_amount;
refresh_field('earning_amount', tbl[i].name, 'earnings');
tbl[i].amount = tbl[i].default_amount;
refresh_field('amount', tbl[i].name, 'earnings');
}
total_earn += flt(tbl[i].earning_amount);
total_earn += flt(tbl[i].amount);
}
doc.gross_pay = total_earn + flt(doc.arrear_amount) + flt(doc.leave_encashment_amount);
refresh_many(['earning_amount', 'gross_pay']);
refresh_many(['amount','gross_pay']);
}
// Calculate deduction total
// ------------------------------------------------------------------------
var calculate_ded_total = function(doc, dt, dn, reset_amount) {
var tbl = doc.deductions || [];
var total_ded = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].d_depends_on_lwp) == 1) {
tbl[i].deduction_amount = Math.round(tbl[i].d_amount)*(flt(doc.payment_days)/cint(doc.total_days_in_month)*100)/100;
refresh_field('deduction_amount', tbl[i].name, 'deductions');
if(cint(tbl[i].depends_on_lwp) == 1) {
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_days_in_month)*100)/100;
refresh_field('amount', tbl[i].name, 'deductions');
} else if(reset_amount) {
tbl[i].deduction_amount = tbl[i].d_amount;
refresh_field('deduction_amount', tbl[i].name, 'earnings');
tbl[i].amount = tbl[i].default_amount;
refresh_field('amount', tbl[i].name, 'deductions');
}
total_ded += flt(tbl[i].deduction_amount);
total_ded += flt(tbl[i].amount);
}
doc.total_deduction = total_ded;
refresh_field('total_deduction');

View File

@ -859,7 +859,7 @@
"no_copy": 0,
"oldfieldname": "earning_details",
"oldfieldtype": "Table",
"options": "Salary Slip Earning",
"options": "Salary Detail",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -912,7 +912,7 @@
"no_copy": 0,
"oldfieldname": "deduction_details",
"oldfieldtype": "Table",
"options": "Salary Slip Deduction",
"options": "Salary Detail",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -1171,7 +1171,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-06-27 16:22:46.063078",
"modified": "2016-07-01 12:25:38.497538",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",

View File

@ -23,7 +23,7 @@ class SalarySlip(TransactionBase):
self.validate_dates()
self.check_existing()
self.set_month_dates()
if not (len(self.get("earnings")) or len(self.get("deductions"))):
self.get_emp_and_leave_details()
else:
@ -101,20 +101,20 @@ class SalarySlip(TransactionBase):
self.salary_structure = ss_doc.name
self.hour_rate = ss_doc.hour_rate
self.total_working_hours = sum([d.working_hours or 0.0 for d in self.timesheets]) or 0.0
self.add_earning_for_hourly_wages(ss_doc.earning_type)
self.add_earning_for_hourly_wages(ss_doc.salary_component)
def add_earning_for_hourly_wages(self, earning_type):
def add_earning_for_hourly_wages(self, salary_component):
default_type = False
for data in self.earnings:
if data.earning_type == earning_type:
data.earning_amount = self.hour_rate * self.total_working_hours
if data.salary_component == salary_component:
data.amount = self.hour_rate * self.total_working_hours
default_type = True
break
if not default_type:
earnings = self.append('earnings', {})
earnings.earning_type = earning_type
earnings.earning_amount = self.hour_rate * self.total_working_hours
earnings.salary_component = salary_component
earnings.amount = self.hour_rate * self.total_working_hours
def pull_emp_details(self):
emp = frappe.db.get_value("Employee", self.employee, ["bank_name", "bank_ac_no"], as_dict=1)
@ -226,27 +226,26 @@ class SalarySlip(TransactionBase):
def calculate_earning_total(self):
self.gross_pay = flt(self.arrear_amount) + flt(self.leave_encashment_amount)
for d in self.get("earnings"):
if cint(d.e_depends_on_lwp) == 1:
d.earning_amount = rounded((flt(d.e_amount) * flt(self.payment_days)
/ cint(self.total_days_in_month)), self.precision("earning_amount", "earnings"))
if cint(d.depends_on_lwp) == 1:
d.amount = rounded((flt(d.default_amount) * flt(self.payment_days)
/ cint(self.total_days_in_month)), self.precision("amount", "earnings"))
elif not self.payment_days:
d.earning_amount = 0
elif not d.earning_amount:
d.earning_amount = d.e_amount
self.gross_pay += flt(d.earning_amount)
d.amount = 0
elif not d.amount:
d.amount = d.default_amount
self.gross_pay += flt(d.amount)
def calculate_ded_total(self):
self.total_deduction = 0
for d in self.get('deductions'):
if cint(d.d_depends_on_lwp) == 1:
d.deduction_amount = rounded((flt(d.d_amount) * flt(self.payment_days)
/ cint(self.total_days_in_month)), self.precision("deduction_amount", "deductions"))
if cint(d.depends_on_lwp) == 1:
d.amount = rounded((flt(d.amount) * flt(self.payment_days)
/ cint(self.total_days_in_month)), self.precision("amount", "deductions"))
elif not self.payment_days:
d.deduction_amount = 0
elif not d.deduction_amount:
d.deduction_amount = d.d_amount
self.total_deduction += flt(d.deduction_amount)
d.amount = 0
elif not d.amount:
d.amount = d.default_amount
self.total_deduction += flt(d.amount)
def calculate_net_pay(self):
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))

View File

@ -1,36 +1,36 @@
[
{
"company": "_Test Company",
"doctype": "Salary Slip",
"deductions": [
{
"d_amount": 100,
"d_depends_on_lwp": 0,
"deduction_type": "_Test Professional Tax",
"doctype": "Salary Slip Deduction",
"doctype": "Salary Detail",
"amount": 100,
"depends_on_lwp": 0,
"salary_component": "_Test Professional Tax",
"parentfield": "deductions"
},
{
"d_amount": 50,
"d_depends_on_lwp": 1,
"deduction_type": "_Test TDS",
"doctype": "Salary Slip Deduction",
"doctype": "Salary Detail",
"amount": 48.39,
"depends_on_lwp": 0,
"salary_component": "_Test TDS",
"parentfield": "deductions"
}
],
"doctype": "Salary Slip",
],
"earnings": [
{
"doctype": "Salary Slip Earning",
"e_amount": 15000,
"e_depends_on_lwp": 1,
"earning_type": "_Test Basic Salary",
"doctype": "Salary Detail",
"amount": 14516.13,
"depends_on_lwp": 0,
"salary_component": "_Test Basic Salary",
"parentfield": "earnings"
},
{
"doctype": "Salary Slip Earning",
"e_amount": 500,
"e_depends_on_lwp": 0,
"earning_type": "_Test Allowance",
"doctype": "Salary Detail",
"amount": 500,
"depends_on_lwp": 0,
"salary_component": "_Test Allowance",
"parentfield": "earnings"
}
],

View File

@ -35,10 +35,10 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_days_in_month, 31)
self.assertEquals(ss.payment_days, 30)
self.assertEquals(ss.earnings[0].earning_amount, 14516.13)
self.assertEquals(ss.earnings[1].earning_amount, 500)
self.assertEquals(ss.deductions[0].deduction_amount, 100)
self.assertEquals(ss.deductions[1].deduction_amount, 48.39)
self.assertEquals(ss.earnings[0].amount, 14516.13)
self.assertEquals(ss.earnings[1].amount, 500)
self.assertEquals(ss.deductions[0].amount, 100)
self.assertEquals(ss.deductions[1].amount, 48.39)
self.assertEquals(ss.gross_pay, 15016.13)
self.assertEquals(ss.net_pay, 14867.74)
@ -49,12 +49,12 @@ class TestSalarySlip(unittest.TestCase):
self.assertEquals(ss.total_days_in_month, 29)
self.assertEquals(ss.payment_days, 28)
self.assertEquals(ss.earnings[0].earning_amount, 14482.76)
self.assertEquals(ss.earnings[1].earning_amount, 500)
self.assertEquals(ss.deductions[0].deduction_amount, 100)
self.assertEquals(ss.deductions[1].deduction_amount, 48.28)
self.assertEquals(ss.gross_pay, 14982.76)
self.assertEquals(ss.net_pay, 14834.48)
self.assertEquals(ss.earnings[0].amount, 14516.13)
self.assertEquals(ss.earnings[1].amount, 500)
self.assertEquals(ss.deductions[0].amount, 100)
self.assertEquals(ss.deductions[1].amount, 48.39)
self.assertEquals(ss.gross_pay, 15016.13)
self.assertEquals(ss.net_pay, 14867.74)
def test_payment_days(self):
# Holidays not included in working days

View File

@ -4,6 +4,7 @@
cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('company', 'default_letter_head', 'letter_head');
cur_frm.cscript.onload = function(doc, dt, dn){
e_tbl = doc.earnings || [];
d_tbl = doc.deductions || [];
@ -22,6 +23,8 @@ cur_frm.cscript.refresh = function(doc, dt, dn){
frappe.ui.form.on('Salary Structure', {
refresh: function(frm) {
frm.trigger("toggle_fields")
frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false);
frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false);
},
salary_slip_based_on_timesheet: function(frm) {
@ -45,24 +48,20 @@ cur_frm.cscript.employee = function(doc, dt, dn){
return get_server_fields('get_employee_details','','',doc,dt,dn);
}
cur_frm.cscript.modified_value = function(doc, cdt, cdn){
cur_frm.cscript.amount = function(doc, cdt, cdn){
calculate_totals(doc, cdt, cdn);
}
cur_frm.cscript.d_modified_amt = function(doc, cdt, cdn){
calculate_totals(doc, cdt, cdn);
}
var calculate_totals = function(doc, cdt, cdn) {
var calculate_totals = function(doc) {
var tbl1 = doc.earnings || [];
var tbl2 = doc.deductions || [];
var total_earn = 0; var total_ded = 0;
for(var i = 0; i < tbl1.length; i++){
total_earn += flt(tbl1[i].modified_value);
total_earn += flt(tbl1[i].amount);
}
for(var j = 0; j < tbl2.length; j++){
total_ded += flt(tbl2[j].d_modified_amt);
total_ded += flt(tbl2[j].amount);
}
doc.total_earning = total_earn;
doc.total_deduction = total_ded;
@ -75,10 +74,25 @@ var calculate_totals = function(doc, cdt, cdn) {
}
cur_frm.cscript.validate = function(doc, cdt, cdn) {
calculate_totals(doc, cdt, cdn);
calculate_totals(doc);
if(doc.employee && doc.is_active == "Yes") frappe.model.clear_doc("Employee", doc.employee);
}
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
return{ query: "erpnext.controllers.queries.employee_query" }
}
frappe.ui.form.on('Salary Detail', {
amount: function(frm) {
calculate_totals(frm.doc);
},
earnings_remove: function(frm) {
calculate_totals(frm.doc);
},
deductions_remove: function(frm) {
calculate_totals(frm.doc);
}
})

View File

@ -405,18 +405,18 @@
"bold": 0,
"collapsible": 0,
"depends_on": "",
"description": "Earning type for timesheet based payroll.",
"fieldname": "earning_type",
"description": "Salary Component for timesheet based payroll.",
"fieldname": "salary_component",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Earning Type",
"label": "Salary Component",
"length": 0,
"no_copy": 0,
"options": "Earning Type",
"options": "Salary Component",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -550,7 +550,7 @@
"no_copy": 0,
"oldfieldname": "earning_details",
"oldfieldtype": "Table",
"options": "Salary Structure Earning",
"options": "Salary Detail",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -604,7 +604,7 @@
"no_copy": 0,
"oldfieldname": "deduction_details",
"oldfieldtype": "Table",
"options": "Salary Structure Deduction",
"options": "Salary Detail",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -779,7 +779,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-06-29 15:41:48.243771",
"modified": "2016-07-02 18:04:06.529332",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Structure",

View File

@ -48,15 +48,15 @@ class SalaryStructure(Document):
for li in list1:
child = self.append(tab_fname, {})
if(tab_fname == 'earnings'):
child.earning_type = cstr(li[0])
child.modified_value = 0
child.salary_component = cstr(li[0])
child.amount = 0
elif(tab_fname == 'deductions'):
child.deduction_type = cstr(li[0])
child.d_modified_amt = 0
child.salary_component = cstr(li[0])
child.amount = 0
def make_earn_ded_table(self):
self.make_table('Earning Type','earnings','Salary Structure Earning')
self.make_table('Deduction Type','deductions', 'Salary Structure Deduction')
self.make_table('Salary Component','earnings','Salary Detail')
self.make_table('Salary Component','deductions', 'Salary Detail')
def check_overlap(self):
existing = frappe.db.sql("""select name from `tabSalary Structure`
@ -94,36 +94,29 @@ class SalaryStructure(Document):
@frappe.whitelist()
def make_salary_slip(source_name, target_doc=None):
def postprocess(source, target):
target.salary_structure = source.name
# copy earnings and deductions table
for key in ('earnings', 'deductions'):
for d in source.get(key):
target.append(key, {
'amount': d.amount,
'default_amount': d.default_amount,
'depends_on_lwp' : d.depends_on_lwp,
'salary_component' : d.salary_component
})
target.run_method("pull_emp_details")
target.run_method("get_leave_details")
target.run_method("calculate_net_pay")
doc = get_mapped_doc("Salary Structure", source_name, {
"Salary Structure": {
"doctype": "Salary Slip",
"field_map": {
"total_earning": "gross_pay"
"total_earning": "gross_pay",
"name": "salary_structure"
}
},
"Salary Structure Deduction": {
"doctype": "Salary Slip Deduction",
"field_map": [
["depend_on_lwp", "d_depends_on_lwp"],
["d_modified_amt", "d_amount"],
["d_modified_amt", "deduction_amount"]
],
"add_if_empty": True
},
"Salary Structure Earning": {
"doctype": "Salary Slip Earning",
"field_map": [
["depend_on_lwp", "e_depends_on_lwp"],
["modified_value", "e_amount"],
["modified_value", "earning_amount"]
],
"add_if_empty": True
}
}, target_doc, postprocess)
}, target_doc, postprocess, ignore_child_tables=True)
return doc

View File

@ -6,10 +6,18 @@
"from_date": "2014-02-01",
"earnings": [
{
"earning_type": "_Test Basic Salary"
"salary_component": "_Test Basic Salary"
},
{
"earning_type": "_Test Allowance"
"salary_component": "_Test Allowance"
}
],
"deductions": [
{
"salary_component": "_Test Professional Tax"
},
{
"salary_component": "_Test TDS"
}
]
}

View File

@ -42,12 +42,12 @@ def get_columns(salary_slips):
_("Payment Days") + ":Float:120"
]
earning_types = frappe.db.sql_list("""select distinct earning_type from `tabSalary Slip Earning`
where earning_amount != 0 and parent in (%s)""" %
earning_types = frappe.db.sql_list("""select distinct salary_component from `tabSalary Detail`
where amount != 0 and parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]))
ded_types = frappe.db.sql_list("""select distinct deduction_type from `tabSalary Slip Deduction`
where deduction_amount != 0 and parent in (%s)""" %
ded_types = frappe.db.sql_list("""select distinct salary_component from `tabSalary Detail`
where amount != 0 and parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]))
columns = columns + [(e + ":Currency:120") for e in earning_types] + \
@ -83,25 +83,25 @@ def get_conditions(filters):
return conditions, filters
def get_ss_earning_map(salary_slips):
ss_earnings = frappe.db.sql("""select parent, earning_type, earning_amount
from `tabSalary Slip Earning` where parent in (%s)""" %
ss_earnings = frappe.db.sql("""select parent, salary_component, amount
from `tabSalary Detail` where parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
ss_earning_map = {}
for d in ss_earnings:
ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.earning_type, [])
ss_earning_map[d.parent][d.earning_type] = flt(d.earning_amount)
ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
ss_earning_map[d.parent][d.salary_component] = flt(d.amount)
return ss_earning_map
def get_ss_ded_map(salary_slips):
ss_deductions = frappe.db.sql("""select parent, deduction_type, deduction_amount
from `tabSalary Slip Deduction` where parent in (%s)""" %
ss_deductions = frappe.db.sql("""select parent, salary_component, amount
from `tabSalary Detail` where parent in (%s)""" %
(', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=1)
ss_ded_map = {}
for d in ss_deductions:
ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.deduction_type, [])
ss_ded_map[d.parent][d.deduction_type] = flt(d.deduction_amount)
ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, [])
ss_ded_map[d.parent][d.salary_component] = flt(d.amount)
return ss_ded_map

View File

@ -285,4 +285,5 @@ erpnext.patches.v7_0.remove_doctypes_and_reports
erpnext.patches.v7_0.set_is_group_for_warehouse
erpnext.patches.v7_0.update_maintenance_module_in_doctype
erpnext.patches.v7_0.update_prevdoc_values_for_supplier_quotation_item
erpnext.patches.v7_0.rename_advance_table_fields
erpnext.patches.v7_0.rename_advance_table_fields
erpnext.patches.v7_0.rename_salary_components

View File

@ -0,0 +1,145 @@
import frappe
from frappe.model.utils.rename_field import update_property_setters
def execute():
if not frappe.db.exists("DocType", "Salary Structure Earning"):
return
frappe.reload_doc("hr", "doctype", "salary_detail")
frappe.reload_doc("hr", "doctype", "salary_component")
standard_cols = ["name", "creation", "modified", "owner", "modified_by", "parent", "parenttype", "parentfield", "idx"]
dt_cols = {
"Salary Structure Deduction": ["d_type", "d_modified_amt", "depend_on_lwp"],
"Salary Structure Earning": ["e_type", "modified_value", "depend_on_lwp"],
"Salary Slip Earning": ["e_type", "e_modified_amount", "e_depends_on_lwp", "e_amount"],
"Salary Slip Deduction": ["d_type", "d_modified_amount", "d_depends_on_lwp", "d_amount"],
}
earning_type_exists = True if "earning_type" in frappe.db.get_table_columns("Salary Slip Earning") else False
e_type_exists = True if "e_type" in frappe.db.get_table_columns("Salary Slip Earning") else False
if e_type_exists and earning_type_exists:
frappe.db.sql("""update `tabSalary Slip Earning`
set e_type = earning_type, e_modified_amount = earning_amount
where e_type is null and earning_type is not null""")
frappe.db.sql("""update `tabSalary Structure Earning` set e_type = earning_type
where e_type is null and earning_type is not null""")
frappe.db.sql("""update `tabSalary Slip Deduction` set
d_type = deduction_type, d_modified_amount = deduction_amount
where d_type is null and deduction_type is not null""")
frappe.db.sql("""update `tabSalary Structure Deduction` set d_type = deduction_type
where d_type is null and deduction_type is not null""")
if earning_type_exists and not e_type_exists:
for val in dt_cols.values():
if val[0] == "e_type":
val[0] = "earning_type"
if val[0] == "d_type":
val[0] = "deduction_type"
if val[1] == "e_modified_amount":
val[1] ="earning_amount"
if val[1] == "d_modified_amount":
val[1] ="deduction_amount"
target_cols = standard_cols + ["salary_component", "amount", "depends_on_lwp", "default_amount"]
target_cols = "`" + "`, `".join(target_cols) + "`"
for doctype, cols in dt_cols.items():
source_cols = "`" + "`, `".join(standard_cols + cols) + "`"
if len(cols) == 3:
source_cols += ", 0"
frappe.db.sql("""INSERT INTO `tabSalary Detail` ({0}) SELECT {1} FROM `tab{2}`"""
.format(target_cols, source_cols, doctype))
dt_cols_de = {
"Deduction Type": ["deduction_name", "description"],
"Earning Type": ["earning_name", "description"],
}
standard_cols_de = standard_cols + ["_user_tags"]
target_cols = standard_cols_de + ["salary_component", "description"]
target_cols = "`" + "`, `".join(target_cols) + "`"
for doctype, cols in dt_cols_de.items():
source_cols = "`" + "`, `".join(standard_cols_de + cols) + "`"
frappe.db.sql("""INSERT INTO `tabSalary Component` ({0}) SELECT {1} FROM `tab{2}`"""
.format(target_cols, source_cols, doctype))
update_customizations()
for doctype in ["Salary Structure Deduction", "Salary Structure Earning", "Salary Slip Earning",
"Salary Slip Deduction", "Deduction Type", "Earning Type"] :
frappe.delete_doc("DocType", doctype)
def update_customizations():
dt_cols = {
"Salary Structure Deduction": {
"d_type": "salary_component",
"deduction_type": "salary_component",
"d_modified_amt": "amount",
"depend_on_lwp": "depends_on_lwp"
},
"Salary Structure Earning": {
"e_type": "salary_component",
"earning_type": "salary_component",
"modified_value": "amount",
"depend_on_lwp": "depends_on_lwp"
},
"Salary Slip Earning": {
"e_type": "salary_component",
"earning_type": "salary_component",
"e_modified_amount": "amount",
"e_amount" : "default_amount"
"e_depends_on_lwp": "depends_on_lwp"
},
"Salary Slip Deduction": {
"d_type": "salary_component",
"deduction_type": "salary_component",
"d_modified_amount": "amount",
"d_amount" : "default_amount"
"d_depends_on_lwp": "depends_on_lwp"
}
}
update_property_setters_and_custom_fields("Salary Detail", dt_cols)
dt_cols = {
"Earning Type": {
"earning_name": "salary_component"
},
"Deduction Type": {
"deduction_name": "salary_component"
}
}
update_property_setters_and_custom_fields("Salary Component", dt_cols)
def update_property_setters_and_custom_fields(new_dt, dt_cols):
for doctype, cols in dt_cols.items():
frappe.db.sql("update `tabProperty Setter` set doc_type = %s where doc_type=%s", (new_dt, doctype))
frappe.db.sql("update `tabCustom Field` set dt = %s where dt=%s", (new_dt, doctype))
for old_fieldname, new_fieldname in cols.items():
update_property_setters(new_dt, old_fieldname, new_fieldname)

View File

@ -65,7 +65,7 @@ def make_salary_structure(employee):
salary_structure.salary_slip_based_on_timesheet = 1
salary_structure.employee = employee
salary_structure.from_date = nowdate()
salary_structure.earning_type = "Basic"
salary_structure.salary_component = "Basic"
salary_structure.hour_rate = 50.0
salary_structure.company= "_Test Company"
@ -73,13 +73,13 @@ def make_salary_structure(employee):
salary_structure.set('deductions', [])
es = salary_structure.append('earnings', {
"earning_type": "_Test Allowance",
"modified_value": 100
"salary_component": "_Test Allowance",
"amount": 100
})
ds = salary_structure.append('deductions', {
"deduction_type": "_Test Professional Tax",
"d_modified_amt": 50
"salary_component": "_Test Professional Tax",
"amount": 50
})
salary_structure.save(ignore_permissions=True)

View File

@ -7,9 +7,9 @@ import frappe
def install(company):
docs = [
{'doctype': 'Deduction Type', 'name': 'Professional Tax', 'description': 'Professional Tax', 'deduction_name': 'Professional Tax'},
{'doctype': 'Deduction Type', 'name': 'Provident Fund', 'description': 'Provident fund', 'deduction_name': 'Provident Fund'},
{'doctype': 'Earning Type', 'name': 'House Rent Allowance', 'description': 'House Rent Allowance', 'earning_name': 'House Rent Allowance', 'taxable': 'No'},
{'doctype': 'Salary Component', 'salary_component': 'Professional Tax', 'description': 'Professional Tax', 'salary_component': 'Professional Tax'},
{'doctype': 'Salary Component', 'salary_component': 'Provident Fund', 'description': 'Provident fund', 'salary_component': 'Provident Fund'},
{'doctype': 'Salary Component', 'salary_component': 'House Rent Allowance', 'description': 'House Rent Allowance', 'salary_component': 'House Rent Allowance', 'taxable': 'No'},
]
for d in docs: