refator: Payroll processing and tax calculation (#17617)

* refactor: Payroll processing and tax calculation

* fix: payroll test cases

* fix: Codacy fixes

* fix: payroll test cases

* fix: removed unwanted code
This commit is contained in:
Nabin Hait 2019-05-16 11:23:04 +05:30 committed by GitHub
parent 0e31089af6
commit 6b9d64ce1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 2866 additions and 2788 deletions

View File

@ -22,7 +22,5 @@ class BankGuarantee(Document):
@frappe.whitelist() @frappe.whitelist()
def get_vouchar_detials(column_list, doctype, docname): def get_vouchar_detials(column_list, doctype, docname):
print (column_list, doctype, docname)
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s''' return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0] .format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]

View File

@ -111,10 +111,10 @@
<th style="width: 4%">{%= __("Age (Days)") %}</th> <th style="width: 4%">{%= __("Age (Days)") %}</th>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %} {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
<th style="width: 16%">{%= __("Reference") %}</th> <th style="width: 14%">{%= __("Reference") %}</th>
<th style="width: 10%">{%= __("Sales Person") %}</th> <th style="width: 10%">{%= __("Sales Person") %}</th>
{% } else { %} {% } else { %}
<th style="width: 26%">{%= __("Reference") %}</th> <th style="width: 24%">{%= __("Reference") %}</th>
{% } %} {% } %}
{% if(!filters.show_pdc_in_print) { %} {% if(!filters.show_pdc_in_print) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th> <th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
@ -127,7 +127,7 @@
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th> <th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
{% if(filters.show_pdc_in_print) { %} {% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %} {% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 10%">{%= __("Customer LPO No.") %}</th> <th style="width: 12%">{%= __("Customer LPO No.") %}</th>
{% } %} {% } %}
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th> <th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th> <th style="width: 10%">{%= __("PDC/LC Amount") %}</th>

View File

@ -3,13 +3,8 @@
frappe.ui.form.on('Additional Salary', { frappe.ui.form.on('Additional Salary', {
setup: function(frm) { setup: function(frm) {
frm.set_query("salary_component", function() { frm.add_fetch("salary_component", "deduct_full_tax_on_selected_payroll_date", "deduct_full_tax_on_selected_payroll_date");
return {
filters: {
is_additional_component: true
}
};
});
frm.set_query("employee", function() { frm.set_query("employee", function() {
return { return {
filters: { filters: {

View File

@ -1,5 +1,6 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0, "allow_guest_to_view": 0,
"allow_import": 1, "allow_import": 1,
"allow_rename": 0, "allow_rename": 0,
@ -21,6 +22,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "", "default": "",
"fetch_if_empty": 0,
"fieldname": "naming_series", "fieldname": "naming_series",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0, "hidden": 0,
@ -54,6 +56,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "employee", "fieldname": "employee",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -87,6 +90,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "salary_component", "fieldname": "salary_component",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -120,6 +124,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "amount", "fieldname": "amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
@ -153,6 +158,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "1", "default": "1",
"fetch_if_empty": 0,
"fieldname": "overwrite_salary_structure_amount", "fieldname": "overwrite_salary_structure_amount",
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
@ -185,6 +191,40 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "deduct_full_tax_on_selected_payroll_date",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Deduct Full Tax on Selected Payroll Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_5", "fieldname": "column_break_5",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
@ -217,6 +257,8 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"default": "", "default": "",
"description": "Date on which this component is applied",
"fetch_if_empty": 0,
"fieldname": "payroll_date", "fieldname": "payroll_date",
"fieldtype": "Date", "fieldtype": "Date",
"hidden": 0, "hidden": 0,
@ -250,6 +292,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_from": "employee.employee_name", "fetch_from": "employee.employee_name",
"fetch_if_empty": 0,
"fieldname": "employee_name", "fieldname": "employee_name",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -284,6 +327,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_from": "employee.department", "fetch_from": "employee.department",
"fetch_if_empty": 0,
"fieldname": "department", "fieldname": "department",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -317,6 +361,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "company", "fieldname": "company",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -350,6 +395,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "salary_slip", "fieldname": "salary_slip",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -384,6 +430,7 @@
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_from": "salary_component.type", "fetch_from": "salary_component.type",
"fetch_if_empty": 0,
"fieldname": "type", "fieldname": "type",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
@ -417,6 +464,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fetch_if_empty": 0,
"fieldname": "amended_from", "fieldname": "amended_from",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
@ -453,7 +501,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2018-08-21 14:44:22.921926", "modified": "2019-05-09 19:53:37.475839",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Additional Salary", "name": "Additional Salary",

View File

@ -41,7 +41,8 @@ class AdditionalSalary(Document):
@frappe.whitelist() @frappe.whitelist()
def get_additional_salary_component(employee, start_date, end_date): def get_additional_salary_component(employee, start_date, end_date):
additional_components = frappe.db.sql(""" additional_components = frappe.db.sql("""
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount from `tabAdditional Salary` select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
from `tabAdditional Salary`
where employee=%(employee)s where employee=%(employee)s
and docstatus = 1 and docstatus = 1
and payroll_date between %(from_date)s and %(to_date)s and payroll_date between %(from_date)s and %(to_date)s
@ -54,16 +55,20 @@ def get_additional_salary_component(employee, start_date, end_date):
}, as_dict=1) }, as_dict=1)
additional_components_list = [] additional_components_list = []
component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
for d in additional_components: for d in additional_components:
component = frappe.get_doc("Salary Component", d.salary_component) struct_row = frappe._dict({'salary_component': d.salary_component})
struct_row = {'salary_component': d.salary_component} component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
for field in ["depends_on_payment_days", "abbr", "is_tax_applicable", "variable_based_on_taxable_salary", "is_additional_component"]: if component:
struct_row[field] = component.get(field) struct_row.update(component[0])
additional_components_list.append({ struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
struct_row['is_additional_component'] = 1
additional_components_list.append(frappe._dict({
'amount': d.amount, 'amount': d.amount,
'type': component.type, 'type': component[0].type,
'struct_row': struct_row, 'struct_row': struct_row,
'overwrite': d.overwrite_salary_structure_amount 'overwrite': d.overwrite_salary_structure_amount,
}) }))
return additional_components_list return additional_components_list

View File

@ -3,13 +3,18 @@
frappe.ui.form.on('Employee Benefit Application', { frappe.ui.form.on('Employee Benefit Application', {
setup: function(frm) { setup: function(frm) {
frm.set_query("earning_component", "employee_benefits", function() { if(!frm.doc.employee || !frm.doc.date) {
return { frappe.throw(__("Please select Employee and Date first"));
query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components", } else {
filters: {date: frm.doc.date, employee: frm.doc.employee} frm.set_query("earning_component", "employee_benefits", function() {
}; return {
}); query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
filters: {date: frm.doc.date, employee: frm.doc.employee}
};
});
}
}, },
employee: function(frm) { employee: function(frm) {
var method, args; var method, args;
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){ if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){

View File

@ -7,7 +7,7 @@ import frappe
from frappe import _ from frappe import _
from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount
@ -131,7 +131,7 @@ def get_max_benefits_remaining(employee, on_date, payroll_period):
salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component) salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component)
if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1: if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1:
have_depends_on_payment_days = True have_depends_on_payment_days = True
benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, salary_component.max_benefit_amount) benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, salary_component.max_benefit_amount)
amount_per_day = benefit_amount / payroll_period_days amount_per_day = benefit_amount / payroll_period_days
per_day_amount_total += amount_per_day per_day_amount_total += amount_per_day
@ -167,16 +167,14 @@ def calculate_lwp(employee, start_date, holidays, working_days):
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 get_benefit_component_amount(employee, start_date, end_date, struct_row, sal_struct, period_length, frequency): def get_benefit_component_amount(employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period):
payroll_period, period_factor, actual_payroll_days = get_payroll_period_days(start_date, end_date, employee)
if not payroll_period: if not payroll_period:
frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}.") frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}")
.format(struct_row.salary_component)) .format(salary_component))
return False return False
# Considering there is only one application for a year # Considering there is only one application for a year
benefit_application_name = frappe.db.sql(""" benefit_application = frappe.db.sql("""
select name select name
from `tabEmployee Benefit Application` from `tabEmployee Benefit Application`
where where
@ -185,69 +183,58 @@ def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal
and docstatus = 1 and docstatus = 1
""", { """, {
'employee': employee, 'employee': employee,
'payroll_period': payroll_period 'payroll_period': payroll_period.name
}) })
if frappe.db.get_value("Salary Component", struct_row.salary_component, "depends_on_payment_days") != 1: current_benefit_amount = 0.0
if frequency == "Monthly" and actual_payroll_days in range(360, 370): component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component",
period_length = 1 salary_component, ["max_benefit_amount", "depends_on_payment_days"])
period_factor = 12
if period_factor: if benefit_application:
# If there is application for benefit then fetch the amount from the application. benefit_amount = frappe.db.get_value("Employee Benefit Application Detail",
# else Split the max benefits to the pro-rata components with the ratio of their max_benefit_amount {"parent": benefit_application[0][0], "earning_component": salary_component}, "amount")
if benefit_application_name: elif component_max_benefit:
benefit_application = frappe.get_doc("Employee Benefit Application", benefit_application_name[0][0]) benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit)
return get_benefit_amount(benefit_application, struct_row, period_factor, period_length)
# TODO: Check if there is benefit claim for employee then pro-rata divide the rest of amount (Late Benefit Application) current_benefit_amount = 0
else: if benefit_amount:
component_max = frappe.db.get_value("Salary Component", struct_row.salary_component, "max_benefit_amount") total_sub_periods = get_period_factor(employee,
if component_max: start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days)[0]
benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, component_max)
return get_amount(period_factor, benefit_amount, period_length)
return False
def get_benefit_pro_rata_ratio_amount(sal_struct, component_max): current_benefit_amount = benefit_amount / total_sub_periods
total_pro_rata_max = 0
return current_benefit_amount
def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit):
max_benefits_total = 0
benefit_amount = 0 benefit_amount = 0
for sal_struct_row in sal_struct.get("earnings"): for d in sal_struct.get("earnings"):
pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", if d.is_flexible_benefit == 1:
sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) component = frappe.db.get_value("Salary Component", d.salary_component, ["max_benefit_amount", "pay_against_benefit_claim"], as_dict=1)
if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: if not component.pay_against_benefit_claim:
total_pro_rata_max += max_benefit_amount max_benefits_total += component.max_benefit_amount
if total_pro_rata_max > 0:
benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max if max_benefits_total > 0:
if benefit_amount > component_max: benefit_amount = sal_struct.max_benefits * component.max_benefit_amount / max_benefits_total
benefit_amount = component_max if benefit_amount > component_max_benefit:
benefit_amount = component_max_benefit
return benefit_amount return benefit_amount
def get_benefit_amount(application, struct_row, period_factor, period_length):
amount = 0
for employee_benefit in application.employee_benefits:
if employee_benefit.earning_component == struct_row.salary_component:
amount += get_amount(period_factor, employee_benefit.amount, period_length)
return amount if amount > 0 else False
def get_amount(period_factor, amount, period_length):
amount_per_day = amount / period_factor
total_amount = amount_per_day * period_length
return total_amount
def get_earning_components(doctype, txt, searchfield, start, page_len, filters): def get_earning_components(doctype, txt, searchfield, start, page_len, filters):
if len(filters) < 2: if len(filters) < 2:
return {} return {}
employee = filters['employee']
date = filters['date'] salary_structure = get_assigned_salary_structure(filters['employee'], filters['date'])
salary_structure = get_assigned_salary_structure(employee, date)
if salary_structure: if salary_structure:
query = """select salary_component from `tabSalary Detail` where parent = '{salary_structure}' return frappe.db.sql("""
and is_flexible_benefit = 1 select salary_component
order by name""" from `tabSalary Detail`
where parent = %s and is_flexible_benefit = 1
return frappe.db.sql(query.format(**{ order by name
"salary_structure": salary_structure """, salary_structure)
})) else:
frappe.throw(_("Salary Structure not found for employee {0} and date {1}")
return {} .format(filters['employee'], filters['date']))

View File

@ -5,9 +5,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
from erpnext.hr.utils import get_payroll_period, get_previous_claimed_amount from erpnext.hr.utils import get_previous_claimed_amount
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
class EmployeeBenefitClaim(Document): class EmployeeBenefitClaim(Document):
@ -97,31 +99,28 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct):
benefit_amount_total += benefit_amount benefit_amount_total += benefit_amount
return benefit_amount_total return benefit_amount_total
def get_benefit_claim_amount(employee, start_date, end_date, salary_component): def get_benefit_claim_amount(employee, start_date, end_date, salary_component=None):
query = """select claimed_amount from `tabEmployee Benefit Claim` query = """
where employee=%(employee)s select sum(claimed_amount)
and docstatus = 1 and pay_against_benefit_claim = 1 from `tabEmployee Benefit Claim`
where
employee=%(employee)s
and docstatus = 1
and pay_against_benefit_claim = 1
and claim_date between %(start_date)s and %(end_date)s
""" """
if not start_date:
query += "and claim_date <= %(end_date)s"
else:
query += "and (claim_date between %(start_date)s and %(end_date)s)"
if salary_component: if salary_component:
query += "and earning_component = %(earning_component)s" query += " and earning_component = %(earning_component)s"
benefit_claim_details = frappe.db.sql(query, { claimed_amount = flt(frappe.db.sql(query, {
'employee': employee, 'employee': employee,
'start_date': start_date, 'start_date': start_date,
'end_date': end_date, 'end_date': end_date,
'earning_component': salary_component 'earning_component': salary_component
}, as_dict = True) })[0][0])
if benefit_claim_details:
claimed_amount = 0 return claimed_amount
for claim_detail in benefit_claim_details:
claimed_amount += claim_detail.claimed_amount
return claimed_amount
return False
def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period): def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period):
pro_rata_amount = 0 pro_rata_amount = 0
@ -140,11 +139,11 @@ def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payro
else: else:
pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct) pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct)
claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date, False) claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date)
return claimed_amount + pro_rata_amount return claimed_amount + pro_rata_amount
def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, current_flexi_amount, payroll_period, sal_struct): def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, payroll_period, sal_struct):
max_benefits = get_max_benefits(employee, payroll_period.end_date) max_benefits = get_max_benefits(employee, payroll_period.end_date)
if not max_benefits: if not max_benefits:
max_benefits = 0 max_benefits = 0

View File

@ -446,6 +446,72 @@
"set_only_once": 0, "set_only_once": 0,
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "other_incomes_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Other Incomes",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "income_from_other_sources",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Income From Other Sources",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "has_web_view": 0,
@ -458,7 +524,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2019-04-25 16:38:05.847925", "modified": "2019-05-11 16:13:50.472670",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employee Tax Exemption Declaration", "name": "Employee Tax Exemption Declaration",

View File

@ -25,7 +25,8 @@ class EmployeeTaxExemptionDeclaration(Document):
filters = { filters = {
"employee": self.employee, "employee": self.employee,
"payroll_period": self.payroll_period, "payroll_period": self.payroll_period,
"name": ["!=", self.name] "name": ["!=", self.name],
"docstatus": ["!=", 2]
} }
) )
if duplicate: if duplicate:

View File

@ -448,6 +448,72 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "other_incomes_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Other Incomes",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "income_from_other_sources",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Income From Other Sources",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -558,7 +624,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2019-04-25 17:06:36.569549", "modified": "2019-05-13 12:17:18.045171",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Employee Tax Exemption Proof Submission", "name": "Employee Tax Exemption Proof Submission",

View File

@ -543,14 +543,15 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr
payroll_entry.email_salary_slip(submitted_ss) payroll_entry.email_salary_slip(submitted_ss)
payroll_entry.db_set("salary_slips_submitted", 1) payroll_entry.db_set("salary_slips_submitted", 1)
payroll_entry.notify_update() payroll_entry.notify_update()
if not submitted_ss and not not_submitted_ss: if not submitted_ss and not not_submitted_ss:
frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted")) frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted"))
if not_submitted_ss: if not_submitted_ss:
frappe.msgprint(_("Could not submit some Salary Slips")) frappe.msgprint(_("Could not submit some Salary Slips"))
def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters): def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(""" return frappe.db.sql("""
select name from `tabPayroll Entry` select name from `tabPayroll Entry`

View File

@ -5,7 +5,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import date_diff, getdate, formatdate, cint from frappe.utils import date_diff, getdate, formatdate, cint, month_diff, flt
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.hr.utils import get_holidays_for_employee from erpnext.hr.utils import get_holidays_for_employee
@ -48,12 +48,13 @@ class PayrollPeriod(Document):
def get_payroll_period_days(start_date, end_date, employee): def get_payroll_period_days(start_date, end_date, employee):
company = frappe.db.get_value("Employee", employee, "company") company = frappe.db.get_value("Employee", employee, "company")
payroll_period = frappe.db.sql(""" payroll_period = frappe.db.sql("""
select name, start_date, end_date from `tabPayroll Period` select name, start_date, end_date
where company=%(company)s from `tabPayroll Period`
and ( where
(%(start_date)s between start_date and end_date) company=%(company)s
and (%(end_date)s between start_date and end_date) and %(start_date)s between start_date and end_date
)""", { and %(end_date)s between start_date and end_date
""", {
'company': company, 'company': company,
'start_date': start_date, 'start_date': start_date,
'end_date': end_date 'end_date': end_date
@ -67,3 +68,38 @@ def get_payroll_period_days(start_date, end_date, employee):
working_days -= len(holidays) working_days -= len(holidays)
return payroll_period[0][0], working_days, actual_no_of_days return payroll_period[0][0], working_days, actual_no_of_days
return False, False, False return False, False, False
def get_payroll_period(from_date, to_date, company):
payroll_period = frappe.db.sql("""
select name, start_date, end_date
from `tabPayroll Period`
where start_date<=%s and end_date>= %s and company=%s
""", (from_date, to_date, company), as_dict=1)
return payroll_period[0] if payroll_period else None
def get_period_factor(employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0):
# TODO if both deduct checked update the factor to make tax consistent
period_start, period_end = payroll_period.start_date, payroll_period.end_date
joining_date, relieving_date = frappe.db.get_value("Employee", employee, ["date_of_joining", "relieving_date"])
if getdate(joining_date) > getdate(period_start):
period_start = joining_date
if relieving_date and getdate(relieving_date) < getdate(period_end):
period_end = relieving_date
total_sub_periods, remaining_sub_periods = 0.0, 0.0
if payroll_frequency == "Monthly" and not depends_on_payment_days:
total_sub_periods = month_diff(payroll_period.end_date, payroll_period.start_date)
remaining_sub_periods = month_diff(period_end, start_date)
else:
salary_days = date_diff(end_date, start_date) + 1
days_in_payroll_period = date_diff(payroll_period.end_date, payroll_period.start_date) + 1
total_sub_periods = flt(days_in_payroll_period) / flt(salary_days)
remaining_days_in_payroll_period = date_diff(period_end, start_date) + 1
remaining_sub_periods = flt(remaining_days_in_payroll_period) / flt(salary_days)
return total_sub_periods, remaining_sub_periods

View File

@ -24,14 +24,6 @@ frappe.ui.form.on('Salary Component', {
is_flexible_benefit: function(frm) { is_flexible_benefit: function(frm) {
if(frm.doc.is_flexible_benefit){ if(frm.doc.is_flexible_benefit){
set_value_for_condition_and_formula(frm); set_value_for_condition_and_formula(frm);
frm.set_value("is_additional_component", 0);
frm.set_value("formula", '');
frm.set_value("amount", 0);
}
},
is_additional_component: function(frm) {
if(frm.doc.is_additional_component){
frm.set_value("is_flexible_benefit", 0);
frm.set_value("formula", ''); frm.set_value("formula", '');
frm.set_value("amount", 0); frm.set_value("amount", 0);
} }

View File

@ -115,40 +115,6 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_flexible_benefit != 1",
"fetch_if_empty": 0,
"fieldname": "is_additional_component",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Additional Component",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -285,6 +251,39 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "deduct_full_tax_on_selected_payroll_date",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Deduct Full Tax on Selected Payroll Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -424,7 +423,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:doc.type==\"Earning\" && doc.is_additional_component != 1 && doc.statistical_component!=1", "depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1",
"fetch_if_empty": 0, "fetch_if_empty": 0,
"fieldname": "flexible_benefits", "fieldname": "flexible_benefits",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -458,7 +457,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"depends_on": "eval:doc.is_additional_component != 1", "depends_on": "",
"fetch_if_empty": 0, "fetch_if_empty": 0,
"fieldname": "is_flexible_benefit", "fieldname": "is_flexible_benefit",
"fieldtype": "Check", "fieldtype": "Check",
@ -1035,7 +1034,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2019-04-16 19:08:55.323567", "modified": "2019-05-13 12:55:55.424370",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Salary Component", "name": "Salary Component",

View File

@ -35,7 +35,6 @@
"salary_component": "Leave Encashment", "salary_component": "Leave Encashment",
"type": "Earning", "type": "Earning",
"is_payable": 1, "is_payable": 1,
"is_tax_applicable": 1, "is_tax_applicable": 1
"is_additional_component": 1
} }
] ]

View File

@ -19,7 +19,5 @@ def create_salary_component(component_name, **args):
"salary_component": component_name, "salary_component": component_name,
"type": args.get("type") or "Earning", "type": args.get("type") or "Earning",
"is_payable": args.get("is_payable") or 1, "is_payable": args.get("is_payable") or 1,
"is_tax_applicable": args.get("is_tax_applicable") or 1, "is_tax_applicable": args.get("is_tax_applicable") or 1
"is_additional_component": args.get("is_additional_component") or 1
}).insert() }).insert()

View File

@ -218,41 +218,6 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_from": "salary_component.is_additional_component",
"fetch_if_empty": 0,
"fieldname": "is_additional_component",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Additional Component",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -323,6 +288,39 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "deduct_full_tax_on_selected_payroll_date",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Deduct Full Tax on Selected Payroll Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -566,6 +564,41 @@
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
}, },
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_from": "",
"fetch_if_empty": 0,
"fieldname": "additional_amount",
"fieldtype": "Currency",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Additional Amount",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{ {
"allow_bulk_edit": 0, "allow_bulk_edit": 0,
"allow_in_quick_entry": 0, "allow_in_quick_entry": 0,
@ -713,7 +746,7 @@
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"max_attachments": 0, "max_attachments": 0,
"modified": "2019-04-16 19:09:31.726597", "modified": "2019-05-11 17:33:08.508653",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Salary Detail", "name": "Salary Detail",

View File

@ -11,7 +11,7 @@ frappe.ui.form.on("Salary Slip", {
{fieldname: 'salary_component', columns: 6}, {fieldname: 'salary_component', columns: 6},
{fieldname: 'amount', columns: 4} {fieldname: 'amount', columns: 4}
]; ];
}) });
frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){ frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){
return { return {
@ -19,31 +19,39 @@ frappe.ui.form.on("Salary Slip", {
employee: frm.doc.employee employee: frm.doc.employee
} }
} }
} };
frm.set_query("salary_component", "earnings", function() { frm.set_query("salary_component", "earnings", function() {
return { return {
filters: { filters: {
type: "earning" type: "earning"
} }
} }
}) });
frm.set_query("salary_component", "deductions", function() { frm.set_query("salary_component", "deductions", function() {
return { return {
filters: { filters: {
type: "deduction" type: "deduction"
} }
} }
}) });
frm.set_query("employee", function() {
return{
query: "erpnext.controllers.queries.employee_query"
}
});
}, },
start_date: function(frm, dt, dn){ start_date: function(frm){
if(frm.doc.start_date){ if(frm.doc.start_date){
frm.trigger("set_end_date"); frm.trigger("set_end_date");
} }
}, },
end_date: function(frm, dt, dn) { end_date: function(frm) {
get_emp_and_leave_details(frm.doc, dt, dn); frm.events.get_emp_and_leave_details(frm);
}, },
set_end_date: function(frm){ set_end_date: function(frm){
@ -70,43 +78,54 @@ frappe.ui.form.on("Salary Slip", {
refresh: function(frm) { refresh: function(frm) {
frm.trigger("toggle_fields") frm.trigger("toggle_fields")
frm.trigger("toggle_reqd_fields")
var salary_detail_fields = ["formula", "abbr", "statistical_component", "is_tax_applicable", var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"];
"is_flexible_benefit", "variable_based_on_taxable_salary", "is_additional_component"]
cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false); cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false);
cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false); cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
}, },
salary_slip_based_on_timesheet: function(frm, dt, dn) { salary_slip_based_on_timesheet: function(frm) {
frm.trigger("toggle_fields"); frm.trigger("toggle_fields");
get_emp_and_leave_details(frm.doc, dt, dn); frm.events.get_emp_and_leave_details(frm);
}, },
payroll_frequency: function(frm, dt, dn) { payroll_frequency: function(frm) {
frm.trigger("toggle_fields"); frm.trigger("toggle_fields");
frm.set_value('end_date', ''); frm.set_value('end_date', '');
}, },
employee: function(frm, dt, dn) { employee: function(frm) {
get_emp_and_leave_details(frm.doc, dt, dn); frm.events.get_emp_and_leave_details(frm);
},
leave_without_pay: function(frm){
if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) {
return frappe.call({
method: 'process_salary_based_on_leave',
doc: frm.doc,
args: {"lwp": frm.doc.leave_without_pay},
callback: function(r, rt) {
frm.refresh();
}
});
}
}, },
toggle_fields: function(frm) { toggle_fields: function(frm) {
frm.toggle_display(['hourly_wages', 'timesheets'], frm.toggle_display(['hourly_wages', 'timesheets'], cint(frm.doc.salary_slip_based_on_timesheet)===1);
cint(frm.doc.salary_slip_based_on_timesheet)==1);
frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'], frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'],
frm.doc.payroll_frequency!=""); frm.doc.payroll_frequency!="");
}
})
frappe.ui.form.on('Salary Detail', {
earnings_remove: function(frm, dt, dn) {
calculate_all(frm.doc, dt, dn);
}, },
deductions_remove: function(frm, dt, dn) {
calculate_all(frm.doc, dt, dn); get_emp_and_leave_details: function(frm) {
return frappe.call({
method: 'get_emp_and_leave_details',
doc: frm.doc,
callback: function(r, rt) {
frm.refresh();
}
});
} }
}) })
@ -119,133 +138,29 @@ frappe.ui.form.on('Salary Slip Timesheet', {
} }
}); });
// Get leave details
//---------------------------------------------------------------------
var get_emp_and_leave_details = function(doc, dt, dn) {
return frappe.call({
method: 'get_emp_and_leave_details',
doc: locals[dt][dn],
callback: function(r, rt) {
cur_frm.refresh();
calculate_all(doc, dt, dn);
}
});
}
cur_frm.cscript.leave_without_pay = function(doc,dt,dn){
if (doc.employee && doc.start_date && doc.end_date) {
return $c_obj(doc, 'get_leave_details', {"lwp": doc.leave_without_pay}, function(r, rt) {
var doc = locals[dt][dn];
cur_frm.refresh();
calculate_all(doc, dt, dn);
});
}
}
var calculate_all = function(doc, dt, dn) {
calculate_earning_total(doc, dt, dn);
calculate_ded_total(doc, dt, dn);
calculate_net_pay(doc, dt, dn);
}
cur_frm.cscript.amount = function(doc,dt,dn){
var child = locals[dt][dn];
if(!doc.salary_structure){
frappe.model.set_value(dt,dn, "default_amount", child.amount)
}
calculate_all(doc, dt, dn);
}
cur_frm.cscript.depends_on_payment_days = function(doc,dt,dn){
calculate_earning_total(doc, dt, dn, true);
calculate_ded_total(doc, dt, dn, true);
calculate_net_pay(doc, dt, dn);
refresh_many(['amount','gross_pay', 'rounded_total', 'net_pay', 'loan_repayment']);
};
// Calculate earning total
// ------------------------------------------------------------------------
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].depends_on_payment_days) == 1) {
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
cint(doc.total_working_days)*100)/100;
} else if(reset_amount && tbl[i].default_amount) {
tbl[i].amount = tbl[i].default_amount;
}
if(!tbl[i].do_not_include_in_total) {
total_earn += flt(tbl[i].amount);
}
}
doc.gross_pay = total_earn;
refresh_many(['earnings', '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].depends_on_payment_days) == 1) {
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_working_days)*100)/100;
} else if(reset_amount && tbl[i].default_amount) {
tbl[i].amount = tbl[i].default_amount;
}
if(!tbl[i].do_not_include_in_total) {
total_ded += flt(tbl[i].amount);
}
}
doc.total_deduction = total_ded;
refresh_many(['deductions', 'total_deduction']);
}
var calculate_net_pay = function(doc, dt, dn) {
doc.net_pay = flt(doc.gross_pay) - flt(doc.total_deduction);
doc.rounded_total = Math.round(doc.net_pay);
refresh_many(['net_pay', 'rounded_total']);
}
cur_frm.cscript.validate = function(doc, dt, dn) {
calculate_all(doc, dt, dn);
}
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
return{
query: "erpnext.controllers.queries.employee_query"
}
}
// calculate total working hours, earnings based on hourly wages and totals // calculate total working hours, earnings based on hourly wages and totals
// ------------------------------------------------------------------------
var total_work_hours = function(frm, dt, dn) { var total_work_hours = function(frm, dt, dn) {
frm.set_value('total_working_hours', 0); var total_working_hours = 0.0;
$.each(frm.doc["timesheets"] || [], function(i, timesheet) { $.each(frm.doc["timesheets"] || [], function(i, timesheet) {
frm.doc.total_working_hours += timesheet.working_hours; total_working_hours += timesheet.working_hours;
}); });
frm.refresh_field('total_working_hours'); frm.set_value('total_working_hours', total_working_hours);
var wages_amount = frm.doc.total_working_hours * frm.doc.hour_rate; var wages_amount = frm.doc.total_working_hours * frm.doc.hour_rate;
frappe.db.get_value('Salary Structure', {'name': frm.doc.salary_structure}, 'salary_component', (r) => { frappe.db.get_value('Salary Structure', {'name': frm.doc.salary_structure}, 'salary_component', (r) => {
frm.set_value('gross_pay', 0); var gross_pay = 0.0;
$.each(frm.doc["earnings"], function(i, earning) { $.each(frm.doc["earnings"], function(i, earning) {
if (earning.salary_component == r.salary_component) { if (earning.salary_component == r.salary_component) {
earning.amount = wages_amount; earning.amount = wages_amount;
frm.refresh_fields('earnings'); frm.refresh_fields('earnings');
} }
frm.doc.gross_pay += earning.amount; gross_pay += earning.amount;
}); });
frm.set_value('gross_pay', gross_pay);
frm.refresh_field('gross_pay'); frm.doc.net_pay = flt(frm.doc.gross_pay) - flt(frm.doc.total_deduction);
calculate_net_pay(frm.doc, dt, dn); frm.doc.rounded_total = Math.round(frm.doc.net_pay);
refresh_many(['net_pay', 'rounded_total']);
}); });
} }

View File

@ -1019,71 +1019,7 @@
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"columns": 0, "columns": 0,
"fieldname": "column_break_01", "fetch_if_empty": 0,
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Salary Slip",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_32", "fieldname": "section_break_32",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
@ -1893,6 +1829,41 @@
"set_only_once": 0, "set_only_once": 0,
"translatable": 0, "translatable": 0,
"unique": 0 "unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Salary Slip",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
} }
], ],
"has_web_view": 0, "has_web_view": 0,
@ -1906,7 +1877,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2019-02-18 18:54:36.161027", "modified": "2019-05-13 13:10:14.524119",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "HR", "module": "HR",
"name": "Salary Slip", "name": "Salary Slip",

File diff suppressed because it is too large Load Diff

View File

@ -45,12 +45,12 @@ class TestSalarySlip(unittest.TestCase):
self.assertEqual(ss.total_working_days, no_of_days[0]) self.assertEqual(ss.total_working_days, no_of_days[0])
self.assertEqual(ss.payment_days, no_of_days[0]) self.assertEqual(ss.payment_days, no_of_days[0])
self.assertEqual(ss.earnings[0].amount, 25000) self.assertEqual(ss.earnings[0].amount, 50000)
self.assertEqual(ss.earnings[1].amount, 3000) self.assertEqual(ss.earnings[1].amount, 3000)
self.assertEqual(ss.deductions[0].amount, 5000) self.assertEqual(ss.deductions[0].amount, 5000)
self.assertEqual(ss.deductions[1].amount, 5000) self.assertEqual(ss.deductions[1].amount, 5000)
self.assertEqual(ss.gross_pay, 40500) self.assertEqual(ss.gross_pay, 78000)
self.assertEqual(ss.net_pay, 29918) self.assertEqual(ss.net_pay, 67418.0)
def test_salary_slip_with_holidays_excluded(self): def test_salary_slip_with_holidays_excluded(self):
no_of_days = self.get_no_of_days() no_of_days = self.get_no_of_days()
@ -64,13 +64,13 @@ class TestSalarySlip(unittest.TestCase):
self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1]) self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1])
self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1]) self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1])
self.assertEqual(ss.earnings[0].amount, 25000) self.assertEqual(ss.earnings[0].amount, 50000)
self.assertEqual(ss.earnings[0].default_amount, 25000) self.assertEqual(ss.earnings[0].default_amount, 50000)
self.assertEqual(ss.earnings[1].amount, 3000) self.assertEqual(ss.earnings[1].amount, 3000)
self.assertEqual(ss.deductions[0].amount, 5000) self.assertEqual(ss.deductions[0].amount, 5000)
self.assertEqual(ss.deductions[1].amount, 5000) self.assertEqual(ss.deductions[1].amount, 5000)
self.assertEqual(ss.gross_pay, 40500) self.assertEqual(ss.gross_pay, 78000)
self.assertEqual(ss.net_pay, 29918) self.assertEqual(ss.net_pay, 67418.0)
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()
@ -211,7 +211,7 @@ class TestSalarySlip(unittest.TestCase):
tax_paid = get_tax_paid_in_period(employee) tax_paid = get_tax_paid_in_period(employee)
# total taxable income 586000, 250000 @ 5%, 86000 @ 20% ie. 12500 + 17200 # total taxable income 586000, 250000 @ 5%, 86000 @ 20% ie. 12500 + 17200
annual_tax = 29700 annual_tax = 113567.79
try: try:
self.assertEqual(tax_paid, annual_tax) self.assertEqual(tax_paid, annual_tax)
except AssertionError: except AssertionError:
@ -250,7 +250,7 @@ class TestSalarySlip(unittest.TestCase):
# total taxable income 416000, 166000 @ 5% ie. 8300 # total taxable income 416000, 166000 @ 5% ie. 8300
try: try:
self.assertEqual(tax_paid, 8300) self.assertEqual(tax_paid, 88607.79)
except AssertionError: except AssertionError:
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
raise raise
@ -265,7 +265,7 @@ class TestSalarySlip(unittest.TestCase):
# total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200 # total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200
tax_paid = get_tax_paid_in_period(employee) tax_paid = get_tax_paid_in_period(employee)
try: try:
self.assertEqual(tax_paid, 25700) self.assertEqual(tax_paid, 121211.48)
except AssertionError: except AssertionError:
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n") print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
raise raise
@ -365,7 +365,7 @@ def make_earning_salary_component(setup=False, test_tax=False):
"salary_component": 'Basic Salary', "salary_component": 'Basic Salary',
"abbr":'BS', "abbr":'BS',
"condition": 'base > 10000', "condition": 'base > 10000',
"formula": 'base*.5', "formula": 'base',
"type": "Earning", "type": "Earning",
"amount_based_on_formula": 1 "amount_based_on_formula": 1
}, },
@ -386,7 +386,6 @@ def make_earning_salary_component(setup=False, test_tax=False):
{ {
"salary_component": "Leave Encashment", "salary_component": "Leave Encashment",
"abbr": 'LE', "abbr": 'LE',
"is_additional_component": 1,
"type": "Earning" "type": "Earning"
} }
] ]
@ -398,7 +397,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
"is_flexible_benefit": 1, "is_flexible_benefit": 1,
"type": "Earning", "type": "Earning",
"pay_against_benefit_claim": 1, "pay_against_benefit_claim": 1,
"max_benefit_amount": 100000 "max_benefit_amount": 100000,
"depends_on_payment_days": 0
}, },
{ {
"salary_component": "Medical Allowance", "salary_component": "Medical Allowance",
@ -409,9 +409,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
"max_benefit_amount": 15000 "max_benefit_amount": 15000
}, },
{ {
"salary_component": "Perfomance Bonus", "salary_component": "Performance Bonus",
"abbr": 'B', "abbr": 'B',
"is_additional_component": 1,
"type": "Earning" "type": "Earning"
} }
]) ])
@ -442,7 +441,8 @@ def make_deduction_salary_component(setup=False, test_tax=False):
"abbr":'T', "abbr":'T',
"formula": 'base*.1', "formula": 'base*.1',
"type": "Deduction", "type": "Deduction",
"amount_based_on_formula": 1 "amount_based_on_formula": 1,
"depends_on_payment_days": 0
} }
] ]
if not test_tax: if not test_tax:
@ -512,21 +512,23 @@ def create_tax_slab(payroll_period):
{ {
"from_amount": 250000, "from_amount": 250000,
"to_amount": 500000, "to_amount": 500000,
"percent_deduction": 5 "percent_deduction": 5.2,
"condition": "annual_taxable_earning > 500000"
}, },
{ {
"from_amount": 500000, "from_amount": 500001,
"to_amount": 1000000, "to_amount": 1000000,
"percent_deduction": 20 "percent_deduction": 20.8
}, },
{ {
"from_amount": 1000000, "from_amount": 1000001,
"percent_deduction": 30 "percent_deduction": 31.2
} }
] ]
payroll_period.taxable_salary_slabs = [] payroll_period.taxable_salary_slabs = []
for item in data: for item in data:
payroll_period.append("taxable_salary_slabs", item) payroll_period.append("taxable_salary_slabs", item)
payroll_period.standard_tax_exemption_amount = 52500
payroll_period.save() payroll_period.save()
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True): def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True):
@ -559,7 +561,7 @@ def create_additional_salary(employee, payroll_period, amount):
"doctype": "Additional Salary", "doctype": "Additional Salary",
"employee": employee, "employee": employee,
"company": erpnext.get_default_company(), "company": erpnext.get_default_company(),
"salary_component": "Perfomance Bonus", "salary_component": "Performance Bonus",
"payroll_date": salary_date, "payroll_date": salary_date,
"amount": amount, "amount": amount,
"type": "Earning" "type": "Earning"

View File

@ -147,7 +147,8 @@ frappe.ui.form.on('Salary Structure', {
source_name: frm.doc.name, source_name: frm.doc.name,
employee: values.employee, employee: values.employee,
as_print: 1, as_print: 1,
print_format: print_format print_format: print_format,
for_preview: 1
}, },
callback: function(r) { callback: function(r) {
var new_window = window.open(); var new_window = window.open();

View File

@ -137,7 +137,7 @@ def get_existing_assignments(employees, salary_structure,from_date):
return salary_structures_assignments return salary_structures_assignments
@frappe.whitelist() @frappe.whitelist()
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None): def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0):
def postprocess(source, target): def postprocess(source, target):
if employee: if employee:
employee_details = frappe.db.get_value("Employee", employee, employee_details = frappe.db.get_value("Employee", employee,
@ -147,7 +147,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
target.branch = employee_details.branch target.branch = employee_details.branch
target.designation = employee_details.designation target.designation = employee_details.designation
target.department = employee_details.department target.department = employee_details.department
target.run_method('process_salary_structure') target.run_method('process_salary_structure', for_preview=for_preview)
doc = get_mapped_doc("Salary Structure", source_name, { doc = get_mapped_doc("Salary Structure", source_name, {
"Salary Structure": { "Salary Structure": {

View File

@ -39,18 +39,19 @@ class TestSalaryStructure(unittest.TestCase):
holiday_list.save() holiday_list.save()
def test_amount_totals(self): def test_amount_totals(self):
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"}) sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})
if not sal_slip: if not sal_slip:
sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample") sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample")
self.assertEqual(sal_slip.get("salary_structure"), 'Salary Structure Sample') self.assertEqual(sal_slip.get("salary_structure"), 'Salary Structure Sample')
self.assertEqual(sal_slip.get("earnings")[0].amount, 25000) self.assertEqual(sal_slip.get("earnings")[0].amount, 50000)
self.assertEqual(sal_slip.get("earnings")[1].amount, 3000) self.assertEqual(sal_slip.get("earnings")[1].amount, 3000)
self.assertEqual(sal_slip.get("earnings")[2].amount, 12500) self.assertEqual(sal_slip.get("earnings")[2].amount, 25000)
self.assertEqual(sal_slip.get("gross_pay"), 40500) self.assertEqual(sal_slip.get("gross_pay"), 78000)
self.assertEqual(sal_slip.get("deductions")[0].amount, 5000) self.assertEqual(sal_slip.get("deductions")[0].amount, 5000)
self.assertEqual(sal_slip.get("deductions")[1].amount, 5000) self.assertEqual(sal_slip.get("deductions")[1].amount, 5000)
self.assertEqual(sal_slip.get("total_deduction"), 10000) self.assertEqual(sal_slip.get("total_deduction"), 10000)
self.assertEqual(sal_slip.get("net_pay"), 30500) self.assertEqual(sal_slip.get("net_pay"), 68000)
def test_whitespaces_in_formula_conditions_fields(self): def test_whitespaces_in_formula_conditions_fields(self):
salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True) salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True)

View File

@ -35,6 +35,8 @@ def execute(filters=None):
for d in ded_types: for d in ded_types:
row.append(ss_ded_map.get(ss.name, {}).get(d)) row.append(ss_ded_map.get(ss.name, {}).get(d))
row.append(ss.total_loan_repayment)
row += [ss.total_deduction, ss.net_pay] row += [ss.total_deduction, ss.net_pay]
data.append(row) data.append(row)
@ -67,7 +69,7 @@ def get_columns(salary_slips):
columns = columns + [(e + ":Currency:120") for e in salary_components[_("Earning")]] + \ columns = columns + [(e + ":Currency:120") for e in salary_components[_("Earning")]] + \
[_("Gross Pay") + ":Currency:120"] + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] + \ [_("Gross Pay") + ":Currency:120"] + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] + \
[_("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"] [_("Loan Repayment") + ":Currency:120", _("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"]
return columns, salary_components[_("Earning")], salary_components[_("Deduction")] return columns, salary_components[_("Earning")], salary_components[_("Deduction")]

View File

@ -126,9 +126,9 @@ def get_employee_fields_label():
fields = [] fields = []
for df in frappe.get_meta("Employee").get("fields"): for df in frappe.get_meta("Employee").get("fields"):
if df.fieldname in ["salutation", "user_id", "employee_number", "employment_type", if df.fieldname in ["salutation", "user_id", "employee_number", "employment_type",
"holiday_list", "branch", "department", "designation", "grade", "holiday_list", "branch", "department", "designation", "grade",
"notice_number_of_days", "reports_to", "leave_policy", "company_email"]: "notice_number_of_days", "reports_to", "leave_policy", "company_email"]:
fields.append({"value": df.fieldname, "label": df.label}) fields.append({"value": df.fieldname, "label": df.label})
return fields return fields
@frappe.whitelist() @frappe.whitelist()
@ -263,12 +263,6 @@ def get_leave_period(from_date, to_date, company):
if leave_period: if leave_period:
return leave_period return leave_period
def get_payroll_period(from_date, to_date, company):
payroll_period = frappe.db.sql("""select name, start_date, end_date from
`tabPayroll Period`
where start_date<=%s and end_date>= %s and company=%s""", (from_date, to_date, company), as_dict=1)
return payroll_period[0] if payroll_period else None
def allocate_earned_leaves(): def allocate_earned_leaves():
'''Allocate earned leaves to Employees''' '''Allocate earned leaves to Employees'''
e_leave_types = frappe.get_all("Leave Type", e_leave_types = frappe.get_all("Leave Type",

View File

@ -146,11 +146,9 @@ def calculate_annual_eligible_hra_exemption(doc):
hra_component = frappe.get_cached_value('Company', doc.company, "hra_component") hra_component = frappe.get_cached_value('Company', doc.company, "hra_component")
if not (basic_component and hra_component): if not (basic_component and hra_component):
frappe.throw(_("Please mention Basic and HRA component in Company")) frappe.throw(_("Please mention Basic and HRA component in Company"))
annual_exemption, monthly_exemption, hra_amount = 0, 0, 0 annual_exemption, monthly_exemption, hra_amount = 0, 0, 0
if hra_component and basic_component: if hra_component and basic_component:
assignment = get_salary_assignment(doc.employee, nowdate()) assignment = get_salary_assignment(doc.employee, nowdate())
if assignment: if assignment:
hra_component_exists = frappe.db.exists("Salary Detail", { hra_component_exists = frappe.db.exists("Salary Detail", {
"parent": assignment.salary_structure, "parent": assignment.salary_structure,
@ -158,18 +156,19 @@ def calculate_annual_eligible_hra_exemption(doc):
"parentfield": "earnings", "parentfield": "earnings",
"parenttype": "Salary Structure" "parenttype": "Salary Structure"
}) })
if hra_component_exists: if hra_component_exists:
basic_amount, hra_amount = get_component_amt_from_salary_slip(doc.employee, basic_amount, hra_amount = get_component_amt_from_salary_slip(doc.employee,
assignment.salary_structure, basic_component, hra_component) assignment.salary_structure, basic_component, hra_component)
if hra_amount: if hra_amount:
if doc.monthly_house_rent: if doc.monthly_house_rent:
annual_exemption = calculate_hra_exemption(assignment.salary_structure, annual_exemption = calculate_hra_exemption(assignment.salary_structure,
basic_amount, hra_amount, doc.monthly_house_rent, basic_amount, hra_amount, doc.monthly_house_rent, doc.rented_in_metro_city)
doc.rented_in_metro_city)
if annual_exemption > 0: if annual_exemption > 0:
monthly_exemption = annual_exemption / 12 monthly_exemption = annual_exemption / 12
else: else:
annual_exemption = 0 annual_exemption = 0
elif doc.docstatus == 1: elif doc.docstatus == 1:
frappe.throw(_("Salary Structure must be submitted before submission of Tax Ememption Declaration")) frappe.throw(_("Salary Structure must be submitted before submission of Tax Ememption Declaration"))
@ -180,7 +179,7 @@ def calculate_annual_eligible_hra_exemption(doc):
}) })
def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component): def get_component_amt_from_salary_slip(employee, salary_structure, basic_component, hra_component):
salary_slip = make_salary_slip(salary_structure, employee=employee) salary_slip = make_salary_slip(salary_structure, employee=employee, for_preview=1)
basic_amt, hra_amt = 0, 0 basic_amt, hra_amt = 0, 0
for earning in salary_slip.earnings: for earning in salary_slip.earnings:
if earning.salary_component == basic_component: if earning.salary_component == basic_component:

View File

@ -11,11 +11,6 @@ frappe.ui.form.on("Company", {
filters: {"type": "Earning"} filters: {"type": "Earning"}
} }
}); });
frm.set_query("arrear_component", function(){
return {
filters: {"is_additional_component": 1}
}
});
frm.set_query("parent_company", function() { frm.set_query("parent_company", function() {
return { return {