Skip to content

Commit

Permalink
[11.0][MIG] hr_timesheet_sheet
Browse files Browse the repository at this point in the history
  • Loading branch information
MiquelRForgeFlow committed May 9, 2018
1 parent fd42765 commit 319b8cf
Show file tree
Hide file tree
Showing 31 changed files with 846 additions and 1,022 deletions.
73 changes: 73 additions & 0 deletions hr_timesheet_sheet/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3

==================
HR Timesheet Sheet
==================

This module supplies a new screen enabling you to manage your work encoding (timesheet) by period.
Timesheet entries are made by employees each day. At the end of the defined period,
employees validate their sheet and the manager must then approve his team's entries.
Periods are defined in the company forms and you can set them to run monthly or weekly.


Installation
============

This module relies on:

* The OCA module '2D matrix for x2many fields', and can be downloaded from
Github: https://github.com/OCA/web/tree/11.0/web_widget_x2many_2d_matrix


Configuration
=============

If you want other default ranges different from weekly, you need to go:

* In the menu `Configuration` -> `Settings` -> **Timesheet Options**,
and select in **Timesheet Sheet Range** the default range you want.


.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/117/11.0


Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/OCA/hr-timesheet/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smash it by providing detailed and welcomed feedback.

Credits
=======

Images
------

* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.

Contributors
------------

* Miquel Raïch <miquel.raich@eficent.com>


Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit https://odoo-community.org.
6 changes: 1 addition & 5 deletions hr_timesheet_sheet/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import models
import wizard
from . import models
41 changes: 13 additions & 28 deletions hr_timesheet_sheet/__manifest__.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,27 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

# Copyright 2018 Eficent
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
'name': 'Timesheets / Attendances',
'version': '1.1',
'name': 'HR Timesheet Sheet',
'version': '11.0.1.0.0',
'category': 'Human Resources',
'sequence': 80,
'summary': 'Timesheets, Activities',
'description': """
Record and validate timesheets and attendances easily
=====================================================
This application supplies a new screen enabling you to manage your work encoding (timesheet) by period. Timesheet entries are made by employees each day. At the end of the defined period, employees validate their sheet and the manager must then approve his team's entries. Periods are defined in the company forms and you can set them to run monthly or weekly.
The complete timesheet validation process is:
---------------------------------------------
* Draft sheet
* Confirmation at the end of the period by the employee
* Validation by the project manager
The validation can be configured in the company:
------------------------------------------------
* Period size (Day, Week, Month)
* Maximal difference between timesheet and attendances
""",
'website': 'https://www.odoo.com/page/employees',
'depends': ['hr_timesheet'],
'summary': 'Timesheet Sheets, Activities',
'license': 'AGPL-3',
'author': 'Eficent, Odoo Community Association (OCA)',
'website': 'https://github.com/OCA/hr-timesheet',
'depends': [
'hr_timesheet', 'account',
'web_widget_x2many_2d_matrix',
],
'data': [
'security/ir.model.access.csv',
'security/hr_timesheet_sheet_security.xml',
'data/hr_timesheet_sheet_data.xml',
'views/hr_timesheet_sheet_templates.xml',
'views/hr_timesheet_sheet_views.xml',
'views/hr_department_views.xml',
'views/hr_timesheet_sheet_config_settings_views.xml',
'views/res_config_settings_views.xml',
],
'installable': True,
'auto_install': False,
'qweb': ['static/src/xml/timesheet.xml', ],
}
14 changes: 2 additions & 12 deletions hr_timesheet_sheet/data/hr_timesheet_sheet_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,16 @@
<odoo>
<data noupdate="1">

<record id="ir_actions_server_timesheet_sheet" model="ir.actions.server">
<field name="sequence" eval="5"/>
<field name="state">code</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="model_hr_timesheet_current_open"/>
<field name="code">action = model.open_timesheet()</field>
<field name="condition">True</field>
<field name="name">My Timesheet</field>
</record>

<!-- Timesheet sheet related subtypes for messaging / Chatter -->
<record id="mt_timesheet_confirmed" model="mail.message.subtype">
<field name="name">Waiting Approval</field>
<field name="res_model">hr_timesheet_sheet.sheet</field>
<field name="res_model">hr_timesheet.sheet</field>
<field name="default" eval="True"/>
<field name="description">waiting approval</field>
</record>
<record id="mt_timesheet_approved" model="mail.message.subtype">
<field name="name">Approved</field>
<field name="res_model">hr_timesheet_sheet.sheet</field>
<field name="res_model">hr_timesheet.sheet</field>
<field name="default" eval="True"/>
<field name="description">Timesheet approved</field>
</record>
Expand Down
15 changes: 6 additions & 9 deletions hr_timesheet_sheet/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

import account_analytic_line
import hr_department
import hr_employee
import hr_timesheet_sheet
import hr_timesheet_sheet_config_settings
import res_company
from . import account_analytic_line
from . import hr_department
from . import hr_employee
from . import hr_timesheet_sheet
from . import res_company
from . import res_config
63 changes: 39 additions & 24 deletions hr_timesheet_sheet/models/account_analytic_line.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,58 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
# Copyright 2018 Eficent Business and IT Consulting Services, S.L.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models, _
from odoo.exceptions import UserError


class AccountAnalyticLine(models.Model):
_inherit = "account.analytic.line"
_inherit = 'account.analytic.line'

sheet_id_computed = fields.Many2one('hr_timesheet_sheet.sheet', string='Sheet', compute='_compute_sheet', index=True, ondelete='cascade',
search='_search_sheet')
sheet_id = fields.Many2one('hr_timesheet_sheet.sheet', compute='_compute_sheet', string='Sheet', store=True)
sheet_id_computed = fields.Many2one(
comodel_name='hr_timesheet.sheet',
string='Sheet',
compute='_compute_sheet',
index=True,
ondelete='cascade',
search='_search_sheet'
)
sheet_id = fields.Many2one(
comodel_name='hr_timesheet.sheet',
string='Sheet',
compute='_compute_sheet',
store=True,
)

@api.depends('date', 'user_id', 'project_id', 'sheet_id_computed.date_to', 'sheet_id_computed.date_from', 'sheet_id_computed.employee_id')
@api.depends('date', 'user_id', 'project_id', 'task_id',
'sheet_id.date_start', 'sheet_id.date_end',
'sheet_id.employee_id')
def _compute_sheet(self):
"""Links the timesheet line to the corresponding sheet
"""
for ts_line in self:
if not ts_line.project_id:
"""Links the timesheet line to the corresponding sheet"""
for timesheet in self:
if timesheet.sheet_id or not timesheet.project_id:
continue
sheets = self.env['hr_timesheet_sheet.sheet'].search(
[('date_to', '>=', ts_line.date), ('date_from', '<=', ts_line.date),
('employee_id.user_id.id', '=', ts_line.user_id.id),
('state', 'in', ['draft', 'new'])])
sheets = self.env['hr_timesheet.sheet'].search(
[('date_end', '>=', timesheet.date),
('date_start', '<=', timesheet.date),
('employee_id.user_id.id', '=', timesheet.user_id.id),
('state', 'in', ['draft', 'new']),
])
if sheets:
# [0] because only one sheet possible for an employee between 2 dates
ts_line.sheet_id_computed = sheets[0]
ts_line.sheet_id = sheets[0]
timesheet.sheet_id_computed = sheets[0]
timesheet.sheet_id = sheets[0]

def _search_sheet(self, operator, value):
assert operator == 'in'
ids = []
for ts in self.env['hr_timesheet_sheet.sheet'].browse(value):
for ts in self.env['hr_timesheet.sheet'].browse(value):
self._cr.execute("""
SELECT l.id
FROM account_analytic_line l
WHERE %(date_to)s >= l.date
AND %(date_from)s <= l.date
WHERE %(date_end)s >= l.date
AND %(date_start)s <= l.date
AND %(user_id)s = l.user_id
GROUP BY l.id""", {'date_from': ts.date_from,
'date_to': ts.date_to,
GROUP BY l.id""", {'date_start': ts.date_start,
'date_end': ts.date_end,
'user_id': ts.employee_id.user_id.id, })
ids.extend([row[0] for row in self._cr.fetchall()])
return [('id', 'in', ids)]
Expand All @@ -57,5 +70,7 @@ def unlink(self):
def _check_state(self):
for line in self:
if line.sheet_id and line.sheet_id.state not in ('draft', 'new'):
raise UserError(_('You cannot modify an entry in a confirmed timesheet.'))
raise UserError(
_('You cannot modify an entry in a confirmed '
'timesheet sheet.'))
return True
23 changes: 15 additions & 8 deletions hr_timesheet_sheet/models/hr_department.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
# Copyright 2018 Eficent Business and IT Consulting Services, S.L.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models


class HrDepartment(models.Model):
_inherit = 'hr.department'

timesheet_to_approve_count = fields.Integer(
compute='_compute_timesheet_to_approve', string='Timesheet to Approve')
timesheet_sheet_to_approve_count = fields.Integer(
compute='_compute_timesheet_to_approve',
string='Timesheet Sheets to Approve',
)

@api.multi
def _compute_timesheet_to_approve(self):
timesheet_data = self.env['hr_timesheet_sheet.sheet'].read_group(
[('department_id', 'in', self.ids), ('state', '=', 'confirm')], ['department_id'], ['department_id'])
result = dict((data['department_id'][0], data['department_id_count']) for data in timesheet_data)
timesheet_data = self.env['hr_timesheet.sheet'].read_group(
[('department_id', 'in', self.ids), ('state', '=', 'confirm')],
['department_id'], ['department_id'])
result = dict(
(data['department_id'][0], data['department_id_count'])
for data in timesheet_data
)
for department in self:
department.timesheet_to_approve_count = result.get(department.id, 0)
department.timesheet_sheet_to_approve_count = \
result.get(department.id, 0)
12 changes: 8 additions & 4 deletions hr_timesheet_sheet/models/hr_employee.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
# Copyright 2018 Eficent Business and IT Consulting Services, S.L.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import api, fields, models

Expand All @@ -8,9 +8,13 @@ class HrEmployee(models.Model):
_inherit = 'hr.employee'
_description = 'Employee'

timesheet_count = fields.Integer(compute='_compute_timesheet_count', string='Timesheets')
timesheet_count = fields.Integer(
compute='_compute_timesheet_count',
string='Timesheets',
)

@api.multi
def _compute_timesheet_count(self):
for employee in self:
employee.timesheet_count = employee.env['hr_timesheet_sheet.sheet'].search_count([('employee_id', '=', employee.id)])
employee.timesheet_count = employee.env['account.analytic.line'].\
search_count([('employee_id', '=', employee.id)])
Loading

0 comments on commit 319b8cf

Please sign in to comment.