# -*- coding: utf-8 -*- import json from odoo.tests.common import HttpCase, tagged @tagged('-at_install', 'post_install', 'fusion_clock') class TestPinKioskIdentity(HttpCase): @classmethod def setUpClass(cls): super().setUpClass() cls.ICP = cls.env['ir.config_parameter'].sudo() cls.ICP.set_param('fusion_clock.enable_kiosk', 'True') cls.location = cls.env['fusion.clock.location'].create({ 'name': 'PIN Plant', 'latitude': 43.65, 'longitude': -79.38, 'radius': 100, }) cls.env.company.x_fclk_nfc_kiosk_location_id = cls.location.id cls.env['res.users'].create({ 'name': 'PIN Kiosk Op', 'login': 'pin-kiosk-op', 'password': 'kioskpass123', 'group_ids': [(4, cls.env.ref('fusion_clock.group_fusion_clock_manager').id)], }) cls.withpin = cls.env['hr.employee'].create({ 'name': 'Pat WithPin', 'x_fclk_enable_clock': True, 'x_fclk_kiosk_pin': '1234', }) cls.nopin = cls.env['hr.employee'].create({ 'name': 'Nora NoPin', 'x_fclk_enable_clock': True, }) def _call(self, route, params): self.authenticate('pin-kiosk-op', 'kioskpass123') resp = self.url_open(route, data=json.dumps({ 'jsonrpc': '2.0', 'method': 'call', 'params': params, }), headers={'Content-Type': 'application/json'}) return resp.json().get('result', {}) def test_search_returns_avatar_and_has_pin(self): res = self._call('/fusion_clock/kiosk/search', {'query': ''}) rows = {e['name']: e for e in res['employees']} self.assertIn('Pat WithPin', rows) self.assertTrue(rows['Pat WithPin']['has_pin']) self.assertFalse(rows['Nora NoPin']['has_pin']) self.assertIn('/web/image/hr.employee.public/', rows['Pat WithPin']['avatar_url']) def test_verify_pin_correct(self): res = self._call('/fusion_clock/kiosk/verify_pin', {'employee_id': self.withpin.id, 'pin': '1234'}) self.assertTrue(res.get('success')) def test_verify_pin_incorrect(self): res = self._call('/fusion_clock/kiosk/verify_pin', {'employee_id': self.withpin.id, 'pin': '9999'}) self.assertEqual(res.get('error'), 'invalid_pin') def test_verify_pin_needs_setup(self): res = self._call('/fusion_clock/kiosk/verify_pin', {'employee_id': self.nopin.id, 'pin': ''}) self.assertTrue(res.get('needs_setup')) def test_set_pin_success_then_required(self): res = self._call('/fusion_clock/kiosk/set_pin', {'employee_id': self.nopin.id, 'pin': '4321'}) self.assertTrue(res.get('success')) self.assertEqual(self.nopin.x_fclk_kiosk_pin, '4321') res2 = self._call('/fusion_clock/kiosk/set_pin', {'employee_id': self.nopin.id, 'pin': '0000'}) self.assertEqual(res2.get('error'), 'already_set') def test_set_pin_rejects_bad_format(self): res = self._call('/fusion_clock/kiosk/set_pin', {'employee_id': self.withpin.id, 'pin': '12'}) self.assertEqual(res.get('error'), 'bad_pin') @tagged('-at_install', 'post_install', 'fusion_clock') class TestPinKioskClock(HttpCase): PNG = ('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwC' 'AAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=') @classmethod def setUpClass(cls): super().setUpClass() cls.ICP = cls.env['ir.config_parameter'].sudo() cls.ICP.set_param('fusion_clock.enable_kiosk', 'True') cls.location = cls.env['fusion.clock.location'].create({ 'name': 'PIN Plant 2', 'latitude': 43.65, 'longitude': -79.38, 'radius': 100, }) cls.env.company.x_fclk_nfc_kiosk_location_id = cls.location.id cls.env['res.users'].create({ 'name': 'PIN Op2', 'login': 'pin-op2', 'password': 'kioskpass123', 'group_ids': [(4, cls.env.ref('fusion_clock.group_fusion_clock_manager').id)], }) cls.emp = cls.env['hr.employee'].create({ 'name': 'Quinn Clock', 'x_fclk_enable_clock': True, 'x_fclk_kiosk_pin': '1234', }) def _clock(self, employee_id, photo_b64=''): self.authenticate('pin-op2', 'kioskpass123') resp = self.url_open('/fusion_clock/kiosk/clock', data=json.dumps({ 'jsonrpc': '2.0', 'method': 'call', 'params': {'employee_id': employee_id, 'photo_b64': photo_b64}, }), headers={'Content-Type': 'application/json'}) return resp.json().get('result', {}) def _latest(self, emp_id): return self.env['hr.attendance'].search( [('employee_id', '=', emp_id)], order='check_in desc', limit=1) def test_clock_in_uses_kiosk_location(self): res = self._clock(self.emp.id) self.assertTrue(res.get('success')) self.assertEqual(res.get('action'), 'clock_in') att = self._latest(self.emp.id) self.assertEqual(att.x_fclk_clock_source, 'kiosk') self.assertEqual(att.x_fclk_location_id, self.location) def test_photo_off_stores_nothing(self): self.ICP.set_param('fusion_clock.enable_photo_verification', 'False') emp = self.env['hr.employee'].create({ 'name': 'Quinn Off', 'x_fclk_enable_clock': True, 'x_fclk_kiosk_pin': '1234'}) self._clock(emp.id, self.PNG) self.assertFalse(self._latest(emp.id).x_fclk_check_in_photo) def test_photo_on_stores_selfie(self): self.ICP.set_param('fusion_clock.enable_photo_verification', 'True') emp = self.env['hr.employee'].create({ 'name': 'Quinn On', 'x_fclk_enable_clock': True, 'x_fclk_kiosk_pin': '1234'}) self._clock(emp.id, self.PNG) self.assertTrue(self._latest(emp.id).x_fclk_check_in_photo) def test_no_location_configured(self): self.env.company.x_fclk_nfc_kiosk_location_id = False emp = self.env['hr.employee'].create({ 'name': 'Quinn NoLoc', 'x_fclk_enable_clock': True, 'x_fclk_kiosk_pin': '1234'}) res = self._clock(emp.id) self.assertEqual(res.get('error'), 'no_location_configured')