@ -0,0 +1,40 @@ |
|||
# Timesheet Based Payroll |
|||
|
|||
Payroll based on Timesheet hours. While computing payroll the approved total sheet hours submitted by |
|||
employees are calculated. This addon creates a rule of category BASIC for hour based calculations. So while creating |
|||
salary structure for timesheet based payroll we have to select the rule there. |
|||
|
|||
- Flag which enables the contract as timesheet based. |
|||
- Flag which enables the salary structure for timesheet based. |
|||
- Timesheet hours and total hours on payslip view. |
|||
- Salary Rule for timesheet based calculations. |
|||
|
|||
|
|||
### Depends |
|||
Timesheet Based Payroll depends on hr, hr_timesheet_sheet, hr_timesheet_attendance,hr_payroll |
|||
|
|||
### Tech |
|||
|
|||
* [Python] - Models |
|||
* [XML] - Odoo views |
|||
|
|||
### Installation |
|||
- www.odoo.com/documentation/10.0/setup/install.html |
|||
- Install our custom addon, which also installs its depends [hr, hr_payroll, hr_timesheet_sheet, hr_timesheet_attendance] |
|||
|
|||
### Usage |
|||
> Enable Timesheet Based Payroll on contract. |
|||
> Enable Timesheet Based structure on salary structure. |
|||
> Select Hourly Pay (Timesheet) rule while creating salary structure. |
|||
> payslip will display total hours and total timesheet hours, |
|||
if the employee contract is enabled Timesheet Based Payroll |
|||
|
|||
|
|||
|
|||
License |
|||
---- |
|||
GNU LESSER GENERAL PUBLIC LICENSE, Version 3 (LGPLv3) |
|||
(http://www.gnu.org/licenses/agpl.html) |
|||
|
|||
|
|||
|
@ -0,0 +1,3 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from . import models |
@ -0,0 +1,30 @@ |
|||
# -*- coding: utf-8 -*- |
|||
{ |
|||
'name': "TimeSheet Based Payroll", |
|||
'version': "10.0.1.0.0", |
|||
'summary': """ |
|||
Payrolls Are Computed According to the Submitted & Confirmed Timesheets By Employees.""", |
|||
'description': """ |
|||
Payrolls are computed according to the submitted and confirmed Timesheets By employees. |
|||
""", |
|||
|
|||
'author': "Cybrosys Techno Solutions", |
|||
'company': "Cybrosys Techno Solutions", |
|||
'website': "https://cybrosys.com/", |
|||
'category': "Generic Modules/Human Resources", |
|||
'depends': [ |
|||
"base", |
|||
"hr_payroll", |
|||
"hr_timesheet_sheet", |
|||
"hr_timesheet_attendance", |
|||
], |
|||
'data': [ |
|||
'data/data.xml', |
|||
'views/views.xml', |
|||
], |
|||
'demo': [], |
|||
'images': ['static/description/banner.jpg'], |
|||
'license': "LGPL-3", |
|||
'installable': True, |
|||
'application': True |
|||
} |
@ -0,0 +1,13 @@ |
|||
<odoo> |
|||
<data noupdate="1"> |
|||
<record id="timesheet_salary_rule" model="hr.salary.rule"> |
|||
<field name="name">Hourly Pay (Timesheet)</field> |
|||
<field name="category_id" ref="hr_payroll.BASIC"/> |
|||
<field name="code">HT</field> |
|||
<field name="sequence" eval="1"/> |
|||
<field name="condition_select">none</field> |
|||
<field name="amount_select">code</field> |
|||
<field name="amount_python_compute">result = (contract.wage/payslip.total_hours)*payslip.timesheet_hours if payslip.total_hours else 0.0</field> |
|||
</record> |
|||
</data> |
|||
</odoo> |
@ -0,0 +1,3 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
from . import models |
@ -0,0 +1,126 @@ |
|||
# -*- coding: utf-8 -*- |
|||
|
|||
import itertools |
|||
from odoo import models, fields, api |
|||
|
|||
|
|||
class PayrollStructure(models.Model): |
|||
_inherit = 'hr.payroll.structure' |
|||
|
|||
timesheet_structure = fields.Boolean(string='Timesheet Based Structure', default=False, |
|||
help='Flag Which says the Structure is based on Timesheets submitted by employee') |
|||
|
|||
|
|||
class HrContract(models.Model): |
|||
_inherit = 'hr.contract' |
|||
|
|||
timesheet_payroll = fields.Boolean(string='Timesheet Based Payroll', default=False, |
|||
help='Flag Which says the payroll is based on Timesheets submitted by employee') |
|||
|
|||
@api.onchange('timesheet_payroll') |
|||
def onchange_timesheet_payroll(self): |
|||
if self.timesheet_payroll: |
|||
self.struct_id = False |
|||
return { |
|||
'domain': { |
|||
'struct_id': [('timesheet_structure', '=', True)] |
|||
}, |
|||
} |
|||
else: |
|||
return { |
|||
'domain': {'struct_id': []}, |
|||
} |
|||
|
|||
|
|||
class HrPayslip(models.Model): |
|||
_inherit = 'hr.payslip' |
|||
|
|||
timesheet_hours = fields.Integer(string='Timesheet Hours', states={'done': [('readonly', True)]}, |
|||
help='Total Timesheet hours approved for employee.') |
|||
total_hours = fields.Integer(string='Total Hours', states={'done': [('readonly', True)]}, |
|||
help='Total Hours By Working schedule') |
|||
timesheet_payroll = fields.Boolean(related='contract_id.timesheet_payroll') |
|||
|
|||
def compute_timesheet_hours(self, contract_id, date_from, date_to): |
|||
""" |
|||
Function which computes total hours, timesheethours, attendances, timehseet difference, |
|||
:param employee_id: |
|||
:param date_from: |
|||
:param date_to: |
|||
:return: computed total timesheet hours within duration, total hours by working schedule |
|||
""" |
|||
if not contract_id: |
|||
return {} |
|||
env = self.env |
|||
employee_id = contract_id.employee_id |
|||
timesheet_object = env['hr_timesheet_sheet.sheet'] |
|||
total_hours = 0.0 |
|||
timesheet_hours = timesheet_attendance = timesheet_difference = 0.0 |
|||
for line in self.worked_days_line_ids: |
|||
total_hours += line.number_of_hours if line.code == 'WORK100' else 0.0 |
|||
sheets = timesheet_object.search([ |
|||
('employee_id', '=', employee_id.id), |
|||
('date_from', '>=', date_from), |
|||
('date_to', '<=', date_to), |
|||
('state', '=', 'done') |
|||
|
|||
]) |
|||
period_ids = [] |
|||
period_ids += [sheet.period_ids.ids for sheet in sheets] |
|||
period_ids = list(itertools.chain.from_iterable(period_ids)) |
|||
if len(period_ids): |
|||
self.env.cr.execute(""" |
|||
SELECT |
|||
sum(total_attendance) as total_attendance, |
|||
sum(total_timesheet) as total_timesheet, |
|||
sum(total_difference) as total_difference |
|||
FROM hr_timesheet_sheet_sheet_day |
|||
WHERE id IN %s |
|||
""", (tuple(period_ids),)) |
|||
data = self.env.cr.dictfetchall() |
|||
for x in data: |
|||
timesheet_hours = x.pop('total_timesheet') |
|||
timesheet_attendance = x.pop('total_attendance') |
|||
timesheet_difference = x.pop('total_difference') |
|||
|
|||
return { |
|||
'timesheet_hours': timesheet_hours, |
|||
'timesheet_attendance': timesheet_attendance, |
|||
'timesheet_difference': timesheet_difference, |
|||
'total_hours': total_hours, |
|||
} |
|||
|
|||
@api.onchange('employee_id', 'date_from') |
|||
def onchange_employee(self): |
|||
super(HrPayslip, self).onchange_employee() |
|||
if self.contract_id.timesheet_payroll: |
|||
datas = self.compute_timesheet_hours(self.contract_id, self.date_from, self.date_to) |
|||
self.timesheet_hours = datas.get('timesheet_hours') or 0.0 |
|||
self.total_hours = datas.get('total_hours') or 0.0 |
|||
return |
|||
|
|||
@api.model |
|||
def get_payslip_lines(self, contract_ids, payslip_id): |
|||
class BrowsableObject(object): |
|||
def __init__(self, employee_id, dict): |
|||
self.employee_id = employee_id |
|||
self.dict = dict |
|||
|
|||
def __getattr__(self, attr): |
|||
return attr in self.dict and self.dict.__getitem__(attr) or 0.0 |
|||
class Payslips(BrowsableObject): |
|||
"""a class that will be used into the python code, mainly for usability purposes""" |
|||
|
|||
def sum(self, code, from_date, to_date=None): |
|||
if to_date is None: |
|||
to_date = fields.Date.today() |
|||
self.env.cr.execute("""SELECT sum(case when hp.credit_note = False then (pl.total) else (-pl.total) end) |
|||
FROM hr_payslip as hp, hr_payslip_line as pl |
|||
WHERE hp.employee_id = %s AND hp.state = 'done' |
|||
AND hp.date_from >= %s AND hp.date_to <= %s AND hp.id = pl.slip_id AND pl.code = %s""", |
|||
(self.employee_id, from_date, to_date, code)) |
|||
res = self.env.cr.fetchone() |
|||
return res and res[0] or 0.0 |
|||
timesheet_hours = self.timesheet_hours |
|||
total_hours = self.total_hours |
|||
return super(HrPayslip, self).get_payslip_lines(contract_ids, payslip_id) |
|
After Width: | Height: | Size: 104 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,171 @@ |
|||
<section class="oe_container oe_dark"> |
|||
<div class="oe_row oe_spaced"> |
|||
<h2 class="oe_slogan">Timesheet Based Payroll</h2> |
|||
<h4 class="oe_slogan"><a href="https://www.cybrosys.com">Cybrosys Technologies</a></h4> |
|||
</div> |
|||
</section> |
|||
<section class="oe_container"> |
|||
<div class="oe_row oe_spaced"> |
|||
<h2 class="oe_slogan" style="color:#875A7B;">Payroll based on Timesheet hours. </h2> |
|||
<h3 class="oe_slogan"> |
|||
While creating payroll the approved total sheet hours submitted by |
|||
employees are calculated. |
|||
</h3> |
|||
</div> |
|||
|
|||
<div class="oe_container oe_dark"> |
|||
<div class="row mt32 o_animate o_animate_in_children o_animate_offset_min" |
|||
style="animation-name: none; visibility: hidden; animation-play-state: paused;"> |
|||
|
|||
<div class="col-md-5 col-md-offset-1" style="transition-delay: 0ms;"> |
|||
<h2 class=" mt32 mb16"><b>Specifies Timesheet based Contract</b></h2> |
|||
</div> |
|||
<div class="col-md-6" style="transition-delay: 500ms;"> |
|||
<div class=" oe_demo oe_picture oe_screenshot"> |
|||
<img style="max-width: 100%;-moz-transform: scale(1.2);-webkit-transform: scale(1.2); -o-transform: scale(1.2); -ms-transform: scale(1.2); transform: scale(1.2);" |
|||
src="contract.png"> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="oe_container"> |
|||
<div class="row mt32 o_animate o_animate_in_children o_animate_offset_min" |
|||
style="animation-name: none; visibility: hidden; animation-play-state: paused;"> |
|||
|
|||
<div class="col-md-5 col-md-offset-1" style="transition-delay: 0ms;"> |
|||
<h2 class=" mt32 mb16"><b>Timesheet Based Salary Structure</b></h2> |
|||
|
|||
</div> |
|||
<div class="col-md-6" style="transition-delay: 500ms;"> |
|||
<div class=" oe_demo oe_picture oe_screenshot"> |
|||
<img style="max-width: 100%;-moz-transform: scale(1.2);-webkit-transform: scale(1.2); -o-transform: scale(1.2); -ms-transform: scale(1.2); transform: scale(1.2);" |
|||
src="structure.png"> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="oe_container oe_dark"> |
|||
<div class="row mt32 o_animate o_animate_in_children o_animate_offset_min" |
|||
style="animation-name: none; visibility: hidden; animation-play-state: paused;"> |
|||
|
|||
<div class="col-md-5 col-md-offset-1" style="transition-delay: 0ms;"> |
|||
<h2 class=" mt32 mb16"><b>Salary Rule & calculations for Timesheet Based Payroll.</b></h2> |
|||
</div> |
|||
<div class="col-md-6" style="transition-delay: 500ms;"> |
|||
<div class=" oe_demo oe_picture oe_screenshot"> |
|||
<img style="max-width: 100%;-moz-transform: scale(1.2);-webkit-transform: scale(1.2); -o-transform: scale(1.2); -ms-transform: scale(1.2); transform: scale(1.2);" |
|||
src="rule.png"> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="oe_container"> |
|||
<div class="row mt32 o_animate o_animate_in_children o_animate_offset_min" |
|||
style="animation-name: none; visibility: hidden; animation-play-state: paused;"> |
|||
|
|||
<div class="col-md-5 col-md-offset-1" style="transition-delay: 0ms;"> |
|||
<h2 class=" mt32 mb16"><b>Total Timesheet hours on payslip.</b></h2> |
|||
</div> |
|||
<div class="col-md-6" style="transition-delay: 500ms;"> |
|||
<div class=" oe_demo oe_picture oe_screenshot"> |
|||
<img style="max-width: 100%;-moz-transform: scale(1.2);-webkit-transform: scale(1.2); -o-transform: scale(1.2); -ms-transform: scale(1.2); transform: scale(1.2);" |
|||
src="payslip1.png"> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="oe_container oe_dark"> |
|||
<div class="row mt32 o_animate o_animate_in_children o_animate_offset_min" |
|||
style="animation-name: none; visibility: hidden; animation-play-state: paused;"> |
|||
|
|||
<div class="col-md-5 col-md-offset-1" style="transition-delay: 0ms;"> |
|||
<h2 class=" mt32 mb16"><b>Computation as per Timesheet Hour based rule.</b></h2> |
|||
</div> |
|||
<div class="col-md-6" style="transition-delay: 500ms;"> |
|||
<div class=" oe_demo oe_picture oe_screenshot"> |
|||
<img style="max-width: 100%;-moz-transform: scale(1.2);-webkit-transform: scale(1.2); -o-transform: scale(1.2); -ms-transform: scale(1.2); transform: scale(1.2);" |
|||
src="payslip2.png"> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="oe_container"> |
|||
<div class="row mt32 o_animate o_animate_in_children o_animate_offset_min" |
|||
style="animation-name: none; visibility: hidden; animation-play-state: paused;"> |
|||
|
|||
<div class="col-md-5 col-md-offset-1" style="transition-delay: 0ms;"> |
|||
<h2 class=" mt32 mb16"><b>Details as per salary rule category.</b></h2> |
|||
</div> |
|||
<div class="col-md-6" style="transition-delay: 500ms;"> |
|||
<div class=" oe_demo oe_picture oe_screenshot"> |
|||
<img style="max-width: 100%;-moz-transform: scale(1.2);-webkit-transform: scale(1.2); -o-transform: scale(1.2); -ms-transform: scale(1.2); transform: scale(1.2);" |
|||
src="payslip2.png"> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="oe_container oe_dark"> |
|||
<div class="row mt32 o_animate o_animate_in_children o_animate_offset_min" |
|||
style="animation-name: none; visibility: hidden; animation-play-state: paused;"> |
|||
|
|||
<div class="col-md-5 col-md-offset-1" style="transition-delay: 0ms;"> |
|||
<h2 class=" mt32 mb16"><b>Payslip Report.</b></h2> |
|||
</div> |
|||
<div class="col-md-6" style="transition-delay: 500ms;"> |
|||
<div class=" oe_demo oe_picture oe_screenshot"> |
|||
<img style="max-width: 100%;-moz-transform: scale(1.2);-webkit-transform: scale(1.2); -o-transform: scale(1.2); -ms-transform: scale(1.2); transform: scale(1.2);" |
|||
src="report1.png"> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<div class="oe_container oe_dark"> |
|||
<div class="row mt32 o_animate o_animate_in_children o_animate_offset_min" |
|||
style="animation-name: none; visibility: hidden; animation-play-state: paused;"> |
|||
|
|||
<div class="col-md-5 col-md-offset-1" style="transition-delay: 0ms;"> |
|||
<h2 class=" mt32 mb16"><b>Payslip Report With Details.</b></h2> |
|||
</div> |
|||
<div class="col-md-6" style="transition-delay: 500ms;"> |
|||
<div class=" oe_demo oe_picture oe_screenshot"> |
|||
<img style="max-width: 100%;-moz-transform: scale(1.2);-webkit-transform: scale(1.2); -o-transform: scale(1.2); -ms-transform: scale(1.2); transform: scale(1.2);" |
|||
src="report2.png"> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
</section> |
|||
<section class="oe_container oe_dark"> |
|||
<h2 class="oe_slogan" style="margin-top:20px;" >Need Any Help?</h2> |
|||
<div class="oe_slogan" style="margin-top:10px !important;"> |
|||
<div> |
|||
<a class="btn btn-primary btn-lg mt8" |
|||
style="color: #FFFFFF !important;border-radius: 0;" href="https://www.cybrosys.com"><i |
|||
class="fa fa-envelope"></i> Email </a> <a |
|||
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
|||
href="https://www.cybrosys.com/contact/"><i |
|||
class="fa fa-phone"></i> Contact Us </a> <a |
|||
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
|||
href="https://www.cybrosys.com/odoo-customization-and-installation/"><i |
|||
class="fa fa-check-square"></i> Request Customization </a> |
|||
</div> |
|||
<br> |
|||
<img src="cybro_logo.png" style="width: 190px; margin-bottom: 20px;" class="center-block"> |
|||
<div> |
|||
<a href="https://twitter.com/cybrosys" target="_blank"><i class="fa fa-2x fa-twitter" style="color:white;background: #00a0d1;width:35px;"></i></a></td> |
|||
<a href="https://www.linkedin.com/company/cybrosys-technologies-pvt-ltd" target="_blank"><i class="fa fa-2x fa-linkedin" style="color:white;background: #31a3d6;width:35px;padding-left: 3px;"></i></a></td> |
|||
<a href="https://www.facebook.com/cybrosystechnologies" target="_blank"><i class="fa fa-2x fa-facebook" style="color:white;background: #3b5998;width:35px;padding-left: 8px;"></i></a></td> |
|||
<a href="https://plus.google.com/106641282743045431892/about" target="_blank"><i class="fa fa-2x fa-google-plus" style="color:white;background: #c53c2c;width:35px;padding-left: 3px;"></i></a></td> |
|||
<a href="https://in.pinterest.com/cybrosys" target="_blank"><i class="fa fa-2x fa-pinterest" style="color:white;background: #ac0f18;width:35px;padding-left: 3px;"></i></a></td> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
|
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 98 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 44 KiB |
@ -0,0 +1,39 @@ |
|||
<openerp> |
|||
<data> |
|||
<record id="view_hr_employee_grade_form_extend" model="ir.ui.view"> |
|||
<field name="name">Timesheet Based Structure</field> |
|||
<field name="model">hr.payroll.structure</field> |
|||
<field name="inherit_id" ref="hr_payroll.view_hr_employee_grade_form"/> |
|||
<field name="arch" type="xml"> |
|||
<field name="code" position="after"> |
|||
<field name="timesheet_structure"/> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
<record id="hr_contract_view_form_extend" model="ir.ui.view"> |
|||
<field name="name">Timesheet Based Payroll</field> |
|||
<field name="model">hr.contract</field> |
|||
<field name="inherit_id" ref="hr_contract.hr_contract_view_form"/> |
|||
<field name="arch" type="xml"> |
|||
<field name="struct_id" position="after"> |
|||
<field name="timesheet_payroll"/> |
|||
</field> |
|||
</field> |
|||
</record> |
|||
<record id="view_hr_payslip_form_extend" model="ir.ui.view"> |
|||
<field name="name">Timesheet View on Payroll</field> |
|||
<field name="model">hr.payslip</field> |
|||
<field name="inherit_id" ref="hr_payroll.view_hr_payslip_form"/> |
|||
<field name="arch" type="xml"> |
|||
<field name="name" position="after"> |
|||
<field name="timesheet_payroll" invisible="1"/> |
|||
<field name="timesheet_hours" |
|||
attrs="{'invisible': [('timesheet_payroll', '=', False)]}"/> |
|||
<field name="total_hours" |
|||
attrs="{'invisible': [('timesheet_payroll', '=', False)]}"/> |
|||
</field> |
|||
|
|||
</field> |
|||
</record> |
|||
</data> |
|||
</openerp> |