diff --git a/mass_sorting/README.rst b/mass_sorting/README.rst new file mode 100644 index 000000000..87238ee44 --- /dev/null +++ b/mass_sorting/README.rst @@ -0,0 +1,106 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +============ +Mass Sorting +============ + +This module extends the functionality of odoo to allow users to sort an +one2many fields in any model. + +Typically, you can sort sale order lines on a sale order, using any fields. + +Configuration +============= + +To configure this module, you need to: + +* Go to Settings / Technical / Mass Sorting + +* Create a new item and define: + * a name + * the model you want to sort + * the field of the model, you want to sort + * The lists of the fields, by which the sort will be done + +.. image:: /mass_sorting/static/description/1_mass_sort_config.png + :width: 70% + +(You can allow users to change or not the values, by checking 'Allow custom Setting') + +* Click on the button 'Add sidebar button' + +Usage +===== + +* Go to the form view of the given model, in this sample, a sale order. (or select items in a tree view) + +.. image:: /mass_sorting/static/description/4_before.png + +* click on the button 'Action' and then select the according action + +.. image:: /mass_sorting/static/description/2_button.png + +* On the pop up (depending of the configuration), change the fields and the order + +.. image:: /mass_sorting/static/description/3_mass_sort_wizard_custom.png + +(If changing configuration is not allowed, a simple message is displayed.) + +.. image:: /mass_sorting/static/description/3_mass_sort_wizard.png + +* The items will be reordered. + +.. image:: /mass_sorting/static/description/5_after.png + + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/149/10.0 + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Sylvain LE GAL (https://twitter.com/legalsylvain) + +Funders +------- + +The development of this module has been financially supported by: + +* GRAP (http://www.grap.coop) + +This module is highly inspired by 'mass_editing' module. (by OCA and SerpentCS) + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. + diff --git a/mass_sorting/__openerp__.py b/mass_sorting/__manifest__.py similarity index 65% rename from mass_sorting/__openerp__.py rename to mass_sorting/__manifest__.py index 81b9cc95f..b268bbafb 100644 --- a/mass_sorting/__openerp__.py +++ b/mass_sorting/__manifest__.py @@ -6,30 +6,19 @@ { 'name': 'Mass Sorting', - 'version': '1.0', + 'version': "10.0.1.0.0", 'author': 'GRAP,Odoo Community Association (OCA)', + 'summary': 'Sort any models by any fields list', 'category': 'Tools', 'website': 'http://www.grap.coop', 'license': 'AGPL-3', - "description": """ -Mass Sorting -============ - -This module provides the functionality to sort an one2many fields in any model. - -Typically, you can sort sale order line on a sale order, using any fields. - -See screenshots in the description folder. - -This module is highly inspired by 'mass_editing' module. (by OCA and SerpentCS) - """, 'depends': [ 'base', ], 'data': [ 'security/ir.model.access.csv', - 'views/mass_sort_config_view.xml', - 'views/mass_sort_wizard_view.xml', + 'views/view_mass_sort_config.xml', + 'views/view_mass_sort_wizard.xml', 'views/action.xml', 'views/menu.xml', ], @@ -41,4 +30,9 @@ This module is highly inspired by 'mass_editing' module. (by OCA and SerpentCS) 'static/description/4_before.png', 'static/description/5_after.png', ], + 'demo': [ + 'demo/mass_sort_config.xml', + 'demo/mass_sort_config_line.xml', + 'demo/function.xml', + ], } diff --git a/mass_sorting/demo/function.xml b/mass_sorting/demo/function.xml new file mode 100644 index 000000000..2c99f6d86 --- /dev/null +++ b/mass_sorting/demo/function.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/mass_sorting/demo/mass_sort_config.xml b/mass_sorting/demo/mass_sort_config.xml new file mode 100644 index 000000000..c5e580b34 --- /dev/null +++ b/mass_sorting/demo/mass_sort_config.xml @@ -0,0 +1,16 @@ + + + + + + + Self Mass Sort Demo + + + + + diff --git a/mass_sorting/demo/mass_sort_config_line.xml b/mass_sorting/demo/mass_sort_config_line.xml new file mode 100644 index 000000000..6865ac6d9 --- /dev/null +++ b/mass_sorting/demo/mass_sort_config_line.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + diff --git a/mass_sorting/i18n/fr.po b/mass_sorting/i18n/fr.po index 7fb21ab21..b0efd0d24 100644 --- a/mass_sorting/i18n/fr.po +++ b/mass_sorting/i18n/fr.po @@ -1,13 +1,13 @@ -# Translation of OpenERP Server. +# Translation of Odoo Server. # This file contains the translation of the following modules: # * mass_sorting # msgid "" msgstr "" -"Project-Id-Version: OpenERP Server 7.0\n" +"Project-Id-Version: Odoo Server 10.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-02-08 09:48+0000\n" -"PO-Revision-Date: 2016-02-08 09:48+0000\n" +"POT-Creation-Date: 2017-01-19 16:46+0000\n" +"PO-Revision-Date: 2017-01-19 16:46+0000\n" "Last-Translator: <>\n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -16,66 +16,129 @@ msgstr "" "Plural-Forms: \n" #. module: mass_sorting -#: code:addons/mass_sorting/models/mass_sort_config.py:95 +#: code:addons/mass_sorting/models/mass_sort_config.py:84 #, python-format msgid "%s (copy)" msgstr "%s (copie)" #. module: mass_sorting -#: view:mass.sort.config:0 +#: model:ir.ui.view,arch_db:mass_sorting.view_mass_sort_config_form msgid "Add sidebar button" msgstr "Ajouter un bouton" #. module: mass_sorting -#: field:mass.sort.config,allow_custom_setting:0 -#: field:mass.sort.wizard,allow_custom_setting:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_allow_custom_setting +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_allow_custom_setting msgid "Allow Custom Setting" msgstr "Autoriser un paramétrage personnalisé" #. module: mass_sorting -#: view:mass.sort.wizard:0 +#: model:ir.ui.view,arch_db:mass_sorting.view_mass_sort_wizard_form msgid "Cancel" msgstr "Annuler" #. module: mass_sorting -#: view:mass.sort.wizard:0 +#: model:ir.ui.view,arch_db:mass_sorting.view_mass_sort_wizard_form msgid "Confirm" msgstr "Confirmer" #. module: mass_sorting -#: field:mass.sort.wizard,description:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_create_uid +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_create_uid +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_create_uid +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: mass_sorting +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_create_date +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_create_date +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_create_date +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_create_date +msgid "Created on" +msgstr "Créé le" + +#. module: mass_sorting +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_description msgid "Description" msgstr "Description" #. module: mass_sorting -#: view:mass.sort.config:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_display_name +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_display_name +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_display_name +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_display_name +msgid "Display Name" +msgstr "Nom affiché" + +#. module: mass_sorting +#: model:ir.ui.view,arch_db:mass_sorting.view_mass_sort_config_form msgid "Display a button in the sidebar of related model to open a wizard" msgstr "Afficher un bouton dans la barre de menu du modèle pour ouvrir un assistant" #. module: mass_sorting -#: field:mass.sort.config.line,field_id:0 -#: field:mass.sort.wizard.line,field_id:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_field_id +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_field_id msgid "Field" msgstr "Champ" #. module: mass_sorting -#: field:mass.sort.config,one2many_field_id:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_one2many_field_id msgid "Field to Sort" msgstr "Champ à trier" #. module: mass_sorting -#: help:mass.sort.config,allow_custom_setting:0 +#: model:ir.model.fields,help:mass_sorting.field_mass_sort_config_one2many_model +#: model:ir.model.fields,help:mass_sorting.field_mass_sort_wizard_one2many_model +msgid "For relationship fields, the technical name of the target model" +msgstr "Pour les champs de relation, le nom technique du modèle cible" + +#. module: mass_sorting +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_id +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_id +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_id +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_id +msgid "ID" +msgstr "ID" + +#. module: mass_sorting +#: model:ir.model.fields,help:mass_sorting.field_mass_sort_config_allow_custom_setting +#: model:ir.model.fields,help:mass_sorting.field_mass_sort_wizard_allow_custom_setting msgid "If checked, any user could have the possibility to change fields, and use others." msgstr "Si la case est cochée, chaque utilisateur aura la possibilité de changer les champs et d'en utiliser d'autres." #. module: mass_sorting -#: field:mass.sort.config.line,desc:0 -#: field:mass.sort.wizard.line,desc:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_desc +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_desc msgid "Inverse Order" msgstr "Ordre inverse" #. module: mass_sorting -#: code:addons/mass_sorting/models/mass_sort_config.py:108 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config___last_update +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line___last_update +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard___last_update +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line___last_update +msgid "Last Modified on" +msgstr "Dernière Modification le" + +#. module: mass_sorting +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_write_uid +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_write_uid +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_write_uid +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_write_uid +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: mass_sorting +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_write_date +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_write_date +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_write_date +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_write_date +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: mass_sorting +#: code:addons/mass_sorting/models/mass_sort_config.py:93 #, python-format msgid "Mass Sort (%s)" msgstr "Tri en masse (%s)" @@ -83,93 +146,98 @@ msgstr "Tri en masse (%s)" #. module: mass_sorting #: model:ir.actions.act_window,name:mass_sorting.action_mass_sort_config #: model:ir.ui.menu,name:mass_sorting.menu_mass_sort_config -#: view:mass.sort.config:0 -#: view:mass.sort.wizard:0 +#: model:ir.ui.view,arch_db:mass_sorting.view_mass_sort_config_tree msgid "Mass Sorting" msgstr "Tri en masse" #. module: mass_sorting -#: field:mass.sort.config,model_id:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_model_id msgid "Model" msgstr "Modèle" #. module: mass_sorting -#: field:mass.sort.config,one2many_model:0 -#: field:mass.sort.wizard,one2many_model:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_one2many_model +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_one2many_model msgid "Model Name of the Field to Sort" msgstr "Nom du modèle du champ à trier" #. module: mass_sorting -#: field:mass.sort.config,name:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_name msgid "Name" msgstr "Nom" #. module: mass_sorting -#: constraint:mass.sort.wizard:0 -msgid "Please Select at least ona Sorting Criteria." +#: code:addons/mass_sorting/models/mass_sort_wizard.py:71 +#, python-format +msgid "Please Select at least one Sorting Criteria." msgstr "Veuillez renseigner au moins un critère de tri." #. module: mass_sorting -#: model:mass.sort.config,name:mass_sorting.mass_sort_config -msgid "Recursive Demo Configuration" -msgstr "Configuration de démo (récursive)" - -#. module: mass_sorting -#: view:mass.sort.config:0 +#: model:ir.ui.view,arch_db:mass_sorting.view_mass_sort_config_form msgid "Remove sidebar button" msgstr "Retirer le bouton" #. module: mass_sorting -#: field:mass.sort.config.line,sequence:0 -#: field:mass.sort.wizard.line,sequence:0 +#: model:mass.sort.config,name:mass_sorting.mass_sort_config_demo +msgid "Self Mass Sort Demo" +msgstr "Self Mass Sort Demo" + +#. module: mass_sorting +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_sequence +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_sequence msgid "Sequence" msgstr "Séquence" #. module: mass_sorting -#: field:mass.sort.config,ref_ir_act_window:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_ref_ir_act_window msgid "Sidebar Action" msgstr "Action" #. module: mass_sorting -#: field:mass.sort.config,ref_ir_value:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_ref_ir_value msgid "Sidebar Button" -msgstr "Bouton" +msgstr "Bouton de la barre latérale" #. module: mass_sorting -#: field:mass.sort.config,line_ids:0 -#: view:mass.sort.wizard:0 -#: field:mass.sort.wizard,line_ids:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_ids +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_ids +#: model:ir.ui.view,arch_db:mass_sorting.view_mass_sort_config_form +#: model:ir.ui.view,arch_db:mass_sorting.view_mass_sort_wizard_form msgid "Sorting Criterias" msgstr "Critères de tri" #. module: mass_sorting -#: constraint:mass.sort.config:0 -msgid "The selected Field to Sort doesn't belong to the selected model." -msgstr "Le champ sélectionné pour le tri n'appartient pas au modèle sélectionné." +#: code:addons/mass_sorting/models/mass_sort_config.py:63 +#, python-format +msgid "The selected Field to Sort '%s' doesn't belong to the selected model '%s'." +msgstr "Le champ sélectionné pour le tri '%s' n'appartient pas au modèle sélectionné '%s'." #. module: mass_sorting -#: constraint:mass.sort.config:0 +#: code:addons/mass_sorting/models/mass_sort_config.py:55 +#, python-format msgid "The selected Field to Sort doesn't not have 'sequence' field defined." msgstr "Le champ sélectionné pour le tri n'a pas de champ séquence défini." #. module: mass_sorting -#: constraint:mass.sort.config.line:0 -msgid "The selected criteria must belong to the parent model." -msgstr "Le critère sélectionné doit appartenir au modèle parent." +#: code:addons/mass_sorting/models/mass_sort_config_line.py:30 +#, python-format +msgid "The selected criteria '%s' must belong to the model of the Field to Sort." +msgstr "Le critère sélectionné '%s' doit appartenir au modèle du champ à trier." #. module: mass_sorting -#: field:mass.sort.config.line,config_id:0 -#: field:mass.sort.wizard.line,wizard_id:0 +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_config_line_config_id +#: model:ir.model.fields,field_description:mass_sorting.field_mass_sort_wizard_line_wizard_id msgid "Wizard" msgstr "Assistant" #. module: mass_sorting -#: constraint:mass.sort.config:0 +#: code:addons/mass_sorting/models/mass_sort_config.py:72 +#, python-format msgid "You have to define field(s) in 'Sorting Criterias' if you uncheck 'Allow Custom Setting'." -msgstr "You have to define field(s) in 'Sorting Criterias' if you uncheck 'Allow Custom Setting'." +msgstr "Vous devez définir des champs dans 'Critères de Tri', si vous décochez 'Autoriser un paramétrage personnalisé'." #. module: mass_sorting -#: code:addons/mass_sorting/models/mass_sort_wizard.py:44 +#: code:addons/mass_sorting/models/mass_sort_wizard.py:54 #, python-format msgid "You will sort the field '%(field)s' for %(qty)d %(model)s(s). \n" "\n" @@ -177,3 +245,24 @@ msgid "You will sort the field '%(field)s' for %(qty)d %(model)s(s). \n" msgstr "Vous allez trier le champ '%(field)s' pour %(qty)d %(model)s(s). \n" "\n" "Le tri sera réalisé par %(field_list)s." + +#. module: mass_sorting +#: model:ir.model,name:mass_sorting.model_mass_sort_config +msgid "mass.sort.config" +msgstr "mass.sort.config" + +#. module: mass_sorting +#: model:ir.model,name:mass_sorting.model_mass_sort_config_line +msgid "mass.sort.config.line" +msgstr "mass.sort.config.line" + +#. module: mass_sorting +#: model:ir.model,name:mass_sorting.model_mass_sort_wizard +msgid "mass.sort.wizard" +msgstr "mass.sort.wizard" + +#. module: mass_sorting +#: model:ir.model,name:mass_sorting.model_mass_sort_wizard_line +msgid "mass.sort.wizard.line" +msgstr "mass.sort.wizard.line" + diff --git a/mass_sorting/models/mass_sort_config.py b/mass_sorting/models/mass_sort_config.py index 47e4831d5..68e68f810 100644 --- a/mass_sorting/models/mass_sort_config.py +++ b/mass_sorting/models/mass_sort_config.py @@ -1,144 +1,118 @@ # -*- coding: utf-8 -*- # Copyright (C): -# * 2012-Today Serpent Consulting Services () -# * 2016-Today GRAP (http://www.grap.coop) +# * 2012-Today Serpent Consulting Services () +# * 2016-Today GRAP (http://www.grap.coop) # @author: Sylvain LE GAL (https://twitter.com/legalsylvain) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp.osv import fields -from openerp.osv.orm import Model -from openerp.tools.translate import _ +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError -class MassSortConfig(Model): +class MassSortConfig(models.Model): _name = 'mass.sort.config' # Column Section - _columns = { - 'name': fields.char( - string='Name', translate=True, required=True), - 'model_id': fields.many2one( - 'ir.model', string='Model', required=True), - 'allow_custom_setting': fields.boolean( - string='Allow Custom Setting', help="If checked, any user could" - " have the possibility to change fields, and use others."), - 'one2many_field_id': fields.many2one( - 'ir.model.fields', string='Field to Sort', required=True, - domain="[('model_id', '=', model_id)," - "('ttype', '=', 'one2many')]"), - 'one2many_model': fields.related( - 'one2many_field_id', 'relation', type='char', - string='Model Name of the Field to Sort', readonly=True), - 'ref_ir_act_window': fields.many2one( - 'ir.actions.act_window', 'Sidebar Action', readonly=True), - 'ref_ir_value': fields.many2one( - 'ir.values', 'Sidebar Button', readonly=True), - 'line_ids': fields.one2many( - 'mass.sort.config.line', 'config_id', 'Sorting Criterias'), - } - - _defaults = { - 'allow_custom_setting': True, - } + name = fields.Char(string='Name', translate=True, required=True) + + model_id = fields.Many2one( + comodel_name='ir.model', string='Model', required=True) + + allow_custom_setting = fields.Boolean( + string='Allow Custom Setting', default=True, help="If checked, any" + " user could have the possibility to change fields, and use others.") + + one2many_field_id = fields.Many2one( + comodel_name='ir.model.fields', string='Field to Sort', required=True, + domain="[('model_id', '=', model_id),('ttype', '=', 'one2many')]") + + one2many_model = fields.Char( + related='one2many_field_id.relation', readonly=True, + string='Model Name of the Field to Sort', hel="Technical field," + "used in the model 'mass.sort.config.line'", store=True) + + ref_ir_act_window = fields.Many2one( + comodel_name='ir.actions.act_window', string='Sidebar Action', + readonly=True, copy=False) + + ref_ir_value = fields.Many2one( + comodel_name='ir.values', string='Sidebar Button', readonly=True, + copy=False) + + line_ids = fields.One2many( + comodel_name='mass.sort.config.line', inverse_name='config_id', + string='Sorting Criterias') # Constraint Section - def _check_model_sequence(self, cr, uid, ids, context=None): - field_obj = self.pool['ir.model.fields'] - for config in self.browse(cr, uid, ids, context=context): - if len(field_obj.search(cr, uid, [ + @api.constrains('one2many_field_id') + def _check_model_sequence(self): + field_obj = self.env['ir.model.fields'] + for config in self: + if len(field_obj.search([ ('model', '=', config.one2many_field_id.relation), - ('name', '=', 'sequence')], context=context)) != 1: - return False - return True - - def _check_model_field(self, cr, uid, ids, context=None): - for config in self.browse(cr, uid, ids, context=context): - if config.model_id.id != config.one2many_field_id.model_id.id: - return False - return True - - def _check_line_ids(self, cr, uid, ids, context=None): - for config in self.browse(cr, uid, ids, context=context): - if not config.allow_custom_setting and len(config.line_ids) == 0: - return False - return True - - _constraints = [ - (_check_model_sequence, "The selected Field to Sort doesn't not have" - " 'sequence' field defined.", ['one2many_field_id']), - (_check_model_field, "The selected Field to Sort doesn't belong to the" - " selected model.", ['model_id', 'one2many_field_id']), - (_check_line_ids, "You have to define field(s) in 'Sorting Criterias'" - " if you uncheck 'Allow Custom Setting'.", - ['line_ids', 'allow_custom_setting']), - ] - - # View Section - def on_change_one2many_field_id( - self, cr, uid, ids, one2many_field_id, context=None): - field_obj = self.pool['ir.model.fields'] - if not one2many_field_id: - return {'value': {'one2many_model': False}} - field = field_obj.browse(cr, uid, one2many_field_id, context=context) - return {'value': {'one2many_model': field.relation}} + ('name', '=', 'sequence')])) != 1: + raise ValidationError( + _("The selected Field to Sort doesn't not have" + " 'sequence' field defined.")) + + @api.constrains('model_id', 'one2many_field_id') + def _check_model_field(self): + for config in self: + if config.model_id != config.one2many_field_id.model_id: + raise ValidationError( + _("The selected Field to Sort '%s' doesn't belong to the" + " selected model '%s'.") % ( + config.one2many_field_id.model_id.name, + config.model_id.name)) + + @api.constrains('allow_custom_setting', 'line_ids') + def _check_line_ids(self): + for config in self: + if not config.allow_custom_setting and not len(config.line_ids): + raise ValidationError(_( + "You have to define field(s) in 'Sorting Criterias' if" + " you uncheck 'Allow Custom Setting'.")) # Overload Section - def unlink(self, cr, uid, ids, context=None): - self.unlink_action(cr, uid, ids, context=context) - return super(MassSortConfig, self).unlink( - cr, uid, ids, context=context) - - def copy(self, cr, uid, id, value=None, context=None): - value = value or {} - config = self.browse(cr, uid, id, context=context) - value.update({ - 'name': _('%s (copy)') % config.name, - 'ref_ir_act_window': False, - 'ref_ir_value': False}) - return super(MassSortConfig, self).copy( - cr, uid, id, value, context=context) + def unlink(self): + self.unlink_action() + return super(MassSortConfig, self).unlink() + + def copy(self, default=None): + default = default or {} + default.update({ + 'name': _('%s (copy)') % self.name}) + return super(MassSortConfig, self).copy(default=default) # Custom Section - def create_action(self, cr, uid, ids, context=None): - vals = {} - action_obj = self.pool['ir.actions.act_window'] - values_obj = self.pool['ir.values'] - for config in self.browse(cr, uid, ids, context=context): - src_obj = config.model_id.model + @api.multi + def create_action(self): + action_obj = self.env['ir.actions.act_window'] + values_obj = self.env['ir.values'] + for config in self: button_name = _('Mass Sort (%s)') % config.name - vals['ref_ir_act_window'] = action_obj.create(cr, uid, { + config.ref_ir_act_window = action_obj.create({ 'name': button_name, 'type': 'ir.actions.act_window', 'res_model': 'mass.sort.wizard', - 'src_model': src_obj, + 'src_model': config.model_id.model, 'view_type': 'form', 'context': "{'mass_sort_config_id' : %d}" % (config.id), 'view_mode': 'form,tree', 'target': 'new', - 'auto_refresh': 1, - }, context) - vals['ref_ir_value'] = values_obj.create(cr, uid, { + }) + config.ref_ir_value = values_obj.create({ 'name': button_name, - 'model': src_obj, + 'model': config.model_id.model, 'key2': 'client_action_multi', 'value': ( - "ir.actions.act_window,%s" % vals['ref_ir_act_window']), - 'object': True, - }, context) - self.write(cr, uid, ids, { - 'ref_ir_act_window': vals.get('ref_ir_act_window', False), - 'ref_ir_value': vals.get('ref_ir_value', False), - }, context) - return True - - def unlink_action(self, cr, uid, ids, context=None): - action_obj = self.pool['ir.actions.act_window'] - values_obj = self.pool['ir.values'] - for config in self.browse(cr, uid, ids, context=context): + "ir.actions.act_window,%s" % config.ref_ir_act_window.id), + }) + + @api.multi + def unlink_action(self): + for config in self: if config.ref_ir_act_window: - action_obj.unlink( - cr, uid, config.ref_ir_act_window.id, context=context) + config.ref_ir_act_window.unlink() if config.ref_ir_value: - values_obj.unlink( - cr, uid, config.ref_ir_value.id, context=context) - return True + config.ref_ir_value.unlink() diff --git a/mass_sorting/models/mass_sort_config_line.py b/mass_sorting/models/mass_sort_config_line.py index 9a135605f..d961327ca 100644 --- a/mass_sorting/models/mass_sort_config_line.py +++ b/mass_sorting/models/mass_sort_config_line.py @@ -3,36 +3,30 @@ # @author: Sylvain LE GAL (https://twitter.com/legalsylvain) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp.osv import fields -from openerp.osv.orm import Model +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError -class MassSortConfigLine(Model): +class MassSortConfigLine(models.Model): _name = 'mass.sort.config.line' _order = 'config_id, sequence, id' - _columns = { - 'sequence': fields.integer('Sequence', required=True), - 'config_id': fields.many2one( - 'mass.sort.config', string='Wizard'), - 'field_id': fields.many2one( - 'ir.model.fields', string='Field', required=True, domain="[" - "('model', '=', parent.one2many_model)]"), - 'desc': fields.boolean('Inverse Order'), - } + sequence = fields.Integer(string='Sequence', required=True, default=1) - _defaults = { - 'sequence': 1, - } + config_id = fields.Many2one( + comodel_name='mass.sort.config', string='Wizard') + + field_id = fields.Many2one( + comodel_name='ir.model.fields', string='Field', required=True, + domain="[('model', '=', parent.one2many_model)]") + + desc = fields.Boolean(string='Inverse Order') # Constraint Section - def _check_field_id(self, cr, uid, ids, context=None): - for line in self.browse(cr, uid, ids, context=context): + @api.constrains('field_id', 'config_id') + def _check_field_id(self): + for line in self: if line.field_id.model != line.config_id.one2many_model: - return False - return True - - _constraints = [ - (_check_field_id, "The selected criteria must belong to the parent" - " model.", ['config_id', 'field_id']), - ] + raise ValidationError(_( + "The selected criteria '%s' must belong to the model of" + " the Field to Sort.") % (line.field_id.field_description)) diff --git a/mass_sorting/models/mass_sort_wizard.py b/mass_sorting/models/mass_sort_wizard.py index c707babcf..85e5ef704 100644 --- a/mass_sorting/models/mass_sort_wizard.py +++ b/mass_sorting/models/mass_sort_wizard.py @@ -3,95 +3,83 @@ # @author: Sylvain LE GAL (https://twitter.com/legalsylvain) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp.osv import fields -from openerp.osv.orm import TransientModel -from openerp.tools.translate import _ +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError -class MassSortWizard(TransientModel): +class MassSortWizard(models.TransientModel): _name = 'mass.sort.wizard' - _columns = { - 'description': fields.text( - string='Description', readonly=True), - 'allow_custom_setting': fields.boolean( - string='Allow Custom Setting', readonly=True), - 'one2many_model': fields.char( - string='Model Name of the Field to Sort', readonly=True), - 'line_ids': fields.one2many( - 'mass.sort.wizard.line', 'wizard_id', 'Sorting Criterias'), - } - - # Constraint Section - def _check_line_ids(self, cr, uid, ids, context=None): - for wizard in self.browse(cr, uid, ids, context=context): - if not len(wizard.line_ids): - return False - return True - - _constraints = [( - _check_line_ids, "Please Select at least ona Sorting Criteria.", - ['line_ids']), - ] - # Default Section - def _default_description(self, cr, uid, context=None): - config_obj = self.pool['mass.sort.config'] - config = config_obj.browse( - cr, uid, context.get('mass_sort_config_id'), context=context) - - return _( - "You will sort the field '%(field)s' for %(qty)d %(model)s(s)" - ". \n\nThe sorting will be done by %(field_list)s.") % ({ - 'field': config.one2many_field_id.field_description, - 'qty': len(context.get('active_ids', False)), - 'model': config.model_id.name, - 'field_list': ', '.join( - [x.field_id.field_description for x in config.line_ids]) - }) - - def _default_allow_custom_setting(self, cr, uid, context=None): - config_obj = self.pool['mass.sort.config'] - return config_obj.browse( - cr, uid, context.get('mass_sort_config_id'), context=context)\ - .allow_custom_setting - - def _default_one2many_model(self, cr, uid, context=None): - config_obj = self.pool['mass.sort.config'] - return config_obj.browse( - cr, uid, context.get('mass_sort_config_id'), context=context)\ - .one2many_model - - def _default_line_ids(self, cr, uid, context=None): - config_obj = self.pool['mass.sort.config'] - res = [] + def _default_config_id(self): + return self._context.get('mass_sort_config_id', False) + + def _default_line_ids(self): + config_obj = self.env['mass.sort.config'] + line_ids = [] config = config_obj.browse( - cr, uid, context.get('mass_sort_config_id'), context=context) - for line in config.line_ids: - res.append((0, 0, { + self._context.get('mass_sort_config_id', False)) + if config: + for line in config.line_ids: + line_ids.append((0, 0, { 'sequence': line.sequence, 'field_id': line.field_id.id, 'desc': line.desc})) - return res + return line_ids + + # Column Section + config_id = fields.Many2one( + comodel_name='mass.sort.config', default=_default_config_id, + required=True) + + description = fields.Text( + string='Description', readonly=True, compute='_compute_description') + + allow_custom_setting = fields.Boolean( + string='Allow Custom Setting', readonly=True, + related='config_id.allow_custom_setting') + + one2many_model = fields.Char( + string='Model Name of the Field to Sort', readonly=True, + related='config_id.one2many_model') + + line_ids = fields.One2many( + comodel_name='mass.sort.wizard.line', inverse_name='wizard_id', + string='Sorting Criterias', default=_default_line_ids) + + # Compute Section + @api.depends('config_id') + def _compute_description(self): + for wizard in self: + wizard.description = _( + "You will sort the field '%(field)s' for %(qty)d %(model)s(s)" + ". \n\nThe sorting will be done by %(field_list)s.") % ({ + 'field': + wizard.config_id.one2many_field_id.field_description, + 'qty': len(self._context.get('active_ids', False)), + 'model': wizard.config_id.model_id.name, + 'field_list': ', '.join( + [x.field_id.field_description + for x in wizard.line_ids]) + }) - _defaults = { - 'description': _default_description, - 'allow_custom_setting': _default_allow_custom_setting, - 'one2many_model': _default_one2many_model, - 'line_ids': _default_line_ids, - } + # Constraint Section + @api.constrains('line_ids') + def _check_line_ids(self): + for wizard in self: + if not len(wizard.line_ids): + raise ValidationError(_( + "Please Select at least one Sorting Criteria.")) # Action Section - def button_apply(self, cr, uid, ids, context=None): - config_obj = self.pool['mass.sort.config'] - model_obj = self.pool[context.get('active_model')] - active_ids = context.get('active_ids') - config = config_obj.browse( - cr, uid, context.get('mass_sort_config_id'), context=context) - wizard = self.browse(cr, uid, ids[0], context=context) + def button_apply(self): + self.ensure_one() + wizard = self + active_ids = self._context.get('active_ids') - one2many_obj = self.pool[config.one2many_field_id.relation] - parent_field = config.one2many_field_id.relation_field + model_obj = self.env[wizard.config_id.model_id.model] + one2many_obj = self.env[wizard.config_id.one2many_field_id.relation] + parent_field = wizard.config_id.one2many_field_id.relation_field order_list = [] for line in wizard.line_ids: @@ -100,15 +88,13 @@ class MassSortWizard(TransientModel): '%s desc' % line.field_id.name or line.field_id.name) order = ', '.join(order_list) - for item in model_obj.browse(cr, uid, active_ids, context=context): + for item in model_obj.browse(active_ids): # DB Query sort by the correct order - line_ids = one2many_obj.search( - cr, uid, [(parent_field, '=', item.id)], order=order, - context=context) + lines = one2many_obj.search( + [(parent_field, '=', item.id)], order=order) + # Write new sequence to sort lines sequence = 1 - for id in line_ids: - one2many_obj.write( - cr, uid, [id], {'sequence': sequence}, context=context) + for line in lines: + line.sequence = sequence sequence += 1 - return {'type': 'ir.actions.act_window_close'} diff --git a/mass_sorting/models/mass_sort_wizard_line.py b/mass_sorting/models/mass_sort_wizard_line.py index 28d4b571a..371ba2e74 100644 --- a/mass_sorting/models/mass_sort_wizard_line.py +++ b/mass_sorting/models/mass_sort_wizard_line.py @@ -3,23 +3,19 @@ # @author: Sylvain LE GAL (https://twitter.com/legalsylvain) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp.osv import fields -from openerp.osv.orm import TransientModel +from odoo import fields, models -class TransientModelLine(TransientModel): +class TransientModelLine(models.TransientModel): _name = 'mass.sort.wizard.line' - _columns = { - 'sequence': fields.integer('Sequence', required=True), - 'wizard_id': fields.many2one( - 'mass.sort.wizard', string='Wizard'), - 'field_id': fields.many2one( - 'ir.model.fields', string='Field', required=True, domain="[" - "('model', '=', parent.one2many_model)]"), - 'desc': fields.boolean('Inverse Order'), - } + sequence = fields.Integer(string='Sequence', required=True, default=1) - _defaults = { - 'sequence': 1, - } + wizard_id = fields.Many2one( + comodel_name='mass.sort.wizard', string='Wizard') + + field_id = fields.Many2one( + comodel_name='ir.model.fields', string='Field', required=True, + domain="[('model', '=', parent.one2many_model)]") + + desc = fields.Boolean('Inverse Order') diff --git a/mass_sorting/static/description/1_mass_sort_config.png b/mass_sorting/static/description/1_mass_sort_config.png index 417a55018..e73902e5e 100644 Binary files a/mass_sorting/static/description/1_mass_sort_config.png and b/mass_sorting/static/description/1_mass_sort_config.png differ diff --git a/mass_sorting/static/description/2_button.png b/mass_sorting/static/description/2_button.png index 6fd1fe0f4..d30c683da 100644 Binary files a/mass_sorting/static/description/2_button.png and b/mass_sorting/static/description/2_button.png differ diff --git a/mass_sorting/static/description/3_mass_sort_wizard.png b/mass_sorting/static/description/3_mass_sort_wizard.png index bcc36f685..06ff6c4ff 100644 Binary files a/mass_sorting/static/description/3_mass_sort_wizard.png and b/mass_sorting/static/description/3_mass_sort_wizard.png differ diff --git a/mass_sorting/static/description/3_mass_sort_wizard_custom.png b/mass_sorting/static/description/3_mass_sort_wizard_custom.png index 5ed7fcc50..0d10735c2 100644 Binary files a/mass_sorting/static/description/3_mass_sort_wizard_custom.png and b/mass_sorting/static/description/3_mass_sort_wizard_custom.png differ diff --git a/mass_sorting/static/description/4_before.png b/mass_sorting/static/description/4_before.png index 01d94b30f..6769686ac 100644 Binary files a/mass_sorting/static/description/4_before.png and b/mass_sorting/static/description/4_before.png differ diff --git a/mass_sorting/static/description/5_after.png b/mass_sorting/static/description/5_after.png index faf52af83..aa4bbb2e3 100644 Binary files a/mass_sorting/static/description/5_after.png and b/mass_sorting/static/description/5_after.png differ diff --git a/mass_sorting/static/description/icon.png b/mass_sorting/static/description/icon.png new file mode 100644 index 000000000..c233ee5bb Binary files /dev/null and b/mass_sorting/static/description/icon.png differ diff --git a/mass_sorting/views/menu.xml b/mass_sorting/views/menu.xml index 27a34f1d9..5755fd686 100644 --- a/mass_sorting/views/menu.xml +++ b/mass_sorting/views/menu.xml @@ -5,10 +5,10 @@ Copyright (C) 2016-Today GRAP (http://www.grap.coop) License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> - + + parent="base.menu_custom" sequence="30"/> - + diff --git a/mass_sorting/views/mass_sort_config_view.xml b/mass_sorting/views/view_mass_sort_config.xml similarity index 66% rename from mass_sorting/views/mass_sort_config_view.xml rename to mass_sorting/views/view_mass_sort_config.xml index 0ee0868e2..1a2881f12 100644 --- a/mass_sorting/views/mass_sort_config_view.xml +++ b/mass_sorting/views/view_mass_sort_config.xml @@ -5,12 +5,38 @@ Copyright (C) 2016-Today GRAP (http://www.grap.coop) License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). --> - + + + + mass.sort.config + + + + + + + + + mass.sort.config + + + + + + + mass.sort.config -
+ +
+
@@ -18,17 +44,13 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
- + -