[IMP] hr_attendance

- remove wizards 'hr.sign.in.out.ask' and 'hr.sign.in.out' for simply Attendance action.
- code clean of 'attendance_action_change' of hr.employee.
- code clean of AttendanceNotifier widget and also overrides methods of DataSet widget to update AttendanceNotifier widget base on employee attendance.

bzr revid: hmo@tinyerp.com-20120511054711-uah7h2af9bbx7nch
This commit is contained in:
Harry (OpenERP) 2012-05-11 11:17:11 +05:30
parent 1fd242f47d
commit d6df3fb47f
15 changed files with 84 additions and 367 deletions

View File

@ -44,7 +44,6 @@ actions(Sign in/Sign out) performed by them.
'wizard/hr_attendance_bymonth_view.xml',
'wizard/hr_attendance_byweek_view.xml',
'wizard/hr_attendance_error_view.xml',
'wizard/hr_attendance_sign_in_out_view.xml',
],
'demo_xml': ['hr_attendance_demo.xml'],
'test': [
@ -57,7 +56,6 @@ actions(Sign in/Sign out) performed by them.
#web
"js": ["static/src/js/attendance.js"],
"css":["static/src/css/attendance.css"],
'qweb' : ["static/src/xml/attendance.xml"],
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -122,30 +122,25 @@ class hr_employee(osv.osv):
res = cr.fetchone()
return not (res and (res[0]>=(dt or time.strftime('%Y-%m-%d %H:%M:%S'))))
def attendance_action_change(self, cr, uid, ids, type='action', context=None, dt=False, *args):
obj_attendance = self.pool.get('hr.attendance')
id = False
warning_sign = 'sign'
res = {}
def attendance_action_change(self, cr, uid, ids, context=None):
if context is None:
context = {}
action_date = context.get('action_date', False)
action = context.get('action', False)
hr_attendance = self.pool.get('hr.attendance')
warning_sign = {'sign_in': _('Sign In'), 'sign_out': _('Sign Out')}
for employee in self.browse(cr, uid, ids, context=context):
if not action:
if employee.state == 'present': action = 'sign_out'
if employee.state == 'absent': action = 'sign_in'
#Special case when button calls this method: type=context
if isinstance(type, dict):
type = type.get('type','action')
if type == 'sign_in':
warning_sign = "Sign In"
elif type == 'sign_out':
warning_sign = "Sign Out"
for emp in self.read(cr, uid, ids, ['id'], context=context):
if not self._action_check(cr, uid, emp['id'], dt, context):
raise osv.except_osv(_('Warning'), _('You tried to %s with a date anterior to another event !\nTry to contact the administrator to correct attendances.')%(warning_sign,))
if not self._action_check(cr, uid, employee.id, action_date, context):
raise osv.except_osv(_('Warning'), _('You tried to %s with a date anterior to another event !\nTry to contact the HR Manager to correct attendances.')%(warning_sign[action],))
res = {'action': type, 'employee_id': emp['id']}
if dt:
res['name'] = dt
id = obj_attendance.create(cr, uid, res, context=context)
if type != 'action':
return id
vals = {'action': action, 'employee_id': employee.id}
if action_date:
vals['name'] = action_date
hr_attendance.create(cr, uid, vals, context=context)
return True
hr_employee()

View File

@ -130,8 +130,8 @@
<field name="coach_id" position="after">
<group colspan="2">
<field name="state"/>
<button name="%(action_hr_attendance_sigh_in_out)d" states="present" type="action" string="Sign Out" icon="gtk-go-forward" context="{'type':'sign_out'}" groups="base.group_hr_user"/>
<button name="%(action_hr_attendance_sigh_in_out)d" states="absent" type="action" string="Sign In" icon="gtk-go-back" context="{'type':'sign_in'}" groups="base.group_hr_user"/>
<button name="attendance_action_change" states="present" type="object" string="Sign Out" icon="gtk-go-forward" groups="base.group_hr_manager"/>
<button name="attendance_action_change" states="absent" type="object" string="Sign In" icon="gtk-go-back" groups="base.group_hr_manager"/>
</group>
</field>
</field>
@ -139,3 +139,5 @@
</data>
</openerp>

View File

@ -1,4 +0,0 @@
.oe_attendance_button {
cursor:pointer;
}

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

View File

@ -1,71 +1,87 @@
openerp.hr_attendance = function(instance) {
var QWeb = instance.web.qweb;
instance.hr_attendance.attendancestatus = false,
instance.hr_attendance.checkstatus = function(){
attendance = instance.hr_attendance.currentstatus;
if (!attendance){
attendance = new instance.hr_attendance.AttendanceNotifier(self);
instance.hr_attendance.currentstatus = attendance;
}
attendance.renderElement();
},
instance.hr_attendance.callback = function(callback){
var old_callback = callback;
callback = function(result){
if(old_callback){old_callback(result);}
instance.hr_attendance.checkstatus();
}
return callback;
},
instance.hr_attendance.AttendanceNotifier = instance.web.Widget.extend({
template: 'AttendanceNotifier',
renderElement: function() {
var self = this;
if (this.attendance_status == 'present'){
action_type = 'sign_out';
}
else{
action_type = 'sign_in';
}
this.$element = $(QWeb.render(this.template, {'action':action_type}));
employee = new instance.web.DataSetSearch(this, 'hr.employee', this.session.user_context, [['user_id','=', this.session.uid]]);
employee.read_slice(['id','name','state']).done(function(employee) {
if(_.isEmpty(employee)) return;
self.employee = employee[0];
self.do_update_notifier();
});
},
do_update_notifier: function(){
var self = this;
this.$element = $(QWeb.render(this.template, {'employee':this.employee}));
this.$element.click(self.on_click);
element = $('.oe_attendance_button')
if (element.length != 0){
element.attr('src', this.$element.attr('src'));
element.replaceWith(this.$element);
}
else{
this.$element.appendTo($('.oe_systray'));
this.$element.click(self.on_click);
}
},
on_click: function() {
var self = this;
action = new instance.web.DataSetSearch(this, 'ir.actions.act_window', {}, [['res_model', '=', 'hr.sign.in.out']]);
action.read_slice().done(function(action) {
action = action[0];
action.context = JSON.parse(action.context);
var action_manager = new instance.web.ActionManager(self);
action_manager.do_action(action);
});
hr_employee = new instance.web.DataSet(self, 'hr.employee');
hr_employee.call('attendance_action_change', [[self.employee.id]]).done(function(result){self.renderElement()});
},
});
instance.hr_attendance.AttendanceStatus = instance.web.Class.extend({
init: function(parent){
this.session = parent.session;
attendance = new instance.hr_attendance.AttendanceNotifier(self);
employee = new instance.web.DataSetSearch(this, 'hr.employee', this.session.user_context, [['user_id','=', this.session.uid]]);
employee.read_slice(['state']).done(function(employee) {
if(_.isEmpty(employee)) return;
attendance.attendance_status = employee[0]['state'];
attendance.renderElement();
});
},
});
instance.web.ActionManager.include({
do_action: function(action, on_close) {
var self = this;
if (action.res_model == "hr.sign.in.out"){
var old_close = on_close;
on_close = function(){
if(old_close){old_close();}
new instance.hr_attendance.AttendanceStatus(self);
}
instance.web.DataSet.include({
create: function(data, callback, error_callback) {
if (this._model.name == "hr.attendance"){
callback = instance.hr_attendance.callback(callback);
}
this._super(action, on_close);
},
return this._super(data, callback, error_callback);
},
write: function (id, data, options, callback, error_callback) {
if (this._model.name == "hr.attendance"){
callback = instance.hr_attendance.callback(callback);
}
return this._super(id, data, options, callback, error_callback);
},
unlink: function(ids, callback, error_callback) {
if (this._model.name == "hr.attendance"){
callback = instance.hr_attendance.callback(callback);
}
return this._super(ids, callback, error_callback);
},
call_button: function (method, args, callback, error_callback) {
if (this._model.name == "hr.employee" && method == "attendance_action_change"){
callback = instance.hr_attendance.callback(callback);
}
return this._super(method, args, callback, error_callback);
},
});
instance.web.UserMenu.include({
do_update: function() {
var self = this;
this._super();
this.update_promise.then(function() {
new instance.hr_attendance.AttendanceStatus(self);
instance.hr_attendance.checkstatus();
});
},
});

View File

@ -1,11 +1,7 @@
<template>
<t t-name="AttendanceNotifier">
<t t-if="action=='sign_in'">
<img src="/hr_attendance/static/src/img/emp-in.png" class="oe_attendance_button"></img>
</t>
<t t-if="action=='sign_out'">
<img src="/hr_attendance/static/src/img/emp-out.png" class="oe_attendance_button"></img>
</t>
<a href="#" class="oe_attendance_button" t-attf-title="You are #{employee.state}">
<img t-attf-src="/hr_attendance/static/src/img/emp-#{employee.state}.png" ></img>
</a>
</t>
</template>

View File

@ -19,9 +19,8 @@
#
##############################################################################
import hr_attendance_sign_in_out
import hr_attendance_error
import hr_attendance_byweek
import hr_attendance_bymonth
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,191 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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 time
from osv import osv, fields
from tools.translate import _
class hr_si_so_ask(osv.osv_memory):
_name = 'hr.sign.in.out.ask'
_description = 'Ask for Sign In Out'
_columns = {
'name': fields.char('Employees name', size=32, required=True, readonly=True),
'last_time': fields.datetime('Your last sign out', required=True),
'emp_id': fields.many2one('hr.employee', 'Empoyee ID', readonly=True),
}
def _get_empname(self, cr, uid, context=None):
emp_id = context.get('emp_id', self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context))
if emp_id:
employee = self.pool.get('hr.employee').browse(cr, uid, emp_id, context=context)[0].name
return employee
return ''
def _get_empid(self, cr, uid, context=None):
emp_id = context.get('emp_id', self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context))
if emp_id:
return emp_id[0]
return False
_defaults = {
'name': _get_empname,
'emp_id': _get_empid,
}
def sign_in(self, cr, uid, ids, context=None):
data = self.read(cr, uid, ids, [], context=context)[0]
data['emp_id'] = data['emp_id'] and data['emp_id'][0]
return self.pool.get('hr.sign.in.out').sign_in(cr, uid, data, context)
def sign_out(self, cr, uid, ids, context=None):
data = self.read(cr, uid, ids, [], context=context)[0]
data['emp_id'] = data['emp_id'] and data['emp_id'][0]
return self.pool.get('hr.sign.in.out').sign_out(cr, uid, data, context)
hr_si_so_ask()
class hr_sign_in_out(osv.osv_memory):
_name = 'hr.sign.in.out'
_description = 'Sign In Sign Out'
_columns = {
'name': fields.char('Employees name', size=32, required=True, readonly=True),
'state': fields.char('Current state', size=32, required=True, readonly=True),
'emp_id': fields.many2one('hr.employee', 'Empoyee ID', readonly=True),
}
def _get_empid(self, cr, uid, context=None):
emp_id = context.get('emp_id', self.pool.get('hr.employee').search(cr, uid, [('user_id', '=', uid)], context=context))
if emp_id:
employee = self.pool.get('hr.employee').browse(cr, uid, emp_id, context=context)[0]
return {'name': employee.name, 'state': employee.state, 'emp_id': emp_id[0]}
return {}
def default_get(self, cr, uid, fields_list, context=None):
res = super(hr_sign_in_out, self).default_get(cr, uid, fields_list, context=context)
res_emp = self._get_empid(cr, uid, context=context)
act_obj=self.pool.get('ir.actions.act_window')
action_id = act_obj.search(cr, uid, [('res_model','=','hr.sign.in.out')],context=context)
action = act_obj.browse(cr, uid, action_id[0], context=context)
if res_emp['state']=='present':
name = act_obj.write(cr, uid, action_id[0], {'name':'Sign Out'}, context=context)
if res_emp['state']=='absent':
name = act_obj.write(cr, uid, action_id[0], {'name':'Sign In'}, context=context)
res.update(res_emp)
return res
def si_check(self, cr, uid, ids, context=None):
obj_model = self.pool.get('ir.model.data')
att_obj = self.pool.get('hr.attendance')
data = self.read(cr, uid, ids, [], context=context)[0]
data['emp_id'] = data['emp_id'] and data['emp_id'][0]
emp_id = data['emp_id']
att_id = att_obj.search(cr, uid, [('employee_id', '=', emp_id)], limit=1, order='name desc')
last_att = att_obj.browse(cr, uid, att_id, context=context)
if last_att:
last_att = last_att[0]
cond = not last_att or last_att.action == 'sign_out'
if cond:
return self.sign_in(cr, uid, data, context)
else:
model_data_ids = obj_model.search(cr,uid,[('model','=','ir.ui.view'),('name','=','view_hr_attendance_so_ask')], context=context)
resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
return {
'name': _('Sign in '),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'hr.sign.in.out.ask',
'views': [(resource_id,'form')],
'type': 'ir.actions.act_window',
'context': context,
'target': 'new',
}
def so_check(self, cr, uid, ids, context=None):
obj_model = self.pool.get('ir.model.data')
att_obj = self.pool.get('hr.attendance')
data = self.read(cr, uid, ids, [], context=context)[0]
data['emp_id'] = data['emp_id'] and data['emp_id'][0]
emp_id = data['emp_id']
att_id = att_obj.search(cr, uid, [('employee_id', '=', emp_id),('action', '!=', 'action')], limit=1, order='name desc')
last_att = att_obj.browse(cr, uid, att_id, context=context)
if last_att:
last_att = last_att[0]
if not att_id and not last_att:
model_data_ids = obj_model.search(cr, uid, [('model','=','ir.ui.view'),('name','=','view_hr_attendance_message')], context=context)
resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
return {
'name': _('Sign in / Sign out'),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'hr.sign.in.out',
'views': [(resource_id,'form')],
'type': 'ir.actions.act_window',
'context': context,
'target': 'new',
}
cond = last_att and last_att['action'] == 'sign_in'
if cond:
return self.sign_out(cr, uid, data, context)
else:
model_data_ids = obj_model.search(cr, uid, [('model','=','ir.ui.view'),('name','=','view_hr_attendance_si_ask')], context=context)
resource_id = obj_model.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
return {
'name': _('Sign in / Sign out'),
'view_type': 'form',
'view_mode': 'tree,form',
'res_model': 'hr.sign.in.out.ask',
'views': [(resource_id,'form')],
'type': 'ir.actions.act_window',
'target': 'new',
}
def sign_in(self, cr, uid, data, context=None):
if context is None:
context = {}
emp_id = data['emp_id']
if 'last_time' in data:
if data['last_time'] > time.strftime('%Y-%m-%d %H:%M:%S'):
raise osv.except_osv(_('UserError'), _('The sign-out date must be in the past'))
self.pool.get('hr.attendance').create(cr, uid, {'name': data['last_time'], 'action': 'sign_out',
'employee_id': emp_id}, context=context)
try:
self.pool.get('hr.employee').attendance_action_change(cr, uid, [emp_id], 'sign_in')
except:
raise osv.except_osv(_('UserError'), _('A sign-in must be right after a sign-out !'))
return {'type': 'ir.actions.act_window_close'} # To do: Return Success message
def sign_out(self, cr, uid, data, context=None):
emp_id = data['emp_id']
if 'last_time' in data:
if data['last_time'] > time.strftime('%Y-%m-%d %H:%M:%S'):
raise osv.except_osv(_('UserError'), _('The Sign-in date must be in the past'))
self.pool.get('hr.attendance').create(cr, uid, {'name':data['last_time'], 'action':'sign_in', 'employee_id':emp_id}, context=context)
try:
self.pool.get('hr.employee').attendance_action_change(cr, uid, [emp_id], 'sign_out')
except:
raise osv.except_osv(_('UserError'), _('A sign-out must be right after a sign-in !'))
return {'type': 'ir.actions.act_window_close'} # To do: Return Success message
hr_sign_in_out()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,92 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_hr_attendance_sigh_in_out" model="ir.ui.view">
<field name="name">hr.sign.in.out.form</field>
<field name="model">hr.sign.in.out</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Sign in / Sign out">
<label colspan="4" nolabel="1" string="If you need your staff to sign in when they arrive at work and sign out again at the end of the day, OpenERP allows you to manage this with this tool. If each employee has been linked to a system user, then they can encode their time with this action button."/>
<newline/>
<group colspan="4" col="6">
<field name="name" />
<field name="state" />
</group>
<separator colspan="4"/>
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="terp-gtk-jump-to-ltr" string="Sign in" name="si_check" type="object" attrs="{'invisible':[('state','=','present')]}"/>
<button icon="terp-gtk-jump-to-rtl" string="Sign out" name="so_check" type="object" attrs="{'invisible':[('state','=','absent')]}"/>
</group>
</form>
</field>
</record>
<record id="view_hr_attendance_message" model="ir.ui.view">
<field name="name">hr.sign.in.out.form</field>
<field name="model">hr.sign.in.out</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Sign in / Sign out">
<separator string="Sign-Out Entry must follow Sign-In." colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Ok"/>
</group>
</form>
</field>
</record>
<record id="action_hr_attendance_sigh_in_out" model="ir.actions.act_window">
<field name="name">Sign In / Sign Out</field>
<field name="res_model">hr.sign.in.out</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="view_hr_attendance_sigh_in_out"/>
<field name="target">new</field>
<field name="help">Sign in / Sign out. In some companies, staff have to sign in when they arrive at work and sign out again at the end of the day. If each employee has been linked to a system user, then they can encode their time with this action button.</field>
</record>
<record id="view_hr_attendance_so_ask" model="ir.ui.view">
<field name="name">hr.sign.in.out.ask.form</field>
<field name="model">hr.sign.in.out.ask</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="hr.sign.out.ask">
<group colspan="4" >
<separator string="You did not sign out the last time. Please enter the date and time you signed out." colspan="4" />
<field name="name" />
<field name="last_time" string="Your last sign out" />
</group>
<separator colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-go-back" string="Sign in" name="sign_in" type="object"/>
</group>
</form>
</field>
</record>
<record id="view_hr_attendance_si_ask" model="ir.ui.view">
<field name="name">hr.sign.in.out.ask.form</field>
<field name="model">hr.sign.in.out.ask</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="hr.sign.in.out.ask">
<group colspan="4" >
<separator string="You did not sign in the last time. Please enter the date and time you signed in." colspan="4" />
<field name="name" />
<field name="last_time" string="Your last sign in" />
</group>
<separator colspan="4" />
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-go-back" string="Sign out" name="sign_out" type="object"/>
</group>
</form>
</field>
</record>
</data>
</openerp>

View File

@ -91,8 +91,6 @@
</field>
<group col="4" colspan="1">
<field name="state_attendance"/>
<button name="%(hr_attendance.action_hr_attendance_sigh_in_out)d" type="action" string="Sign In" attrs="{'invisible':[('state_attendance','=','present')]}" icon="terp-gtk-jump-to-ltr" />
<button name="%(hr_attendance.action_hr_attendance_sigh_in_out)d" type="action" string="Sign Out" attrs="{'invisible':[('state_attendance','=','absent')]}" icon="terp-gtk-jump-to-rtl" />
<field name="total_attendance_day" widget="float_time" colspan="4"/>
</group>
<field colspan="4" context="{'date':date_current,'user_id':user_id}" domain="[('name','=',date_current)]" name="timesheet_ids" nolabel="1">