diff --git a/payroll_timesheet/README.md b/payroll_timesheet/README.md new file mode 100644 index 000000000..ab5b5cdf4 --- /dev/null +++ b/payroll_timesheet/README.md @@ -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) + + + diff --git a/payroll_timesheet/__init__.py b/payroll_timesheet/__init__.py new file mode 100644 index 000000000..cde864bae --- /dev/null +++ b/payroll_timesheet/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/payroll_timesheet/__manifest__.py b/payroll_timesheet/__manifest__.py new file mode 100644 index 000000000..a64162488 --- /dev/null +++ b/payroll_timesheet/__manifest__.py @@ -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 +} diff --git a/payroll_timesheet/data/data.xml b/payroll_timesheet/data/data.xml new file mode 100644 index 000000000..2f84529c2 --- /dev/null +++ b/payroll_timesheet/data/data.xml @@ -0,0 +1,13 @@ + + + + Hourly Pay (Timesheet) + + HT + + none + code + result = (contract.wage/payslip.total_hours)*payslip.timesheet_hours if payslip.total_hours else 0.0 + + + \ No newline at end of file diff --git a/payroll_timesheet/models/__init__.py b/payroll_timesheet/models/__init__.py new file mode 100644 index 000000000..cde864bae --- /dev/null +++ b/payroll_timesheet/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/payroll_timesheet/models/models.py b/payroll_timesheet/models/models.py new file mode 100644 index 000000000..ec821b798 --- /dev/null +++ b/payroll_timesheet/models/models.py @@ -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) \ No newline at end of file diff --git a/payroll_timesheet/security/ir.model.access.csv b/payroll_timesheet/security/ir.model.access.csv new file mode 100644 index 000000000..41d05b5b7 --- /dev/null +++ b/payroll_timesheet/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_payroll_timesheet_payroll_timesheet,payroll_timesheet.payroll_timesheet,model_payroll_timesheet_payroll_timesheet,,1,0,0,0 \ No newline at end of file diff --git a/payroll_timesheet/static/description/banner.jpg b/payroll_timesheet/static/description/banner.jpg new file mode 100644 index 000000000..54dadb311 Binary files /dev/null and b/payroll_timesheet/static/description/banner.jpg differ diff --git a/payroll_timesheet/static/description/contract.png b/payroll_timesheet/static/description/contract.png new file mode 100644 index 000000000..e3be58d45 Binary files /dev/null and b/payroll_timesheet/static/description/contract.png differ diff --git a/payroll_timesheet/static/description/cybro_logo.png b/payroll_timesheet/static/description/cybro_logo.png new file mode 100644 index 000000000..bb309114c Binary files /dev/null and b/payroll_timesheet/static/description/cybro_logo.png differ diff --git a/payroll_timesheet/static/description/icon.png b/payroll_timesheet/static/description/icon.png new file mode 100644 index 000000000..4a0d510ba Binary files /dev/null and b/payroll_timesheet/static/description/icon.png differ diff --git a/payroll_timesheet/static/description/index.html b/payroll_timesheet/static/description/index.html new file mode 100644 index 000000000..0f82bd7a9 --- /dev/null +++ b/payroll_timesheet/static/description/index.html @@ -0,0 +1,171 @@ +
+
+

Timesheet Based Payroll

+

Cybrosys Technologies

+
+
+
+
+

Payroll based on Timesheet hours.

+

+ While creating payroll the approved total sheet hours submitted by + employees are calculated. +

+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+

Need Any Help?

+
+
+ Email Contact Us Request Customization +
+
+ +
+ + + + + +
+
+
+ diff --git a/payroll_timesheet/static/description/payslip1.png b/payroll_timesheet/static/description/payslip1.png new file mode 100644 index 000000000..368db1825 Binary files /dev/null and b/payroll_timesheet/static/description/payslip1.png differ diff --git a/payroll_timesheet/static/description/payslip2.png b/payroll_timesheet/static/description/payslip2.png new file mode 100644 index 000000000..22d80ddbb Binary files /dev/null and b/payroll_timesheet/static/description/payslip2.png differ diff --git a/payroll_timesheet/static/description/payslip3.png b/payroll_timesheet/static/description/payslip3.png new file mode 100644 index 000000000..d186b1124 Binary files /dev/null and b/payroll_timesheet/static/description/payslip3.png differ diff --git a/payroll_timesheet/static/description/report1.png b/payroll_timesheet/static/description/report1.png new file mode 100644 index 000000000..17c49eefb Binary files /dev/null and b/payroll_timesheet/static/description/report1.png differ diff --git a/payroll_timesheet/static/description/report2.png b/payroll_timesheet/static/description/report2.png new file mode 100644 index 000000000..a7b4e5650 Binary files /dev/null and b/payroll_timesheet/static/description/report2.png differ diff --git a/payroll_timesheet/static/description/rule.png b/payroll_timesheet/static/description/rule.png new file mode 100644 index 000000000..f0f243d32 Binary files /dev/null and b/payroll_timesheet/static/description/rule.png differ diff --git a/payroll_timesheet/static/description/structure.png b/payroll_timesheet/static/description/structure.png new file mode 100644 index 000000000..8916f7283 Binary files /dev/null and b/payroll_timesheet/static/description/structure.png differ diff --git a/payroll_timesheet/views/views.xml b/payroll_timesheet/views/views.xml new file mode 100644 index 000000000..04f700e70 --- /dev/null +++ b/payroll_timesheet/views/views.xml @@ -0,0 +1,39 @@ + + + + Timesheet Based Structure + hr.payroll.structure + + + + + + + + + Timesheet Based Payroll + hr.contract + + + + + + + + + Timesheet View on Payroll + hr.payslip + + + + + + + + + + + + \ No newline at end of file