├── images ├── scanner_screen.png ├── scanner_hardware.png └── scanner_scenario.png ├── hardware ├── i18n │ ├── en │ │ └── LC_MESSAGES │ │ │ └── sentinel.mo │ ├── fr │ │ └── LC_MESSAGES │ │ │ └── sentinel.mo │ ├── translate.sh │ └── sentinel │ │ ├── sentinel.pot │ │ ├── en.po │ │ └── fr.po └── sentinel.py ├── tests ├── __init__.py └── test_stock_scanner.py ├── wizard ├── __init__.py ├── stock_scanner_config_wizard.py └── stock_scanner_config_wizard_view.xml ├── demo ├── Tutorial │ ├── Step_types │ │ ├── test_file │ │ ├── scanner_scenario_step_step_types_final.py │ │ ├── scanner_scenario_step_step_types_confirm.py │ │ ├── scanner_scenario_step_step_types_list.py │ │ ├── scanner_scenario_step_step_types_number_integer.py │ │ ├── scanner_scenario_step_step_types_quantity_float.py │ │ ├── scanner_scenario_step_step_types_message.py │ │ ├── scanner_scenario_step_step_types_text_input.py │ │ ├── scanner_scenario_step_step_types_error.py │ │ ├── scanner_scenario_step_step_types_introduction.py │ │ └── Step_types.scenario │ ├── Tutorial.scenario │ └── Sentinel │ │ ├── scanner_scenario_step_sentinel_going_back.py │ │ ├── scanner_scenario_step_sentinel_list_scrolling.py │ │ ├── scanner_scenario_step_sentinel_introduction.py │ │ ├── scanner_scenario_step_sentinel_scrolling.py │ │ ├── scanner_scenario_step_sentinel_return_values.py │ │ └── Sentinel.scenario └── stock_scanner_demo.xml ├── __init__.py ├── data ├── stock_scanner.xml ├── scenarios │ ├── Login │ │ ├── stock_scanner.scanner_scenario_login_step_login.py │ │ ├── stock_scanner.scanner_scenario_login_step_error.py │ │ ├── stock_scanner.scanner_scenario_login_step_pwd.py │ │ ├── stock_scanner.scanner_scenario_login_step_done.py │ │ └── Login.scenario │ └── Logout │ │ ├── stock_scanner.scanner_scenario_logout_step_confirm.py │ │ ├── stock_scanner.scanner_scenario_logout_step_confirmed.py │ │ ├── stock_scanner.scanner_scenario_logout_step_cancelled.py │ │ └── Logout.scenario └── ir_cron.xml ├── models ├── __init__.py ├── common.py ├── scanner_hardware_step_history.py ├── scanner_scenario_step.py ├── scanner_scenario_transition.py ├── scanner_scenario.py ├── scanner_scenario_custom.py └── scanner_hardware.py ├── __openerp__.py ├── views ├── menu.xml ├── scanner_scenario_transition.xml ├── scanner_scenario_custom.xml ├── scanner_hardware.xml ├── scanner_scenario_step.xml └── scanner_scenario.xml ├── security ├── ir.model.access.csv └── stock_scanner_security.xml ├── load_scenario.py ├── scripts └── export_scenario.py └── README.rst /images/scanner_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subteno-it/stock_scanner/HEAD/images/scanner_screen.png -------------------------------------------------------------------------------- /images/scanner_hardware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subteno-it/stock_scanner/HEAD/images/scanner_hardware.png -------------------------------------------------------------------------------- /images/scanner_scenario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subteno-it/stock_scanner/HEAD/images/scanner_scenario.png -------------------------------------------------------------------------------- /hardware/i18n/en/LC_MESSAGES/sentinel.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subteno-it/stock_scanner/HEAD/hardware/i18n/en/LC_MESSAGES/sentinel.mo -------------------------------------------------------------------------------- /hardware/i18n/fr/LC_MESSAGES/sentinel.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subteno-it/stock_scanner/HEAD/hardware/i18n/fr/LC_MESSAGES/sentinel.mo -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2015 Laurent Mignon 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 4 | 5 | from . import test_stock_scanner 6 | -------------------------------------------------------------------------------- /wizard/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2015 Laurent Mignon 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 4 | 5 | from . import stock_scanner_config_wizard 6 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/test_file: -------------------------------------------------------------------------------- 1 | sample terminal code 2 | :KEY_DOWN 3 | :KEY_UP 4 | 5 | :KEY_UP 6 | :KEY_DOWN 7 | 8 | 9 | 10 | 11 | 12 | :KEY_DOWN 13 | 14 | :KEY_LEFT 15 | 16 | 24 17 | 1.5 18 | sample text 19 | 20 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2015 Sylvain Garancher 3 | # © 2015 Damien CRIER 4 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 5 | 6 | from . import load_scenario 7 | from . import models 8 | from . import wizard 9 | -------------------------------------------------------------------------------- /data/stock_scanner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | hardware_scanner_session_timeout 6 | 1800 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2011 Sylvain Garancher 3 | # © 2011 Damien CRIER 4 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 5 | 6 | from . import scanner_scenario 7 | from . import scanner_scenario_step 8 | from . import scanner_scenario_transition 9 | from . import scanner_scenario_custom 10 | from . import scanner_hardware 11 | from . import scanner_hardware_step_history 12 | -------------------------------------------------------------------------------- /data/scenarios/Login/stock_scanner.scanner_scenario_login_step_login.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 'Use or to retrieve the data transmitted by the scanner.' 3 | 'Use or to retrieve the running terminal browse record.' 4 | 'Put the returned action code in , as a single character.' 5 | 'Put the returned result or message in , as a list of strings.' 6 | 'Put the returned value in , as an integer' 7 | 8 | act = 'T' 9 | res = [ 10 | _('Login ?'), 11 | ] 12 | -------------------------------------------------------------------------------- /demo/Tutorial/Tutorial.scenario: -------------------------------------------------------------------------------- 1 | 2 | 3 | scanner_scenario_tutorial 4 | 5 | Tutorial 6 | 7 | 0 8 | 9 | False 10 | OpenERP S.A. 11 | Shop 1 12 | Shop 2 13 | True 14 | menu 15 | 16 | -------------------------------------------------------------------------------- /data/scenarios/Login/stock_scanner.scanner_scenario_login_step_error.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 'Use or to retrieve the data transmitted by the scanner.' 3 | 'Use or to retrieve the running terminal browse record.' 4 | 'Put the returned action code in , as a single character.' 5 | 'Put the returned result or message in , as a list of strings.' 6 | 'Put the returned value in , as an integer' 7 | 8 | act = 'E' 9 | res = [ 10 | _('Wrong login/password'), 11 | ] 12 | val = True 13 | -------------------------------------------------------------------------------- /data/scenarios/Logout/stock_scanner.scanner_scenario_logout_step_confirm.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 'Use or to retrieve the data transmitted by the scanner.' 3 | 'Use or to retrieve the running terminal browse record.' 4 | 'Put the returned action code in , as a single character.' 5 | 'Put the returned result or message in , as a list of strings.' 6 | 'Put the returned value in , as an integer' 7 | 8 | act = 'C' 9 | res = [ 10 | '', 11 | _('Do you really want to logout?'), 12 | ] 13 | -------------------------------------------------------------------------------- /data/scenarios/Logout/stock_scanner.scanner_scenario_logout_step_confirmed.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 'Use or to retrieve the data transmitted by the scanner.' 3 | 'Use or to retrieve the running terminal browse record.' 4 | 'Put the returned action code in , as a single character.' 5 | 'Put the returned result or message in , as a list of strings.' 6 | 'Put the returned value in , as an integer' 7 | 8 | terminal.logout() 9 | 10 | act = 'F' 11 | res = [ 12 | _('You are now logged out'), 13 | _('Bye !'), 14 | ] 15 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/scanner_scenario_step_step_types_final.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'F' 9 | res = [ 10 | _('|Final step'), 11 | '', 12 | _('After this step, the scenario is finished.'), 13 | ] 14 | -------------------------------------------------------------------------------- /models/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2011 Sylvain Garancher 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 4 | 5 | PYTHON_CODE_DEFAULT = """ 6 | 'Use or to retrieve the data transmitted by the scanner.' 7 | 'Use or to retrieve the running terminal browse record.' 8 | 'Put the returned action code in , as a single character.' 9 | 'Put the returned result or message in , as a list of strings.' 10 | 'Put the returned value in , as an integer' 11 | """ 12 | -------------------------------------------------------------------------------- /data/scenarios/Login/stock_scanner.scanner_scenario_login_step_pwd.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 'Use or to retrieve the data transmitted by the scanner.' 3 | 'Use or to retrieve the running terminal browse record.' 4 | 'Put the returned action code in , as a single character.' 5 | 'Put the returned result or message in , as a list of strings.' 6 | 'Put the returned value in , as an integer' 7 | 8 | terminal.write({'tmp_val1': message}) 9 | 10 | act = 'T' 11 | res = [ 12 | _('| Login %s') % message, 13 | _('Pwd ?'), 14 | ] 15 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/scanner_scenario_step_step_types_confirm.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'C' 9 | res = [ 10 | _('|Confirm step'), 11 | '', 12 | _('This step waits for a confirmation from the user.'), 13 | ] 14 | -------------------------------------------------------------------------------- /data/scenarios/Logout/stock_scanner.scanner_scenario_logout_step_cancelled.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 'Use or to retrieve the data transmitted by the scanner.' 3 | 'Use or to retrieve the running terminal browse record.' 4 | 'Put the returned action code in , as a single character.' 5 | 'Put the returned result or message in , as a list of strings.' 6 | 'Put the returned value in , as an integer' 7 | 8 | act = 'F' 9 | res = [ 10 | _('Logout cancelled'), 11 | _('You are authenticated as %s !') % terminal.user_id.name, 12 | ] 13 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/scanner_scenario_step_step_types_list.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'L' 9 | res = [ 10 | ('|', _('List step')), 11 | ('error', _('Go to Error step')), 12 | ('continue', _('Go to next step')), 13 | ] 14 | -------------------------------------------------------------------------------- /data/scenarios/Login/stock_scanner.scanner_scenario_login_step_done.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 'Use or to retrieve the data transmitted by the scanner.' 3 | 'Use or to retrieve the running terminal browse record.' 4 | 'Put the returned action code in , as a single character.' 5 | 'Put the returned result or message in , as a list of strings.' 6 | 'Put the returned value in , as an integer' 7 | 8 | terminal.login(terminal.tmp_val1, message) 9 | 10 | act = 'F' 11 | res = [ 12 | _('You are now authenticated as %s !') % terminal.tmp_val1, 13 | ] 14 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/scanner_scenario_step_step_types_number_integer.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'N' 9 | res = [ 10 | _('|Number (integer) step'), 11 | '', 12 | _('This step allows the user to send integer numbers.'), 13 | ] 14 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/scanner_scenario_step_step_types_quantity_float.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'Q' 9 | res = [ 10 | _('|Quantity (float) step'), 11 | '', 12 | _('This step allows the user to send float quantities.'), 13 | ] 14 | -------------------------------------------------------------------------------- /demo/Tutorial/Sentinel/scanner_scenario_step_sentinel_going_back.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'M' 9 | res = [ 10 | _('Going back'), 11 | '', 12 | _('By pressing the escape key, you can return back to the last \'Return step\'.'), 13 | ] 14 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/scanner_scenario_step_step_types_message.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'M' 9 | res = [ 10 | _('|Message step'), 11 | '', 12 | _('A step designed to display some information, without waiting for any user input.'), 13 | ] 14 | -------------------------------------------------------------------------------- /demo/Tutorial/Sentinel/scanner_scenario_step_sentinel_list_scrolling.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'L' 9 | res = [ 10 | ('|', _('List Scrolling')), 11 | ('', _('Long texts in a list allows the user to scroll horizontally with left/right arrow keys')), 12 | ] 13 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/scanner_scenario_step_step_types_text_input.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'T' 9 | res = [ 10 | _('|Text input step'), 11 | '', 12 | _('This step allows the user to enter custom text.'), 13 | '', 14 | _('You will also use this step for barcode scanning.'), 15 | ] 16 | -------------------------------------------------------------------------------- /demo/Tutorial/Sentinel/scanner_scenario_step_sentinel_introduction.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'M' 9 | res = [ 10 | _('Introduction'), 11 | '', 12 | _('This scenario will explain some features of the sentinel ncurses client.'), 13 | '', 14 | _('This client is specific to the stock_scanner module.'), 15 | ] 16 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/scanner_scenario_step_step_types_error.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'E' 9 | res = [ 10 | _('|Error step'), 11 | '', 12 | _('This step shows an error message, using the error colors defined in the hardware configuration.'), 13 | '', 14 | _('After this step, the scenario automatically goes back if the returned value is set to True.'), 15 | ] 16 | val = True 17 | -------------------------------------------------------------------------------- /data/ir_cron.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Remove the current user_id and scenario values on session timeout 6 | 1 7 | minutes 8 | 2014-02-10 21:00:00 9 | -1 10 | 11 | 12 | scanner.hardware 13 | timeout_session 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/stock_scanner_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | sentinel 7 | sentinel 8 | sentinel 9 | 10 | 11 | 12 | 13 | 14 | 15 | Hardware 1 16 | hard1 17 | 40 18 | 20 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/scanner_scenario_step_step_types_introduction.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'M' 9 | res = [ 10 | _('|Introduction'), 11 | '', 12 | _('Welcome on the stock_scanner module.'), 13 | '', 14 | _('This scenario will explain all step types.'), 15 | # '', 16 | # _('All step types allow scrolling, if the displayed text doesn\'t fit on the screen.'), 17 | # _('To scroll, simply use the arrow keys.'), 18 | # '', 19 | # _('For \'List\' steps, the scrolling is horizontal, because the vertical moves are used to choose the value.'), 20 | # _('For all other types of steps, the scrolling is vertical.'), 21 | ] 22 | -------------------------------------------------------------------------------- /models/scanner_hardware_step_history.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2015 Sylvain Garancher 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 4 | 5 | from openerp import models, fields 6 | 7 | 8 | class ScannerHardwareStepHistory(models.Model): 9 | _name = 'scanner.hardware.step.history' 10 | _description = 'Steps History of Scanner Hardware' 11 | _order = 'id' 12 | 13 | hardware_id = fields.Many2one( 14 | comodel_name='scanner.hardware', string='Hardware', 15 | required=True, help='Hardware linked to this history line.') 16 | step_id = fields.Many2one( 17 | comodel_name='scanner.scenario.step', string='Step', 18 | help='Step executed during this history line.') 19 | transition_id = fields.Many2one( 20 | comodel_name='scanner.scenario.transition', string='Transition', 21 | help='Transition executed during this history line.') 22 | message = fields.Char( 23 | string='Message', 24 | help='Message sent during execution of the step.') 25 | -------------------------------------------------------------------------------- /hardware/i18n/translate.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | function display_usage() { 4 | echo 'Usage :' 5 | echo 'translate.sh extract' 6 | echo 'translate.sh init [LANG]' 7 | echo 'translate.sh compile [LANG]' 8 | exit 1 9 | } 10 | 11 | if [ $# -lt 1 ]; then 12 | display_usage 13 | fi 14 | 15 | case $1 in 16 | extract) 17 | # Extract translatable strings from python files 18 | mkdir -p sentinel 19 | xgettext --language=Python --keyword=_ --output=sentinel/sentinel.pot --from-code=UTF-8 --package-name=sentinel --package-version=1.0 ../sentinel.py 20 | ;; 21 | init) 22 | if [ $# -lt 2 ]; then 23 | display_usage 24 | fi 25 | # Initialize translation file 26 | mkdir -p sentinel 27 | msginit --input=sentinel/sentinel.pot --output=sentinel/$2.po --locale=$2 28 | ;; 29 | update) 30 | if [ $# -lt 2 ]; then 31 | display_usage 32 | fi 33 | # Initialize translation file 34 | mkdir -p sentinel 35 | msgmerge --previous sentinel/$2.po sentinel/sentinel.pot --output=sentinel/$2.po 36 | ;; 37 | compile) 38 | if [ $# -lt 2 ]; then 39 | display_usage 40 | fi 41 | # Compile translated .po file in a .mo file 42 | mkdir -p $2/LC_MESSAGES 43 | msgfmt sentinel/$2.po --output-file $2/LC_MESSAGES/sentinel.mo 44 | ;; 45 | *) 46 | display_usage 47 | esac 48 | -------------------------------------------------------------------------------- /demo/Tutorial/Sentinel/scanner_scenario_step_sentinel_scrolling.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'M' 9 | res = [ 10 | _('Scrolling'), 11 | '', 12 | _('All step types allow the user to scroll if the text doesn\'t fit on screen.'), 13 | '', 14 | _('Except the list steps, the user can scroll using the up/down arrow keys.'), 15 | '', 16 | _('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam nibh lorem, volutpat sit amet faucibus ut, adipiscing vitae lacus. Vestibulum hendrerit, sapien a porta posuere, augue ipsum mattis neque, sit amet sagittis felis lectus ut risus. Sed aliquam felis ut orci adipiscing auctor. Donec malesuada lorem eu nunc vestibulum elementum. Quisque orci quam, lobortis at mattis sit amet, gravida semper quam. Curabitur lobortis, eros ut porttitor rutrum, erat sem tincidunt augue, sed accumsan felis magna at purus. Sed laoreet consectetur viverra. Aliquam a odio sapien. Suspendisse potenti. Donec dignissim sagittis lacus, a auctor leo ultricies vel. Pellentesque eleifend porta nulla elementum faucibus.'), 17 | ] 18 | -------------------------------------------------------------------------------- /wizard/stock_scanner_config_wizard.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2015 Laurent Mignon 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 4 | 5 | from openerp import models, api, fields 6 | 7 | ACTIVABLE_XML_IDS = [ 8 | 'stock_scanner.hardware_reset_user_id_on_timeout', 9 | 'stock_scanner.scanner_scenario_login', 10 | 'stock_scanner.scanner_scenario_logout', 11 | ] 12 | 13 | 14 | class StockConfig(models.TransientModel): 15 | """Add options to configure login/logout on scanner""" 16 | _inherit = 'stock.config.settings' 17 | 18 | is_login_enabled = fields.Boolean('Login/logout scenarii enabled') 19 | session_timeout_delay = fields.Integer('Session validity in seconds') 20 | 21 | @api.multi 22 | def get_default_scanner_config(self, fields): 23 | is_login_enabled = self.env.ref(ACTIVABLE_XML_IDS[0]).active 24 | session_timeout_delay = self.env.ref( 25 | 'stock_scanner.hardware_scanner_session_timeout_sec').value 26 | return {'is_login_enabled': is_login_enabled, 27 | 'session_timeout_delay': int(session_timeout_delay)} 28 | 29 | @api.multi 30 | def set_default_scanner_config(self): 31 | for xml_id in ACTIVABLE_XML_IDS: 32 | self.env.ref(xml_id).active = self.is_login_enabled 33 | 34 | self.env['ir.config_parameter'].set_param( 35 | 'hardware_scanner_session_timeout', self.session_timeout_delay) 36 | -------------------------------------------------------------------------------- /hardware/i18n/sentinel/sentinel.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: sentinel 1.0\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2015-11-09 16:30+0100\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: ../sentinel.py:143 21 | msgid "" 22 | "Autoconfiguration failed !\n" 23 | "Please enter terminal code" 24 | msgstr "" 25 | 26 | #: ../sentinel.py:458 27 | msgid "No value available" 28 | msgstr "" 29 | 30 | #: ../sentinel.py:512 31 | msgid "" 32 | "An error occured\n" 33 | "\n" 34 | "Please contact your administrator" 35 | msgstr "" 36 | 37 | #: ../sentinel.py:546 38 | msgid "No scenario available !" 39 | msgstr "" 40 | 41 | #: ../sentinel.py:549 42 | msgid "Scenarios" 43 | msgstr "" 44 | 45 | #: ../sentinel.py:574 46 | msgid "Yes" 47 | msgstr "" 48 | 49 | #: ../sentinel.py:577 50 | msgid "No" 51 | msgstr "" 52 | 53 | #: ../sentinel.py:680 54 | #, python-format 55 | msgid "Selected : %s" 56 | msgstr "" 57 | 58 | #: ../sentinel.py:859 59 | #, python-format 60 | msgid "Selected : %d" 61 | msgstr "" 62 | -------------------------------------------------------------------------------- /data/scenarios/Logout/Logout.scenario: -------------------------------------------------------------------------------- 1 | 2 | 3 | base.group_user 4 | Logout 5 | 9999 6 | 7 | 8 | False 9 | False 10 | scanner.hardware 11 | scenario 12 | stock_scanner.scanner_scenario_logout 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /demo/Tutorial/Sentinel/scanner_scenario_step_sentinel_return_values.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | # Use or to retrieve the data transmitted by the scanner. 3 | # Use or to retrieve the running terminal browse record. 4 | # Put the returned action code in , as a single character. 5 | # Put the returned result or message in , as a list of strings. 6 | # Put the returned value in , as an integer 7 | 8 | act = 'F' 9 | res = [ 10 | _('Return values of a step'), 11 | '', 12 | _('All steps must return three values :'), 13 | _('- act : Type of step'), 14 | _('- res : Content to display'), 15 | _('- val : Default value'), 16 | '', 17 | _('The \'act\' variable must be a single character string.'), 18 | '', 19 | _('The \'res\' variable can contain multiple values :'), 20 | _('- A list of strings (all step types). Each string corresponds to a line of text to display. The long lines will be splitted automatically to fit on screen.'), 21 | _('- A list of tuples of two strings (list steps). The first value of each tuple is the returned value, the second value is the displayed value.'), 22 | _('- A dict of strings (list step). The key is the returned value, the second value is the displayed value.'), 23 | '', 24 | _('The \'val\' variable can contain multiple values :'), 25 | _('- A single value (all step types). The type of the value depends on the type of step (float, integer, boolean...).'), 26 | _('- A dict (text input step). The key \'default\' is the default value, the key \'size\' is the maximum size before automatic validation.'), 27 | ] 28 | -------------------------------------------------------------------------------- /wizard/stock_scanner_config_wizard_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | stock.config.settings.form 6 | stock.config.settings 7 | 8 | 9 | 10 | 11 | 12 | 13 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /__openerp__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2011-2015 Sylvain Garancher 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 4 | 5 | { 6 | 'name': 'Stock Scanner', 7 | 'summary': 'Allows managing barcode readers with simple scenarios', 8 | 'version': '8.0.1.0.0', 9 | 'category': 'Generic Modules/Inventory Control', 10 | 'website': 'https://odoo-community.org/', 11 | 'author': 'SYLEAM,' 12 | 'ACSONE SA/NV,' 13 | 'Odoo Community Association (OCA)', 14 | 'license': 'AGPL-3', 15 | 'application': True, 16 | 'installable': True, 17 | 'depends': [ 18 | 'product', 19 | 'stock', 20 | ], 21 | 'data': [ 22 | 'security/stock_scanner_security.xml', 23 | 'security/ir.model.access.csv', 24 | 'data/stock_scanner.xml', 25 | 'data/ir_cron.xml', 26 | 'data/scenarios/Login/Login.scenario', 27 | 'data/scenarios/Logout/Logout.scenario', 28 | 'wizard/stock_scanner_config_wizard_view.xml', 29 | 'views/menu.xml', 30 | 'views/scanner_scenario.xml', 31 | 'views/scanner_scenario_step.xml', 32 | 'views/scanner_scenario_transition.xml', 33 | 'views/scanner_scenario_custom.xml', 34 | 'views/scanner_hardware.xml', 35 | ], 36 | 'demo': [ 37 | 'demo/stock_scanner_demo.xml', 38 | 'demo/Tutorial/Tutorial.scenario', 39 | 'demo/Tutorial/Step_types/Step_types.scenario', 40 | 'demo/Tutorial/Sentinel/Sentinel.scenario', 41 | ], 42 | 'images': [ 43 | 'images/scanner_hardware.png', 44 | 'images/scanner_scenario.png', 45 | 'images/scanner_screen.png', 46 | ], 47 | } 48 | -------------------------------------------------------------------------------- /views/menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ############################################################################## 5 | # 6 | # stock_scanner module for OpenERP, Allows managing barcode readers with simple scenarios 7 | # Copyright (C) 2011 SYLEAM Info Services ([http://www.Syleam.fr/]) 8 | # Sylvain Garancher [sylvain.garancher@syleam.fr] 9 | # Copyright (C) 2015 Objectif-PI ([http://www.objectif-pi.com]). 10 | # Damien CRIER [damien.crier@objectif-pi.com] 11 | # 12 | # This file is a part of stock_scanner 13 | # 14 | # stock_scanner is free software: you can redistribute it and/or modify 15 | # it under the terms of the GNU Affero General Public License as published by 16 | # the Free Software Foundation, either version 3 of the License, or 17 | # (at your option) any later version. 18 | # 19 | # stock_scanner is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU Affero General Public License for more details. 23 | # 24 | # You should have received a copy of the GNU Affero General Public License 25 | # along with this program. If not, see [http://www.gnu.org/licenses/]. 26 | # 27 | ############################################################################## 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /hardware/i18n/sentinel/en.po: -------------------------------------------------------------------------------- 1 | # English translations for sentinel package. 2 | # Copyright (C) 2012 THE sentinel'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the sentinel package. 4 | # Sylvain Garancher , 2012. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: sentinel 1.0\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2015-11-09 16:30+0100\n" 11 | "PO-Revision-Date: 2012-05-15 18:03+0100\n" 12 | "Last-Translator: Sylvain Garancher \n" 13 | "Language-Team: English\n" 14 | "Language: en\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 19 | 20 | #: ../sentinel.py:143 21 | msgid "" 22 | "Autoconfiguration failed !\n" 23 | "Please enter terminal code" 24 | msgstr "" 25 | "Autoconfiguration failed !\n" 26 | "Please enter terminal code" 27 | 28 | #: ../sentinel.py:458 29 | msgid "No value available" 30 | msgstr "No value available" 31 | 32 | #: ../sentinel.py:512 33 | msgid "" 34 | "An error occured\n" 35 | "\n" 36 | "Please contact your administrator" 37 | msgstr "" 38 | "An error occured\n" 39 | "\n" 40 | "Please contact your administrator" 41 | 42 | #: ../sentinel.py:546 43 | msgid "No scenario available !" 44 | msgstr "No scenario available !" 45 | 46 | #: ../sentinel.py:549 47 | msgid "Scenarios" 48 | msgstr "Scenarios" 49 | 50 | #: ../sentinel.py:574 51 | msgid "Yes" 52 | msgstr "Yes" 53 | 54 | #: ../sentinel.py:577 55 | msgid "No" 56 | msgstr "No" 57 | 58 | #: ../sentinel.py:680 59 | #, python-format 60 | msgid "Selected : %s" 61 | msgstr "Selected : %s" 62 | 63 | #: ../sentinel.py:859 64 | #, python-format 65 | msgid "Selected : %d" 66 | msgstr "Selected : %d" 67 | -------------------------------------------------------------------------------- /hardware/i18n/sentinel/fr.po: -------------------------------------------------------------------------------- 1 | # French translations for sentinel package. 2 | # Copyright (C) 2012 THE sentinel'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the sentinel package. 4 | # Sylvain Garancher , 2012. 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: sentinel 1.0\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2015-11-09 16:30+0100\n" 11 | "PO-Revision-Date: 2012-05-15 18:03+0100\n" 12 | "Last-Translator: Sylvain Garancher \n" 13 | "Language-Team: French\n" 14 | "Language: fr\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 19 | 20 | #: ../sentinel.py:143 21 | msgid "" 22 | "Autoconfiguration failed !\n" 23 | "Please enter terminal code" 24 | msgstr "" 25 | "Echec d'autoconfiguration !\n" 26 | "Saisir le code du terminal" 27 | 28 | #: ../sentinel.py:458 29 | msgid "No value available" 30 | msgstr "Pas de valeur disponible" 31 | 32 | #: ../sentinel.py:512 33 | msgid "" 34 | "An error occured\n" 35 | "\n" 36 | "Please contact your administrator" 37 | msgstr "" 38 | "Une erreur est survenue\n" 39 | "\n" 40 | "Merci de contacter votre administrateur" 41 | 42 | #: ../sentinel.py:546 43 | msgid "No scenario available !" 44 | msgstr "Pas de scénario disponible !" 45 | 46 | #: ../sentinel.py:549 47 | msgid "Scenarios" 48 | msgstr "Scénarios" 49 | 50 | #: ../sentinel.py:574 51 | msgid "Yes" 52 | msgstr "Oui" 53 | 54 | #: ../sentinel.py:577 55 | msgid "No" 56 | msgstr "Non" 57 | 58 | #: ../sentinel.py:680 59 | #, python-format 60 | msgid "Selected : %s" 61 | msgstr "Sélection : %s" 62 | 63 | #: ../sentinel.py:859 64 | #, python-format 65 | msgid "Selected : %d" 66 | msgstr "Sélection : %d" 67 | -------------------------------------------------------------------------------- /security/ir.model.access.csv: -------------------------------------------------------------------------------- 1 | "id","name","model_id/id","group_id/id","perm_read","perm_write","perm_create","perm_unlink" 2 | "scanner_hardware_user","scanner_hardware_user","stock_scanner.model_scanner_hardware",,1,1,, 3 | "scanner_hardware_sentinel","scanner_hardware_sentinel","stock_scanner.model_scanner_hardware","group_stock_scanner_sentinel",1,1,, 4 | "scanner_hardware_manager","scanner_hardware_manager","stock_scanner.model_scanner_hardware","stock.group_stock_manager",1,1,1,1 5 | "scanner_hardware_step_history_user","scanner_hardware_step_history_user","stock_scanner.model_scanner_hardware_step_history",,1,1,1,1 6 | "scanner_hardware_step_history_sentinel","scanner_hardware_step_history_sentinel","stock_scanner.model_scanner_hardware_step_history","group_stock_scanner_sentinel",1,1,1,1 7 | "scanner_hardware_step_history_manager","scanner_hardware_step_history_manager","stock_scanner.model_scanner_hardware_step_history","stock.group_stock_manager",1,1,1,1 8 | "scanner_scenario_user","scanner_scenario_user","stock_scanner.model_scanner_scenario",,1,,, 9 | "scanner_scenario_manager","scanner_scenario_manager","stock_scanner.model_scanner_scenario","stock.group_stock_manager",1,1,1,1 10 | "scanner_scenario_step_user","scanner_scenario_step_user","stock_scanner.model_scanner_scenario_step",,1,,, 11 | "scanner_scenario_step_manager","scanner_scenario_step_manager","stock_scanner.model_scanner_scenario_step","stock.group_stock_manager",1,1,1,1 12 | "scanner_scenario_transition_user","scanner_scenario_transition_user","stock_scanner.model_scanner_scenario_transition",,1,,, 13 | "scanner_scenario_transition_manager","scanner_scenario_transition_manager","stock_scanner.model_scanner_scenario_transition","stock.group_stock_manager",1,1,1,1 14 | "scanner_scenario_custom_user","scanner_scenario_custom_user","stock_scanner.model_scanner_scenario_custom",,1,1,1,1 15 | -------------------------------------------------------------------------------- /data/scenarios/Login/Login.scenario: -------------------------------------------------------------------------------- 1 | 2 | 3 | stock_scanner.group_stock_scanner_sentinel 4 | Login 5 | 0 6 | 7 | 8 | False 9 | False 10 | scanner.hardware 11 | scenario 12 | stock_scanner.scanner_scenario_login 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /demo/Tutorial/Sentinel/Sentinel.scenario: -------------------------------------------------------------------------------- 1 | 2 | 3 | scanner_scenario_sentinel 4 | scanner.hardware 5 | Sentinel 6 | 7 | 20 8 | scanner_scenario_tutorial 9 | False 10 | OpenERP S.A. 11 | Shop 1 12 | Shop 2 13 | True 14 | scenario 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /models/scanner_scenario_step.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2011 Sylvain Garancher 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 4 | 5 | import sys 6 | import compiler 7 | import traceback 8 | from openerp import models, api, fields, exceptions 9 | from openerp import _ 10 | 11 | from .common import PYTHON_CODE_DEFAULT 12 | 13 | import logging 14 | logger = logging.getLogger('stock_scanner') 15 | 16 | 17 | class ScannerScenarioStep(models.Model): 18 | _name = 'scanner.scenario.step' 19 | _description = 'Step for scenario' 20 | 21 | # =========================================================================== 22 | # COLUMNS 23 | # =========================================================================== 24 | name = fields.Char( 25 | string='Name', 26 | required=False, 27 | help='Name of the step.') 28 | scenario_id = fields.Many2one( 29 | comodel_name='scanner.scenario', 30 | string='Scenario', 31 | required=True, 32 | ondelete='cascade', 33 | help='Scenario for this step.') 34 | step_start = fields.Boolean( 35 | string='Step start', 36 | default=False, 37 | help='Check this if this is the first step of the scenario.') 38 | step_stop = fields.Boolean( 39 | string='Step stop', 40 | default=False, 41 | help='Check this if this is the last step of the scenario.') 42 | step_back = fields.Boolean( 43 | string='Step back', 44 | default=False, 45 | help='Check this to stop at this step when returning back.') 46 | no_back = fields.Boolean( 47 | string='No back', 48 | default=False, 49 | help='Check this to prevent returning back this step.') 50 | out_transition_ids = fields.One2many( 51 | comodel_name='scanner.scenario.transition', 52 | inverse_name='from_id', 53 | string='Outgoing transitions', 54 | ondelete='cascade', 55 | help='Transitions which goes to this step.') 56 | in_transition_ids = fields.One2many( 57 | comodel_name='scanner.scenario.transition', 58 | inverse_name='to_id', 59 | string='Incoming transitions', 60 | ondelete='cascade', 61 | help='Transitions which goes to the next step.') 62 | python_code = fields.Text( 63 | string='Python code', 64 | default=PYTHON_CODE_DEFAULT, 65 | help='Python code to execute.') 66 | 67 | @api.multi 68 | @api.constrains('python_code') 69 | def _check_python_code_syntax(self): 70 | """ 71 | Syntax check the python code of a step 72 | """ 73 | for step in self: 74 | try: 75 | compiler.parse(step.python_code) 76 | except SyntaxError, exception: 77 | logger.error(''.join(traceback.format_exception( 78 | sys.exc_type, 79 | sys.exc_value, 80 | sys.exc_traceback, 81 | ))) 82 | raise exceptions.ValidationError( 83 | _('Error in python code for step "%s"' 84 | ' at line %d, offset %d:\n%s') % ( 85 | step.name, 86 | exception.lineno, 87 | exception.offset, 88 | exception.msg, 89 | )) 90 | 91 | return True 92 | -------------------------------------------------------------------------------- /demo/Tutorial/Step_types/Step_types.scenario: -------------------------------------------------------------------------------- 1 | 2 | 3 | scanner_scenario_step_types 4 | scanner.hardware 5 | Step types 6 | 7 | 10 8 | scanner_scenario_tutorial 9 | False 10 | OpenERP S.A. 11 | Shop 1 12 | Shop 2 13 | True 14 | scenario 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /models/scanner_scenario_transition.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2011 Sylvain Garancher 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 4 | 5 | import sys 6 | import compiler 7 | import traceback 8 | from openerp import models, api, fields, exceptions 9 | from openerp import _ 10 | 11 | import logging 12 | logger = logging.getLogger('stock_scanner') 13 | 14 | 15 | class ScannerScenarioTransition(models.Model): 16 | _name = 'scanner.scenario.transition' 17 | _description = 'Transition for scenario' 18 | 19 | _order = 'sequence' 20 | 21 | @api.model 22 | def _transition_type_get(self): 23 | return [ 24 | ('scanner', 'Scanner'), 25 | ('keyboard', 'Keyboard'), 26 | ] 27 | 28 | # =========================================================================== 29 | # COLUMNS 30 | # =========================================================================== 31 | name = fields.Char( 32 | string='Name', 33 | required=True, 34 | help='Name of the transition.') 35 | sequence = fields.Integer( 36 | string='Sequence', 37 | default=0, 38 | required=False, 39 | help='Sequence order.') 40 | from_id = fields.Many2one( 41 | comodel_name='scanner.scenario.step', 42 | string='From', 43 | required=True, 44 | ondelete='cascade', 45 | help='Step which launches this transition.') 46 | to_id = fields.Many2one( 47 | comodel_name='scanner.scenario.step', 48 | string='To', 49 | required=True, 50 | ondelete='cascade', 51 | help='Step which is reached by this transition.') 52 | condition = fields.Char( 53 | string='Condition', 54 | required=True, 55 | default='True', 56 | help='The transition is followed only if this condition is evaluated ' 57 | 'as True.') 58 | transition_type = fields.Selection( 59 | selection='_transition_type_get', 60 | string='Transition Type', 61 | default="keyboard", 62 | help='Type of transition.') 63 | tracer = fields.Char( 64 | string='Tracer', 65 | required=False, 66 | default=False, 67 | help='Used to determine fron which transition we arrive to the ' 68 | 'destination step.') 69 | scenario_id = fields.Many2one( 70 | comodel_name='scanner.scenario', 71 | string='Scenario', 72 | required=False, 73 | related="from_id.scenario_id", 74 | store=True, 75 | ondelete='cascade', 76 | readonly=True) 77 | 78 | @api.one 79 | @api.constrains('from_id', 'to_id') 80 | def _check_scenario(self): 81 | if self.from_id.scenario_id.id != self.to_id.scenario_id.id: 82 | raise exceptions.Warning( 83 | _('Error ! You can not create recursive scenarios.'), 84 | ) 85 | 86 | return True 87 | 88 | @api.multi 89 | @api.constrains('condition') 90 | def _check_condition_syntax(self): 91 | """ 92 | Syntax check the python condition of a transition 93 | """ 94 | for transition in self: 95 | try: 96 | compiler.parse(transition.condition) 97 | except SyntaxError, exception: 98 | logger.error(''.join(traceback.format_exception( 99 | sys.exc_type, 100 | sys.exc_value, 101 | sys.exc_traceback, 102 | ))) 103 | raise exceptions.ValidationError( 104 | _('Error in condition for transition "%s"' 105 | ' at line %d, offset %d:\n%s') % ( 106 | transition.name, 107 | exception.lineno, 108 | exception.offset, 109 | exception.msg, 110 | )) 111 | 112 | return True 113 | -------------------------------------------------------------------------------- /security/stock_scanner_security.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sentinel: technical users 7 | 8 | 9 | 10 | Scanner hardware users rules 11 | 12 | 13 | 14 | 15 | 16 | 17 | ['|', ('user_id','=',user.id), ('user_id','=', False)] 18 | 19 | 20 | 21 | Scanner hardware group sentinel rule 22 | 23 | 24 | 25 | 26 | 27 | 28 | [(1,'=', 1)] 29 | 30 | 31 | 32 | Scanner scenario users and groups rules 33 | 34 | 35 | 36 | 37 | 38 | ['|', ('user_ids','=',user.id), ('group_ids','in', [x.id for x in user.groups_id])] 39 | 40 | 41 | 42 | Scanner scenario step users and groups rules 43 | 44 | 45 | 46 | 47 | 48 | ['|', ('scenario_id.user_ids','=',user.id), ('scenario_id.group_ids','in', [x.id for x in user.groups_id])] 49 | 50 | 51 | 52 | Scanner scenario transition users and groups rules 53 | 54 | 55 | 56 | 57 | 58 | ['|', ('scenario_id.user_ids','=',user.id), ('scenario_id.group_ids','in', [x.id for x in user.groups_id])] 59 | 60 | 61 | 62 | Scanner scenario custom users and groups rules 63 | 64 | 65 | 66 | 67 | 68 | ['|', ('scenario_id.user_ids','=',user.id), ('scenario_id.group_ids','in', [x.id for x in user.groups_id])] 69 | 70 | 71 | -------------------------------------------------------------------------------- /models/scanner_scenario.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # © 2011 Sylvain Garancher 3 | # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). 4 | 5 | from openerp import models, api, fields, exceptions 6 | from openerp import _ 7 | 8 | import logging 9 | logger = logging.getLogger('stock_scanner') 10 | 11 | 12 | class ScannerScenario(models.Model): 13 | _name = 'scanner.scenario' 14 | _description = 'Scenario for scanner' 15 | 16 | _order = 'sequence' 17 | 18 | _parent_name = 'parent_id' 19 | 20 | @api.model 21 | def _type_get(self): 22 | return [ 23 | ('scenario', 'Scenario'), 24 | ('menu', 'Menu'), 25 | ('shortcut', 'Shortcut'), 26 | ] 27 | 28 | # =========================================================================== 29 | # COLUMNS 30 | # =========================================================================== 31 | name = fields.Char( 32 | string='Name', 33 | required=True, 34 | translate=True, 35 | help='Appear on barcode reader screen.') 36 | sequence = fields.Integer( 37 | string='Sequence', 38 | default=0, 39 | required=False, 40 | help='Sequence order.') 41 | active = fields.Boolean( 42 | string='Active', 43 | default=True, 44 | help='If check, this object is always available.') 45 | model_id = fields.Many2one( 46 | comodel_name='ir.model', 47 | string='Model', 48 | required=False, 49 | ondelete='restrict', 50 | help='Model used for this scenario.') 51 | step_ids = fields.One2many( 52 | comodel_name='scanner.scenario.step', 53 | inverse_name='scenario_id', 54 | string='Scenario', 55 | ondelete='cascade', 56 | help='Step of the current running scenario.') 57 | warehouse_ids = fields.Many2many( 58 | comodel_name='stock.warehouse', 59 | relation='scanner_scenario_warehouse_rel', 60 | column1='scenario_id', 61 | column2='warehouse_id', 62 | string='Warehouses', 63 | help='Warehouses for this scenario.') 64 | notes = fields.Text( 65 | string='Notes', 66 | help='Store different notes, date and title for modification, etc...') 67 | shared_custom = fields.Boolean( 68 | string='Shared Custom', 69 | default=False, 70 | help='Allows to share the custom values with a shared scanner in the ' 71 | 'same warehouse.') 72 | parent_id = fields.Many2one( 73 | comodel_name='scanner.scenario', 74 | string='Parent', 75 | required=False, 76 | ondelete='restrict', 77 | help='Parent scenario, used to create menus.') 78 | type = fields.Selection( 79 | selection='_type_get', 80 | string='Type', 81 | required=True, 82 | default='scenario', 83 | help='Defines if this scenario is a menu or an executable scenario.') 84 | company_id = fields.Many2one( 85 | comodel_name='res.company', 86 | string='Company', 87 | required=True, 88 | default=lambda self: self.env.user.company_id.id, 89 | ondelete='restrict', 90 | help='Company to be used on this scenario.') 91 | group_ids = fields.Many2many( 92 | comodel_name='res.groups', 93 | relation='scanner_scenario_res_groups_rel', 94 | column1='scenario_id', 95 | column2='group_id', 96 | string='Allowed Groups', 97 | default=lambda self: [self.env.ref('stock.group_stock_user').id]) 98 | user_ids = fields.Many2many( 99 | comodel_name='res.users', 100 | relation='scanner_scenario_res_users_rel', 101 | column1='scenario_id', 102 | column2='user_id', 103 | string='Allowed Users') 104 | 105 | @api.one 106 | @api.constrains('parent_id') 107 | def _check_recursion(self): 108 | if not super(ScannerScenario, self)._check_recursion(): 109 | raise exceptions.Warning( 110 | _('Error ! You can not create recursive scenarios.'), 111 | ) 112 | -------------------------------------------------------------------------------- /views/scanner_scenario_transition.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ############################################################################## 5 | # 6 | # stock_scanner module for OpenERP, Allows managing barcode readers with simple scenarios 7 | # Copyright (C) 2011 SYLEAM Info Services ([http://www.Syleam.fr/]) 8 | # Sylvain Garancher [sylvain.garancher@syleam.fr] 9 | # Copyright (C) 2015 Objectif-PI ([http://www.objectif-pi.com]). 10 | # Damien CRIER [damien.crier@objectif-pi.com] 11 | # 12 | # This file is a part of stock_scanner 13 | # 14 | # stock_scanner is free software: you can redistribute it and/or modify 15 | # it under the terms of the GNU Affero General Public License as published by 16 | # the Free Software Foundation, either version 3 of the License, or 17 | # (at your option) any later version. 18 | # 19 | # stock_scanner is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU Affero General Public License for more details. 23 | # 24 | # You should have received a copy of the GNU Affero General Public License 25 | # along with this program. If not, see [http://www.gnu.org/licenses/]. 26 | # 27 | ############################################################################## 28 | 29 | 30 | scanner.scenario.transition.search 31 | scanner.scenario.transition 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | scanner.scenario.transition.tree 43 | scanner.scenario.transition 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | scanner.scenario.transition.form 59 | scanner.scenario.transition 60 | 61 | 62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 |
74 |
75 |
76 | 77 | Transition 78 | ir.actions.act_window 79 | scanner.scenario.transition 80 | form 81 | tree,form 82 | [] 83 | {} 84 | 85 | 86 | 87 | 88 | form 89 | 90 | 91 | 92 | 93 | 94 | tree 95 | 96 | 97 | 98 |
99 |
100 | -------------------------------------------------------------------------------- /views/scanner_scenario_custom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ############################################################################## 5 | # 6 | # stock_scanner module for OpenERP, Allows managing barcode readers with simple scenarios 7 | # Copyright (C) 2011 SYLEAM Info Services ([http://www.Syleam.fr/]) 8 | # Sylvain Garancher [sylvain.garancher@syleam.fr] 9 | # Copyright (C) 2015 Objectif-PI ([http://www.objectif-pi.com]). 10 | # Damien CRIER [damien.crier@objectif-pi.com] 11 | # 12 | # This file is a part of stock_scanner 13 | # 14 | # stock_scanner is free software: you can redistribute it and/or modify 15 | # it under the terms of the GNU Affero General Public License as published by 16 | # the Free Software Foundation, either version 3 of the License, or 17 | # (at your option) any later version. 18 | # 19 | # stock_scanner is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU Affero General Public License for more details. 23 | # 24 | # You should have received a copy of the GNU Affero General Public License 25 | # along with this program. If not, see [http://www.gnu.org/licenses/]. 26 | # 27 | ############################################################################## 28 | 29 | 30 | scanner.scenario.custom.tree 31 | scanner.scenario.custom 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | scanner.scenario.custom.form 44 | scanner.scenario.custom 45 | 46 | 47 |
48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
81 |
82 |
83 | 84 | Scenario Custom Values 85 | ir.actions.act_window 86 | scanner.scenario.custom 87 | form 88 | tree,form 89 | [] 90 | {} 91 | 92 | 93 | 94 | 95 | form 96 | 97 | 98 | 99 | 100 | 101 | tree 102 | 103 | 104 | 105 |
106 |
107 | -------------------------------------------------------------------------------- /views/scanner_hardware.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ############################################################################## 5 | # 6 | # stock_scanner module for OpenERP, Allows managing barcode readers with simple scenarios 7 | # Copyright (C) 2011 SYLEAM Info Services ([http://www.Syleam.fr/]) 8 | # Sylvain Garancher [sylvain.garancher@syleam.fr] 9 | # Copyright (C) 2015 Objectif-PI ([http://www.objectif-pi.com]). 10 | # Damien CRIER [damien.crier@objectif-pi.com] 11 | # 12 | # This file is a part of stock_scanner 13 | # 14 | # stock_scanner is free software: you can redistribute it and/or modify 15 | # it under the terms of the GNU Affero General Public License as published by 16 | # the Free Software Foundation, either version 3 of the License, or 17 | # (at your option) any later version. 18 | # 19 | # stock_scanner is distributed in the hope that it will be useful, 20 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | # GNU Affero General Public License for more details. 23 | # 24 | # You should have received a copy of the GNU Affero General Public License 25 | # along with this program. If not, see [http://www.gnu.org/licenses/]. 26 | # 27 | ############################################################################## 28 | 29 | 30 | scanner.hardware.tree 31 | scanner.hardware 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | scanner.hardware.form 45 | scanner.hardware 46 | 47 | 48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |