feature/odoo18 #2

Merged
administrator merged 43 commits from feature/odoo18 into develop 2025-05-22 16:16:43 +05:30
60 changed files with 4164 additions and 0 deletions
Showing only changes of commit 59111fc8c8 - Show all commits

View File

@ -0,0 +1,7 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
from . import report
from . import wizard
from . import controller

View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Indian Payroll',
'countries': ['in'],
'category': 'Human Resources/Payroll',
'depends': ['hr_payroll'],
'auto_install': ['hr_payroll'],
'description': """
Indian Payroll Salary Rules.
============================
-Configuration of hr_payroll for India localization
-All main contributions rules for India payslip.
* New payslip report
* Employee Contracts
* Allow to configure Basic / Gross / Net Salary
* Employee PaySlip
* Allowance / Deduction
* Integrated with Leaves Management
* Medical Allowance, Travel Allowance, Child Allowance, ...
- Payroll Advice and Report
- Yearly Salary by Employee Report
""",
'data': [
'data/report_paperformat.xml',
'views/l10n_in_hr_payroll_report.xml',
'data/res_partner_data.xml',
'data/hr_salary_rule_category_data.xml',
'data/hr_payroll_structure_type_data.xml',
'data/hr_payroll_structure_data.xml',
'data/salary_rules/hr_salary_rule_stipend_data.xml',
'data/salary_rules/hr_salary_rule_ind_emp_data.xml',
'data/salary_rules/hr_salary_rule_regular_pay_data.xml',
'data/salary_rules/hr_salary_rule_worker_data.xml',
'data/hr_contract_type_data.xml',
'data/hr_rule_parameters_data.xml',
'data/ir_sequence_data.xml',
'data/hr_payroll_dashboard_warning_data.xml',
'wizard/hr_tds_calculation.xml',
'views/hr_contract_views.xml',
'views/res_users_views.xml',
'views/hr_employee_views.xml',
'views/res_config_settings_views.xml',
'security/ir.model.access.csv',
'views/report_payslip_details_template.xml',
'wizard/hr_salary_register.xml',
'views/report_hr_epf_views.xml',
'wizard/hr_yearly_salary_detail_view.xml',
'wizard/hr_payroll_payment_report.xml',
'views/report_hr_yearly_salary_detail_template.xml',
'views/report_payroll_advice_template.xml',
'views/l10n_in_salary_statement.xml',
'views/report_l10n_in_salary_statement.xml',
],
'demo': [
'data/l10n_in_hr_payroll_demo.xml',
],
'license': 'OEEL-1',
}

View File

@ -0,0 +1,3 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import main

View File

@ -0,0 +1,136 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from io import BytesIO
from copy import deepcopy
from odoo import http, _
from odoo.http import request
from odoo.tools.misc import xlsxwriter
class L10nInSalaryRegisterController(http.Controller):
def _get_payslip_rules(self, employee_ids, payslips, struct_id=None):
rule_by_name = []
if struct_id and struct_id.rule_ids:
rule_by_name = [(i.code, i.name) for i in struct_id.rule_ids]
else:
rule_by_name = [
(rule.code, rule.name)
for payslip in payslips
for rule in payslip.struct_id.rule_ids
]
child_dict = {code: [name, 0] for code, name in rule_by_name}
rules_per_employee = {employee_id: deepcopy(child_dict) for employee_id in employee_ids}
rules_by_name = dict(rule_by_name)
return rules_by_name, rules_per_employee
@http.route(['/export/salary-register/<int:wizard_id>'], type='http', auth='user')
def export_salary_register(self, wizard_id):
wizard = request.env['salary.register.wizard'].browse(wizard_id)
if not wizard.exists() or not request.env.user.has_group('hr_payroll.group_hr_payroll_user'):
return request.render(
'http_routing.http_error',
{
'status_code': 'Oops',
'status_message': _('It seems that you either not have the rights to access the Salary Register '
'or that you try to access it outside normal circumstances. '
'If you think there is a problem, please contact an administrator.')
}
)
output = BytesIO()
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
worksheet = workbook.add_worksheet('salary_register_report')
style_highlight = workbook.add_format({'bold': True, 'pattern': 1, 'bg_color': '#E0E0E0', 'align': 'center'})
style_normal = workbook.add_format({'align': 'center'})
column_width = 30
date_from = str(wizard.date_from)
date_to = str(wizard.date_to)
employee_ids = wizard.employee_ids
struct_id = wizard.struct_id
# VERTICAL HEADERS
vertical_headers = [
_('EMPLOYER ID'),
_('EMPLOYER NAME'),
_('FROM DATE'),
_('TO DATE'),
]
# VERTICAL DATA
vertical_data = [
wizard.company_id.company_registry or '',
wizard.company_id.name,
date_from,
date_to,
]
blank_lines = 1
# HORIZONTAL HEADERS
horizontal_headers = [
_('EMPLOYEE CODE'),
_('EMPLOYEE NAME'),
]
# HORIZONTAL DATA
domain = [('employee_id', 'in', employee_ids.ids), ('date_from', '>=', date_from), ('date_to', '<=', date_to), ('state', '=', 'paid')]
if struct_id:
domain.append(('struct_id', '=', struct_id.id))
payslips_per_employee = dict(request.env['hr.payslip']._read_group(
domain=domain,
groupby=['employee_id'],
aggregates=['id:recordset'],
))
rules_by_name, rules_per_employee = self._get_payslip_rules(employee_ids, payslips_per_employee.values(), struct_id=struct_id)
rules_per_employee = {
employee: rules
for employee, rules in rules_per_employee.items()
if employee in payslips_per_employee
}
# Dynamically calculated headers
horizontal_headers = [*horizontal_headers, *rules_by_name.values()]
for employee_id, payslips in payslips_per_employee.items():
rule_codes = payslips.struct_id.rule_ids.mapped('code')
payslip_rules = payslips._get_line_values(rule_codes, compute_sum=True)
for code, rule in payslip_rules.items():
rules_per_employee[employee_id][code][1] += rule['sum']['total']
horizontal_data = []
for employee_id in rules_per_employee:
dynamic_horizontal_data = [data[1] for data in rules_per_employee[employee_id].values()]
horizontal_data.append((
employee_id.registration_number or "",
employee_id.name,
*dynamic_horizontal_data,
))
# WRITE IN WORKSHEET
row = 0
for (vertical_header, vertical_point) in zip(vertical_headers, vertical_data):
worksheet.write(row, 0, vertical_header, style_highlight)
worksheet.write(row, 1, vertical_point, style_normal)
row += 1
row += blank_lines
for col, horizontal_header in enumerate(horizontal_headers):
worksheet.write(row, col, horizontal_header, style_highlight)
worksheet.set_column(col, col, column_width)
for payroll_line in horizontal_data:
row += 1
for col, payroll_point in enumerate(payroll_line):
worksheet.write(row, col, payroll_point, style_normal)
row += 1
workbook.close()
xlsx_data = output.getvalue()
date_obj = datetime.strptime(date_from, '%Y-%m-%d').date()
filename = _("salary_register_report_%(year)s_%(month)s", year=date_obj.year, month=date_obj.month)
response = request.make_response(
xlsx_data,
headers=[
('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'),
('Content-Disposition', f'attachment; filename={filename}.xlsx')],
)
return response

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="l10n_in_contract_type_probation" model="hr.contract.type">
<field name="name">Probation</field>
<field name="sequence">4</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_contract_type_intern" model="hr.contract.type">
<field name="name">Intern</field>
<field name="sequence">5</field>
<field name="country_id" ref="base.in"/>
</record>
</odoo>

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="hr_payroll_dashboard_warning_missing_pan" model="hr.payroll.dashboard.warning">
<field name="name">Employees Without PAN Number</field>
<field name="country_id" ref="base.in"/>
<field name="evaluation_code">
indian_companies = self.env.companies.filtered(lambda c: c.country_id.code == 'IN')
if indian_companies:
# Employees Without PAN Number
employees_wo_pan = self.env['hr.employee'].search([
('l10n_in_pan', '=', False),
('company_id', 'in', indian_companies.ids),
])
if employees_wo_pan:
warning_count = len(employees_wo_pan)
warning_records = employees_wo_pan
</field>
</record>
<record id="hr_payroll_dashboard_warning_missing_uan" model="hr.payroll.dashboard.warning">
<field name="name">Employees Without UAN Number</field>
<field name="country_id" ref="base.in"/>
<field name="evaluation_code">
indian_companies = self.env.companies.filtered(lambda c: c.country_id.code == 'IN')
if indian_companies:
# Employees Without PAN Number
employees_wo_uan = self.env['hr.employee'].search([
('l10n_in_uan', '=', False),
('company_id', 'in', indian_companies.ids),
])
if employees_wo_uan:
warning_count = len(employees_wo_uan)
warning_records = employees_wo_uan
</field>
</record>
<record id="hr_payroll_dashboard_warning_missing_esic" model="hr.payroll.dashboard.warning">
<field name="name">Employees Without ESIC Number</field>
<field name="country_id" ref="base.in"/>
<field name="evaluation_code">
indian_companies = self.env.companies.filtered(lambda c: c.country_id.code == 'IN')
if indian_companies:
# Employees Without PAN Number
employees_wo_esic = self.env['hr.employee'].search([
('l10n_in_esic_number', '=', False),
('company_id', 'in', indian_companies.ids),
])
if employees_wo_esic:
warning_count = len(employees_wo_esic)
warning_records = employees_wo_esic
</field>
</record>
<record id="hr_payroll_dashboard_warning_incoming_probation" model="hr.payroll.dashboard.warning">
<field name="name">Employees Probation ends within a week</field>
<field name="country_id" ref="base.in"/>
<field name="evaluation_code">
indian_companies = self.env.companies.filtered(lambda c: c.country_id.code == 'IN')
if indian_companies:
# Employees who are on the probation and their contracts expire within a week
probation_contract_type = self.env.ref('l10n_in_hr_payroll.l10n_in_contract_type_probation', raise_if_not_found=False)
if probation_contract_type:
nearly_expired_contracts = self.env['hr.contract'].search([
('contract_type_id', '=', probation_contract_type.id),
('state', '=', 'open'), ('kanban_state', '!=', 'blocked'),
('date_end', '&lt;=', date.today() + relativedelta(days=7)),
('date_end', '&gt;=', date.today() + relativedelta(days=1)),
])
if nearly_expired_contracts:
warning_count = len(nearly_expired_contracts.employee_id)
warning_records = nearly_expired_contracts.employee_id
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Salary Structure and structure type -->
<record id="hr_payroll_structure_in_employee_salary" model="hr.payroll.structure">
<field name="name">India: Regular Pay</field>
<field name="type_id" ref="l10n_in_hr_payroll.hr_payroll_salary_structure_type_ind_emp_pay"/>
<field name="unpaid_work_entry_type_ids" eval="[(4, ref('hr_work_entry_contract.work_entry_type_unpaid_leave'))]"/>
<field name="country_id" ref="base.in"/>
<field name="report_id" ref="payslip_details_report"/>
</record>
<record id="structure_worker_0001" model="hr.payroll.structure">
<field name="name">Worker Pay</field>
<field name="type_id" ref="l10n_in_hr_payroll.hr_payroll_salary_structure_type_ind_worker"/>
<field name="country_id" ref="base.in"/>
<field name="report_id" ref="payslip_details_report"/>
</record>
<record id="hr_payroll_structure_in_stipend" model="hr.payroll.structure">
<field name="name">Stipend</field>
<field name="type_id" ref="l10n_in_hr_payroll.hr_payroll_salary_structure_type_ind_intern"/>
<field name="country_id" ref="base.in"/>
<field name="report_id" ref="payslip_details_report"/>
<field name="payslip_name">Stipend</field>
</record>
<record id="hr_payroll_salary_structure_ind_emp" model="hr.payroll.structure">
<field name="name">Non-Executive Employee</field>
<field name="country_id" ref="base.in"/>
<field name="type_id" ref="hr_payroll_salary_structure_type_ind_emp"/>
<field name="report_id" ref="payslip_details_report"/>
</record>
<record id="l10n_in_hr_payroll.hr_payroll_salary_structure_type_ind_emp_pay" model="hr.payroll.structure.type">
<field name="default_struct_id" ref="hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_payroll.hr_payroll_salary_structure_type_ind_worker" model="hr.payroll.structure.type">
<field name="default_struct_id" ref="structure_worker_0001"/>
</record>
<record id="l10n_in_hr_payroll.hr_payroll_salary_structure_type_ind_intern" model="hr.payroll.structure.type">
<field name="default_struct_id" ref="hr_payroll_structure_in_stipend"/>
</record>
<record id="l10n_in_hr_payroll.hr_payroll_salary_structure_type_ind_emp" model="hr.payroll.structure.type">
<field name="default_struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
</odoo>

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_contract.structure_type_worker" model="hr.payroll.structure.type">
<field name="wage_type">hourly</field>
</record>
<record id="hr_payroll_salary_structure_type_ind_emp_pay" model="hr.payroll.structure.type">
<field name="name">India: Employee Pay</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="hr_payroll_salary_structure_type_ind_intern" model="hr.payroll.structure.type">
<field name="name">India: Intern</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="hr_payroll_salary_structure_type_ind_worker" model="hr.payroll.structure.type">
<field name="name">India: Worker</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="hr_payroll_salary_structure_type_ind_emp" model="hr.payroll.structure.type">
<field name="name">India: Non-Executives</field>
<field name="country_id" ref="base.in"/>
</record>
</odoo>

View File

@ -0,0 +1,304 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="l10n_in_rule_parameter_basic" model="hr.rule.parameter">
<field name="name">India: Basic Salary Value</field>
<field name="code">l10n_in_basic_value</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_basic_value" model="hr.rule.parameter.value">
<field name="parameter_value">7000</field>
<field name="rule_parameter_id" ref="l10n_in_hr_payroll.l10n_in_rule_parameter_basic"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_basic_percent" model="hr.rule.parameter">
<field name="name">India: Basic Salary Percentage</field>
<field name="code">l10n_in_basic_percent</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_basic_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">0.60</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_basic_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_regular_basic_day" model="hr.rule.parameter">
<field name="name">India: Basic Salary Days</field>
<field name="code">l10n_in_basic_days</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_regular_basic_day_value" model="hr.rule.parameter.value">
<field name="parameter_value">0.31</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_regular_basic_day"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_hra" model="hr.rule.parameter">
<field name="name">India: House Rent Allowance Value</field>
<field name="code">l10n_in_hra_value</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_hra_value" model="hr.rule.parameter.value">
<field name="parameter_value">0.40</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_hra"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_std_alw" model="hr.rule.parameter">
<field name="name">India: Standard Allowance</field>
<field name="code">l10n_in_std_alw</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_std_alw_value" model="hr.rule.parameter.value">
<field name="parameter_value">4167</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_std_alw"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_bonus_percent" model="hr.rule.parameter">
<field name="name">India: Bonus Value and Percentage</field>
<field name="code">l10n_in_bonus_percent</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_bonus_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">[
(450000.00, (12, 0.30)),
(550000.00, (3, 0.20)),
(0.00, (1, 0.10)),
]</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_bonus_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_tds_percent" model="hr.rule.parameter">
<field name="name">India: TDS Rate Chart</field>
<field name="code">l10n_in_tds_rate_chart</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_tds_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">[
(0.0, (0, 300000)),
(0.05, (300000, 600000)),
(0.1, (600000, 900000)),
(0.15, (900000, 1200000)),
(0.2, (1200000, 1500000)),
(0.3, (1500000, 'inf'))
]</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_tds_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_surcharg_percent" model="hr.rule.parameter">
<field name="name">India: Surcharge Rate</field>
<field name="code">l10n_in_surcharge_rate</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_surcharge_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">[
(0.0, (0, 5000000)),
(0.1, (5000000, 10000000)),
(0.15, (10000000, 20000000)),
(0.25, (20000000, 'inf')),
]</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_surcharg_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_standard_deduction" model="hr.rule.parameter">
<field name="name">India: Standard Deduction</field>
<field name="code">l10n_in_standard_deduction</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_standard_deduction_value" model="hr.rule.parameter.value">
<field name="parameter_value">50000</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_standard_deduction"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_min_income_tax_rebate" model="hr.rule.parameter">
<field name="name">India: Minimun Income for Tax Rebate</field>
<field name="code">l10n_in_min_income_tax_rebate</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_min_income_tax_rebate_value" model="hr.rule.parameter.value">
<field name="parameter_value">700000</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_min_income_tax_rebate"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_min_income_surcharge" model="hr.rule.parameter">
<field name="name">India: Miminum Income for Surcharge</field>
<field name="code">l10n_in_min_income_surcharge</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_min_income_surcharge_value" model="hr.rule.parameter.value">
<field name="parameter_value">5000000</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_min_income_surcharge"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_max_surcharge_tax_rate" model="hr.rule.parameter">
<field name="name">India: Maximum Tax Rate for Surcharge</field>
<field name="code">l10n_in_max_surcharge_tax_rate</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_max_surcharge_tax_rate_value" model="hr.rule.parameter.value">
<field name="parameter_value">[
(5000000, 1200000, 0),
(10000000, 2700000, 270000),
(20000000, 5700000, 855000),
(50000000, 14700000, 3675000),
]</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_max_surcharge_tax_rate"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_lta_percent" model="hr.rule.parameter">
<field name="name">India: Leave Travel Allowance Value and Percentage</field>
<field name="code">l10n_in_lta_percent</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_lta_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">[
(450000.00, (12, 0.30)),
(550000.00, (3, 0.20)),
(0.00, (1, 0.10)),
]</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_lta_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_professional_tax" model="hr.rule.parameter">
<field name="name">India: Professional Tax</field>
<field name="code">l10n_in_professional_tax</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_professional_tax_value" model="hr.rule.parameter.value">
<field name="parameter_value">[-200, -150, -80]</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_professional_tax"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_pf_percent" model="hr.rule.parameter">
<field name="name">India: Provident Fund Percentage</field>
<field name="code">l10n_in_pf_percent</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_empr_pf_ctb_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">0.12</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_pf_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_pf_amount" model="hr.rule.parameter">
<field name="name">India: Provident Fund Value</field>
<field name="code">l10n_in_pf_amount</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_pf_amount_value" model="hr.rule.parameter.value">
<field name="parameter_value">15000.00</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_pf_amount"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_eps_contri_percent" model="hr.rule.parameter">
<field name="name">India: EPS Contri Percentage</field>
<field name="code">l10n_in_eps_contri_percent</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_eps_contri_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">0.0833</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_eps_contri_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_esicf_percent" model="hr.rule.parameter">
<field name="name">India: Employer's State Insurance Corporation Percentage</field>
<field name="code">l10n_in_esicf_percent</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_esicf_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">0.0325</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_esicf_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_city_alw_percent" model="hr.rule.parameter">
<field name="name">India: City Compensatory Allowance Percentage</field>
<field name="code">l10n_in_city_alw_percent</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_city_alw_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">0.10</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_city_alw_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_chea" model="hr.rule.parameter">
<field name="name">India: Child Education Allowance</field>
<field name="code">l10n_in_chea_value</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_chea_value" model="hr.rule.parameter.value">
<field name="parameter_value">[100, 200]</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_chea"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_child" model="hr.rule.parameter">
<field name="name">India: Child Hostel Allowance Value</field>
<field name="code">l10n_in_child_hostel_allowance</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_child_value" model="hr.rule.parameter.value">
<field name="parameter_value">[300, 600]</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_child"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_esicf" model="hr.rule.parameter">
<field name="name">India: Employer's State Insurance Corporation Value</field>
<field name="code">l10n_in_esicf_value</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_esicf_value" model="hr.rule.parameter.value">
<field name="parameter_value">21000</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_esicf"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_leave_days" model="hr.rule.parameter">
<field name="name">India: Leave Days</field>
<field name="code">l10n_in_leave_days</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_leave_days_value" model="hr.rule.parameter.value">
<field name="parameter_value">22</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_leave_days"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_cbda_percent" model="hr.rule.parameter">
<field name="name">India: India:Employee's NPS Contribution</field>
<field name="code">l10n_in_cbda_percent</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_cbda_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">0.10</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_cbda_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
<record id="l10n_in_rule_parameter_performace_bonus_percent" model="hr.rule.parameter">
<field name="name">India: Performace Bonus Percentage</field>
<field name="code">l10n_in_regular_bonus_percent</field>
<field name="country_id" ref="base.in"/>
</record>
<record id="l10n_in_rule_parameter_performace_bonus_percent_value" model="hr.rule.parameter.value">
<field name="parameter_value">0.40</field>
<field name="rule_parameter_id" ref="l10n_in_rule_parameter_performace_bonus_percent"/>
<field name="date_from" eval="datetime(2000, 1, 1).date()"/>
</record>
</odoo>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- salary category -->
<record id="SPA" model="hr.salary.rule.category">
<field name="name">Special Allowance</field>
<field name="code">SPA</field>
</record>
<record id="LEAVE" model="hr.salary.rule.category">
<field name="name">Leave Allowance</field>
<field name="code">LEAVE</field>
</record>
<record id="PBS" model="hr.salary.rule.category">
<field name="name">Performance Bonus</field>
<field name="code">PBS</field>
<field name="parent_id" ref="hr_payroll.DED"/>
</record>
</odoo>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<record id="seq_payment_advice" model="ir.sequence">
<field name="name">Payment Advice</field>
<field name="code">payment.advice</field>
<field name="padding">3</field>
<field name="company_id" eval="False" />
</record>
</data>
</odoo>

View File

@ -0,0 +1,430 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="base.partner_demo_company_in" model="res.partner" forcecreate="1">
<field name="name">IN Company</field>
<field name="street">Block no. 401</field>
<field name="street2">Street 2</field>
<field name="city">Hyderabad</field>
<field name="country_id" ref="base.in"/>
<field name="state_id" ref="base.state_in_ts"/>
<field name="zip">500001</field>
<field name="phone">+91 81234 56789</field>
</record>
<record id="base.demo_company_in" model="res.company" forcecreate="1">
<field name="name">IN Company</field>
<field name="partner_id" ref="base.partner_demo_company_in"/>
<field name="currency_id" ref="base.INR"/>
<field name="vat">24DUMMY1234AAZA</field>
</record>
<record id="bank_hdfc" model="res.bank">
<field name="name">HDFC Bank</field>
<field name="bic">HDFC0000123</field>
</record>
<record id="bank_sbi" model="res.bank">
<field name="name">State Bank</field>
<field name="bic">SBIS0000321</field>
</record>
<function model="res.users" name="write">
<value eval="[ref('base.user_root'), ref('base.user_admin'), ref('base.user_demo')]"/>
<value eval="{'company_ids': [(4, ref('base.demo_company_in'))]}"/>
</function>
<record id="l10n_in_res_partner_vihaan" model="res.partner">
<field name="name">Vihaan Sengupta</field>
<field name="street">A-4 Gunj Society</field>
<field name="street2">near AK Hospital</field>
<field name="city">Hyderabad</field>
<field name="zip">385426</field>
<field name="state_id" ref="base.state_in_ap"/>
<field name="country_id" ref="base.in"/>
<field name="phone">+91 98765 43021</field>
<field name="email">vihaan.sengupta@example.com</field>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<record id="l10n_in_partner_aisha_sharma" model="res.partner">
<field name="name">Alisha Sharma</field>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<record id="l10n_in_partner_shaurya_khurana" model="res.partner">
<field name="name">Shaurya Khurana</field>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<record id="l10n_in_emp_vihaan_bank_account" model="res.partner.bank">
<field name="acc_number">1245670000123</field>
<field name="bank_id" ref="l10n_in_hr_payroll.bank_sbi"/>
<field name="company_id" ref="base.demo_company_in" />
<field name="partner_id" ref="l10n_in_hr_payroll.l10n_in_res_partner_vihaan"/>
<field name="currency_id" ref="base.INR"/>
<field name="allow_out_payment" eval="True"/>
</record>
<record id="l10n_in_emp_alisha_sharma_bank_account" model="res.partner.bank">
<field name="acc_number">0112340000998</field>
<field name="bank_id" ref="l10n_in_hr_payroll.bank_hdfc"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="partner_id" ref="l10n_in_hr_payroll.l10n_in_partner_aisha_sharma"/>
<field name="currency_id" ref="base.INR"/>
<field name="allow_out_payment" eval="True"/>
</record>
<record id="l10n_in_emp_shaurya_khurana_bank_account" model="res.partner.bank">
<field name="acc_number">0222340000789</field>
<field name="bank_id" ref="l10n_in_hr_payroll.bank_hdfc"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="partner_id" ref="l10n_in_hr_payroll.l10n_in_partner_shaurya_khurana"/>
<field name="currency_id" ref="base.INR"/>
<field name="allow_out_payment" eval="True"/>
</record>
<record id="l10n_in_user_vihaan" model="res.users">
<field name="partner_id" ref="l10n_in_hr_payroll.l10n_in_res_partner_vihaan"/>
<field name="login">vihaan@example.com</field>
<field name="password">vihaan@123</field>
<field name="signature" type="html"><span>--<br />+V. Sengupta</span></field>
<field name="company_ids" eval="[(4, ref('base.demo_company_in'))]"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="groups_id" eval="[(6,0, [ref('base.group_user')])]"/>
<field name="tz">Asia/Kolkata</field>
<field name="image_1920" type="base64" file="l10n_in_hr_payroll/static/img/hr_employee_vihaan.jpg"/>
</record>
<!-- resource calendar -->
<record id="l10n_in_resource_calendar_part_time" model="resource.calendar">
<field name="name">Standard 24 hours/week</field>
<field name="company_id" ref="base.demo_company_in"/>
<field name="hours_per_day">8</field>
<field name="attendance_ids"
eval="[(5, 0, 0),
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
(0, 0, {'name': 'Monday Afternoon', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 17.0, 'day_period': 'afternoon'}),
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
(0, 0, {'name': 'Tuesday Afternoon', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17.0, 'day_period': 'afternoon'}),
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
(0, 0, {'name': 'Thursday Afternoon', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17.0, 'day_period': 'afternoon'}),
]"
/>
</record>
<record id="l10n_in_hr_department_rd" model="hr.department">
<field name="name">Research &amp; Development IN</field>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<record id="l10n_in_hr_department_tech_support" model="hr.department">
<field name="name">Technical Support &amp; Investigation</field>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<record id="l10n_in_hr_department_marketing" model="hr.department">
<field name="name">Marketing</field>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<record id="l10n_in_job_developer" model="hr.job">
<field name="name">Experienced Developer</field>
<field name="department_id" ref="l10n_in_hr_payroll.l10n_in_hr_department_rd"/>
<field name="no_of_recruitment">5</field>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<record id="l10n_in_job_intern" model="hr.job">
<field name="name">Technical Support &amp; Investigation Intern</field>
<field name="department_id" ref="l10n_in_hr_payroll.l10n_in_hr_department_tech_support"/>
<field name="no_of_recruitment">10</field>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<!-- Employee -->
<record id="l10n_in_hr_employee_vihaan" model="hr.employee">
<field name="name">Vihaan Sengupta</field>
<field name="gender">male</field>
<field name="marital">single</field>
<field name="work_phone">+91 8765432109</field>
<field name="department_id" ref="l10n_in_hr_payroll.l10n_in_hr_department_rd"/>
<field name="private_street">A-4 Gunj Society</field>
<field name="private_street2">near AK Hospital</field>
<field name="private_city">Hyderabad</field>
<field name="private_zip">385426</field>
<field name="private_state_id" ref="base.state_in_ap"/>
<field name="private_country_id" ref="base.in"/>
<field name="private_phone">+91 7654321098</field>
<field name="private_email">vihaan.sengupta123@example.com</field>
<field name="job_id" ref="l10n_in_hr_payroll.l10n_in_job_developer"/>
<field name="job_title">Software Developer</field>
<field name="work_contact_id" ref="l10n_in_hr_payroll.l10n_in_res_partner_vihaan"/>
<field name="emergency_contact">Harshiv Sengupta</field>
<field name="emergency_phone">+91 9348762345</field>
<field name="place_of_birth">India</field>
<field name="country_of_birth" ref="base.in"/>
<field name="certificate">bachelor</field>
<field name="study_field">Computer Engineering</field>
<field name="study_school">TechInnova University</field>
<field name="country_id" ref="base.in"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="resource_calendar_id" ref="resource.resource_calendar_std"/>
<field name="identification_id">82735682375623</field>
<field name="bank_account_id" ref="l10n_in_hr_payroll.l10n_in_emp_vihaan_bank_account"/>
<field name="image_1920" type="base64" file="l10n_in_hr_payroll/static/img/hr_employee_vihaan.jpg"/>
<field name="user_id" ref="l10n_in_hr_payroll.l10n_in_user_vihaan"/>
<field name="l10n_in_relationship">Father</field>
<field name="l10n_in_uan">124567334654</field>
<field name="l10n_in_esic_number">93874947361284657</field>
<field name="l10n_in_pan">HDIUE8765M</field>
</record>
<record id="l10n_in_hr_employee_shaurya" model="hr.employee">
<field name="name">Shaurya Khurana</field>
<field name="job_id" ref="l10n_in_hr_payroll.l10n_in_job_developer"/>
<field name="gender">male</field>
<field name="department_id" ref="l10n_in_hr_payroll.l10n_in_hr_department_tech_support"/>
<field name="work_contact_id" ref="l10n_in_hr_payroll.l10n_in_partner_shaurya_khurana"/>
<field name="marital">single</field>
<field name="work_phone">+91 7890123456</field>
<field name="private_street">503, highsky residency</field>
<field name="private_street2">VR Road</field>
<field name="private_city">Ahmedabad</field>
<field name="private_zip">385876</field>
<field name="private_state_id" ref="base.state_in_gj"/>
<field name="private_country_id" ref="base.in"/>
<field name="private_phone">+91 9870165432</field>
<field name="private_email">shaurya.khurana@example.com</field>
<field name="place_of_birth">India</field>
<field name="country_of_birth" ref="base.in"/>
<field name="tz">Asia/Kolkata</field>
<field name="country_id" ref="base.in"/>
<field name="resource_calendar_id" ref="l10n_in_resource_calendar_part_time"/>
<field name="identification_id">82735682375623</field>
<field name="bank_account_id" ref="l10n_in_hr_payroll.l10n_in_emp_shaurya_khurana_bank_account"/>
<field name="image_1920" type="base64" file="l10n_in_hr_payroll/static/img/hr_employee_shaurya.jpg"/>
<field name="parent_id" ref="l10n_in_hr_payroll.l10n_in_hr_employee_vihaan"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="l10n_in_uan">387438790384</field>
<field name="l10n_in_esic_number">93487475300284657</field>
<field name="l10n_in_pan">KUPYH9876I</field>
</record>
<record id="l10n_in_hr_employee_alisha_sharma" model="hr.employee">
<field name="name">Alisha Sharma</field>
<field name="job_id" ref="l10n_in_hr_payroll.l10n_in_job_intern"/>
<field name="department_id" ref="l10n_in_hr_payroll.l10n_in_hr_department_marketing"/>
<field name="work_contact_id" ref="l10n_in_hr_payroll.l10n_in_partner_aisha_sharma"/>
<field name="gender">female</field>
<field name="tz">Asia/Kolkata</field>
<field name="work_phone">+91 9887756789</field>
<field name="private_email">alisha.sharma@example.com</field>
<field name="place_of_birth">India</field>
<field name="country_of_birth" ref="base.in"/>
<field name="country_id" ref="base.in"/>
<field name="parent_id" ref="l10n_in_hr_payroll.l10n_in_hr_employee_shaurya"/>
<field name="bank_account_id" ref="l10n_in_hr_payroll.l10n_in_emp_alisha_sharma_bank_account"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="image_1920" type="base64" file="l10n_in_hr_payroll/static/img/hr_employee_alisha_sharma.jpg"/>
<field name="l10n_in_uan">398175628304</field>
<field name="l10n_in_esic_number">4658302649025064783</field>
<field name="l10n_in_pan">GUNI5723P</field>
</record>
<!-- Running Contract-->
<record id="l10n_in_hr_contract_alisha_sharma" model="hr.contract">
<field name="name">Alisha Sharma</field>
<field name="department_id" ref="l10n_in_hr_department_rd"/>
<field name="employee_id" ref="l10n_in_hr_employee_alisha_sharma"/>
<field name="structure_type_id" ref="l10n_in_hr_payroll.hr_payroll_salary_structure_type_ind_intern"/>
<field name="wage" eval="25000"/>
<field name="state">open</field>
<field name="company_id" ref="base.demo_company_in"/>
<field name="hr_responsible_id" ref="base.user_demo"/>
<field name="date_start" eval="(DateTime.today() + relativedelta(years=-1, month=1, day=1))"/>
</record>
<record id="l10n_in_hr_contract_shaurya" model="hr.contract">
<field name="name">Shaurya Contract</field>
<field name="department_id" ref="l10n_in_hr_department_rd"/>
<field name="employee_id" ref="l10n_in_hr_employee_shaurya"/>
<field name="structure_type_id" ref="hr_contract.structure_type_worker"/>
<field name="wage_type">hourly</field>
<field name="hourly_wage">250</field>
<field name="wage" eval="15000"/>
<field name="time_credit" eval="True"/>
<field name="time_credit_type_id" ref="hr_work_entry_contract.work_entry_type_unpaid_leave"/>
<field name="resource_calendar_id" ref="l10n_in_resource_calendar_part_time"/>
<field name="state">open</field>
<field name="company_id" ref="base.demo_company_in"/>
<field name="hr_responsible_id" ref="base.user_demo"/>
<field name="date_start" eval="(DateTime.today() + relativedelta(years=-1, month=1, day=1))"/>
</record>
<record id="l10n_in_hr_contract_vihaan" model="hr.contract">
<field name="name">Vihaan Sengupta Contract</field>
<field name="department_id" ref="l10n_in_hr_department_rd"/>
<field name="employee_id" ref="l10n_in_hr_employee_vihaan"/>
<field name="structure_type_id" ref="l10n_in_hr_payroll.hr_payroll_salary_structure_type_ind_emp"/>
<field name="wage">50000</field>
<field name="state">open</field>
<field name="company_id" ref="base.demo_company_in"/>
<field name="l10n_in_tds">5000</field>
<field name="l10n_in_gratuity">1442.50</field>
<field name="l10n_in_medical_insurance">6550</field>
<field name="l10n_in_voluntary_provident_fund">12</field>
<field name="l10n_in_house_rent_allowance_metro_nonmetro">12</field>
<field name="l10n_in_supplementary_allowance">6850</field>
<field name="l10n_in_leave_allowance">2750</field>
<field name="l10n_in_esic_amount">1560</field>
<field name="hr_responsible_id" ref="base.user_demo"/>
<field name="date_start" eval="(DateTime.today() + relativedelta(years=-1, month=1, day=1))"/>
</record>
<!-- Expired Contract-->
<record id="l10n_in_hr_contract_vihaan_expired" model="hr.contract">
<field name="name">Vihaan Sengupta Contract</field>
<field name="department_id" ref="l10n_in_hr_department_rd"/>
<field name="employee_id" ref="l10n_in_hr_employee_vihaan"/>
<field name="structure_type_id" ref="hr_contract.structure_type_employee"/>
<field name="wage">40000</field>
<field name="state">close</field>
<field name="company_id" ref="base.demo_company_in"/>
<field name="hr_responsible_id" ref="base.user_demo"/>
<field name="date_start" eval="(DateTime.today() + relativedelta(years=-2, month=1, day=1))"/>
<field name="date_end" eval="(DateTime.today() + relativedelta(years=-1, month=1, day=1, days=-1))"/>
<field name="l10n_in_tds">4000</field>
<field name="l10n_in_gratuity">1142</field>
<field name="l10n_in_medical_insurance">5100</field>
<field name="l10n_in_voluntary_provident_fund">12</field>
<field name="l10n_in_house_rent_allowance_metro_nonmetro">16</field>
<field name="l10n_in_supplementary_allowance">5560</field>
<field name="l10n_in_leave_allowance">2000</field>
<field name="l10n_in_esic_amount">1160</field>
</record>
<!-- allocation -->
<record id="l10n_in_cl_allocation_1" model="hr.leave.allocation">
<field name="name">Paid Time Off for Indian Employee 1</field>
<field name="date_from" eval="time.strftime('%Y-1-1')"/>
<field name="date_to" eval="time.strftime('%Y-12-31')"/>
<field name="holiday_status_id" ref="hr_holidays.holiday_status_cl"/>
<field name="number_of_days">24</field>
<field name="state">confirm</field>
<field name="employee_id" ref="l10n_in_hr_payroll.l10n_in_hr_employee_vihaan"/>
</record>
<record id="l10n_in_cl_allocation_2" model="hr.leave.allocation">
<field name="name">Paid Time Off for Indian Employee 2</field>
<field name="date_from" eval="time.strftime('%Y-1-1')"/>
<field name="date_to" eval="time.strftime('%Y-12-31')"/>
<field name="holiday_status_id" ref="hr_holidays.holiday_status_cl"/>
<field name="number_of_days">24</field>
<field name="state">confirm</field>
<field name="employee_id" ref="l10n_in_hr_payroll.l10n_in_hr_employee_alisha_sharma"/>
</record>
<record id="l10n_in_cl_allocation_3" model="hr.leave.allocation">
<field name="name">Paid Time Off for Indian Employee 3</field>
<field name="date_from" eval="time.strftime('%Y-1-1')"/>
<field name="date_to" eval="time.strftime('%Y-12-31')"/>
<field name="holiday_status_id" ref="hr_holidays.holiday_status_cl"/>
<field name="number_of_days">24</field>
<field name="state">confirm</field>
<field name="employee_id" ref="l10n_in_hr_payroll.l10n_in_hr_employee_shaurya"/>
</record>
<!-- time-off -->
<record id="l10n_in_vihaan_time_off" model="hr.leave">
<field name="request_date_from" eval="DateTime.today() - relativedelta(years=1, month=9, day=11)"/>
<field name="request_date_to" eval="DateTime.today() - relativedelta(years=1, month=9, day=11)"/>
<field name="number_of_days">1</field>
<field name="holiday_status_id" ref="hr_holidays.holiday_status_cl"/>
<field name="employee_id" ref="l10n_in_hr_payroll.l10n_in_hr_employee_vihaan"/>
<field name="state">confirm</field>
</record>
<record id="l10n_in_alisha_half_time_off" model="hr.leave">
<field name="request_date_from" eval="DateTime.today() - relativedelta(years=1, month=11, day=6)"/>
<field name="request_date_to" eval="DateTime.today() - relativedelta(years=1, month=11, day=6)"/>
<field name="number_of_days">0.5</field>
<field name="request_unit_half" eval="True"/>
<field name="holiday_status_id" ref="hr_holidays.holiday_status_unpaid"/>
<field name="employee_id" ref="l10n_in_hr_payroll.l10n_in_hr_employee_alisha_sharma"/>
<field name="state">confirm</field>
</record>
<function model="hr.leave" name="action_validate">
<value model="hr.leave"
eval="[
ref('l10n_in_hr_payroll.l10n_in_vihaan_time_off'),
ref('l10n_in_hr_payroll.l10n_in_alisha_half_time_off'),
]"/>
</function>
<!-- salary attachment -->
<record id="l10n_in_salary_attachement_in_health_insurance" model="hr.salary.attachment">
<field name="employee_ids" eval="[(4, ref('l10n_in_hr_employee_vihaan'))]"/>
<field name="monthly_amount">5500</field>
<field name="total_amount">5500</field>
<field name="other_input_type_id" ref="hr_payroll.input_assignment_salary"/>
<field name="date_start" eval="DateTime.today() - relativedelta(years=1, month=10, day=1)"/>
<field name="description">Health Insurance</field>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<!-- payslip batch -->
<record id="l10n_in_hr_payslip_batch_year_1" model="hr.payslip.run">
<field name="name" eval="'India batch for ' + (DateTime.today() - relativedelta(years=1, month=10)).strftime('%B %Y')"/>
<field name="date_start" eval="(DateTime.today() - relativedelta(years=1, month=10, day=1)).strftime('%Y-%m-%d')"/>
<field name="date_end" eval="(DateTime.today() - relativedelta(years=1, month=10, day=31)).strftime('%Y-%m-%d')"/>
<field name="company_id" ref="base.demo_company_in"/>
</record>
<!-- payslip -->
<record id="l10n_in_hr_payslip_in_vihaan_sep" model="hr.payslip">
<field name="employee_id" ref="l10n_in_hr_employee_vihaan"/>
<field name="name">Vihaan Payslip September</field>
<field name="date_from" eval="DateTime.today() - relativedelta(years=1, month=9, day=1)"/>
<field name="date_to" eval="DateTime.today() - relativedelta(years=1, month=9, day=30)"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_payslip_in_shaurya_sep" model="hr.payslip">
<field name="employee_id" ref="l10n_in_hr_employee_shaurya"/>
<field name="name">Shaurya Payslip September</field>
<field name="date_from" eval="DateTime.today() - relativedelta(years=1, month=9, day=1)"/>
<field name="date_to" eval="DateTime.today() - relativedelta(years=1, month=9, day=30)"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="struct_id" ref="l10n_in_hr_payroll.structure_worker_0001"/>
</record>
<record id="l10n_in_hr_payslip_in_vihaan_oct" model="hr.payslip">
<field name="employee_id" ref="l10n_in_hr_employee_vihaan"/>
<field name="name">Vihaan Payslip October</field>
<field name="payslip_run_id" ref="l10n_in_hr_payslip_batch_year_1"/>
<field name="date_from" eval="DateTime.today() - relativedelta(years=1, month=10, day=1)"/>
<field name="date_to" eval="DateTime.today() - relativedelta(years=1, month=10, day=31)"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_payslip_in_alisha_sharma" model="hr.payslip">
<field name="employee_id" ref="l10n_in_hr_employee_alisha_sharma"/>
<field name="name">Alisha Sharma November</field>
<field name="date_from" eval="DateTime.today() - relativedelta(years=1, month=11, day=1)"/>
<field name="date_to" eval="DateTime.today() - relativedelta(years=1, month=11, day=30)"/>
<field name="company_id" ref="base.demo_company_in"/>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_stipend"/>
</record>
<function model="hr.payslip" name="compute_sheet">
<value model="hr.payslip" eval="[
ref('l10n_in_hr_payroll.l10n_in_hr_payslip_in_vihaan_sep'),
ref('l10n_in_hr_payroll.l10n_in_hr_payslip_in_vihaan_oct'),
ref('l10n_in_hr_payroll.l10n_in_hr_payslip_in_shaurya_sep'),
ref('l10n_in_hr_payroll.l10n_in_hr_payslip_in_alisha_sharma')
]"/>
</function>
<function model="hr.payslip" name="action_payslip_done">
<value model="hr.payslip" eval="[
ref('l10n_in_hr_payroll.l10n_in_hr_payslip_in_vihaan_sep'),
ref('l10n_in_hr_payroll.l10n_in_hr_payslip_in_vihaan_oct'),
ref('l10n_in_hr_payroll.l10n_in_hr_payslip_in_alisha_sharma')
]"/>
</function>
</data>
</odoo>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="paperformat_yealy_salary_head" model="report.paperformat">
<field name="name">Yealy Salary Head Report</field>
<field name="default" eval="True" />
<field name="format">A4</field>
<field name="page_height">0</field>
<field name="page_width">0</field>
<field name="orientation">Landscape</field>
<field name="margin_top">15</field>
<field name="margin_bottom">15</field>
<field name="margin_left">5</field>
<field name="margin_right">5</field>
<field name="disable_shrinking" eval="True"/>
<field name="dpi">96</field>
</record>
</odoo>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_houserent_allowance_register" model="res.partner">
<field name="name">Register for House Rent Allowance</field>
</record>
<record id="hr_register_provident_fund" model="res.partner">
<field name="name">Register for Provident Fund</field>
</record>
<record id="hr_professional_tax_deduction_register" model="res.partner">
<field name="name">Register for Professional Tax</field>
</record>
<record id="hr_food_coupen_register" model="res.partner">
<field name="name">Register for Food Coupen</field>
</record>
<record id="hr_tds_register" model="res.partner">
<field name="name">Register for TDS</field>
</record>
<record id="hr_nps_contribution_register" model="res.partner">
<field name="name">Register for NPS Contribution</field>
</record>
<record id="hr_vpf_contribution_register" model="res.partner">
<field name="name">Register for Voluntary Provident Fund</field>
</record>
<record id="hr_company_transport_register" model="res.partner">
<field name="name">Register for Company Provided Transport Deduction</field>
</record>
<record id="hr_labour_Welfare_fund_register" model="res.partner">
<field name="name">Register for State Labour Welfare Fund Deduction</field>
</record>
<record id="hr_group_term_insurance_register" model="res.partner">
<field name="name">Register for Company Provided Group Term Insurance Deduction</field>
</record>
<record id="hr_leave_availed_register" model="res.partner">
<field name="name">Register for Leave Availed Deduction</field>
</record>
<record id="hr_medical_insurance_register" model="res.partner">
<field name="name">Register for Company Provided Medical Insurance Deduction</field>
</record>
<record id="hr_other_deduction_register" model="res.partner">
<field name="name">Register for Other Deduction from Employer</field>
</record>
</odoo>

View File

@ -0,0 +1,511 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_salary_rule_houserentallowancemetro_nonmetro" model="hr.salary.rule">
<field name="code">HRAMN</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
nonmetro_alw = contract.l10n_in_house_rent_allowance_metro_nonmetro / 100
result = contract.wage * nonmetro_alw
</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="name">House Rent Allowance</field>
<field name="partner_id" ref="hr_houserent_allowance_register"/>
<field name="sequence" eval="51"/>
<field name="note">HRA is an allowance given by the employer to the employee for taking care of his rental or accommodation expenses.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_special" model="hr.salary.rule">
<field name="code">SA</field>
<field name="name">Grade/Special/Management/Supplementary Allowance</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_supplementary_allowance)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = contract.l10n_in_supplementary_allowance</field>
<field name="sequence" eval="20"/>
<field name="note">This allowance is normally given as an additional benefit to employees and is fully taxable.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payroll_rule_child1" model="hr.salary.rule">
<field name="code">CHEA</field>
<field name="name">Child Education Allowance</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(employee.children)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
amounts = payslip._rule_parameter('l10n_in_chea_value')
if employee.children == 1:
result = amounts[0]
else:
result = amounts[1]
</field>
<field name="sequence" eval="18"/>
<field name="note">Per school going child 1200 per annum is non-taxable.Maximum for 2 children, so max 2400 per annum becomes non-taxable.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payroll_rule_child2" model="hr.salary.rule">
<field name="code">CHEAH</field>
<field name="name">Child Hostel Allowance</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(employee.l10n_in_residing_child_hostel)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
amounts = payslip._rule_parameter('l10n_in_child_hostel_allowance')
if employee.l10n_in_residing_child_hostel == 1:
result = amounts[0]
else:
result = amounts[1]
</field>
<field name="note">In case the children are in hostel, the exemption available for child.</field>
<field name="sequence" eval="19"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payroll_rule_city1" model="hr.salary.rule">
<field name="code">CBDA</field>
<field name="name">City Compensatory Allowance</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">none</field>
<field name="sequence" eval="21"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
city_alw_percent = payslip._rule_parameter('l10n_in_city_alw_percent')
result = contract.wage * city_alw_percent
</field>
<field name="note">This allowance is paid to Employees who are posted in big cities. The purpose is to compensate the high cost of living in cities like Mumbai, Delhi, etc. However it is Fully Taxable.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payroll_rule_metrocity" model="hr.salary.rule">
<field name="code">CMETRO</field>
<field name="name">City Allowance for Metro city</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">none</field>
<field name="sequence" eval="22"/>
<field name="amount_select">fix</field>
<field name="amount_fix">850.0</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payroll_rule_nonmetrocity" model="hr.salary.rule">
<field name="code">CNMETRO</field>
<field name="name">City Allowance for Non Metro city</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">none</field>
<field name="sequence" eval="25"/>
<field name="amount_select">fix</field>
<field name="amount_fix">800.0</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_arrears" model="hr.salary.rule">
<field name="code">ARRE</field>
<field name="name">Arrears</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = 'ARS' in inputs and inputs['ARS'].amount
result_name = 'ARS' in inputs and inputs['ARS'].name
</field>
<field eval="0.0" name="amount_fix"/>
<field name="sequence" eval="28"/>
<field name="note">Generally arrears are fully taxable, but employee may claim exemption u/s 89(1).
One would need to compute income tax on the arrears if it would have been received in actual year.
Now difference of income tax between payment year and actual year would be allowed for deduction.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_medical" model="hr.salary.rule">
<field name="code">MEDA</field>
<field name="name">Medical Reimbursement</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = 'MR' in inputs and inputs['MR'].amount
result_name = 'MR' in inputs and inputs['MR'].name
</field>
<field name="sequence" eval="32"/>
<field name="note">This component is on-taxable up to 15000 per year (or Rs 1500 per month) on producing medical bills.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_food_coupon" model="hr.salary.rule">
<field name="amount_select">fix</field>
<field eval="50" name="amount_fix"/>
<field name="quantity">'WORK100' in worked_days and worked_days['WORK100'].number_of_days</field>
<field name="code">FC</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="name">Food Allowance</field>
<field name="partner_id" ref="hr_food_coupen_register"/>
<field name="sequence" eval="33"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_employer_alw_esic" model="hr.salary.rule">
<field name="code">ESICF</field>
<field name="name">Employer's State Insurance Corporation</field>
<field name="sequence" eval="168"/>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">python</field>
<field name="condition_python">result = result_rules['GROSS']['total'] &lt;= payslip._rule_parameter('l10n_in_esicf_value')</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
esicf_percent = payslip._rule_parameter('l10n_in_esicf_percent')
gross = categories['GROSS']
result = gross * esicf_percent
</field>
<field name="sequence" eval="110"/>
<field name="appears_on_payslip" eval="False"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_alw_erpf" model="hr.salary.rule">
<field name="code">ERPF</field>
<field name="name">Employer's PF Contribution</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
pf_ctb_percent = payslip._rule_parameter('l10n_in_pf_percent')
result = -contract.wage * pf_ctb_percent
</field>
<field name="sequence" eval="37"/>
<field name="partner_id" ref="hr_register_provident_fund"/>
<field name="appears_on_payslip" eval="False"/>
<field name="note">Both the employees and employer contribute to the fund at the rate of 12% of the basic wages, dearness allowance and retaining allowance, if any, payable to employees per month.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_journals" model="hr.salary.rule">
<field name="code">PERJ</field>
<field name="name">Book and Periodicals Allowance (BNP)</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = 'PJ' in inputs and inputs['PJ'].amount
result_name = 'PJ' in inputs and inputs['PJ'].name
</field>
<field name="sequence" eval="34"/>
<field name="note">Some employers may provide component for buying magazines, journals and books as a part of knowledge enhancement for business growth.This part would become non taxable on providing original bills.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_uniform_senior" model="hr.salary.rule">
<field name="code">UNIFS</field>
<field name="name">Uniform/Dress Allowance for Senior Executive</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">fix</field>
<field eval="1000" name="amount_fix"/>
<field name="sequence" eval="35"/>
<field name="note">Some sections of employees mat get allowance for purchase of office dress/uniform.In such case, the component would become non-taxable.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_telephone" model="hr.salary.rule">
<field name="code">TELR</field>
<field name="name">Telephone Reimbursement</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = 'TR' in inputs and inputs['TR'].amount
result_name = 'TR' in inputs and inputs['TR'].name
</field>
<field name="sequence" eval="36"/>
<field name="note">In some of the cases, companies may provide a component for telephone bills.Employees may provide actual phone usage bills to reimburse this component and make it non-taxable.
</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_prof_develope" model="hr.salary.rule">
<field name="code">PDA</field>
<field name="name">Professional Development Allowance</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">fix</field>
<field eval="0.0" name="amount_fix"/>
<field name="sequence" eval="37"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payroll_rule_car" model="hr.salary.rule">
<field name="code">CAR</field>
<field name="name">Car Expenses Reimbursement</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = 'CEI' in inputs and inputs['CEI'].amount
result_name = 'CEI' in inputs and inputs['CEI'].name
</field>
<field name="sequence" eval="38"/>
<field name="note">In case company provides component for this and employee use self owned car for official and personal purposes, Rs 1800 per month would be non-taxable on showing bills for fuel or can maintenance. This amount would be Rs 2400 in case car is more capacity than 1600cc.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_internet" model="hr.salary.rule">
<field name="code">INT</field>
<field name="name">Mobile and Internet Expense</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = 'IE' in inputs and inputs['IE'].amount
result_name = 'IE' in inputs and inputs['IE'].name
</field>
<field name="sequence" eval="39"/>
<field name="note">Employer may also provide reimbursement of Mobile and Internet Expense and thus this would become non taxable.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_driver" model="hr.salary.rule">
<field name="code">DRI</field>
<field name="name">Driver Salary</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_driver_salay)</field>
<field name="amount_select">fix</field>
<field eval="900.0" name="amount_fix"/>
<field name="sequence" eval="40"/>
<field name="note">Rs. 900 per month (non taxable)</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<!--hr salary rules for Deductions -->
<record id="hr_payslip_rule_epf" model="hr.salary.rule">
<field name="code">EPMF</field>
<field name="name">Employee's PF Contribution</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
pf_ctb_percent = payslip._rule_parameter('l10n_in_pf_percent')
result = -contract.wage * pf_ctb_percent
</field>
<field name= "note">Employer contribution does not become part of employees income and hence income tax is not payable on this part.</field>
<field name="partner_id" ref="hr_register_provident_fund"/>
<field name="sequence" eval="150"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_enps" model="hr.salary.rule">
<field name="code">ENPFC</field>
<field name="name">Employee's NPS Contribution</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
pf_ctb_percent = payslip._rule_parameter('l10n_in_cbda_percent')
result = -contract.wage * pf_ctb_percent
</field>
<field name="sequence" eval="155"/>
<field name="partner_id" ref="hr_nps_contribution_register"/>
<field name="note">Employee can claim deduction even of employer's contribution to NPS.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_vpf" model="hr.salary.rule">
<field name="code">VPF</field>
<field name="name">Voluntary Provident Fund Contribution</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_voluntary_provident_fund)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
VPF = contract.l10n_in_voluntary_provident_fund
result = -contract.wage * VPF / 100
</field>
<field name="sequence" eval="160"/>
<field name="partner_id" ref="hr_vpf_contribution_register"/>
<field name="note">VPF is a safe option wherein you can contribute more than the PF ceiling of 12% that has been mandated by the government.This additional amount enjoys all the benefits of PF except that the employer is not liable to contribute any extra amount apart from 12%.An added advantage is that the interest rate is equal to the interest rate of PF and he withdrawal is tax free. Please note that the maximum contribution towards VPF is 100% of your Basic.The highest rate of interest (close to 9%) makes it a very attractive saving scheme. Because of these advantages many employees chose not to close their PF account even after getting employment else where other than India.Employees also get a major tax break on their entire contribution to the fund up to a ceiling of Rs. 70,000/-</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_cpt" model="hr.salary.rule">
<field name="code">CPT</field>
<field name="name">Deduction for Company Provided Transport</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="condition_select">none</field>
<field name="amount_select">fix</field>
<field eval="-1500.0" name="amount_fix"/>
<field name="partner_id" ref="hr_company_transport_register"/>
<field name="sequence" eval="165"/>
<field name="note">Company provided transport amount is based on company.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_salary_rule_food_coupon_ded" model="hr.salary.rule">
<field name="amount_select">fix</field>
<field eval="-50" name="amount_fix"/>
<field name="quantity">'WORK100' in worked_days and worked_days['WORK100'].number_of_days</field>
<field name="code">FD</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="name">Deduction Towards Food Coupons</field>
<field name="partner_id" ref="hr_food_coupen_register"/>
<field name="sequence" eval="166"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_lwf_employee" model="hr.salary.rule">
<field name="code">LWFE</field>
<field name="name">Employee's Deduction Towards State Labour Welfare Fund</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="amount_select">fix</field>
<field eval="-3.0" name="amount_fix"/>
<field name="sequence" eval="170"/>
<field name="partner_id" ref="hr_labour_Welfare_fund_register"/>
<field name="note">The LWF is applicable to all the members of the organisation except the Management staff (Staffs having authority to sign on the cheque/official documents on behalf of the organisation). for e.x. Employee Contribution is Rs. 3.00 and Employer contribution Rs. 6.00 Total Rs 9.00 and deposited to the LWF office.It is half yearly contribution (June and December).</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_lwf_employer" model="hr.salary.rule">
<field name="code">LWF</field>
<field name="name">Employer's Deduction Towards State Labour Welfare Fund </field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="amount_select">fix</field>
<field eval="-6.0" name="amount_fix"/>
<field name="sequence" eval="171"/>
<field name="partner_id" ref="hr_labour_Welfare_fund_register"/>
<field name="note">The LWF is applicable to all the members of the organisation except the Management staff (Staffs having authority to sign on the cheque/official documents on behalf of the organisation). for e.x. Employee Contribution is Rs. 3.00 and Employer contribution Rs. 6.00 Total Rs 9.00 and deposited to the LWF office.It is half yearly contribution (June and December).</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_cgti" model="hr.salary.rule">
<field name="code">CGTI</field>
<field name="name">Deduction Towards Company Provided Group Term Insurance</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="amount_select">fix</field>
<field eval="-1000.0" name="amount_fix"/>
<field name="partner_id" ref="hr_group_term_insurance_register"/>
<field name="sequence" eval="175"/>
<field name="note">Group term insurance provides a solid foundation to a comprehensive employee benifit program,backed up by government asistance in the form of valuable tax incentives to both employees and employers.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_dla" model="hr.salary.rule">
<field name="code">DLA</field>
<field name="name">Deduction Towards Leave Availed</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'LAI' in inputs</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = -inputs['LAI'].amount
result_name = inputs['LAI'].name
</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_cmt" model="hr.salary.rule">
<field name="code">CMT</field>
<field name="name">Deduction Towards Company Provided Medical Insurance</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_medical_insurance)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = -contract.l10n_in_medical_insurance
</field>
<field eval="-50.0" name="amount_fix"/>
<field name="partner_id" ref="hr_medical_insurance_register"/>
<field name="sequence" eval="185"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_ode" model="hr.salary.rule">
<field name="code">ODE</field>
<field name="name">Other Deduction from Employer</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="amount_select">fix</field>
<field eval="-200.0" name="amount_fix"/>
<field name="partner_id" ref="hr_other_deduction_register"/>
<field name="sequence" eval="187"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_ernps" model="hr.salary.rule">
<field name="code">ENPC</field>
<field name="name">Employer's NPS Contribution</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
pf_ctb_percent = payslip._rule_parameter('l10n_in_cbda_percent')
result = -contract.wage * pf_ctb_percent
</field>
<field name="sequence" eval="190"/>
<field name="partner_id" ref="hr_nps_contribution_register"/>
<field name= "note">Any amount contributed by your employer to your NPS account is treated as part of your salary and is included in your income but you can claim deduction under Section 80C for this too.thus, effectively making it exempt from tax within the limit of 10% of your basic salary. This is very useful and tax efficient for you particularly if you fall in the maximum tax.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_erpf" model="hr.salary.rule">
<field name="code">EPF</field>
<field name="name">Employer's PF Contribution</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
pf_ctb_percent = payslip._rule_parameter('l10n_in_pf_percent')
result = -contract.wage * pf_ctb_percent
</field>
<field name="sequence" eval="195"/>
<field name="partner_id" ref="hr_register_provident_fund"/>
<field name="appears_on_payslip" eval="False"/>
<field name="note">Both the employees and employer contribute to the fund at the rate of 12% of the basic wages, dearness allowance and retaining allowance, if any, payable to employees per month.</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_employer_esics" model="hr.salary.rule">
<field name="code">ESICS</field>
<field name="name">Employer's State Insurance Corporation</field>
<field name="sequence" eval="168"/>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_esic_amount)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = -contract.l10n_in_esic_amount</field>
<field name="appears_on_payslip" eval="False"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_expense" model="hr.salary.rule">
<field name="code">EXPENSE</field>
<field name="name">Refund Expenses</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'EXPENSES' in inputs</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = inputs['EXPENSES'].amount
result_name = inputs['EXPENSES'].name
</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="hr_payslip_rule_gratuity" model="hr.salary.rule">
<field name="code">GRATUITY</field>
<field name="name">Gratuity</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="sequence" eval="169"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_gratuity)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = -contract.l10n_in_gratuity</field>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
<record id="l10n_in_hr_salary_rule_pf" model="hr.salary.rule">
<field name="code">PF</field>
<field name="name">Provident fund</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_provident_fund)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
PF = payslip._rule_parameter('l10n_in_pf_percent')
result = -contract.wage * PF
</field>
<field name="sequence" eval="107"/>
<field name="struct_id" ref="hr_payroll_salary_structure_ind_emp"/>
</record>
</odoo>

View File

@ -0,0 +1,248 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<function model="hr.salary.rule" name="write">
<value model="hr.salary.rule" search="[('struct_id', '=', ref('l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary')),
('code', '=', 'BASIC')]"/>
<value eval="{'amount_python_compute': &quot;result = max(payslip._rule_parameter('l10n_in_basic_value'), payslip.paid_amount * payslip._rule_parameter('l10n_in_basic_percent') * payslip._rule_parameter('l10n_in_basic_days'))&quot;}"/>
</function>
<record id="hr_payslip_rule_tds" model="hr.salary.rule">
<field name="code">TDS</field>
<field name="name">Tax Deducted at Source</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_tds)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = -(contract.l10n_in_tds)</field>
<field name="partner_id" ref="hr_tds_register"/>
<field name="sequence" eval="140"/>
<field name="note">As per income tax rules, all payment which are taxable in nature should be done after deduction of taxes at the source itself. Hence employer compute income tax on salary payment and deduct it every month. This TDS is based on employees saving/investment declaration at the start of year. If investments for tax saving is not done, large amount may be deducted in last few months.</field>
<field name="struct_id" ref="hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_salary_rule_hra" model="hr.salary.rule">
<field name="code">HRA</field>
<field name="name">House Rent Allowance</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="sequence" eval="16"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
hra_value = payslip._rule_parameter('l10n_in_hra_value')
result = categories['BASIC'] * hra_value
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_salary_rule_std" model="hr.salary.rule">
<field name="code">STD</field>
<field name="name">Standard Allowance</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="sequence" eval="20"/>
<field name="quantity">1</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
std_awl = payslip._rule_parameter('l10n_in_std_alw')
work_rate = 1
if payslip.worked_days_line_ids:
total_days = sum(payslip.worked_days_line_ids.mapped('number_of_days'))
total_unpaid_days = worked_days['LEAVE90'].number_of_days if 'LEAVE90' in worked_days else 0
work_rate = (total_days - total_unpaid_days) / total_days
result = std_awl * work_rate
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_salary_rule_bonus" model="hr.salary.rule">
<field name="code">BONUS</field>
<field name="name">Bonus</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="sequence" eval="22"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
bonus_percent = payslip._rule_parameter('l10n_in_bonus_percent')
result = 0
for treshhold, coef in bonus_percent:
annual_wage = contract.wage * coef[0]
if annual_wage &lt; treshhold:
result = categories['BASIC'] * coef[1]
break
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_salary_rule_lta" model="hr.salary.rule">
<field name="code">LTA</field>
<field name="name">Leave Travel Allowance</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="sequence" eval="23"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
lta_percent = payslip._rule_parameter('l10n_in_lta_percent')
result = 0
for treshhold, coef in lta_percent:
annual_wage = contract.wage * coef[0]
if annual_wage &lt; treshhold:
result = categories['BASIC'] * coef[1]
break
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_salary_rule_spl" model="hr.salary.rule">
<field name="code">SPL</field>
<field name="name">Supplementary Allowance</field>
<field name="category_id" ref="l10n_in_hr_payroll.SPA"/>
<field name="sequence" eval="29"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
basic_and_alw = categories['BASIC'] + categories['ALW']
basic_in_per = payslip._rule_parameter('l10n_in_basic_percent')
result = payslip.paid_amount * basic_in_per - basic_and_alw
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_salary_rule_p_bonus" model="hr.salary.rule">
<field name="code">P_BONUS</field>
<field name="name">Performance Bonus</field>
<field name="category_id" ref="l10n_in_hr_payroll.PBS"/>
<field name="sequence" eval="31"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
basic_percent = payslip._rule_parameter('l10n_in_basic_percent')
regular_bonus_percent = payslip._rule_parameter('l10n_in_regular_bonus_percent')
worked_days_ratio = 1
if payslip.worked_days_line_ids:
total_days = sum(payslip.worked_days_line_ids.mapped('number_of_days'))
total_unpaid_days = worked_days['LEAVE90'].number_of_days if 'LEAVE90' in worked_days else 0
worked_days_ratio = (total_days - total_unpaid_days) / total_days
bonus_base_ded = contract.wage * basic_percent * 2 / payslip._rule_parameter('l10n_in_leave_days') + contract.l10n_in_gratuity
bonus_base = contract.wage * regular_bonus_percent - bonus_base_ded
result = bonus_base * worked_days_ratio
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<function model="hr.salary.rule" name="write">
<value model="hr.salary.rule" search="[('struct_id', '=', ref('l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary')),
('code', '=', 'GROSS')]"/>
<value eval="{'amount_python_compute': &quot;result = payslip.paid_amount * payslip._rule_parameter('l10n_in_basic_percent')&quot;}"/>
</function>
<record id="l10n_in_hr_salary_rule_leave" model="hr.salary.rule">
<field name="code">LEAVE</field>
<field name="name">Leave Allowance</field>
<field name="category_id" ref="l10n_in_hr_payroll.LEAVE"/>
<field name="sequence" eval="101"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_leave_allowance)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = contract.l10n_in_leave_allowance</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
<field name="appears_on_payslip">False</field>
</record>
<record id="l10n_in_hr_salary_rule_pt" model="hr.salary.rule">
<field name="code">PT</field>
<field name="name">Professional Tax</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="sequence" eval="105"/>
<field name="amount_select">code</field>
<field name="amount_python_compute">
amounts = payslip._rule_parameter('l10n_in_professional_tax')
if categories['GROSS'] &gt;= 12000:
result = amounts[0]
elif categories['GROSS'] &gt;= 9000 and categories['GROSS'] &lt; 12000:
result = amounts[1]
elif categories['GROSS'] &gt;= 6000 and categories['GROSS'] &lt; 9000:
result = amounts[2]
else:
result = 0
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="hr_salary_rule_pf_with_pf" model="hr.salary.rule">
<field name="code">PF</field>
<field name="name">Provident fund - Employee</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="sequence" eval="107"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_provident_fund)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
total_days = sum(payslip.worked_days_line_ids.mapped('number_of_days'))
total_worked_days = total_days - ('LEAVE90' in worked_days and worked_days['LEAVE90'].number_of_days)
result = - categories['BASIC'] * payslip._rule_parameter('l10n_in_pf_percent') if ((categories['BASIC']) &lt; 15000) else -(1800/total_days) * total_worked_days
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="hr_salary_rule_pfe_with_pf" model="hr.salary.rule">
<field name="code">PFE</field>
<field name="name">Provident fund - Employer</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="sequence" eval="110"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_provident_fund)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
total_days = sum(payslip.worked_days_line_ids.mapped('number_of_days'))
total_worked_days = total_days - ('LEAVE90' in worked_days and worked_days['LEAVE90'].number_of_days)
result = - categories['BASIC'] * payslip._rule_parameter('l10n_in_pf_percent') if ((categories['BASIC']) &lt; 15000) else -(1800/total_days) * total_worked_days
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_salary_rule_attach_salary" model="hr.salary.rule">
<field name="code">ATTACH_SALARY</field>
<field name="name">Attachment of Salary</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="sequence" eval="174"/>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ATTACH_SALARY' in inputs</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = -inputs['ATTACH_SALARY'].amount
result_name = inputs['ATTACH_SALARY'].name
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_salary_rule_assig_salary" model="hr.salary.rule">
<field name="code">ASSIG_SALARY</field>
<field name="name">Assignment of Salary</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="sequence" eval="174"/>
<field name="amount_select">code</field>
<field name="condition_select">python</field>
<field name="condition_python">result = 'ASSIG_SALARY' in inputs</field>
<field name="amount_python_compute">
result = -inputs['ASSIG_SALARY'].amount
result_name = inputs['ASSIG_SALARY'].name</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<record id="l10n_in_hr_salary_rule_expenses_reimbursement" model="hr.salary.rule">
<field name="name">Expenses Reimbursement</field>
<field name="code">EXPENSES</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="sequence" eval="190"/>
<field name="condition_select">python</field>
<field name="condition_python">result = inputs['EXPENSES'].amount > 0.0 if 'EXPENSES' in inputs else False</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
result = inputs['EXPENSES'].amount
result_name = inputs['EXPENSES'].name
</field>
<field name="struct_id" ref="l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary"/>
</record>
<function model="hr.salary.rule" name="write">
<value model="hr.salary.rule" search="[('struct_id', '=', ref('l10n_in_hr_payroll.hr_payroll_structure_in_employee_salary')),
('code', '=', 'NET')]"/>
<value eval="{'amount_python_compute': &quot;result = categories['BASIC'] + categories['ALW'] + categories['DED']&quot;}"/>
</function>
</odoo>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<function model="hr.salary.rule" name="unlink">
<value model="hr.salary.rule" search="[('struct_id', '=', ref('l10n_in_hr_payroll.hr_payroll_structure_in_stipend')),
('code', '=', 'BASIC')]"/>
</function>
<function model="hr.salary.rule" name="write">
<value model="hr.salary.rule" search="[('struct_id', '=', ref('l10n_in_hr_payroll.hr_payroll_structure_in_stipend')),
('code', '=', 'GROSS')]"/>
<value eval="{'amount_python_compute':&quot;result=( contract.wage - worked_days['LEAVE90'].amount) if 'LEAVE90' in worked_days else contract.wage &quot;}"/>
</function>
<function model="hr.salary.rule" name="write">
<value model="hr.salary.rule" search="[('struct_id', '=', ref('l10n_in_hr_payroll.hr_payroll_structure_in_stipend')),
('code', '=', 'NET')]"/>
<value eval="{ 'name': 'Net', 'amount_python_compute':&quot;result=(categories['GROSS'] + categories['ALW'] + categories['DED'])\nresult_name = _('NET')&quot;}"/>
</function>
</odoo>

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_salary_worker_trans_allownce" model="hr.salary.rule">
<field name="code">TCA</field>
<field name="name">Transport/Conveyance Allownace</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="amount_select">fix</field>
<field eval="800.0" name="amount_fix"/>
<field name="sequence" eval="14"/>
<field name="note">A conveyance allowance refers to an amount of money reimbursed to someone for the operation of a vehicle or the riding of a vehicle. The allowance is typically a designated amount or percentage of total transportation expenses that is referenced in a country's tax laws or code. Organizations and private or public businesses may also offer a conveyance allowance in addition to reimbursing employees or members for transportation expenses. In this instance, the conveyance allowance may identify an unusual transport occurrence that may not be covered by a designated travel expense report such as travel to a specific job site that requires a daily bus or taxi ride.</field>
<field name="struct_id" ref="structure_worker_0001"/>
</record>
<record id="hr_salary_rule_worker_special" model="hr.salary.rule">
<field name="code">SA</field>
<field name="name">Special</field>
<field name="category_id" ref="hr_payroll.ALW"/>
<field name="condition_select">python</field>
<field name="condition_python">result = bool(contract.l10n_in_supplementary_allowance)</field>
<field name="amount_select">code</field>
<field name="amount_python_compute">result = contract.l10n_in_supplementary_allowance</field>
<field name="sequence" eval="20"/>
<field name="note">This allowance is normally given as an additional benefit to worker and is fully taxable.</field>
<field name="struct_id" ref="structure_worker_0001"/>
</record>
<record id="hr_payslip_line_worker_professional_tax" model="hr.salary.rule">
<field name="code">PTD</field>
<field name="name">Professional Tax</field>
<field name="category_id" ref="hr_payroll.DED"/>
<field name="condition_select">python</field>
<field name="condition_python">result = categories['GROSS'] &gt;= 3000 </field>
<field name="amount_select">code</field>
<field name="amount_python_compute">
amounts = payslip._rule_parameter('l10n_in_professional_tax')
if categories['GROSS'] &gt;= 12000:
result = amounts[0]
elif categories['GROSS'] &gt;= 9000 and categories &lt; 11999:
result = amounts[1]
elif categories['GROSS'] &gt;= 6000 and categories['GROSS'] &lt;= 8999:
result = amounts[2]
else:
result = 0
</field>
<field name="partner_id" ref="hr_professional_tax_deduction_register"/>
<field eval="145" name="sequence"/>
<field name="note">Workers living in states that impose the professional tax must submit a payment each half-year for the right to practice a profession or trade. It applies equally to employees who work for the national or state government, and those employed by private corporations. The professional tax uses a six-month accounting system, which divides the year into two periods, beginning on April 1 and October 1.</field>
<field name="struct_id" ref="structure_worker_0001"/>
</record>
</odoo>

View File

@ -0,0 +1,26 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * l10n_in_hr_payroll
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server saas~16.2\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-06-05 06:08+0000\n"
"PO-Revision-Date: 2023-06-05 06:08+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: l10n_in_hr_payroll
#: model_terms:ir.ui.view,arch_db:l10n_in_hr_payroll.report_payslip_details_template
msgid "Employee Code"
msgstr ""
#. module: l10n_in_hr_payroll
#: model_terms:ir.ui.view,arch_db:l10n_in_hr_payroll.report_payslip_details_template
msgid "Joining Date"
msgstr ""

View File

@ -0,0 +1,10 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import hr_employee
from . import res_users
from . import hr_contract
from . import hr_payslip
from . import hr_payslip_run
from . import res_company
from . import res_config_settings
from . import l10n_in_salary_statement

View File

@ -0,0 +1,47 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import timedelta
from odoo import api, fields, models, _
class HrContract(models.Model):
"""
Employee contract allows to add different values in fields.
Fields are used in salary rule computation.
"""
_inherit = 'hr.contract'
l10n_in_tds = fields.Float(string='TDS', digits='Payroll',
help='Amount for Tax Deduction at Source')
l10n_in_driver_salay = fields.Boolean(string='Driver Salary', help='Check this box if you provide allowance for driver')
l10n_in_medical_insurance = fields.Float(string='Medical Insurance', digits='Payroll',
help='Deduction towards company provided medical insurance')
l10n_in_provident_fund = fields.Boolean(string='Provident Fund', default=False,
help='Check this box if you contribute for PF')
l10n_in_voluntary_provident_fund = fields.Float(string='Voluntary Provident Fund (%)', digits='Payroll',
help='VPF is a safe option wherein you can contribute more than the PF ceiling of 12% that has been mandated by the government and VPF computed as percentage(%)')
l10n_in_house_rent_allowance_metro_nonmetro = fields.Float(string='House Rent Allowance (%)', digits='Payroll',
help='HRA is an allowance given by the employer to the employee for taking care of his rental or accommodation expenses for metro city it is 50% and for non metro 40%. \nHRA computed as percentage(%)')
l10n_in_supplementary_allowance = fields.Float(string='Supplementary Allowance', digits='Payroll')
l10n_in_gratuity = fields.Float(string='Gratuity')
l10n_in_esic_amount = fields.Float(string='ESIC Amount', digits='Payroll',
help='Deduction towards company provided ESIC Amount')
l10n_in_leave_allowance = fields.Float(string='Leave Allowance', digits='Payroll',
help='Deduction towards company provided Leave Allowance')
@api.model
def update_state(self):
contract_type_id = self.env.ref('l10n_in_hr_payroll.l10n_in_contract_type_probation', raise_if_not_found=False)
if contract_type_id:
one_week_ago = fields.Date.today() - timedelta(weeks=1)
contracts = self.env['hr.contract'].search([
('date_end', '=', one_week_ago), ('state', '=', 'open'), ('contract_type_id', '=', contract_type_id.id)
])
for contract in contracts:
contract.activity_schedule(
'note.mail_activity_data_reminder',
user_id=contract.hr_responsible_id.id,
note=_("End date of %(name)s's contract is today.", name=contract.employee_id.name),
)
return super().update_state()

View File

@ -0,0 +1,24 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from odoo import fields, models
class HrEmployee(models.Model):
_inherit = 'hr.employee'
l10n_in_uan = fields.Char(string='UAN', groups="hr.group_hr_user")
l10n_in_pan = fields.Char(string='PAN', groups="hr.group_hr_user")
l10n_in_esic_number = fields.Char(string='ESIC Number', groups="hr.group_hr_user")
l10n_in_relationship = fields.Char("Relationship", groups="hr.group_hr_user", tracking=True)
l10n_in_residing_child_hostel = fields.Integer("Child Residing in hostel", groups="hr.group_hr_user", tracking=True)
_sql_constraints = [
('unique_l10n_in_uan', 'unique (l10n_in_uan)', 'This UAN already exists'),
('unique_l10n_in_pan', 'unique (l10n_in_pan)', 'This PAN already exists'),
('unique_l10n_in_esic_number', 'unique (l10n_in_esic_number)', 'This ESIC Number already exists'),
]
def _get_employees_with_invalid_ifsc(self):
return self.filtered(lambda emp: not bool(re.match("^[A-Z]{4}0[A-Z0-9]{6}$", emp.bank_account_id.bank_bic)))

View File

@ -0,0 +1,57 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, time
from odoo import api, models, _
from odoo.tools import format_date, date_utils
class HrPayslip(models.Model):
_inherit = 'hr.payslip'
def _get_l10n_in_company_working_time(self, return_hours=False):
self.ensure_one()
slip_date_time = datetime.combine(self.date_from, time(12, 0, 0))
company_work_data = self.company_id.resource_calendar_id.get_work_duration_data(
date_utils.start_of(slip_date_time, 'month'),
date_utils.end_of(slip_date_time, 'month'))
if return_hours:
return company_work_data['hours']
return company_work_data['days']
@api.depends('employee_id', 'struct_id', 'date_from')
def _compute_name(self):
super()._compute_name()
for slip in self.filtered(lambda s: s.country_code == 'IN'):
lang = slip.employee_id.lang or self.env.user.lang
payslip_name = slip.struct_id.payslip_name or _('Salary Slip')
date = format_date(self.env, slip.date_from, date_format="MMMM y", lang_code=lang)
if slip.number:
slip.name = '%(payslip_name)s - %(slip_ref)s - %(dates)s' % {
'slip_ref': slip.number,
'payslip_name': payslip_name,
'dates': date
}
else:
slip.name = '%(payslip_name)s - %(dates)s' % {
'payslip_name': payslip_name,
'dates': date
}
def _get_data_files_to_update(self):
# Note: file order should be maintained
return super()._get_data_files_to_update() + [(
'l10n_in_hr_payroll', [
'data/hr_salary_rule_category_data.xml',
'data/hr_payroll_structure_type_data.xml',
'data/hr_rule_parameters_data.xml',
'data/salary_rules/hr_salary_rule_ind_emp_data.xml',
'data/salary_rules/hr_salary_rule_regular_pay_data.xml',
'data/salary_rules/hr_salary_rule_worker_data.xml',
])]
def _get_base_local_dict(self):
return {**super()._get_base_local_dict(), '_': self.env._}
def _get_employee_timeoff_data(self):
return self.env['hr.leave.type'].with_company(self.company_id).with_context(employee_id=self.employee_id.id).get_allocation_data_request()

View File

@ -0,0 +1,26 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, _
class HrPayslipRun(models.Model):
_inherit = 'hr.payslip.run'
def action_payment_report(self, export_format='advice'):
self.ensure_one()
return {
'name': _('Create Payment'),
'type': 'ir.actions.act_window',
'res_model': 'hr.payroll.payment.report.wizard',
'view_mode': 'form',
'view_id': 'hr_payslip_payment_report_view_form',
'views': [(False, 'form')],
'target': 'new',
'context': {
'default_payslip_ids': self.slip_ids.ids,
'default_payslip_run_id': self.id,
'default_export_format': export_format,
'default_date': self.date_end,
'dialog_size': 'medium',
},
}

View File

@ -0,0 +1,107 @@
import calendar
from odoo import api, fields, models, Command, _
from dateutil.relativedelta import relativedelta
from collections import defaultdict
from datetime import datetime
class L10nInSalaryStatement(models.Model):
_name = 'l10n_in_hr_payroll.salary.statement'
_inherit = 'hr.payroll.declaration.mixin'
_description = 'Salary Statement Report'
name = fields.Char(string="Description", required=True, compute='_compute_name', readonly=False, store=True)
month = fields.Selection([
('1', 'January'),
('2', 'February'),
('3', 'March'),
('4', 'April'),
('5', 'May'),
('6', 'June'),
('7', 'July'),
('8', 'August'),
('9', 'September'),
('10', 'October'),
('11', 'November'),
('12', 'December'),
], required=True, default=lambda self: str((fields.Date.today() + relativedelta(months=-1)).month))
def default_get(self, fields_list):
res = super().default_get(fields_list)
res['year'] = str(datetime.now().year)
return res
@api.depends('year', 'month')
def _compute_name(self):
for sheet in self:
month_name = calendar.month_name[int(sheet.month)]
sheet.name = _('Salary Statement - %(month)s, %(year)s', month=month_name, year=sheet.year)
def action_generate_declarations(self):
for sheet in self:
date_from = datetime(int(sheet.year), int(sheet.month), 1)
date_to = date_from + relativedelta(months=1, days=-1)
employees = self.env['hr.payslip'].search([
('date_to', '<=', date_to),
('date_from', '>=', date_from),
('state', 'in', ['done', 'paid']),
('company_id', '=', sheet.company_id.id),
]).mapped('employee_id')
sheet.write({
'line_ids': [Command.clear()] + [Command.create({
'employee_id': employee.id,
'res_model': 'l10n_in_hr_payroll.salary.statement',
'res_id': sheet.id,
}) for employee in employees]
})
return super().action_generate_declarations()
def _country_restriction(self):
return 'IN'
def _get_pdf_report(self):
return self.env.ref('l10n_in_hr_payroll.action_report_salary_statement')
def _get_rendering_data(self, employees):
self.ensure_one()
date_from = datetime(int(self.year), int(self.month), 1)
date_to = date_from + relativedelta(months=1)
payslips = self.env['hr.payslip'].search([
('employee_id', 'in', employees.ids),
('state', 'in', ['done', 'paid']),
('date_from', '>=', date_from),
('date_to', '<=', date_to),
])
result = defaultdict(lambda: {
'month': calendar.month_name[int(self.month)],
'year': self.year,
'allow_rules': defaultdict(lambda: {'name': '', 'total': 0, 'total_annual': 0}),
'deduct_rules': defaultdict(lambda: {'name': '', 'total': 0, 'total_annual': 0}),
'ctc': 0,
'ctc_annual': 0
})
for line in payslips.line_ids.filtered(lambda l: l.salary_rule_id.appears_on_payslip):
employee_id = line.employee_id
rule_category = result[employee_id]['deduct_rules' if line.total < 0 else 'allow_rules'][line.salary_rule_id]
rule_category['name'] = line.salary_rule_id.name
rule_category['total'] = line.total
rule_category['total_annual'] = line.total * 12
if line.code == 'GROSS' or line.total < 0:
total = abs(line.total)
result[employee_id]['ctc'] += total
result[employee_id]['ctc_annual'] += total * 12
result[employee_id]['date'] = line.date_from.strftime('%d/%m/%Y')
result = dict(result)
return result
def _get_pdf_filename(self, employee):
self.ensure_one()
month_name = calendar.month_name[int(self.month)]
return _('%(employee_name)s-salary-statement-report-%(month)s-%(year)s', employee_name=employee.name, month=month_name, year=self.year)

View File

@ -0,0 +1,16 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class ResCompany(models.Model):
_inherit = 'res.company'
l10n_in_dearness_allowance = fields.Boolean(string='Dearness Allowance', default=True,
help='Check this box if your company provide Dearness Allowance to employee')
l10n_in_epf_employer_id = fields.Char(string="EPF Employer ID",
help="Code of 10 numbers. The first seven numbers represent the establishment ID.\n Next three numbers represent the extension code.")
l10n_in_esic_ip_number = fields.Char(string="ESIC IP Number",
help="Code of 17 digits.\n The Identification number is assigned to the company if registered under the Indian provisions of the Employee\'s State Insurance (ESI) Act.")
l10n_in_pt_number = fields.Char(string="PT Number",
help="Code of 11 digit.\n The P TIN digit number with the first two digits indicating the State.")
l10n_in_is_statutory_compliance = fields.Boolean(string="Statutory Compliance")

View File

@ -0,0 +1,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
_inherit = 'res.config.settings'
l10n_in_dearness_allowance = fields.Boolean(
string="Dearness Allowance", related='company_id.l10n_in_dearness_allowance', readonly=False)
l10n_in_epf_employer_id = fields.Char(related='company_id.l10n_in_epf_employer_id', readonly=False)
l10n_in_esic_ip_number = fields.Char(related='company_id.l10n_in_esic_ip_number', readonly=False)
l10n_in_pt_number = fields.Char(related='company_id.l10n_in_pt_number', readonly=False)
l10n_in_is_statutory_compliance = fields.Boolean(related='company_id.l10n_in_is_statutory_compliance', readonly=False)

View File

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class User(models.Model):
_inherit = 'res.users'
l10n_in_relationship = fields.Char(related='employee_id.l10n_in_relationship', readonly=False, related_sudo=False)
@property
def SELF_READABLE_FIELDS(self):
return super().SELF_READABLE_FIELDS + ['l10n_in_relationship']
@property
def SELF_WRITEABLE_FIELDS(self):
return super().SELF_WRITEABLE_FIELDS + ['l10n_in_relationship']

View File

@ -0,0 +1,6 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import report_payroll_advice
from . import report_hr_yearly_salary_detail
from . import report_hr_epf

View File

@ -0,0 +1,149 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
import io
import calendar
from odoo import api, fields, models
from odoo.tools.misc import xlsxwriter
MONTH_SELECTION = [
('1', 'January'),
('2', 'February'),
('3', 'March'),
('4', 'April'),
('5', 'May'),
('6', 'June'),
('7', 'July'),
('8', 'August'),
('9', 'September'),
('10', 'October'),
('11', 'November'),
('12', 'December'),
]
class HrEPFReport(models.Model):
_name = 'l10n.in.hr.payroll.epf.report'
_description = 'Indian Payroll: Employee Provident Fund Report'
month = fields.Selection(MONTH_SELECTION, default='1', required=True)
year = fields.Integer(required=True, default=lambda self: fields.Date.context_today(self).year)
xls_file = fields.Binary(string="XLS file")
xls_filename = fields.Char()
@api.depends('month', 'year')
def _compute_display_name(self):
month_description = dict(self._fields['month']._description_selection(self.env))
for report in self:
report.display_name = f"{month_description.get(report.month)}-{report.year}"
@api.model
def _get_employee_pf_data(self, year, month):
# Get the relevant records based on the year and month
indian_employees = self.env['hr.employee'].search([('contract_id.l10n_in_provident_fund', '=', True)]).filtered(lambda e: e.company_country_code == 'IN')
result = []
end_date = calendar.monthrange(year, int(month))[1]
payslips = self.env['hr.payslip'].search([
('employee_id', 'in', indian_employees.ids),
('date_from', '>=', f'{year}-{month}-1'),
('date_to', '<=', f'{year}-{month}-{end_date}'),
('state', 'in', ('done', 'paid'))
])
if not payslips:
return []
payslip_line_values = payslips._get_line_values(['GROSS', 'BASIC', 'PF'])
for employee in indian_employees:
wage = 0
epf = 0
eps = 0
epf_contri = 0
payslip_ids = payslips.filtered(lambda p: p.employee_id == employee)
if not payslip_ids:
continue
for payslip in payslip_ids:
pf_value = payslip_line_values['PF'][payslip.id]['total']
if pf_value == 0:
continue
epf_contri -= pf_value
wage += payslip_line_values['GROSS'][payslip.id]['total']
epf += payslip_line_values['BASIC'][payslip.id]['total']
# Skip the employee if there are no valid PF contributions
if epf_contri == 0:
continue
# Calculate contributions and differences
eps = min(payslip_ids[0]._rule_parameter('l10n_in_pf_amount'), epf)
eps_contri = round(eps * payslip_ids[0]._rule_parameter('l10n_in_eps_contri_percent'), 2)
diff = round(epf_contri - eps_contri, 2)
result.append((
employee.l10n_in_uan,
employee.name,
wage,
epf,
eps,
eps,
epf_contri,
eps_contri,
diff,
0, 0,
))
return result
def action_export_xlsx(self):
self.ensure_one()
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
worksheet = workbook.add_worksheet('Employee_provident_fund_report')
style_highlight = workbook.add_format({'bold': True, 'pattern': 1, 'bg_color': '#E0E0E0', 'align': 'center'})
style_normal = workbook.add_format({'align': 'center', 'font_size': 12})
row = 0
worksheet.set_row(row, 20)
headers = [
"UAN",
"MEMBER NAME",
"GROSS WAGES",
"EPF WAGES",
"EPS WAGES",
"EDLI WAGES",
"EPF CONTRIBUTION REMITTED",
"EPS CONTRIBUTION REMITTED",
"EPF EPS DIFFERENCE REMITTED",
"NCP DAYS",
"REFUNDED OF ADVANCES"
]
rows = self._get_employee_pf_data(self.year, self.month)
for col, header in enumerate(headers):
worksheet.write(row, col, header, style_highlight)
worksheet.set_column(col, col, 30)
row = 1
for data_row in rows:
col = 0
worksheet.set_row(row, 20)
for data in data_row:
worksheet.write(row, col, data, style_normal)
col += 1
row += 1
workbook.close()
xlsx_data = output.getvalue()
self.xls_file = base64.encodebytes(xlsx_data)
self.xls_filename = f"{self.display_name} EPF Report.xlsx"

View File

@ -0,0 +1,149 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import date
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.tools import SQL
from odoo.exceptions import UserError
class EmployeesYearlySalaryReport(models.AbstractModel):
_name = 'report.l10n_in_hr_payroll.report_hryearlysalary'
_description = "Indian Yearly Salary Report"
# YTI: This mess deserves a good cleaning
def _get_periods_new(self, form):
months = []
# Get start year-month-date and end year-month-date
first_year = int(form['year'])
first_month = 1
# Get name of the months from integer
month_name = []
for count in range(0, 12):
m = date(first_year, first_month, 1).strftime('%b')
month_name.append(m)
months.append(f"{first_month:02d}-{first_year}")
if first_month == 12:
first_month = 0
first_year += 1
first_month += 1
return [month_name], months
def _get_employee(self, form):
return self.env['hr.employee'].browse(form.get('employee_ids', []))
def _get_employee_detail_new(self, form, employee_id, months, date_from, date_to):
structures_data = {}
payslip_lines = self._cal_monthly_amt(form, employee_id, months, date_from, date_to)
for structure_name, payslip_data in payslip_lines.items():
allow_list = []
deduct_list = []
total = 0.0
gross = False
net = False
for line in payslip_data:
code = line[0]
subline = line[1:]
if code == "GROSS":
gross = [code, subline]
elif code == "NET":
net = [code, subline]
elif subline[-1] > 0.0 and code != "NET":
total += subline[-1]
allow_list.append([code, subline])
elif subline[-1] < 0.0:
total += subline[-1]
deduct_list.append([code, subline])
if gross:
allow_list.append(gross)
if net:
deduct_list.append(net)
structures_data[structure_name] = {
'allow_list': allow_list,
'deduct_list': deduct_list,
'total': total,
}
return structures_data
def _cal_monthly_amt(self, form, emp_id, months, date_from, date_to):
result = {}
salaries = {}
self.env.cr.execute(SQL(
"""
SELECT src.code, pl.name, sum(pl.total), to_char(p.date_to,'mm-yyyy') as to_date, ps.name
FROM hr_payslip_line as pl
LEFT JOIN hr_salary_rule AS sr on sr.id = pl.salary_rule_id
LEFT JOIN hr_salary_rule_category AS src on (sr.category_id = src.id)
LEFT JOIN hr_payslip as p on pl.slip_id = p.id
LEFT JOIN hr_employee as e on e.id = p.employee_id
LEFT JOIN hr_payroll_structure as ps on ps.id = p.struct_id
WHERE p.employee_id = %(employee_id)s
AND p.state = 'paid'
AND p.date_from >= %(date_from)s AND p.date_to <= %(date_to)s
GROUP BY src.parent_id, pl.sequence, pl.id, sr.category_id, pl.name, p.date_to, src.code, ps.name
ORDER BY pl.sequence, src.parent_id
""", employee_id=emp_id, date_from=date_from, date_to=date_to
))
for category_code, item_name, amount, payslip_date, structure_name in self.env.cr.fetchall():
salaries.setdefault(structure_name, {}).setdefault(category_code, {}).setdefault(item_name, {}).setdefault(payslip_date, 0.0)
salaries[structure_name][category_code][item_name][payslip_date] += amount
result = {key: self.salary_list(value, months) for key, value in salaries.items()}
return result
def salary_list(self, salaries, months):
cat_salary_all = []
for code, category_amount in salaries.items():
for category_name, amount in category_amount.items():
cat_salary = [code, category_name]
total = 0.0
for month in months:
if month != 'None':
if len(month) != 7:
month = '0' + str(month)
if amount.get(month):
cat_salary.append(amount[month])
total += amount[month]
else:
cat_salary.append(0.00)
else:
cat_salary.append('')
cat_salary.append(total)
cat_salary_all.append(cat_salary)
return cat_salary_all
@api.model
def _get_report_values(self, docids, data=None):
if not self.env.context.get('active_model') or not self.env.context.get('active_id'):
raise UserError(_("Form content is missing, this report cannot be printed."))
model = self.env.context.get('active_model')
docs = self.env[model].browse(self.env.context.get('active_id'))
employees = self._get_employee(data['form'])
month_name, months = self._get_periods_new(data['form'])
date_from = fields.Date.today() + relativedelta(day=1, month=1, year=int(data['form']['year']))
date_to = fields.Date.today() + relativedelta(day=31, month=12, year=int(data['form']['year']))
employee_data = {}
for employee in employees:
structures_data = self._get_employee_detail_new(data['form'], employee.id, months, date_from, date_to)
employee_data[employee.id] = structures_data
return {
'doc_ids': docids,
'doc_model': model,
'data': data,
'docs': docs,
'date_from': date_from,
'date_to': date_to,
'get_employee': self._get_employee,
'get_periods': lambda form: month_name,
'get_employee_detail_new': lambda emp_id: employee_data.get(emp_id, {}),
}

View File

@ -0,0 +1,34 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from odoo import api, models
class payroll_advice_report(models.AbstractModel):
_name = 'report.l10n_in_hr_payroll.report_payrolladvice'
_description = "Indian Payroll Advice Report"
def get_month(self, input_date):
res = {
'from_name': '', 'to_name': ''
}
slip = self.env['hr.payslip'].search([('date_from', '<=', input_date), ('date_to', '>=', input_date)], limit=1)
if slip:
from_date = slip.date_from
to_date = slip.date_to
res['from_name'] = from_date.strftime('%d') + '-' + from_date.strftime('%B') + '-' + from_date.strftime('%Y')
res['to_name'] = to_date.strftime('%d') + '-' + to_date.strftime('%B') + '-' + to_date.strftime('%Y')
return res
@api.model
def _get_report_values(self, docids, data=None):
payment_report = self.env['hr.payroll.payment.report.wizard'].browse(docids)
return {
'doc_ids': docids,
'doc_model': 'hr.payroll.payment.report.wizard',
'data': data,
'docs': payment_report,
'time': time,
'get_month': self.get_month,
}

View File

@ -0,0 +1,6 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_yearly_salary_detail_user","yearly.salary.detail","model_yearly_salary_detail","hr_payroll.group_hr_payroll_user",1,1,1,0
l10n_in_hr_payroll.access_salary_register_wizard,access_salary_register_wizard,l10n_in_hr_payroll.model_salary_register_wizard,hr_payroll.group_hr_payroll_user,1,1,1,1
l10n_in_hr_payroll.access_l10n_in_tds_computation_wizard,access_l10n_in_tds_computation_wizard,l10n_in_hr_payroll.model_l10n_in_tds_computation_wizard,base.group_user,1,1,1,0
access_report_l10n_in_hr_payroll_report_epf,report.l10n_in_hr_payroll.report_epf,model_l10n_in_hr_payroll_epf_report,hr_payroll.group_hr_payroll_user,1,1,1,1
l10n_in_hr_payroll.access_l10n_in_hr_payroll_salary_statement,access_l10n_in_hr_payroll_salary_statement,l10n_in_hr_payroll.model_l10n_in_hr_payroll_salary_statement,"hr_payroll.group_hr_payroll_user",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_yearly_salary_detail_user yearly.salary.detail model_yearly_salary_detail hr_payroll.group_hr_payroll_user 1 1 1 0
3 l10n_in_hr_payroll.access_salary_register_wizard access_salary_register_wizard l10n_in_hr_payroll.model_salary_register_wizard hr_payroll.group_hr_payroll_user 1 1 1 1
4 l10n_in_hr_payroll.access_l10n_in_tds_computation_wizard access_l10n_in_tds_computation_wizard l10n_in_hr_payroll.model_l10n_in_tds_computation_wizard base.group_user 1 1 1 0
5 access_report_l10n_in_hr_payroll_report_epf report.l10n_in_hr_payroll.report_epf model_l10n_in_hr_payroll_epf_report hr_payroll.group_hr_payroll_user 1 1 1 1
6 l10n_in_hr_payroll.access_l10n_in_hr_payroll_salary_statement access_l10n_in_hr_payroll_salary_statement l10n_in_hr_payroll.model_l10n_in_hr_payroll_salary_statement hr_payroll.group_hr_payroll_user 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_payment_advice_batch
from . import test_hr_contract

View File

@ -0,0 +1,88 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import date
from odoo.tests.common import TransactionCase
from odoo.tests import tagged
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestPayrollCommon(TransactionCase):
def setUp(self):
super(TestPayrollCommon, self).setUp()
self.Bank = self.env['res.partner.bank']
self.Employee = self.env['hr.employee']
self.PayslipRun = self.env['hr.payslip.run']
self.PayslipEmployee = self.env['hr.payslip.employees']
self.Company = self.env['res.company']
self.partner = self.env.ref('base.partner_admin')
self.bank_1 = self.env.ref('base.res_bank_1')
self.in_country = self.env.ref('base.in')
self.rd_dept = self.env.ref('hr.dep_rd')
self.employee_fp = self.env.ref('hr.employee_admin')
self.employee_al = self.env.ref('hr.employee_al')
self.company_in = self.Company.create({
'name': 'Company IN',
'country_code': 'IN',
})
self.in_bank = self.env['res.bank'].create({
'name': 'Bank IN',
'bic': 'ABCD0123456'
})
# I create a new employee “Rahul”
self.rahul_emp = self.Employee.create({
'name': 'Rahul',
'country_id': self.in_country.id,
'department_id': self.rd_dept.id,
})
# I create a new employee “Rahul”
self.jethalal_emp = self.Employee.create({
'name': 'Jethalal',
'country_id': self.in_country.id,
'department_id': self.rd_dept.id,
})
self.res_bank = self.Bank.create({
'acc_number': '3025632343043',
'partner_id': self.rahul_emp.work_contact_id.id,
'acc_type': 'bank',
'bank_id': self.in_bank.id,
'allow_out_payment': True,
})
self.rahul_emp.bank_account_id = self.res_bank
self.res_bank_1 = self.Bank.create({
'acc_number': '3025632343044',
'partner_id': self.jethalal_emp.work_contact_id.id,
'acc_type': 'bank',
'bank_id': self.in_bank.id,
'allow_out_payment': True,
})
self.jethalal_emp.bank_account_id = self.res_bank_1
self.contract_rahul = self.env['hr.contract'].create({
'date_start': date(2023, 1, 1),
'date_end': date(2023, 1, 31),
'name': 'Rahul Probation contract',
'wage': 5000.0,
'employee_id': self.rahul_emp.id,
'state': 'open',
'hr_responsible_id': self.employee_fp.id,
})
self.contract_jethalal = self.env['hr.contract'].create({
'date_start': date(2023, 1, 1),
'date_end': date(2023, 1, 31),
'name': 'Jethalal Probation contract',
'wage': 5000.0,
'employee_id': self.jethalal_emp.id,
'state': 'open',
'hr_responsible_id': self.employee_fp.id,
})

View File

@ -0,0 +1,43 @@
# -*- coding:utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import date
from freezegun import freeze_time
from odoo.addons.l10n_in_hr_payroll.tests.common import TestPayrollCommon
from odoo.tests import tagged
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestHrContract(TestPayrollCommon):
def test_contract_end_reminder_to_hr(self):
""" Check reminder activity is set the for probation contract
Test Case
---------
1) Create contract
2) Check the activity is activity schedule or not
3) Now run cron
4) Check the activity is activity schedule or not
"""
user_admin_id = self.env.ref('base.user_admin').id
contract = self.env['hr.contract'].create({
'date_start': date(2020, 1, 1),
'date_end': date(2020, 4, 30),
'name': 'Rahul Probation contract',
'resource_calendar_id': self.env.company.resource_calendar_id.id,
'wage': 5000.0,
'employee_id': self.rahul_emp.id,
'state': 'open',
'contract_type_id': self.env.ref('l10n_in_hr_payroll.l10n_in_contract_type_probation').id,
'kanban_state': 'done',
'hr_responsible_id': user_admin_id,
})
with freeze_time("2020-04-24"):
mail_activity = self.env['mail.activity'].search([('res_id', '=', contract.id), ('res_model', '=', 'hr.contract')])
self.assertFalse(mail_activity.exists(), "There should be no mail activity as contract is not ends on 2020-04-10")
# run the cron
self.env['hr.contract'].update_state()
mail_activity = self.env['mail.activity'].search([('res_id', '=', contract.id), ('res_model', '=', 'hr.contract')])
self.assertTrue(mail_activity.exists(), "There should be reminder activity as employee rahul's contract end today")

View File

@ -0,0 +1,62 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
from odoo.addons.l10n_in_hr_payroll.tests.common import TestPayrollCommon
from odoo.tests import tagged
@tagged('post_install_l10n', 'post_install', '-at_install')
class TestPaymentAdviceBatch(TestPayrollCommon):
def _prepare_payslip_run(self):
payslip_run = self.env['hr.payslip.run'].create({
'date_start': '2023-01-01',
'date_end': '2023-01-31',
'name': 'January Batch',
'company_id': self.company_in.id,
})
payslip_employee = self.env['hr.payslip.employees'].with_company(self.company_in).create({
'employee_ids': [
Command.set([self.rahul_emp.id, self.jethalal_emp.id])
]
})
payslip_employee.with_context(active_id=payslip_run.id).compute_sheet()
payslip_run.action_validate()
return payslip_run
def test_payment_report_advice_xlsx_creation(self):
payslip_run = self._prepare_payslip_run()
self.assertEqual(payslip_run.state, "close", "Payslip run should be in Done state")
# Generating the XLSX report for the batch
payment_report_dict = self.env["hr.payroll.payment.report.wizard"].create({
'payslip_ids': payslip_run.slip_ids.ids,
'payslip_run_id': payslip_run.id,
'export_format': 'advice',
}).generate_payment_report_xls()
payment_report = self.env['hr.payroll.payment.report.wizard'].browse(payment_report_dict['res_id'])
self.assertTrue(payslip_run.payment_report, "XLSX File should be generated!")
self.assertTrue(payment_report.l10n_in_payment_advice_xlsx, "XLSX File should be generated!")
self.assertEqual(payment_report.l10n_in_payment_advice_filename_xlsx, payment_report.l10n_in_reference + '.xlsx')
self.assertTrue(payslip_run.payment_report_filename)
def test_payment_report_advice_pdf_creation(self):
payslip_run = self._prepare_payslip_run()
self.assertEqual(payslip_run.state, "close", "Payslip run should be in Done state")
# Generating the PDF report for the batch
payment_report_dict = self.env["hr.payroll.payment.report.wizard"].create({
'payslip_ids': payslip_run.slip_ids.ids,
'payslip_run_id': payslip_run.id,
'export_format': 'advice',
}).generate_payment_report_pdf()
payment_report = self.env['hr.payroll.payment.report.wizard'].browse(payment_report_dict['res_id'])
self.assertTrue(payslip_run.payment_report, "PDF File should be generated!")
self.assertTrue(payment_report.l10n_in_payment_advice_pdf, "PDF File should be generated!")
self.assertEqual(payment_report.l10n_in_payment_advice_filename_pdf, payment_report.l10n_in_reference + '.pdf')
self.assertTrue(payslip_run.payment_report_filename)

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_contract_form_in_inherit" model="ir.ui.view">
<field name="name">hr.contract.form.in.inherit</field>
<field name="model">hr.contract</field>
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='salary_info']" position="inside">
<group string="Allowance" invisible="country_code != 'IN'">
<field name="l10n_in_driver_salay"/>
<field name="l10n_in_house_rent_allowance_metro_nonmetro"/>
<field name="l10n_in_supplementary_allowance"/>
<field name="l10n_in_gratuity"/>
</group>
<group string="Deduction" invisible="country_code != 'IN'">
<label for="l10n_in_tds" string="TDS"/>
<div class="d-flex gap-3">
<field name="l10n_in_tds"/>
<button name="%(l10n_in_hr_payroll.action_tds_calculation)d" string="TDS Calculator" type="action" class="w-75 btn btn-secondary"/>
</div>
<field name="l10n_in_provident_fund"/>
<field name="l10n_in_voluntary_provident_fund"/>
<field name="l10n_in_medical_insurance"/>
<field name="l10n_in_leave_allowance"/>
<field name="l10n_in_esic_amount"/>
</group>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_employee_view_form_in_inherit" model="ir.ui.view">
<field name="name">hr.employee.form.in.inherit</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//page[@name='personal_information']/group" position="inside">
<group string="Personal Information" invisible="company_country_code != 'IN'">
<field name="l10n_in_uan" placeholder="12 digit Universal Account Number"/>
<field name="l10n_in_esic_number" placeholder="17 digit ESIC Number"/>
<field name="l10n_in_pan" placeholder="10 digit Permanent Account Number"/>
</group>
</xpath>
<xpath expr="//field[@name='emergency_contact']" position="after">
<field name="l10n_in_relationship" invisible="company_country_code != 'IN'"/>
</xpath>
<xpath expr="//field[@name='children']" position="after">
<field name="l10n_in_residing_child_hostel" invisible="company_country_code != 'IN'"/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,56 @@
<?xml version="1.0"?>
<odoo>
<record id="payslip_details_report" model="ir.actions.report">
<field name="name">PaySlip Details</field>
<field name="model">hr.payslip</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">l10n_in_hr_payroll.report_payslip</field>
<field name="report_file">l10n_in_hr_payroll.report_payslip</field>
<field name="print_report_name">'Payslip - %s' % (object.employee_id.name)</field>
<field name="binding_model_id" ref="hr_payroll.model_hr_payslip"/>
<field name="binding_type">report</field>
</record>
<record id="light_payslip_details_report" model="ir.actions.report">
<field name="name">PaySlip Details (Light)</field>
<field name="model">hr.payslip</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">l10n_in_hr_payroll.report_light_payslip</field>
<field name="report_file">l10n_in_hr_payroll.report_light_payslip</field>
<field name="print_report_name">'Payslip - %s' % (object.employee_id.name)</field>
<field name="binding_model_id" ref="hr_payroll.model_hr_payslip"/>
<field name="binding_type">report</field>
<field name="paperformat_id" ref="hr_payroll.paperformat_euro_light"/>
</record>
<record id="payroll_advice_report" model="ir.actions.report">
<field name="name">Advice Report</field>
<field name="model">hr.payroll.payment.report.wizard</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">l10n_in_hr_payroll.report_payrolladvice</field>
<field name="report_file">l10n_in_hr_payroll.report_payrolladvice</field>
<field name="binding_type">report</field>
</record>
<record id="action_report_hryearlysalary" model="ir.actions.report">
<field name="name">Yearly Salary by Employee</field>
<field name="model">yearly.salary.detail</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">l10n_in_hr_payroll.report_hryearlysalary</field>
<field name="report_file">l10n_in_hr_payroll.report_hryearlysalary</field>
<field name="paperformat_id" ref="l10n_in_hr_payroll.paperformat_yealy_salary_head"/>
<field name="binding_model_id" ref="model_yearly_salary_detail"/>
<field name="binding_type">report</field>
</record>
<record id="action_report_salary_statement" model="ir.actions.report">
<field name="name">Salary Statement</field>
<field name="model">hr.employee</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">l10n_in_hr_payroll.report_salary_statement</field>
<field name="report_file">l10n_in_hr_payroll.report_salary_statement</field>
<field name="print_report_name">(object._get_report_base_filename())</field>
<field name="binding_model_id" eval="False"/>
<field name="binding_type">report</field>
</record>
</odoo>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="l10n_in_hr_payroll_salary_statement_view_form" model="ir.ui.view">
<field name="name">l10n_in_hr_payroll.salary.statement.form</field>
<field name="model">l10n_in_hr_payroll.salary.statement</field>
<field name="arch" type="xml">
<form string="Salary Statement">
<header>
<button name="action_generate_declarations" string="Populate" type="object" class="btn-primary" invisible="lines_count"/>
<button name="action_generate_declarations" string="Populate" type="object" class="btn-secondary" invisible="not lines_count"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_open_declarations" type="object" class="oe_stat_button" icon="fa-users" invisible="not lines_count">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value"><field name="lines_count"/></span>
<span class="o_stat_text">Eligible Employees</span>
</div>
</button>
</div>
<group>
<field name="year" class="o_hr_narrow_field"/>
<field name="month" class="o_hr_narrow_field"/>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>
<record id="l10n_in_hr_payroll_salary_statement_action" model="ir.actions.act_window">
<field name="name">Salary Statement Report</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">l10n_in_hr_payroll.salary.statement</field>
<field name="view_mode">list,form</field>
</record>
<menuitem
id="menu_l10n_in_hr_payroll_salary_statement"
name="Salary Statement Report"
action="l10n_in_hr_payroll_salary_statement_action"
parent="l10n_in_hr_payroll.menu_reporting_l10n_in"
groups="hr_payroll.group_hr_payroll_manager,hr_payroll.group_hr_payroll_user"
/>
</odoo>

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="hr_epf_report_view_list" model="ir.ui.view">
<field name="model">l10n.in.hr.payroll.epf.report</field>
<field name="arch" type="xml">
<list>
<field name="month"/>
<field name="year" options="{'type': 'number'}"/>
</list>
</field>
</record>
<record id="hr_epf_report_view_form" model="ir.ui.view">
<field name="model">l10n.in.hr.payroll.epf.report</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_export_xlsx" string="Export XLSX File" type="object" class="btn-primary"/>
</header>
<sheet>
<group>
<group>
<field name="year" class="o_hr_narrow_field" options="{'type': 'number'}"/>
<field name="month" class="o_hr_narrow_field"/>
<field name="xls_filename" invisible="1"/>
</group>
</group>
<div invisible="not xls_file">
<h2>Generation Complete</h2>
<p class="oe_inline" name="xls_file">
Download the XLSX details file:<br/>
<field name="xls_file" readonly="1" filename="xls_filename"/>
</p>
</div>
</sheet>
</form>
</field>
</record>
<record id="l10n_in_hr_epf_report_action" model="ir.actions.act_window">
<field name="name">EPF Report</field>
<field name="res_model">l10n.in.hr.payroll.epf.report</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="hr_epf_report_view_list"/>
</record>
<menuitem
id="l10n_in_hr_epf_report_menu"
name="EPF Report"
action="l10n_in_hr_epf_report_action"
parent="l10n_in_hr_payroll.menu_reporting_l10n_in"
sequence="5"/>
</odoo>

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_hryearlysalary">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="web.internal_layout">
<t t-foreach="get_employee(data['form'])" t-as="employee">
<div class="page" style="page-break-before: always">
<table class="table table-bordered w-100" style="font-size: 11px">
<tr class="m-0">
<td colspan="8" class="text-center p-0">
<h2>
<span t-field="employee.company_id" class="fs-6"/>
</h2>
<p> From Date
<u>
<span t-out="date_from"/>
</u> To Date
<u>
<span t-out="date_to"/>
</u>
</p>
</td>
</tr>
<tr>
<td><strong>Employee Name</strong></td>
<td>
<t t-if="employee.name">
<span t-field="employee.name"/>
</t>
</td>
<td><strong>Employee Code</strong></td>
<td>
<t t-if="employee.identification_id">
<span t-field="employee.identification_id"/>
</t>
</td>
<td><strong>Designation</strong></td>
<td>
<t t-if="employee.job_id">
<span t-field="employee.job_id.name"/>
</t>
</td>
<td><strong>Department</strong></td>
<td>
<t t-if="employee.department_id">
<span t-field="employee.department_id.name"/>
</t>
</td>
</tr>
</table>
<t t-foreach="get_employee_detail_new(employee.id).items()" t-as="structure">
<div class="page" style="page-break-after: always">
<table class="table table-sm full-width-table" style="font-size: 11px">
<thead>
<tr t-foreach="get_periods(data['form'])" t-as="months">
<th><strong><t t-esc="structure[0]"/></strong></th>
<t t-foreach="months" t-as="month_sal">
<th class="text-end">
<span t-out="month_sal"/>
</th>
</t>
<th class="text-end">Total</th>
</tr>
</thead>
<tbody>
<td><strong> Allowances with Basic:</strong></td>
<tr t-foreach="structure[1]['allow_list']" t-as="allowance">
<t t-if="allowance[0] in ['BASIC','GROSS']">
<td><strong><span t-out="allowance[1][0]"/></strong></td>
<t t-foreach="allowance[1][1:-1]" t-as="allow">
<td class="text-end"><span t-out="allow" t-options="{'widget': 'monetary', 'display_currency': employee.company_id.currency_id}"/></td>
</t>
</t>
<t t-if="allowance[0] not in ['BASIC','GROSS']">
<td><span t-out="allowance[1][0]" class="text-end"/></td>
<t t-foreach="allowance[1][1:-1]" t-as="allow">
<td class="text-end"><span t-out="allow" t-options="{'widget': 'monetary', 'display_currency': employee.company_id.currency_id}"/></td>
</t>
</t>
<td t-out="allowance[1][-1:][0]" class="text-end" t-options="{'widget': 'monetary', 'display_currency': employee.company_id.currency_id}"/>
</tr>
<td><strong> Deductions:</strong></td>
<tr t-foreach="structure[1]['deduct_list']" t-as="deduct">
<t t-if="deduct[0] in ['NET']">
<td><strong><span t-out="deduct[1][0]"/></strong></td>
</t>
<t t-if="deduct[0] not in ['NET']">
<td><span t-out="deduct[1][0]"/></td>
</t>
<t t-foreach="deduct[1][1:-1]" t-as="d">
<td class="text-end"><span t-out="d" t-options="{'widget': 'monetary', 'display_currency': employee.company_id.currency_id}"/></td>
</t>
<td t-out="deduct[1][-1:][0]" class="text-end" t-options="{'widget': 'monetary', 'display_currency': employee.company_id.currency_id}"/>
</tr>
</tbody>
</table>
</div>
</t>
</div>
</t>
</t>
</t>
</t>
</template>
</odoo>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_salary_statement">
<t t-call="web.html_container">
<t t-call="web.internal_layout">
<div t-if="report_data" class="page">
<h2>
<span t-field="employee.company_id">IN Company</span>
</h2>
<h2>Salary Statement Report,
<span t-out="report_data['month']">May</span>
<span t-out="report_data['year']">2024</span>
</h2>
<table class="table table-sm table-borderless">
<tbody>
<tr>
<td><strong>Employee Name</strong></td>
<td><span t-field="employee.name">Lakshya</span></td>
<td><strong>Date of Joining</strong></td>
<td><span t-field="employee.contract_id.date_start">01/01/2024</span></td>
</tr>
<tr>
<td><strong>Designation</strong></td>
<td><span t-field="employee.job_title">Software Developer</span></td>
<td><strong>Salary Effective from</strong></td>
<td><span t-out="report_data['date']">01/05/2024</span></td>
</tr>
</tbody>
</table>
<table class="table table-sm">
<thead>
<tr>
<th class="text-center">Salary Components</th>
<th class="text-center">Monthly Amount</th>
<th class="text-center">Yearly Amount</th>
</tr>
</thead>
<tbody>
<tr><td><h5 class="mb-0 mt-2"><strong>Allowance:</strong></h5></td></tr>
<t t-foreach="report_data['allow_rules']" t-as="rule">
<t t-set="value" t-value="report_data['allow_rules'][rule]"/>
<tr>
<td><t t-out="value['name']">Basic Salary</t></td>
<td><t t-out="value['total']">10000</t></td>
<td><t t-out="round(value['total_annual'],2)">120000</t></td>
</tr>
</t>
<tr><td><h5 class="mb-0 mt-2"><strong>Deduction:</strong></h5></td></tr>
<t t-foreach="report_data['deduct_rules']" t-as="rule">
<t t-set="value" t-value="report_data['deduct_rules'][rule]"/>
<tr>
<td><t t-out="value['name']">PF</t></td>
<td><t t-out="value['total']">-1000</t></td>
<td><t t-out="round(value['total_annual'],2)">-12000</t></td>
</tr>
</t>
<tr>
<td><strong>Cost to Company</strong></td>
<td><strong><t t-out="round(report_data['ctc'],2)">10000</t></strong></td>
<td><strong><t t-out="round(report_data['ctc_annual'],2)">120000</t></strong></td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</template>
</odoo>

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="report_payrolladvice">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-foreach="docs" t-as="o">
<div class="page">
<p class="text-end"> Made on <span t-out="time.strftime('%m-%d-%Y')"/></p>
<p>To,</p>
<p>The Manager,</p>
<p><strong><span t-out="bank['bank_name']"/></strong></p>
<p>Dear Sir/Madam,</p>
<p class="text-center"> <strong>Payment Advice from <span t-out="o.company_id.name"/> A/C no. <span t-out="bank['acc_number']"/><t t-if="o.l10n_in_neft">with NEFT</t><t t-else="">with Cheque No. <span t-out="o.l10n_in_cheque_number"></span></t> form period <span t-out="get_month(current_date)['from_name']"/> to <span t-out="get_month(current_date)['to_name']"/></strong></p>
<p><span t-out="note" /></p>
<table class="o_ignore_layout_styling table table-sm">
<thead>
<tr style="border-top: 1px solid black">
<th>SI No.</th>
<th>Name Of Employee</th>
<th>Bank Account No.</th>
<th>IFSC Code</th>
<th>By salary</th>
<th>C/D</th>
</tr>
</thead>
<tbody>
<t t-set="report_details" t-value="line_ids"/>
<tr t-foreach="report_details['lines']" t-as="l">
<td>
<span t-out="l_index+1">.</span>
</td>
<td>
<span t-out="l['name']"/>
</td>
<td>
<span t-out="l['acc_no']"/>
</td>
<td>
<span t-out="l['ifsc_code']"/>
</td>
<td>
<span t-out="l['bysal']" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/>
</td>
<td>
<span t-out="l['debit_credit']"/>
</td>
</tr>
<tr class="border-black" style="border-bottom: 1px solid black">
<td colspan="2"/>
<td>
<strong>Total:</strong>
</td>
<td colspan="1"/>
<td colspan="2">
<strong><span t-out="report_details['total_bysal']" t-options="{'widget': 'monetary', 'display_currency': o.company_id.currency_id}"/></strong>
</td>
</tr>
</tbody>
</table>
<p>Yours Sincerely</p>
<p>For <span t-out="o.company_id.name"/></p>
<p class="mt32">Authorized Signature</p>
</div>
</t>
</t>
</t>
</template>
</odoo>

View File

@ -0,0 +1,122 @@
<?xml version="1.0"?>
<odoo>
<template id="l10n_in_to_pay">
<div t-if="o.net_wage &gt;= 0">
To pay on <strong t-field="o.employee_id.bank_account_id"/> of <i><span t-field="o.employee_id"/></i>: <strong t-field="o.net_wage"/>
<br/>
Amount In Words Rs: <span t-out="o.currency_id.with_context(lang='en_US').amount_to_text(o.net_wage)"/>
</div>
<div t-else="">The net amount will be recovered from the first positive remuneration established after this.</div>
<div class="mt-2">This is a system generated payslip, hence the signature is not required.</div>
</template>
<template id="report_payslip_details" inherit_id="hr_payroll.report_payslip" primary="True">
<div id="employee_marital" position="replace"/>
<div id="employee_private_address" position="replace"/>
<div id="working_schedule" position="replace">
<strong class="me-2">Working Schedule:</strong>
<t t-if="o.wage_type == 'monthly'">
<span t-out="o._get_l10n_in_company_working_time()"/> Days / Month
</t>
<t t-else="">
<span t-out="o._get_l10n_in_company_working_time(return_hours=True)"/> Hours / Month
</t>
</div>
<div id="infos_table" position="inside">
<table class="table table-sm table-borderless">
<t t-set="timeoff_data_table" t-value="o._get_employee_timeoff_data()"/>
<thead class="o_black_border">
<tr>
<th>Bank Information</th>
<th t-if="timeoff_data_table">Time-Off</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div>
<strong>Bank Account:</strong>
<span t-field="o.employee_id.bank_account_id.acc_number"/>
</div>
<div>
<strong>PAN:</strong>
<span t-field="o.employee_id.l10n_in_pan"/>
</div>
<div>
<strong>UAN:</strong>
<span t-field="o.employee_id.l10n_in_uan"/>
</div>
<div>
<strong>ESIC:</strong>
<span t-field="o.employee_id.l10n_in_esic_number"/>
</div>
</td>
<td t-if="timeoff_data_table">
<div t-foreach="timeoff_data_table" t-as="timeoff_data">
<strong t-out="timeoff_data[0] + ':'"></strong>
<t t-out="timeoff_data[1].get('remaining_leaves')"/>
/
<span t-out="timeoff_data[1].get('max_leaves')"/>
<t t-if="timeoff_data[1].get('request_unit') == 'hour'">Hours</t>
<t t-else="">Days</t>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div id="to_pay" position="replace">
<t t-call="l10n_in_hr_payroll.l10n_in_to_pay" t-lang="o.env.lang"/>
</div>
</template>
<template id="report_payslip">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-set="o" t-value="o.with_context(lang=o.employee_id.sudo().lang or o.env.lang)"/>
<t t-call="l10n_in_hr_payroll.report_payslip_details" t-lang="o.env.lang"/>
</t>
</t>
</template>
<template id="report_light_payslip_details" inherit_id="hr_payroll.report_light_payslip" primary="True">
<div id="to_pay" position="replace">
<t t-call="l10n_in_hr_payroll.l10n_in_to_pay" t-lang="o.env.lang"/>
</div>
<div id="footer" position="inside">
<table class="table table-sm table-borderless">
<thead class="o_black_border">
<tr>
<th><strong>Bank Account</strong></th>
<th><strong>PAN</strong></th>
<th><strong>UAN</strong></th>
<th><strong>ESIC</strong></th>
</tr>
</thead>
<tbody>
<tr>
<td><span t-field="o.employee_id.bank_account_id.acc_number"/></td>
<td><span t-field="o.employee_id.l10n_in_pan"/></td>
<td><span t-field="o.employee_id.l10n_in_uan"/></td>
<td><span t-field="o.employee_id.l10n_in_esic_number"/></td>
</tr>
</tbody>
</table>
</div>
</template>
<template id="report_light_payslip">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-set="o" t-value="o.with_context(lang=o.employee_id.sudo().lang or o.env.lang)"/>
<t t-call="l10n_in_hr_payroll.report_light_payslip_details" t-lang="o.env.lang"/>
</t>
</t>
</template>
</odoo>

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.l10n_in_hr_payroll</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="hr_payroll.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//block[@id='hr_payroll_accountant']" position="after">
<h2>Indian Localization</h2>
<div class="row mt16 o_settings_container" id="l10n_in_hr_payroll">
<div class="col-lg-6 col-12 o_setting_box" id="company_information">
<div class="o_setting_left_pane"/>
<div class="o_setting_right_pane">
<span class="o_form_label">Company Information</span>
<div class="text-muted">
Offical Company Information
</div>
<group class="mt16">
<field name="l10n_in_dearness_allowance"/>
</group>
</div>
</div>
<div class="col-lg-6 col-12 o_setting_box" id="statutory_compliance" invisible="company_country_code != 'IN'">
<div class="o_setting_left_pane">
<field name="l10n_in_is_statutory_compliance"></field>
</div>
<div class="o_setting_right_pane">
<label for="l10n_in_is_statutory_compliance" string="Payroll Statutory Compliance"/>
<group class="mt16" invisible="not l10n_in_is_statutory_compliance">
<field name="l10n_in_epf_employer_id" placeholder="Code of 10 Numbers"/>
<field name="l10n_in_esic_ip_number" placeholder="Code of 17 Digits"/>
<field name="l10n_in_pt_number" placeholder="Code of 11 Digits"/>
</group>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<odoo>
<data>
<record id="res_users_view_form_profile_in_inherit" model="ir.ui.view">
<field name="name">res.user.preferences.view.form.l10n.in.payroll.inherit</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="hr.res_users_view_form_profile"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='emergency_contact']" position="after">
<field name="l10n_in_relationship" readonly="not can_edit"/>
</xpath>
</field>
</record>
</data>
</odoo>

View File

@ -0,0 +1,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import hr_yearly_salary_detail
from . import hr_salary_register
from . import hr_tds_calculation
from . import hr_payroll_payment_report_wizard

View File

@ -0,0 +1,36 @@
<?xml version="1.0"?>
<odoo>
<record id="l10n_in_hr_payroll.hr_payroll_payment_advice_report_view_form" model="ir.ui.view">
<field name="name">hr.payroll.payment.advice.report.wizard.form</field>
<field name="model">hr.payroll.payment.report.wizard</field>
<field name="inherit_id" ref="hr_payroll.hr_payroll_payment_report_view_form"/>
<field name="arch" type="xml">
<group id="payment_report_group" position="inside">
<field name="l10n_in_reference" invisible="export_format != 'advice'"/>
<field name="l10n_in_cheque_number" invisible="export_format != 'advice' or l10n_in_neft"/>
<field name="l10n_in_neft" invisible="export_format != 'advice'"/>
<!-- this field will be used in the name of the pdf -->
<field name="l10n_in_payment_advice_filename_pdf" invisible="1"/>
<field name="l10n_in_payment_advice_pdf" filename="l10n_in_payment_advice_filename_pdf"
invisible="export_format != 'advice' or not l10n_in_state_pdf"/>
<!-- this field will be used in the name of the XLSX -->
<field name="l10n_in_payment_advice_filename_xlsx" invisible="1"/>
<field name="l10n_in_payment_advice_xlsx" filename="l10n_in_payment_advice_filename_xlsx"
invisible="export_format != 'advice' or not l10n_in_state_xlsx"/>
</group>
<xpath expr="//button[@name='generate_payment_report']" position="attributes">
<attribute name="invisible">export_format == 'advice'</attribute>
</xpath>
<xpath expr="//button[@name='generate_payment_report']" position="after">
<button string="Create PDF File" class="btn-primary" name="generate_payment_report_pdf" type="object"
data-hotkey="q" invisible="export_format != 'advice' or l10n_in_state_pdf"/>
<button string="Create XLSX File" class="btn-primary" name="generate_payment_report_xls" type="object"
data-hotkey="e" invisible="export_format != 'advice' or l10n_in_state_xlsx"/>
<button string="Create PDF File" class="btn-secondary" name="generate_payment_report_pdf" type="object"
data-hotkey="w" invisible="export_format != 'advice' or not l10n_in_state_pdf"/>
<button string="Create XLSX File" class="btn-secondary" name="generate_payment_report_xls" type="object"
data-hotkey="r" invisible="export_format != 'advice' or not l10n_in_state_xlsx"/>
</xpath>
</field>
</record>
</odoo>

View File

@ -0,0 +1,158 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import io
import base64
from datetime import datetime, date
from odoo import api, fields, models, _
from odoo.tools.misc import xlsxwriter
from odoo.exceptions import UserError
class HrPayrollPaymentReportWizard(models.TransientModel):
_inherit = 'hr.payroll.payment.report.wizard'
export_format = fields.Selection(selection_add=[('advice', 'Payment Advice')], default='advice', ondelete={'advice': 'set default'})
l10n_in_payment_advice_pdf = fields.Binary('Payment Advice PDF', readonly=True, attachment=False)
l10n_in_payment_advice_filename_pdf = fields.Char()
l10n_in_payment_advice_xlsx = fields.Binary('Payment Advice XLSX', readonly=True, attachment=False)
l10n_in_payment_advice_filename_xlsx = fields.Char()
l10n_in_reference = fields.Char(string="Reference")
l10n_in_neft = fields.Boolean(string="NEFT Transaction", help="Tick this box if your company use online transfer for salary")
l10n_in_cheque_number = fields.Char(string="Cheque Number")
l10n_in_state_pdf = fields.Boolean()
l10n_in_state_xlsx = fields.Boolean()
def _get_report_data(self, payslip):
employee = payslip.employee_id
bank_account = employee.bank_account_id
return {
'name': employee.name,
'acc_no': bank_account.acc_number or '',
'ifsc_code': bank_account.bank_bic or '',
'bysal': payslip.net_wage,
'debit_credit': 'C',
}
def _get_pdf_data(self):
total_bysal = 0
lines = []
partner_bank = self.company_id.partner_id.bank_ids
partner_bank_name = partner_bank[0].bank_id.name if partner_bank else ''
partner_bank_acc_number = partner_bank[0].acc_number if partner_bank else ''
for payslip in self.payslip_run_id.slip_ids:
report_line_data = self._get_report_data(payslip)
lines.append(report_line_data)
total_bysal += report_line_data['bysal']
return {
'bank': {
'bank_name': partner_bank_name,
'acc_number': partner_bank_acc_number,
},
'line_ids': {
'lines': lines,
'total_bysal': total_bysal,
},
'current_date': date.today(),
'l10n_in_neft': self.l10n_in_neft,
'l10n_in_cheque_number': self.l10n_in_cheque_number,
'note': _('Please make the payroll transfer from above account number to the below mentioned account numbers towards employee salaries:'),
}
def _get_wizard(self):
return {
'type': 'ir.actions.act_window',
'name': _('Payment Advice'),
'res_model': self._name,
'view_mode': 'form',
'res_id': self.id,
'views': [(False, 'form')],
'target': 'new',
}
@api.model_create_multi
def create(self, vals_list):
date = datetime.now()
for vals in vals_list:
if not vals.get('l10n_in_reference'):
advice_year = date.strftime('%m-%Y')
number = self.env['ir.sequence'].next_by_code('payment.advice')
vals['l10n_in_reference'] = f"PAY/{advice_year}/{number}"
return super().create(vals_list)
def generate_payment_report_pdf(self):
self.ensure_one()
self._perform_checks()
pdf_content = self.env["ir.actions.report"].sudo()._render_qweb_pdf(
self.env.ref('l10n_in_hr_payroll.payroll_advice_report').id,
res_ids=self.ids, data=self._get_pdf_data()
)[0]
payment_report = base64.encodebytes(pdf_content)
self.l10n_in_payment_advice_pdf = payment_report
self.l10n_in_payment_advice_filename_pdf = f"{self.l10n_in_reference}.pdf"
self._write_file(payment_report, '.pdf', self.l10n_in_reference)
self.l10n_in_state_pdf = True
return self._get_wizard()
def generate_payment_report_xls(self):
self.ensure_one()
self._perform_checks()
output = io.BytesIO()
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
worksheet = workbook.add_worksheet('Payment Advice Report')
header_format = workbook.add_format({'bold': True, 'pattern': 1, 'bg_color': '#E0E0E0', 'align': 'center'})
cell_format = workbook.add_format({'align': 'center'})
total_format = workbook.add_format({'bold': True, 'align': 'center'})
headers = [_('SI No.'), _('Name Of Employee'), _('Bank Account No.'), _('IFSC Code'), _('By Salary'), _('C/D')]
worksheet.write_row(0, 0, headers, header_format)
total_salary = 0
for row_idx, payslip in enumerate(self.payslip_run_id.slip_ids, start=1):
row_data = self._get_report_data(payslip)
worksheet.write(row_idx, 0, row_idx, cell_format)
worksheet.write_row(row_idx, 1, row_data.values(), cell_format)
total_salary += row_data['bysal']
worksheet.set_column(0, 0, 10) # SI No.
worksheet.set_column(1, 1, 20) # Name Of Employee
worksheet.set_column(2, 2, 20) # Bank Account No.
worksheet.set_column(3, 3, 15) # IFSC Code
worksheet.set_column(4, 4, 15) # By Salary
worksheet.set_column(5, 5, 10) # C/D
row_idx += 1
worksheet.write(row_idx + 1, 2, "Total:", total_format)
worksheet.write(row_idx + 1, 4, total_salary, total_format)
workbook.close()
xlsx_data = output.getvalue()
payment_report = base64.encodebytes(xlsx_data)
self.l10n_in_payment_advice_xlsx = payment_report
self.l10n_in_payment_advice_filename_xlsx = f"{self.l10n_in_reference}.xlsx"
self._write_file(payment_report, '.xlsx', self.l10n_in_reference)
self.l10n_in_state_xlsx = True
return self._get_wizard()
def _perform_checks(self):
super()._perform_checks()
if self.company_id.country_code == 'IN':
payslip_ids = self.payslip_run_id.slip_ids.filtered(lambda p: p.state == "done" and p.net_wage > 0)
invalid_ifsc_employee_ids = payslip_ids.employee_id._get_employees_with_invalid_ifsc()
if invalid_ifsc_employee_ids:
raise UserError(_(
'The file cannot be generated, the employees listed below have a bank account with no bank\'s identification number.\n%s',
'\n'.join(invalid_ifsc_employee_ids.mapped('name'))
))

View File

@ -0,0 +1,55 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class YearlySalaryDetail(models.TransientModel):
_name = 'salary.register.wizard'
_description = 'Salary Register'
@api.model
def default_get(self, field_list=None):
if self.env.company.country_id.code != "IN":
raise UserError(_('You must be logged in a Indian company to use this feature'))
return super().default_get(field_list)
def _get_default_date_from(self):
return fields.Date.today() + relativedelta(day=1, month=1)
def _get_default_date_to(self):
return fields.Date.today()
def _get_employee_ids_domain(self):
employees = self.env['hr.payslip'].search([('state', '=', 'paid')]).employee_id.filtered(lambda e: e.company_id.country_id.code == "IN").ids
return [('id', 'in', employees)]
def _get_struct_id_domain(self):
return ['|', ('country_id', '=', self.env.ref('base.in').id), ('country_id', '=', False)]
employee_ids = fields.Many2many('hr.employee', 'emp_register_rel', 'register_id', 'employee_id', string='Employees', required=True,
compute="_compute_employee_ids", store=True, readonly=False, domain=_get_employee_ids_domain)
date_from = fields.Date(string='Start Date', required=True, default=_get_default_date_from)
date_to = fields.Date(string='End Date', required=True, default=_get_default_date_to)
company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.company)
struct_id = fields.Many2one('hr.payroll.structure', string='Salary Structure', domain=_get_struct_id_domain)
def action_export_xlsx(self):
self.ensure_one()
return {
'name': _('Export Salary Register Report into XLSX'),
'type': 'ir.actions.act_url',
'url': '/export/salary-register/%s' % (self.id),
}
@api.depends('date_from', 'date_to', 'struct_id')
def _compute_employee_ids(self):
for record in self:
date_from = record.date_from or self._get_default_date_from()
date_to = record.date_to or self._get_default_date_to()
domain = [('date_from', '>=', date_from), ('date_to', '<=', date_to), ('state', '=', 'paid')]
if record.struct_id:
domain.append(('struct_id', '=', record.struct_id.id))
payslips = record.env['hr.payslip'].search(domain)
record.employee_ids = payslips.employee_id.filtered(lambda e: e.company_id.country_id.code == "IN")

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_salary_register" model="ir.ui.view">
<field name="name">Salary Register</field>
<field name="model">salary.register.wizard</field>
<field name="arch" type="xml">
<form string="Salary Register">
<span class="o_form_label text-muted">Generate an Excel report based on Paid payslips for the selected period of time.</span>
<footer>
<button name="action_export_xlsx" string="Export XLSX" type="object" class="btn-primary disabled" data-hotkey="q" invisible="employee_ids"/>
<button name="action_export_xlsx" string="Export XLSX" type="object" class="btn-primary" data-hotkey="q" invisible="not employee_ids"/>
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="x" />
</footer>
<group class="mt-2">
<field name="date_from"/>
<field name="date_to"/>
<field name="struct_id" string="Salary Structure"/>
<separator string="Employees" />
<field name="employee_ids" nolabel="1" colspan="2" context="{'list_view_ref' : 'l10n_in_hr_payroll.hr_employee_tree_inherit'}">
<list no_open="True" create="False">
<field name="name" string="Employee Name"/>
<field name="registration_number" string="Employee Code"/>
<field name="department_id" string="Department"/>
<field name="job_id" string="Job Position"/>
</list>
</field>
</group>
</form>
</field>
</record>
<record id="action_salary_register" model="ir.actions.act_window">
<field name="name">Salary Register</field>
<field name="res_model">salary.register.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<record id="hr_employee_tree_inherit" model="ir.ui.view">
<field name="name">hr.employee.list.inherit</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_tree"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<field name="activity_ids" position="attributes">
<attribute name="optional">hide</attribute>
</field>
<field name="activity_date_deadline" position="attributes">
<attribute name="optional">hide</attribute>
</field>
<field name="company_id" position="attributes">
<attribute name="optional">hide</attribute>
</field>
<field name="parent_id" position="attributes">
<attribute name="optional">hide</attribute>
</field>
</field>
</record>
<menuitem
id="menu_reporting_l10n_in"
name="India"
sequence="30"
parent="hr_payroll.menu_hr_payroll_report"/>
<menuitem
name="Salary Register"
parent="l10n_in_hr_payroll.menu_reporting_l10n_in"
action="action_salary_register"
sequence="300"
id="menu_salary_register"
groups="hr_payroll.group_hr_payroll_manager,hr_payroll.group_hr_payroll_user"
/>
</odoo>

View File

@ -0,0 +1,122 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, _
from odoo.exceptions import UserError
class HrTdsCalculation(models.TransientModel):
_name = 'l10n.in.tds.computation.wizard'
_description = 'Indian Payroll: TDS computation'
def default_get(self, fields):
res = super().default_get(fields)
if self.env.company.country_id.code != "IN":
raise UserError(_('You must be logged in a Indian company to use this feature'))
if not res.get('contract_id') and self.env.context.get('active_id') and self.env.context.get('active_model') == 'hr.contract':
res['contract_id'] = self.env.context['active_id']
contract_id = self.env['hr.contract'].browse(res['contract_id'])
res['payslip_id'] = self.env['hr.payslip'].search([
('contract_id', '=', contract_id.id),
('employee_id', '=', contract_id.employee_id.id),
('date_from', '>=', contract_id.date_start),
('state', 'in', ['done', 'paid']),
], limit=1, order='date_from desc').id
if not res.get('currency_id') and self.env.company.currency_id:
res['currency_id'] = self.env.company.currency_id.id
if not res.get('standard_deduction'):
# Clearing the cache because the changes made to the parameter value are not being reflected here.
self.env.registry.clear_cache()
res['standard_deduction'] = self.env['hr.rule.parameter']._get_parameter_from_code('l10n_in_standard_deduction')
return res
contract_id = fields.Many2one('hr.contract')
payslip_id = fields.Many2one('hr.payslip')
currency_id = fields.Many2one("res.currency", string='Currency')
total_income = fields.Float(string="Total Income(Year)", compute="_compute_total_income", readonly=False)
standard_deduction = fields.Float(string="Standard Deduction", readonly=True)
taxable_income = fields.Float(string="Taxable Income", compute="_compute_taxable_income")
tax_on_taxable_income = fields.Float(string="Tax on Taxable Income", compute="_compute_taxable_income")
rebate = fields.Float(string="Rebate Under Section 87A(a)", compute="_compute_taxable_income",
help="Reduces tax liability for resident individuals with income up to ₹7 lakh (New Regime)")
total_tax_on_income = fields.Float(string="Total Tax on Income", compute="_compute_taxable_income")
surcharge = fields.Float(string="Surcharge", compute="_compute_taxable_income")
cess = fields.Float(string="Health and Education Cess", compute="_compute_taxable_income", help="4% of Tax on Taxable Income + Surcharge")
total_tax = fields.Float(string="Total Tax to be Paid", compute="_compute_taxable_income")
net_monthly = fields.Float(string="Monthly Net Payable", compute="_compute_net_monthly")
tds_monthly = fields.Float(string="Monthly TDS Payable", compute="_compute_tds_monthly", readonly=False, store=True)
@api.depends('contract_id', 'total_income')
def _compute_net_monthly(self):
for record in self:
record.net_monthly = record.payslip_id.net_wage or record.total_income / 12
@api.depends('net_monthly')
def _compute_total_income(self):
for record in self:
record.total_income = record.net_monthly * 12
@api.depends('total_income')
def _compute_taxable_income(self):
rule_parameter = self.env['hr.rule.parameter']
tax_slabs = rule_parameter._get_parameter_from_code('l10n_in_tds_rate_chart')
tax_slabs_for_surcharge = rule_parameter._get_parameter_from_code('l10n_in_surcharge_rate')
min_income_for_surcharge = rule_parameter._get_parameter_from_code('l10n_in_min_income_surcharge')
min_income_for_rebate = rule_parameter._get_parameter_from_code('l10n_in_min_income_tax_rebate')
for record in self:
record.taxable_income = max(record.total_income - record.standard_deduction, 0)
tax = 0
for rate, (lower, upper) in tax_slabs:
if record.taxable_income <= lower:
break
taxable_amount = min(record.taxable_income, float(upper)) - lower
tax += round(taxable_amount * rate)
record.tax_on_taxable_income = tax
if record.taxable_income >= min_income_for_rebate:
marginal_income = record.taxable_income - min_income_for_rebate
record.rebate = max(record.tax_on_taxable_income - marginal_income, 0)
else:
record.rebate = record.tax_on_taxable_income
record.total_tax_on_income = record.tax_on_taxable_income - record.rebate
if record.taxable_income > min_income_for_surcharge:
surcharge = 0
for rate, amount in tax_slabs_for_surcharge:
if record.taxable_income <= float(amount[1]):
surcharge = record.total_tax_on_income * rate
break
max_tax_slabs = rule_parameter._get_parameter_from_code('l10n_in_max_surcharge_tax_rate')
max_taxable_income, max_tax, max_surcharge = 0, 0, 0
for income, tax, surcharge_rate in max_tax_slabs:
if record.taxable_income <= income:
break
else:
max_taxable_income, max_tax, max_surcharge = income, tax, surcharge_rate
excess_income = record.taxable_income - max_taxable_income
max_tax_with_surcharge = max_tax + max_surcharge
total_tax_with_surcharge = record.total_tax_on_income + surcharge
excess_tax = total_tax_with_surcharge - max_tax_with_surcharge
if excess_tax - excess_income > 0:
record.surcharge = max_tax_with_surcharge + record.taxable_income - max_taxable_income - record.total_tax_on_income
else:
record.surcharge = surcharge
else:
record.surcharge = 0.0
record.cess = (record.total_tax_on_income + record.surcharge) * 0.04
record.total_tax = record.total_tax_on_income + record.cess + record.surcharge
@api.depends('total_tax')
def _compute_tds_monthly(self):
for record in self:
record.tds_monthly = record.total_tax / 12
def set_tds_on_contracts(self):
for record in self:
record.contract_id.l10n_in_tds = record.tds_monthly

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="l10n_in_tds_computation_wizard_view_form" model="ir.ui.view">
<field name="name">l10n.in.tds.computation.wizard.view.form</field>
<field name="model">l10n.in.tds.computation.wizard</field>
<field name="arch" type="xml">
<form string="TDS Calculation">
<group style="grid-template-columns: 250px auto">
<field name="currency_id" invisible="1"/>
<field name="total_income" widget="monetary"/>
<field name="standard_deduction" widget="monetary"/>
<field name="taxable_income" widget="monetary"/>
<field name="tax_on_taxable_income" widget="monetary"/>
<field name="rebate" class="o_text_overflow" widget="monetary"/>
<field name="total_tax_on_income" widget="monetary"/>
<field name="surcharge" widget="monetary"/>
<field name="cess" widget="monetary"/>
<field name="total_tax" widget="monetary"/>
<field name="tds_monthly" widget="monetary"/>
</group>
<footer>
<button name="set_tds_on_contracts" string="Adjust TDS" type="object" class="btn-primary" data-hotkey="q"/>
<button string="Discard" class="btn-secondary" special="cancel" data-hotkey="x" />
</footer>
</form>
</field>
</record>
<record id="action_tds_calculation" model="ir.actions.act_window">
<field name="name">Employee TDS Calculation</field>
<field name="res_model">l10n.in.tds.computation.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{'dialog_size': 'medium'}</field>
</record>
</odoo>

View File

@ -0,0 +1,74 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.osv import expression
class YearlySalaryDetail(models.TransientModel):
_name = 'yearly.salary.detail'
_description = 'Hr Salary Employee By Category Report'
@api.model
def default_get(self, field_list=None):
if self.env.company.country_id.code != "IN":
raise UserError(_('You must be logged in a Indian company to use this feature'))
return super().default_get(field_list)
def _get_domain(self):
self.ensure_one()
domain = [('company_id', '=', self.env.company.id)]
if self.department_id:
domain += [('department_id', '=', self.department_id.id)]
if self.job_id:
domain += [('job_id', '=', self.job_id.id)]
return domain
@api.depends('department_id', 'job_id')
def _compute_related_employee_ids(self):
employee = self.env['hr.employee']
for wizard in self:
domain = wizard._get_domain()
wizard.related_employee_ids = employee.search(domain)
def _get_year_selection(self):
current_year = datetime.now().year
return [(str(i), i) for i in range(1990, current_year + 1)]
year = fields.Selection(
selection='_get_year_selection', string='Year', required=True,
default=lambda x: str(datetime.now().year - 1))
related_employee_ids = fields.Many2many('hr.employee', compute="_compute_related_employee_ids")
employee_ids = fields.Many2many('hr.employee', 'payroll_emp_rel', 'payroll_id', 'employee_id', string='Employees',
required=True, compute="_compute_employee_ids", readonly=False, store=True)
department_id = fields.Many2one('hr.department', string="Department")
job_id = fields.Many2one('hr.job', string="Job Position")
@api.depends('year', 'department_id', 'job_id')
def _compute_employee_ids(self):
payslip = self.env['hr.payslip']
employee = self.env['hr.employee']
for wizard in self:
date_from = fields.Date.today() + relativedelta(day=1, month=1, year=int(wizard.year))
date_to = fields.Date.today() + relativedelta(day=31, month=12, year=int(wizard.year))
payslip_domain = [('date_from', '>=', date_from), ('date_to', '<=', date_to), ('state', '=', 'paid')]
payslip_ids = payslip.search(payslip_domain)
employee_domain = expression.AND([wizard._get_domain(), [('slip_ids', 'in', payslip_ids.ids)]])
wizard.employee_ids = employee.search(employee_domain)
def print_report(self):
"""
To get the date and print the report
@return: return report
"""
self.ensure_one()
if not self.employee_ids:
raise UserError("There must be at least one employee available to generate a report.")
data = {'ids': self.env.context.get('active_ids', [])}
res = self.read()
res = res and res[0] or {}
data.update({'form': res})
return self.env.ref('l10n_in_hr_payroll.action_report_hryearlysalary').with_context(active_model=self._name, discard_logo_check=True).report_action(self, data=data)

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_yearly_salary_detail" model="ir.ui.view">
<field name="name">Employee Yearly Salary</field>
<field name="model">yearly.salary.detail</field>
<field name="arch" type="xml">
<form string="Pay Head Employee Breakup">
<span class="o_form_label">This wizard will print for employees with paid payslips on the selected year.</span>
<footer>
<button name="print_report" string="Print" type="object" class="btn-primary disabled" data-hotkey="q" invisible="employee_ids"/>
<button name="print_report" string="Print" type="object" class="btn-primary" data-hotkey="q" invisible="not employee_ids"/>
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="x" />
</footer>
<group class="mt-4">
<group>
<field name="year"/>
<field name="department_id"/>
</group>
<group>
<field name="job_id"/>
</group>
</group>
<group>
<separator string="Employees" />
<field name="employee_ids" nolabel="1" colspan="2" context="{'list_view_ref' : 'l10n_in_hr_payroll.hr_employee_tree_inherit'}"
domain="[('id', 'in', related_employee_ids)]">
<list no_open="True" create="False">
<field name="name" string="Employee Name"/>
<field name="registration_number" string="Employee Code"/>
<field name="department_id" string="Department"/>
<field name="job_id" string="Job Position"/>
</list>
</field>
</group>
</form>
</field>
</record>
<record id="action_yearly_salary_detail" model="ir.actions.act_window">
<field name="name">Yearly Salary by Employee</field>
<field name="res_model">yearly.salary.detail</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<menuitem
name="Yearly Salary by Employee"
parent="l10n_in_hr_payroll.menu_reporting_l10n_in"
action="action_yearly_salary_detail"
sequence="250"
id="menu_yearly_salary_detail"
groups="hr_payroll.group_hr_payroll_manager,hr_payroll.group_hr_payroll_user"
/>
</odoo>