Skip to content

Commit

Permalink
Merge branch '12.0' into 12.0-autostaging_base-porting
Browse files Browse the repository at this point in the history
  • Loading branch information
Ommo73 committed May 14, 2019
2 parents 7857dd5 + 58f3b3d commit de94a0d
Show file tree
Hide file tree
Showing 59 changed files with 890 additions and 288 deletions.
2 changes: 2 additions & 0 deletions README.md
@@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.com/it-projects-llc/misc-addons.svg?branch=12.0)](https://travis-ci.com/it-projects-llc/misc-addons)

Odoo addons
===========

Expand Down
4 changes: 2 additions & 2 deletions base_attendance/__manifest__.py
@@ -1,13 +1,13 @@
# Copyright (c) 2004-2015 Odoo S.A.
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# Copyright 2018-2019 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{
"name": """Partner Attendances""",
"summary": """Manage partner attendances""",
"category": "Extra Tools",
# "live_test_url": "",
"images": [],
"version": "12.0.1.0.0",
"version": "12.0.1.1.1",
"application": False,

"author": "IT-Projects LLC, Kolushov Alexandr",
Expand Down
19 changes: 19 additions & 0 deletions base_attendance/doc/changelog.rst
@@ -1,3 +1,22 @@
`1.1.1`
-------

- **Fix:** Broken pin pad in the **Kiosk Mode**
- **Fix:** Security issues for ``Attendance Manager`` group on opening the **Kiosk Mode**

`1.1.0`
-------

- **New:** Autocheckout option - closes shifts after defined time
- **New:** Option to support HEX barcode scanners in **Kiosk Mode**
- **Fix:** Error related to incorrect attribute name
- **Fix:** Error message even for correct partner PIN codes

`1.0.1`
-------

- **Fix:** Error on error message for incorrect attendance creation

`1.0.0`
-------

Expand Down
13 changes: 11 additions & 2 deletions base_attendance/doc/index.rst
Expand Up @@ -17,8 +17,9 @@ In order to set access rights for users

* ``Read-Only`` may see only *Attendances* menu
* ``Manual Attendance`` may create and update partner attendances, but not delete
* ``Officer`` may also delete partners attendances, has access to *Partners*, *Reports* menus and *Kiosk Mode*
* ``Manager`` like Officer, but also has access to *Configuration* menu
* ``Manager`` may also delete partners attendances, has access to *Partners*, *Reports* menus and *Kiosk Mode*

* In order to get access to ``Configuration`` menu user has to have **Administration** ``Settings`` rights

Barcode
-------
Expand Down Expand Up @@ -57,6 +58,14 @@ Also it is possible to use barcodes to check partners attendance
* Open menu ``[[ Partner Attendances ]] >> Kiosk Mode``
* Scan a partner's barcode to check him in or out, it depends on the actual partner's presence

Auto Checkout
-------------

Restriction on the maximum partner attendance time. Each ten minutes odoo checks for opened partner session, if a session lasts more then defined time it will be closed.

* Open menu ``[[ Partner Attendances ]] >> Configuration``
* Set ``AutoCheckout`` field in minutes.

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

Expand Down
19 changes: 17 additions & 2 deletions base_attendance/models/res_attendance.py
@@ -1,8 +1,12 @@
# Copyright (c) 2004-2015 Odoo S.A.
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# Copyright 2018-2019 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from datetime import datetime
from odoo import models, fields, api, exceptions, _
import logging

_logger = logging.getLogger(__name__)


class HrAttendance(models.Model):
Expand Down Expand Up @@ -95,7 +99,7 @@ def _check_validity(self):
], order='check_in desc', limit=1)
if last_attendance_before_check_out and last_attendance_before_check_in != last_attendance_before_check_out:
raise exceptions.ValidationError(_("Cannot create new attendance record for %(partner_name)s, the partner was already checked in on %(datetime)s") % {
'partner_name': attendance.partner.name,
'partner_name': attendance.partner_id.name,
'datetime': fields.Datetime.to_string(fields.Datetime.context_timestamp(self, fields.Datetime.from_string(last_attendance_before_check_out.check_in))),
})

Expand All @@ -105,3 +109,14 @@ def copy(self):
# [W8106(method-required-super), HrAttendance.copy] Missing `super` call in "copy" method.
super(HrAttendance, self).copy()
raise exceptions.UserError(_('You cannot duplicate an attendance.'))

@api.multi
def autocheckout_close_shifts(self):
max_interval = float(self.env["ir.config_parameter"].get_param("base_attendance.shift_autocheckout", default=0))
if max_interval:
shifts = self.search([('check_out', '=', False)]).filtered(
lambda x: (datetime.today() - x.check_in).total_seconds() / 3600 >= max_interval / 60)
_logger.info("partner session autologout for: %s, interval: %s", shifts.ids, max_interval)
shifts.write({
'check_out': fields.Datetime.now(),
})
40 changes: 38 additions & 2 deletions base_attendance/models/res_config.py
@@ -1,8 +1,8 @@
# Copyright (c) 2004-2015 Odoo S.A.
# Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# Copyright 2018-2019 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from odoo import fields, models
from odoo import fields, models, api


class BaseConfigSettings(models.TransientModel):
Expand All @@ -13,3 +13,39 @@ class BaseConfigSettings(models.TransientModel):
string='Partner PIN',
help='Enable or disable partner PIN identification at check in',
implied_group="base_attendance.group_hr_attendance_use_pin")
shift_autocheckout = fields.Integer('Autocheckout ', help="Maximum Shift Time in Minutes")
hex_scanner_is_used = fields.Boolean('HEX Scanner', default=False,
help='Some devices scan regular barcodes as hexadecimal. '
'This option decode those types of barcodes')

@api.multi
def set_values(self):
super(BaseConfigSettings, self).set_values()
config_parameters = self.env["ir.config_parameter"].sudo()
for record in self:
config_parameters.set_param("base_attendance.shift_autocheckout",
record.shift_autocheckout or '0')
config_parameters.set_param("base_attendance.hex_scanner_is_used", record.hex_scanner_is_used)
self.checkout_shifts()

@api.multi
def get_values(self):
res = super(BaseConfigSettings, self).get_values()
config_parameters = self.env["ir.config_parameter"].sudo()
res.update(
shift_autocheckout=int(config_parameters.get_param("base_attendance.shift_autocheckout", default=0)),
hex_scanner_is_used=config_parameters.get_param("base_attendance.hex_scanner_is_used", default=False),
)
return res

@api.model
def checkout_shifts(self):
cron_record = self.env.ref('base_attendance.base_attendance_autocheckout')
if self.shift_autocheckout == 0:
cron_record.write({
'active': False,
})
else:
cron_record.write({
'active': True,
})
2 changes: 1 addition & 1 deletion base_attendance/models/res_partner.py
Expand Up @@ -64,7 +64,7 @@ def attendance_scan(self, barcode):
def attendance_manual(self, next_action, entered_pin=None):
self.ensure_one()
if not (entered_pin is None) or \
self.env['res.users'].browse(SUPERUSER_ID).has_group('hr_partner_attendance.group_hr_attendance_use_pin'):
self.env['res.users'].browse(SUPERUSER_ID).has_group('base_attendance.group_hr_attendance_use_pin'):
if entered_pin != self.pin:
return {'warning': _('Wrong PIN')}
return self.attendance_action(next_action)
Expand Down
2 changes: 1 addition & 1 deletion base_attendance/security/ir.model.access.csv
@@ -1,5 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_attendance_readonly_attendance,res.partner.attendance.user,model_res_partner_attendance,base_attendance.group_res_attendance,1,0,0,0
access_hr_attendance_manual_attendance,res.partner.attendance.user,model_res_partner_attendance,base_attendance.group_manual_attendance,1,1,1,0
access_hr_attendance_officer,res.partner.attendance.user,model_res_partner_attendance,base_attendance.group_hr_attendance_user,1,1,1,1
access_hr_attendance_officer,res.partner.attendance.user,model_res_partner_attendance,base_attendance.group_hr_attendance_manager,1,1,1,1
access_hr_attendance_attendance,res.partner.attendance.user,model_res_partner_attendance,,0,0,0,0
12 changes: 3 additions & 9 deletions base_attendance/security/res_attendance_security.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2004-2015 Odoo S.A.
Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
Copyright 2018-2019 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).-->
<odoo>
<record model="ir.module.category" id="module_category_attendance">
Expand All @@ -22,16 +22,10 @@
<field name="comment">The user will gain access to manage partners attendance.</field>
</record>

<record id="group_hr_attendance_user" model="res.groups">
<field name="name">Officer</field>
<field name="category_id" ref="module_category_attendance"/>
<field name="implied_ids" eval="[(4, ref('group_manual_attendance'))]"/>
</record>

<record id="group_hr_attendance_manager" model="res.groups">
<field name="name">Manager</field>
<field name="category_id" ref="module_category_attendance"/>
<field name="implied_ids" eval="[(4, ref('base_attendance.group_hr_attendance_user'))]"/>
<field name="implied_ids" eval="[(4, ref('group_manual_attendance'))]"/>
<field name="users" eval="[(4, ref('base.user_root'))]"/>
</record>

Expand All @@ -51,7 +45,7 @@
<field name="name">attendance officer: full access</field>
<field name="model_id" ref="model_res_partner_attendance"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4,ref('base_attendance.group_hr_attendance_user'))]"/>
<field name="groups" eval="[(4,ref('base_attendance.group_hr_attendance_manager'))]"/>
</record>

</data>
Expand Down
2 changes: 1 addition & 1 deletion base_attendance/static/src/js/greeting_message.js
Expand Up @@ -28,7 +28,7 @@ var GreetingMessage = AbstractAction.extend({
// to the (likely) appropriate menu, according to the user access rights
if(!action.attendance) {
this.activeBarcode = false;
this.getSession().user_has_group('base_attendance.group_hr_attendance_user').then(function(has_group) {
this.getSession().user_has_group('base_attendance.group_hr_attendance_manager').then(function(has_group) {
if(has_group) {
self.next_action = 'base_attendance.hr_attendance_action_kiosk_mode';
} else {
Expand Down
4 changes: 2 additions & 2 deletions base_attendance/static/src/js/kiosk_confirm.js
Expand Up @@ -69,7 +69,7 @@ var KioskConfirm = AbstractAction.extend({
this._rpc({
model: 'res.partner',
method: 'attendance_manual',
args: [[this.partner], this.next_action, this.$('.o_hr_attendance_PINbox').val()],
args: [[self.partner_id], this.next_action, this.$('.o_hr_attendance_PINbox').val()],
}).then(function(result) {
if (result.action) {
self.do_action(result.action);
Expand Down Expand Up @@ -105,7 +105,7 @@ var KioskConfirm = AbstractAction.extend({
start_clock: function () {
this.clock_start = setInterval(function() {
this.$(".o_hr_attendance_clock").text(new Date().toLocaleTimeString(navigator.language, {hour: '2-digit', minute:'2-digit'}));
}, 500);
}, 900);
// First clock refresh before interval to avoid delay
this.$(".o_hr_attendance_clock").text(new Date().toLocaleTimeString(navigator.language, {hour: '2-digit', minute:'2-digit'}));
},
Expand Down
32 changes: 30 additions & 2 deletions base_attendance/static/src/js/kiosk_mode.js
Expand Up @@ -8,6 +8,7 @@ var AbstractAction = require('web.AbstractAction');
var ajax = require('web.ajax');
var core = require('web.core');
var Session = require('web.session');
var local_storage = require('web.local_storage');

var QWeb = core.qweb;

Expand All @@ -18,6 +19,26 @@ var KioskMode = AbstractAction.extend({
},
},

init: function (parent, action) {
action.target = 'fullscreen';
var hex_scanner_is_used = action.context.hex_scanner_is_used;
if (typeof hex_scanner_is_used === 'undefined') {
hex_scanner_is_used = this.get_from_storage('hex_scanner_is_used') || false;
} else {
this.save_locally('hex_scanner_is_used', Boolean(hex_scanner_is_used));
}
this.hex_scanner_is_used = hex_scanner_is_used;
this._super(parent, action);
},

save_locally: function(key, value) {
local_storage.setItem('est.' + key, JSON.stringify(value));
},

get_from_storage: function(key) {
return JSON.parse(local_storage.getItem('est.' + key));
},

start: function () {
var self = this;
core.bus.on('barcode_scanned', this, this._onBarcodeScanned);
Expand All @@ -39,10 +60,17 @@ var KioskMode = AbstractAction.extend({

_onBarcodeScanned: function(barcode) {
var self = this;
var res_barcode = barcode;
if (this.hex_scanner_is_used) {
res_barcode = parseInt(barcode, 16).toString();
if (res_barcode.length % 2) {
res_barcode = '0' + res_barcode;
}
}
this._rpc({
model: 'res.partner',
method: 'attendance_scan',
args: [barcode, ],
args: [res_barcode, ],
}).then(function (result) {
if (result.action) {
self.do_action(result.action);
Expand All @@ -55,7 +83,7 @@ var KioskMode = AbstractAction.extend({
start_clock: function() {
this.clock_start = setInterval(function() {
this.$(".o_hr_attendance_clock").text(new Date().toLocaleTimeString(navigator.language, {hour: '2-digit', minute:'2-digit'}));
}, 700);
}, 900);
// First clock refresh before interval to avoid delay
this.$(".o_hr_attendance_clock").text(new Date().toLocaleTimeString(navigator.language, {hour: '2-digit', minute:'2-digit'}));
},
Expand Down
1 change: 1 addition & 0 deletions base_attendance/static/src/scss/hr_attendance.scss
Expand Up @@ -102,6 +102,7 @@
position: relative;
background-color: #fff;
padding: 2em;
margin-top: 50px;

h1 {
margin: 0 0 2rem 0;
Expand Down
6 changes: 3 additions & 3 deletions base_attendance/static/src/xml/attendance.xml
Expand Up @@ -44,13 +44,13 @@
<h2>Please enter your PIN to check in</h2>
</t>
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<div class="col-8 offset-2">
<div class="row" >
<div class="col-xs-4 col-xs-offset-4"><input class="o_hr_attendance_PINbox" type="password" disabled="true"/></div>
<div class="col-4 offset-4"><input class="o_hr_attendance_PINbox" type="password" disabled="true"/></div>
</div>
<div class="row o_hr_attendance_pin_pad">
<t t-foreach="['1', '2', '3', '4', '5', '6', '7', '8', '9', 'C', '0', 'ok']" t-as="btn_name">
<div class="col-xs-4 o_hr_attendance_pin_pad_border">
<div class="col-4 o_hr_attendance_pin_pad_border">
<a t-attf-class="btn btn-primary btn-block btn-lg o_hr_attendance_btn-round-corners {{ 'o_hr_attendance_pin_pad_button_' + btn_name }}"><t t-esc="btn_name"/></a>
</div>
</t>
Expand Down
43 changes: 35 additions & 8 deletions base_attendance/views/res_attendance_view.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2004-2015 Odoo S.A.
Copyright 2018 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
Copyright 2018-2019 Kolushov Alexandr <https://it-projects.info/team/KolushovAlexandr>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).-->
<odoo>

Expand Down Expand Up @@ -124,10 +124,22 @@
<field name="view_id"></field> <!-- force empty -->
</record>

<record id="hr_attendance_action_kiosk_mode" model="ir.actions.client">
<field name="name">Attendances</field>
<field name="tag">base_attendance_kiosk_mode</field>
<field name="target">fullscreen</field>
<record model="ir.actions.server" id="hr_attendance_action_kiosk_mode">
<field name="name">Get to the Kiosk Mode</field>
<field name="type">ir.actions.server</field>
<field name="model_id" ref="model_res_partner_attendance"/>
<field name="state">code</field>
<field name="code">
hex_scanner_is_used = model.env["ir.config_parameter"].sudo().get_param("base_attendance.hex_scanner_is_used",default=False)
action = {
'type': 'ir.actions.client',
'tag': 'base_attendance_kiosk_mode',
'target': 'fullscreen',
'context': {
'hex_scanner_is_used': hex_scanner_is_used
},
}
</field>
</record>

<record id="hr_attendance_action_greeting_message" model="ir.actions.client">
Expand Down Expand Up @@ -261,9 +273,24 @@

<menuitem id="menu_hr_attendance_view_attendances" name="Attendances" parent="menu_hr_attendance_manage_attendances" sequence="10" groups="base_attendance.group_res_attendance" action="hr_attendance_action"/>

<menuitem id="menu_hr_attendance_view_partners_kanban" name="Partners" parent="menu_hr_attendance_manage_attendances" sequence="15" groups="base_attendance.group_hr_attendance_user" action="base.action_partner_form"/>
<menuitem id="menu_hr_attendance_view_partners_kanban" name="Partners" parent="menu_hr_attendance_manage_attendances" sequence="15" groups="base_attendance.group_hr_attendance_manager" action="base.action_partner_form"/>

<menuitem id="menu_hr_attendance_kiosk_mode" name="Kiosk Mode" parent="menu_hr_attendance_manage_attendances" sequence="20" groups="base_attendance.group_hr_attendance_manager" action="hr_attendance_action_kiosk_mode"/>

<menuitem id="menu_hr_attendance_kiosk_mode" name="Kiosk Mode" parent="menu_hr_attendance_manage_attendances" sequence="20" groups="base_attendance.group_hr_attendance_user" action="hr_attendance_action_kiosk_mode"/>
<menuitem id="menu_hr_attendance_report" name="Reports" parent="menu_base_attendance_root" sequence="30" groups="base_attendance.group_hr_attendance_manager" action="hr_attendance_action_graph"/>

<!--IR CRON-->

<record model="ir.cron" id="base_attendance_autocheckout">
<field name="name">Partner Shift Autocheckout</field>
<field name="active" eval="False"/>
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
<field name="model_id" ref="model_res_partner_attendance"/>
<field name="state">code</field>
<field name="code">model.autocheckout_close_shifts()</field>
</record>

<menuitem id="menu_hr_attendance_report" name="Reports" parent="menu_base_attendance_root" sequence="30" groups="base_attendance.group_hr_attendance_user" action="hr_attendance_action_graph"/>
</odoo>

0 comments on commit de94a0d

Please sign in to comment.