Skip to content
Commits on Source (36)
# Eclipse files
.settings
.project
.pydevproject
.externalToolBuilders
# Python build file.
*.py[co]
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Unit test / coverage reports
.coverage
.tox
coverage.xml
nosetests.xml
setup.py.bak
sonar-project.properties
language: python
sudo: false
cache:
apt: true
directories:
- $HOME/.cache/pip
python:
- "2.7"
addons:
apt:
packages:
- expect-dev # provides unbuffer utility
- python-lxml # because pip installation is slow
- python-simplejson
- python-serial
- python-yaml
- python-ldap
env:
global:
- VERSION="10.0" TESTS="0" LINT_CHECK="0" TRANSIFEX="0"
- PHANTOMJS_VERSION="latest"
- WEBSITE_REPO="1"
matrix:
#- LINT_CHECK="1"
- TESTS="1" ODOO_REPO="odoo/odoo"
- TESTS="1" ODOO_REPO="OCA/OCB"
virtualenv:
system_site_packages: true
install:
- git clone --depth=1 https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- travis_install_nightly
script:
- travis_run_tests
after_success:
- travis_after_tests_success
[![Build Status](https://travis-ci.org/ikus060/odoo-addons.svg?branch=8.0)](https://travis-ci.org/ikus060/odoo-addons)
# odoo-addons
Collection of odoo addons developped by Patrik Dufresne Service Logiciel inc.
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Patrik Dufresne
# Copyright 2017 Patrik Dufresne Service Logiciel inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import models
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Patrik Dufresne
# Copyright 2017 Patrik Dufresne Service Logiciel inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Create User from Employee View',
'version': '10.0.3',
'description': 'This module ease the creation of user from employee view with a single button.',
'category': 'Human Resources',
'author': 'Patrik Dufresne',
'website': 'http://www.patrikdufresne.com',
'company': 'Patrik Dufresne Service Logiciel inc.',
'depends': ['base', 'hr'],
'data': ['views/hr_employee_view.xml'],
'license': 'AGPL-3',
'installable': True,
'auto_install': False,
'application': False,
}
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Patrik Dufresne
# Copyright 2017 Patrik Dufresne Service Logiciel inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import hr_employee
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Patrik Dufresne
# Copyright 2017 Patrik Dufresne Service Logiciel inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from odoo import models, api
class ResUsersInherit(models.Model):
_inherit = 'hr.employee'
@api.multi
def create_user(self):
# TODO may need rework creation of login to use only ascii.
user_id = self.env['res.users'].create({
'name': self.name,
'login': self.work_email or self.name.replace(' ', '.').lower()
})
self.user_id = user_id
self.address_home_id = user_id.partner_id.id
# -*- coding: utf-8 -*-
##############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2017-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: Nilmar Shereef(<http://www.cybrosys.com>)
# you can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# It is forbidden to publish, distribute, sublicense, or sell copies
# of the Software or modified copies of the Software.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from odoo import models, fields, api, _
class ResUsersInherit(models.Model):
_inherit = 'hr.employee'
user_check_tick = fields.Boolean(default=False)
@api.multi
def create_user(self):
user_id = self.env['res.users'].create({'name': self.name,'login': self.work_email})
self.address_home_id = user_id.partner_id.id
self.user_check_tick = True
@api.onchange('address_home_id')
def user_check_tick(self):
if self.address_home_id:
self.user_check_tick = True
else:
self.user_check_tick = False
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="hr_employee_form_inherit" model="ir.ui.view">
<field name="name">hr.employee.form</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//form/sheet" position="before">
<header>
<button name="create_user" string="Create User" type="object" class="oe_highlight"
attrs="{'invisible':[('user_id', '!=', False)]}"/>
</header>
</xpath>
</field>
</record>
</data>
</odoo>
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Patrik Dufresne
# Copyright 2017 Patrik Dufresne Service Logiciel inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import models
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Patrik Dufresne
# Copyright 2017 Patrik Dufresne Service Logiciel inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'Project Timesheets',
'version': '10.0.4',
'category': 'Human Resources',
'sequence': 80,
'summary': 'Timesheets, Activities',
'description': """This module provide a different way to manage time sheet per project instead of employees. It allows a manager to create timesheets for a specific project. For our specific need, this module also add begin, end time. Employee categories into timesheet.""",
'author': 'Patrik Dufresne',
'website': 'http://www.patrikdufresne.com',
'company': 'Patrik Dufresne Service Logiciel inc.',
'depends': [
'hr_timesheet',
'project',
],
'data': [
'security/ir.model.access.csv',
'security/hr_timesheet_project_sheet_security.xml',
"report/report_timesheet_templates.xml",
'data/hr_timesheet_project_sheet_data.xml',
'data/hr_timesheet_action_data.xml',
'data/hr_timesheet_employee_tag.xml',
'views/hr_analytic_timesheet.xml',
'views/hr_timesheet_project_sheet_templates.xml',
'views/hr_timesheet_project_sheet_views.xml',
],
'installable': True,
'auto_install': False,
'qweb': ['static/src/xml/timesheet.xml', ],
'license': 'AGPL-3',
}
<?xml version="1.0" ?>
<odoo>
<!-- Mail template are declared in a NOUPDATE block
so users can freely customize/delete them -->
<data>
<!--Email template -->
<record id="email_template_timesheet" model="mail.template">
<field name="name">Timesheet - Send by Email</field>
<field name="email_from">${(object.user_id.email and '%s &lt;%s&gt;' % (object.user_id.name, object.user_id.email) or '')|safe}</field>
<field name="subject">Timesheet (Ref ${object.number or 'n/a'})</field>
<field name="partner_to">${object.project_id.partner_id.id}</field>
<field name="model_id" ref="hr_timesheet_project_sheet.model_hr_timesheet_project_sheet_sheet"/>
<field name="auto_delete" eval="True"/>
<field name="report_template" ref="timesheet_project_report_sheet"/>
<field name="report_name">Timesheet_${(object.number or '').replace('/','_')}_${object.state == 'draft' and 'draft' or ''}</field>
<field name="lang">${object.project_id.partner_id.lang}</field>
<field name="body_html"><![CDATA[
<p>Dear ${object.project_id.partner_id.name},
</p><p>Here is your timesheet.</p>
<p>Thank you,
%if object.user_id and object.user_id.signature:
${object.user_id.signature | safe}
%endif
</p>
]]></field>
</record>
</data>
</odoo>
<?xml version="1.0" ?>
<odoo>
<data>
<record id="employee_category_employee" model="hr.employee.category">
<field name="name">Manager</field>
<field name="color" eval="6" />
</record>
</data>
</odoo>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data noupdate="1">
<!-- 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_project_sheet.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_project_sheet.sheet</field>
<field name="default" eval="True"/>
<field name="description">Timesheet approved</field>
</record>
<!-- Department (Parent) related subtypes for messaging / Chatter -->
<record id="mt_department_timesheet_confirmed" model="mail.message.subtype">
<field name="name">Timesheets to Approve</field>
<field name="res_model">hr.department</field>
<field name="default" eval="False"/>
<field name="parent_id" eval="ref('mt_timesheet_confirmed')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="5"/>
</record>
<record id="mt_department_timesheet_approved" model="mail.message.subtype">
<field name="name">Timesheets Approved</field>
<field name="res_model">hr.department</field>
<field name="default" eval="False"/>
<field name="parent_id" eval="ref('mt_timesheet_approved')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="5"/>
</record>
</data>
</odoo>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Patrik Dufresne
# Copyright 2017 Patrik Dufresne Service Logiciel inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import account_analytic_line
import hr_timesheet_project_sheet
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Patrik Dufresne
# Copyright 2017 Patrik Dufresne Service Logiciel inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from __future__ import division
from datetime import timedelta
import math
from odoo import models, fields, api, exceptions, _
from odoo.exceptions import UserError
from odoo.tools.float_utils import float_compare
def float_time_convert(float_val):
hours = math.floor(abs(float_val))
mins = abs(float_val) - hours
mins = round(mins * 60)
if mins >= 60.0:
hours = hours + 1
mins = 0.0
return '%02d:%02d' % (hours, mins)
class AccountAnalyticLine(models.Model):
_inherit = "account.analytic.line"
@api.model
def _default_project(self):
return self.env.context.get('project_id', None)
project_sheet_id_computed = fields.Many2one('hr_timesheet_project_sheet.sheet', string='Sheet', compute='_compute_sheet', index=True,
ondelete='cascade', search='_search_sheet')
project_sheet_id = fields.Many2one('hr_timesheet_project_sheet.sheet', compute='_compute_sheet', string='Sheet', store=True)
project_id = fields.Many2one(default=_default_project)
employee_category = fields.Many2one(comodel_name='hr.employee.category', string="Employee Category")
time_start = fields.Float(string='Begin', help='Begin time when the employee start working. This value will be used to compute the total number of hours worked.')
time_stop = fields.Float(string='End', help='End time when the employee stop working. This value will be used to compute the total number of hours worked.')
time_break = fields.Float(string='Break', help='Break time taken by the employee. This value will be used to compute the total number of hours worked.')
@api.depends('date', 'user_id', 'project_id', 'project_sheet_id_computed.date_to', 'project_sheet_id_computed.date_from', 'project_sheet_id_computed.project_id')
def _compute_sheet(self):
"""Links the timesheet line to the corresponding sheet
"""
for ts_line in self:
if not ts_line.project_id:
continue
sheets = self.env['hr_timesheet_project_sheet.sheet'].search(
[('date_to', '>=', ts_line.date), ('date_from', '<=', ts_line.date),
('project_id.id', '=', ts_line.project_id.id),
('state', 'in', ['draft', 'new'])])
if sheets:
# [0] because only one sheet possible for a project between 2 dates
ts_line.project_sheet_id_computed = sheets[0]
ts_line.project_sheet_id = sheets[0]
def _search_sheet(self, operator, value):
assert operator == 'in'
ids = []
for ts in self.env['hr_timesheet_project_sheet.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
AND %(project_id)s = l.project_id
GROUP BY l.id""", {'date_from': ts.date_from,
'date_to': ts.date_to,
'project_id': ts.project_id.id, })
ids.extend([row[0] for row in self._cr.fetchall()])
return [('id', 'in', ids)]
@api.multi
def write(self, values):
self._check_state()
return super(AccountAnalyticLine, self).write(values)
@api.multi
def unlink(self):
self._check_state()
return super(AccountAnalyticLine, self).unlink()
def _check_state(self):
for line in self:
if line.project_sheet_id and line.project_sheet_id.state not in ('draft', 'new'):
raise UserError(_('You cannot modify an entry in a confirmed timesheet.'))
return True
@api.one
@api.constrains('time_start', 'time_stop', 'unit_amount')
def _check_time_start_stop(self):
start = timedelta(hours=self.time_start)
stop = timedelta(hours=self.time_stop)
tbreak = timedelta(hours=self.time_break)
if stop < start:
raise exceptions.ValidationError(
_('The beginning hour (%s) must '
'precede the ending hour (%s).') %
(float_time_convert(self.time_start),
float_time_convert(self.time_stop))
)
hours = (stop - start - tbreak).seconds / 3600
if (hours and
float_compare(hours, self.unit_amount, precision_digits=4)):
raise exceptions.ValidationError(
_('The duration (%s) must be equal to the difference '
'between the hours (%s).') %
(float_time_convert(self.unit_amount),
float_time_convert(hours))
)
# check if lines overlap
if self.user_id:
others = self.search([
('id', '!=', self.id),
('user_id', '=', self.user_id.id),
('date', '=', self.date),
('time_start', '<', self.time_stop),
('time_stop', '>', self.time_start),
])
if others:
message = _("Lines can't overlap:\n")
message += '\n'.join(['%s - %s' %
(float_time_convert(line.time_start),
float_time_convert(line.time_stop))
for line
in (self + others).sorted(
lambda l: l.time_start
)])
raise exceptions.ValidationError(message)
@api.onchange('time_start', 'time_stop', 'time_break')
def onchange_hours_start_stop(self):
start = timedelta(hours=self.time_start)
stop = timedelta(hours=self.time_stop)
tbreak = timedelta(hours=self.time_break)
if stop < start:
return
self.unit_amount = (stop - start - tbreak).seconds / 3600
#@api.onchange('employee_category')
#def onchange_place(self):
# res = {}
# if self.employee_category:
# res['domain'] = {'user_id': [('employee_ids.category_ids', 'in', self.employee_category)]}
# return res
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Patrik Dufresne
# Copyright 2017 Patrik Dufresne Service Logiciel inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from datetime import datetime
from odoo import api, fields, models
from odoo.tools.translate import _
from odoo.tools.sql import drop_view_if_exists
from odoo.exceptions import UserError, ValidationError
class HrTimesheetSheet(models.Model):
_name = "hr_timesheet_project_sheet.sheet"
_inherit = ['mail.thread', 'ir.needaction_mixin']
_table = 'hr_timesheet_project_sheet_sheet'
_order = "id desc"
_description = "Timesheet"
name = fields.Char(string="Event Name", states={'confirm': [('readonly', True)], 'done': [('readonly', True)]}, help="The event name, You may include reference to PO number in this field.")
project_id = fields.Many2one('project.project', string='Project', required=True, readonly=True, states={'new': [('readonly', False)]})
date_from = fields.Date(string='Date From', required=True,
index=True, readonly=True, states={'new': [('readonly', False)]})
date_to = fields.Date(string='Date To', required=True,
index=True, readonly=True, states={'new': [('readonly', False)]})
timesheet_ids = fields.One2many('account.analytic.line', 'project_sheet_id',
string='Timesheet lines',
readonly=True, states={
'draft': [('readonly', False)],
'new': [('readonly', False)]})
# state is created in 'new', automatically goes to 'draft' when created. Then 'new' is never used again ...
# (=> 'new' is completely useless)
state = fields.Selection([
('new', 'New'),
('draft', 'Open'),
('confirm', 'Waiting Approval'),
('done', 'Approved')], default='new', track_visibility='onchange',
string='Status', required=True, readonly=True, index=True,
help=' * The \'Open\' status is used when a user is encoding a new and unconfirmed timesheet. '
'\n* The \'Waiting Approval\' status is used to confirm the timesheet by user. '
'\n* The \'Approved\' status is used when the users timesheet is accepted by his/her senior.')
account_ids = fields.One2many('hr_timesheet_project_sheet.sheet.account', 'project_sheet_id', string='Analytic accounts', readonly=True)
company_id = fields.Many2one('res.company', string='Company')
user_id = fields.Many2one('res.users', string='Responsible', required=False, default=lambda self: self.env.user)
contact_id = fields.Many2one('res.partner', string='Remote Contact', store=True, readonly=False)
location = fields.Char(string="Location", help="Short description describing the location of the event.", store=True, readonly=False)
comment = fields.Text('Additional Information', readonly=True, states={'draft': [('readonly', False)], 'new': [('readonly', False)]})
sent = fields.Boolean(readonly=True, default=False, copy=False, help="It indicates that the timesheet has been sent.")
@api.constrains('date_to', 'date_from', 'project_id')
def _check_sheet_date(self, forced_project_id=False):
for sheet in self:
new_project_id = forced_project_id or sheet.project_id and sheet.project_id.id
if new_project_id:
self.env.cr.execute('''
SELECT id
FROM hr_timesheet_project_sheet_sheet
WHERE (date_from <= %s and %s <= date_to)
AND project_id=%s
AND id <> %s''',
(sheet.date_to, sheet.date_from, new_project_id, sheet.id))
if any(self.env.cr.fetchall()):
raise ValidationError(_('You cannot have 2 timesheets for the same project that overlap!'))
def copy(self, *args, **argv):
raise UserError(_('You cannot duplicate a timesheet.'))
@api.model
def create(self, vals):
res = super(HrTimesheetSheet, self).create(vals)
res.write({'state': 'draft'})
return res
@api.multi
def action_timesheet_draft(self):
if not self.env.user.has_group('hr_timesheet.group_hr_timesheet_user'):
raise UserError(_('Only an HR Officer or Manager can refuse timesheets or reset them to draft.'))
self.write({'state': 'draft'})
return True
@api.multi
def action_timesheet_confirm(self):
self.write({'state': 'confirm'})
return True
@api.multi
def action_timesheet_done(self):
if not self.env.user.has_group('hr_timesheet.group_hr_timesheet_user'):
raise UserError(_('Only an HR Officer or Manager can approve timesheets.'))
if self.filtered(lambda sheet: sheet.state != 'confirm'):
raise UserError(_("Cannot approve a non-submitted timesheet."))
self.write({'state': 'done'})
@api.multi
def timesheet_print(self):
""" Print the timesheet and mark it as sent, so that we can see more
easily the next step of the workflow
"""
self.ensure_one()
self.sent = True
return self.env['report'].get_action(self, 'hr_timesheet_project_sheet.report_project_timesheet')
@api.multi
def action_timesheet_sent(self):
""" Open a window to compose an email, with the edit invoice template
message loaded by default.
"""
self.ensure_one()
template = self.env.ref('hr_timesheet_project_sheet.email_template_timesheet', False)
compose_form = self.env.ref('mail.email_compose_message_wizard_form', False)
ctx = dict(
default_model='hr_timesheet_project_sheet.sheet',
default_res_id=self.id,
default_use_template=bool(template),
default_template_id=template and template.id or False,
default_composition_mode='comment',
)
return {
'name': _('Compose Email'),
'type': 'ir.actions.act_window',
'view_type': 'form',
'view_mode': 'form',
'res_model': 'mail.compose.message',
'views': [(compose_form.id, 'form')],
'view_id': compose_form.id,
'target': 'new',
'context': ctx,
}
@api.multi
def name_get(self):
# week number according to ISO 8601 Calendar
return [(r['id'], _('Week ') + str(datetime.strptime(r['date_from'], '%Y-%m-%d').isocalendar()[1]))
for r in self.read(['date_from'], load='_classic_write')]
@api.multi
def unlink(self):
sheets = self.read(['state'])
for sheet in sheets:
if sheet['state'] in ('confirm', 'done'):
raise UserError(_('You cannot delete a timesheet which is already confirmed.'))
analytic_timesheet_toremove = self.env['account.analytic.line']
for sheet in self:
analytic_timesheet_toremove += sheet.timesheet_ids.filtered(lambda t: not t.task_id)
analytic_timesheet_toremove.unlink()
return super(HrTimesheetSheet, self).unlink()
# ------------------------------------------------
# OpenChatter methods and notifications
# ------------------------------------------------
@api.multi
def _track_subtype(self, init_values):
if self:
record = self[0]
if 'state' in init_values and record.state == 'confirm':
return 'hr_timesheet_project_sheet.mt_timesheet_confirmed'
elif 'state' in init_values and record.state == 'done':
return 'hr_timesheet_project_sheet.mt_timesheet_approved'
return super(HrTimesheetSheet, self)._track_subtype(init_values)
#@api.model
#def _needaction_domain_get(self):
# empids = self.env['hr.employee'].search([('parent_id.user_id', '=', self.env.uid)])
# if not empids:
# return False
# return ['&', ('state', '=', 'confirm'), ('employee_id', 'in', empids.ids)]
class HrTimesheetSheetSheetAccount(models.Model):
_name = "hr_timesheet_project_sheet.sheet.account"
_description = "Timesheets by Period"
_auto = False
_order = 'name'
name = fields.Many2one('account.analytic.account', string='Project / Analytic Account', readonly=True)
project_sheet_id = fields.Many2one('hr_timesheet_project_sheet.sheet', string='Sheet', readonly=True)
total = fields.Float('Total Time', digits=(16, 2), readonly=True)
# still seing _depends in BaseModel, ok to leave this as is?
_depends = {
'account.analytic.line': ['account_id', 'date', 'unit_amount', 'project_id'],
'hr_timesheet_project_sheet.sheet': ['date_from', 'date_to', 'project_id'],
}
@api.model_cr
def init(self):
drop_view_if_exists(self._cr, 'hr_timesheet_project_sheet_sheet_account')
self._cr.execute("""create view hr_timesheet_project_sheet_sheet_account as (
select
min(l.id) as id,
l.account_id as name,
s.id as project_sheet_id,
sum(l.unit_amount) as total
from
account_analytic_line l
LEFT JOIN hr_timesheet_project_sheet_sheet s
ON (s.date_to >= l.date
AND s.date_from <= l.date
AND s.project_id = l.project_id)
group by l.account_id, s.id
)""")