diff --git a/hr_attendance_work_entry/__manifest__.py b/hr_attendance_work_entry/__manifest__.py index 97d1d7ab..61ab485f 100755 --- a/hr_attendance_work_entry/__manifest__.py +++ b/hr_attendance_work_entry/__manifest__.py @@ -6,12 +6,22 @@ 'author': 'Hibou Corp. ', 'license': 'AGPL-3', 'category': 'Human Resources', - 'data': [ - 'data/hr_attendance_work_entry_data.xml', - ], 'depends': [ 'hr_attendance', 'hr_work_entry', ], + 'data': [ + 'data/hr_attendance_work_entry_data.xml', + 'views/attendance_views.xml', + 'views/employee_views.xml', + 'views/web_assets.xml', + 'views/work_entry_views.xml', + ], + 'demo': [ + 'data/hr_attendance_work_entry_demo.xml', + ], + 'qweb': [ + 'static/src/xml/hr_attendance.xml', + ], 'pre_init_hook': 'attn_payroll_pre_init_hook', } diff --git a/hr_attendance_work_entry/data/hr_attendance_work_entry_demo.xml b/hr_attendance_work_entry/data/hr_attendance_work_entry_demo.xml new file mode 100644 index 00000000..e88356b2 --- /dev/null +++ b/hr_attendance_work_entry/data/hr_attendance_work_entry_demo.xml @@ -0,0 +1,20 @@ + + + + + Break + ATTN_BREAK + + break + fa-hourglass-1 + + + + Lunch + ATTN_LUNCH + + lunch + fa-coffee + + + \ No newline at end of file diff --git a/hr_attendance_work_entry/models/attendance.py b/hr_attendance_work_entry/models/attendance.py index 273b1367..cd0d35b5 100644 --- a/hr_attendance_work_entry/models/attendance.py +++ b/hr_attendance_work_entry/models/attendance.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import api, fields, models class HrAttendance(models.Model): @@ -7,3 +7,8 @@ class HrAttendance(models.Model): work_type_id = fields.Many2one('hr.work.entry.type', string='Work Type', default=lambda self: self.env.ref('hr_attendance_work_entry.work_input_attendance', raise_if_not_found=False)) + + @api.model + def gather_attendance_work_types(self): + work_types = self.env['hr.work.entry.type'].sudo().search([('allow_attendance', '=', True)]) + return work_types.read(['id', 'name', 'attendance_icon']) diff --git a/hr_attendance_work_entry/models/employee.py b/hr_attendance_work_entry/models/employee.py index d9caffc4..49edc1d6 100644 --- a/hr_attendance_work_entry/models/employee.py +++ b/hr_attendance_work_entry/models/employee.py @@ -5,7 +5,7 @@ from odoo.exceptions import UserError class HrEmployee(models.Model): _inherit = 'hr.employee' - attendance_state = fields.Selection(selection_add=[('break', 'Break')]) + attendance_state = fields.Selection(selection_add=[('break', 'Break'), ('lunch', 'Lunch')]) @api.depends('last_attendance_id.work_type_id') def _compute_attendance_state(self): @@ -20,6 +20,9 @@ class HrEmployee(models.Model): def attendance_manual(self, next_action, entered_pin=None, work_type_id=None): self = self.with_context(work_type_id=work_type_id) + if not entered_pin: + # fix for pin mode with specific argument order for work_type_id + entered_pin = None return super(HrEmployee, self).attendance_manual(next_action, entered_pin=entered_pin) def _attendance_action_change(self): diff --git a/hr_attendance_work_entry/models/work_entry.py b/hr_attendance_work_entry/models/work_entry.py index b4088b0e..73618223 100644 --- a/hr_attendance_work_entry/models/work_entry.py +++ b/hr_attendance_work_entry/models/work_entry.py @@ -3,6 +3,7 @@ from odoo import fields, models class HrWorkEntryType(models.Model): _inherit = 'hr.work.entry.type' + _order = 'sequence, id' allow_attendance = fields.Boolean(string='Allow in Attendance') attendance_icon = fields.Char(string='Attendance Icon', default='fa-sign-in') @@ -10,4 +11,5 @@ class HrWorkEntryType(models.Model): # ('checked_out', "Checked out"), # reserved for detecting new punch in ('checked_in', "Checked in"), ('break', 'Break'), + ('lunch', 'Lunch'), ], string='Attendance State', default='checked_in') diff --git a/hr_attendance_work_entry/static/src/js/kiosk_confirm.js b/hr_attendance_work_entry/static/src/js/kiosk_confirm.js new file mode 100644 index 00000000..8919f0e3 --- /dev/null +++ b/hr_attendance_work_entry/static/src/js/kiosk_confirm.js @@ -0,0 +1,71 @@ +odoo.define('hr_attendance_work_entry.kiosk_confirm', function (require) { +"use strict"; + +var core = require('web.core'); +var KioskConfirm = require('hr_attendance.kiosk_confirm'); +var KioskConfirmTyped = KioskConfirm.extend({ + events: _.extend({}, KioskConfirm.prototype.events, { + "click .o_hr_attendance_punch_type": _.debounce(function(e) { + var work_entry_type = $(e.target).data('work-entry-type'); + this.update_attendance(work_entry_type); + }, 200, true), + "click .o_hr_attendance_pin_pad_button_work": _.debounce(function(e) { + var work_entry_type = $(e.target).data('work-entry-type'); + this.update_attendance_pin(work_entry_type); + }, 200, true), + }), + willStart: function () { + var self = this; + + var def = this._rpc({ + model: 'hr.attendance', + method: 'gather_attendance_work_types', + args: []}) + .then(function (res) { + self.work_types = res; + }); + + return Promise.all([def, this._super.apply(this, arguments)]); + }, + update_attendance: function (type) { + var self = this; + this._rpc({ + model: 'hr.employee', + method: 'attendance_manual', + args: [[self.employee_id], this.next_action, false, type], + }) + .then(function(result) { + if (result.action) { + self.do_action(result.action); + } else if (result.warning) { + self.do_warn(result.warning); + } + }); + }, + update_attendance_pin: function (type) { + var self = this; + this.$('.o_hr_attendance_pin_pad_button_ok').attr("disabled", "disabled"); + this.$('.o_hr_attendance_pin_pad_button_work').attr("disabled", "disabled"); + this._rpc({ + model: 'hr.employee', + method: 'attendance_manual', + args: [[this.employee_id], this.next_action, this.$('.o_hr_attendance_PINbox').val(), type], + }) + .then(function(result) { + if (result.action) { + self.do_action(result.action); + } else if (result.warning) { + self.do_warn(result.warning); + self.$('.o_hr_attendance_PINbox').val(''); + setTimeout( function() { + self.$('.o_hr_attendance_pin_pad_button_ok').removeAttr("disabled"); + self.$('.o_hr_attendance_pin_pad_button_work').removeAttr("disabled"); + }, 500); + } + }); + }, +}); + +core.action_registry.add('hr_attendance_kiosk_confirm', KioskConfirmTyped); +return KioskConfirmTyped; +}); \ No newline at end of file diff --git a/hr_attendance_work_entry/static/src/js/my_attendances.js b/hr_attendance_work_entry/static/src/js/my_attendances.js new file mode 100644 index 00000000..d9dcf7ac --- /dev/null +++ b/hr_attendance_work_entry/static/src/js/my_attendances.js @@ -0,0 +1,50 @@ +odoo.define('hr_attendance_work_entry.my_attendances', function (require) { +"use strict"; + +var core = require('web.core'); +var MyAttendances = require('hr_attendance.my_attendances'); + +var MyTypedAttendances = MyAttendances.extend({ + events: _.extend({}, MyAttendances.prototype.events, { + "click .o_hr_attendance_punch_type": _.debounce(function(e) { + var work_entry_type = $(e.target).data('work-entry-type'); + this.update_attendance(work_entry_type); + }, 200, true), + }), + + willStart: function () { + var self = this; + + var def = this._rpc({ + model: 'hr.attendance', + method: 'gather_attendance_work_types', + args: []}) + .then(function (res) { + self.work_types = res; + }); + + return Promise.all([def, this._super.apply(this, arguments)]); + }, + + update_attendance: function (type) { + var self = this; + this._rpc({ + model: 'hr.employee', + method: 'attendance_manual', + args: [[self.employee.id], 'hr_attendance.hr_attendance_action_my_attendances', false, type], + }) + .then(function(result) { + if (result.action) { + self.do_action(result.action); + } else if (result.warning) { + self.do_warn(result.warning); + } + }); + }, +}); + +core.action_registry.add('hr_attendance_my_attendances', MyTypedAttendances); + +return MyTypedAttendances; + +}); \ No newline at end of file diff --git a/hr_attendance_work_entry/static/src/scss/hr_attendances.scss b/hr_attendance_work_entry/static/src/scss/hr_attendances.scss new file mode 100644 index 00000000..308412de --- /dev/null +++ b/hr_attendance_work_entry/static/src/scss/hr_attendances.scss @@ -0,0 +1,31 @@ +.o_hr_attendance_sign_in_out_icon { + cursor: pointer; + border-radius: .1em; + box-shadow: inset 0 -3px 0 fade-out(black, 0.7); + + &.btn-secondary:hover { + color: $o-brand-primary; + } +} + +#oe_hr_attendance_status { + color: $o-brand-secondary; + + &.oe_hr_attendance_status_blue { + color: theme-color('info'); + } + + &.oe_hr_attendance_status_orange { + color: theme-color('warning'); + } +} + +.o_hr_attendance_kiosk_mode p.o_hr_attendance_continue { + margin-bottom: 0; + text-align: center; + font-weight: bold; +} + +.o_hr_attendance_pin_pad_button_work { + font-size: 0.9em; +} diff --git a/hr_attendance_work_entry/static/src/xml/hr_attendance.xml b/hr_attendance_work_entry/static/src/xml/hr_attendance.xml new file mode 100644 index 00000000..e356c106 --- /dev/null +++ b/hr_attendance_work_entry/static/src/xml/hr_attendance.xml @@ -0,0 +1,134 @@ + + + + + +
+ + + +
+ +
+

+

Welcome! + Want to check out?

+

Today's work hours:

+
+
+ +
+ + + +

+ Or, continue working as: +

+ + + + + + + +
+ +
+
+
+ + Warning : Your user should be linked to an employee to use attendance. Please contact your administrator. + +
+ +
+
+ + + +