Merge pull request #170 from cyrilgdn/9.0-stock_orderpoint_generator

stock_orderpoint_generator: Migration V9
This commit is contained in:
Dave Lasley
2016-10-22 18:39:06 +02:00
committed by GitHub
18 changed files with 307 additions and 327 deletions

View File

@@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher (Camptocamp)
# Copyright 2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import base_product_config_template
from . import orderpoint_template
import wizard

View File

@@ -1,43 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher (Camptocamp)
# Copyright 2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{'name': 'Configuration of order point in mass',
'version': '1.0',
'author': "Camptocamp,Odoo Community Association (OCA)",
'maintainer': 'Camptocamp',
'category': 'Warehouse',
'complexity': 'easy', #easy, normal, expert
'depends': ['procurement'],
'description': """
Add a wizard to configure massively order points for multiple product""",
'website': 'http://www.openerp.com',
'init_xml': [],
'update_xml': ["wizard/orderpoint_creator_view.xml", "security/ir.model.access.csv"],
'demo_xml': [],
'test': [],
'installable': False,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
'active': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -1,85 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher (Camptocamp)
# Copyright 2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
""" Base template for product config """
from openerp.osv.orm import browse_record, browse_record_list
class BaseProductConfigTemplate():
""" Abstract class for product config """
def _get_model(self):
""" Get the model for which this template is defined
return a browse record of the model for which
is represented by this template
"""
model = self._inherit
model_obj = self.pool.get(model)
return model_obj
def _get_ids_2_clean(self, cursor, uid, template_br,
product_ids, context=None):
""" hook to select model specific objects to clean
return must return a list of id"""
return []
def _disable_old_instances(self, cursor, uid, template_br_list,
product_ids, context=None):
""" Clean old instance by setting those inactives """
model_obj = self._get_model()
for template in template_br_list:
ids2clean = self._get_ids_2_clean(cursor, uid, template,
product_ids, context=context)
if self._clean_mode == 'deactivate':
model_obj.write(cursor, uid, ids2clean,
{'active': False}, context=context)
elif self._clean_mode == 'unlink':
model_obj.unlink(cursor, uid, ids2clean, context=context)
def create_instances(self, cursor, uid, template_br,
product_ids, context=None):
""" Create instances of model using template inherited model """
if not isinstance(product_ids, list):
product_ids = [product_ids]
# data = self.copy_data(cursor, uid, template_br.id, context=context)
# copy data will not work in all case and may retrieve erronus value
model_obj = self._get_model()
data = {}
#May rais error on function fields in future
for key in model_obj._columns.keys():
tmp = template_br[key]
if isinstance(tmp, browse_record):
tmp = tmp.id
if isinstance(tmp, browse_record_list):
tmp = [(6, 0, tmp)]
data[key] = tmp
for product_id in product_ids:
data['product_id'] = product_id
model_obj.create(cursor, uid, data, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -1,52 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher (Camptocamp)
# Copyright 2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
""" Template of order point object """
from openerp.osv.orm import Model, fields
from base_product_config_template import BaseProductConfigTemplate
class OrderpointTemplate(BaseProductConfigTemplate, Model):
""" Template for orderpoints """
_name = 'stock.warehouse.orderpoint.template'
_inherit = 'stock.warehouse.orderpoint'
_table = 'stock_warehouse_orderpoint_template'
_clean_mode = 'deactivate'
_columns = {
'product_id': fields.many2one('product.product',
'Product',
required=False,
ondelete='cascade',
domain=[('type','=','product')]),
}
def _get_ids_2_clean(self, cursor, uid, template_br, product_ids, context=None):
""" hook to select model specific objects to clean
return must return a list of id"""
model_obj = self._get_model()
ids_to_del = model_obj.search(cursor, uid,
[('product_id', 'in', product_ids)])
return ids_to_del
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher (Camptocamp)
# Copyright 2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import orderpoint_creator

View File

@@ -1,69 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Yannick Vaucher (Camptocamp)
# Copyright 2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
""" Wizard defining stock.warehouse.orderpoint configurations for selected
products. Those configs are generated using templates """
from openerp.osv.orm import browse_record, TransientModel, fields
_template_register = ['orderpoint_template_id']
class OrderpointCreator(TransientModel):
_name = 'stock.warehouse.orderpoint.creator'
_description = 'Orderpoint Creator'
_columns = {'orderpoint_template_id': fields.many2many(
'stock.warehouse.orderpoint.template',
rel='order_point_creator_rel',
string='Stock rule template')
}
def _get_template_register(self):
"""return a list of the field names which defines a template
This is a hook to allow expending the list of template"""
return _template_register
def action_configure(self, cursor, uid, wiz_id, context=None):
""" action to retrieve wizard data and launch creation of items """
product_ids = context['active_ids']
if isinstance(wiz_id, list):
wiz_id = wiz_id[0]
current = self.browse(cursor, uid, wiz_id, context=context)
for template_field in self._get_template_register():
template_br_list = current[template_field]
if template_br_list:
if isinstance(template_br_list, browse_record):
template_br_list = [template_br_list]
template_model = template_br_list[0]._model._name
template_obj = self.pool.get(template_model)
template_obj._disable_old_instances(cursor, uid, template_br_list,
product_ids, context=context)
for template_br in template_br_list:
template_obj.create_instances(cursor, uid, template_br,
product_ids, context=context)
return {}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="orderpoint_creator_view" model="ir.ui.view">
<field name="name">stock.warehouse.orderpoint.creator</field>
<field name="model">stock.warehouse.orderpoint.creator</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form>
<group string="templates" colspan="4">
<field name="orderpoint_template_id" colspan="4"/>
</group>
<group colspan="2" col="4">
<button special="cancel" string="Cancel" icon="gtk-cancel"/>
<button name="action_configure" string="Apply" type="object" icon="gtk-execute"/>
</group>
</form>
</field>
</record>
<act_window name="Product warehouse config"
res_model="stock.warehouse.orderpoint.creator"
src_model="product.product"
view_mode="form"
target="new"
key2="client_action_multi"
id="act_create_product_conf"/>
</data>
</openerp>

View File

@@ -0,0 +1,42 @@
.. 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
=====================
Order point generator
=====================
Add a wizard to configure order points for multiple products in one go.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues
<https://github.com/OCA/stock-logistics-warehouse/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.
Credits
=======
Contributors
------------
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Matthieu Dietrich <matthieu.dietrich@camptocamp.com>
* Cyril Gaudin <cyril.gaudin@camptocamp.com>
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 http://odoo-community.org.

View File

@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
from . import models
from . import wizard

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Order point generator',
'summary': 'Mass configuration of stock order points',
'version': '9.0.1.0.0',
'author': "Camptocamp, Odoo Community Association (OCA)",
'category': 'Warehouse',
'license': 'AGPL-3',
'website': "http://www.camptocamp.com",
'depends': ['stock'],
'data': [
"wizard/orderpoint_generator_view.xml",
"security/ir.model.access.csv",
],
'installable': True,
'auto_install': False,
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import orderpoint_template

View File

@@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, fields, models
class OrderpointTemplate(models.Model):
""" Template for orderpoints
Here we use same model as stock.warehouse.orderpoint but set product_id
as non mandatory as we cannot remove it. This field will be ignored.
This has the advantage of ensuring that the order point
and the order point template have the same fields.
_table is redefined to separate templates from orderpoints
"""
_name = 'stock.warehouse.orderpoint.template'
_inherit = 'stock.warehouse.orderpoint'
_table = 'stock_warehouse_orderpoint_template'
name = fields.Char(copy=True)
group_id = fields.Many2one(copy=True)
product_id = fields.Many2one(required=False)
product_uom = fields.Many2one(required=False)
def _disable_old_instances(self, product_ids):
""" Clean old instance by setting those inactives
"""
orderpoints = self.env['stock.warehouse.orderpoint'].search(
[('product_id', 'in', product_ids)]
)
orderpoints.write({'active': False})
def _create_instances(self, product_ids):
""" Create instances of model using template inherited model
"""
orderpoint_model = self.env['stock.warehouse.orderpoint']
for data in self.copy_data():
for product_id in product_ids:
data['product_id'] = product_id
orderpoint_model.create(data)
@api.multi
def create_orderpoints(self, product_ids):
""" Create orderpoint for *product_ids* based on these templates.
:type product_ids: list of int
"""
self._disable_old_instances(product_ids)
self._create_instances(product_ids)

View File

@@ -1,3 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_stock_warehouse_orderpoint_creator,stock.warehouse.manage,model_stock_warehouse_orderpoint_creator,stock.group_stock_manager,1,1,1,1
access_stock_warehouse_orderpoint_template,stock.warehouse.manage,model_stock_warehouse_orderpoint_template,stock.group_stock_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
access_stock_warehouse_orderpoint_creator stock.warehouse.manage model_stock_warehouse_orderpoint_creator stock.group_stock_manager 1 1 1 1
2 access_stock_warehouse_orderpoint_template stock.warehouse.manage model_stock_warehouse_orderpoint_template stock.group_stock_manager 1 1 1 1

View File

@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2016 Cyril Gaudin (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_orderpoint_generator

View File

@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*-
# © 2016 Cyril Gaudin (Camptocamp)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.exceptions import UserError
from openerp.tests import TransactionCase
class TestOrderpointGenerator(TransactionCase):
def setUp(self):
super(TestOrderpointGenerator, self).setUp()
self.wizard_model = self.env['stock.warehouse.orderpoint.generator']
self.orderpoint_model = self.env['stock.warehouse.orderpoint']
self.orderpoint_template_model = self.env[
'stock.warehouse.orderpoint.template'
]
self.product_model = self.env['product.product']
self.p1 = self.product_model.create({'name': 'Unittest P1'})
self.p2 = self.product_model.create({'name': 'Unittest P2'})
self.assertEqual(0, self.orderpoint_model.search_count([
('name', '=', 'OP/000445')
]))
self.template = self.orderpoint_template_model.create({
'company_id': self.ref('base.main_company'),
'location_id': self.ref('stock.stock_location_stock'),
'name': 'OP/000445',
'product_max_qty': 15.0,
'product_min_qty': 5.0,
'qty_multiple': 1,
'warehouse_id': self.ref('stock.warehouse0')
})
def check_orderpoint(self):
orderpoints = self.orderpoint_model.search([
('name', '=', 'OP/000445')
], order='product_id')
self.assertEqual(2, len(orderpoints))
self.assertEqual(self.p1, orderpoints[0].product_id)
self.assertEqual(self.p2, orderpoints[1].product_id)
for orderpoint in orderpoints:
for field in ('company_id', 'location_id', 'product_max_qty',
'product_min_qty', 'qty_multiple', 'warehouse_id'):
self.assertEqual(orderpoint[field], self.template[field])
def test_product_orderpoint(self):
wizard = self.wizard_model.with_context(
active_ids=[self.p1.id, self.p2.id]
).create({
'orderpoint_template_id': [(6, 0, [self.template.id])]
})
wizard.action_configure()
self.check_orderpoint()
def test_template_orderpoint(self):
wizard = self.wizard_model.with_context(
active_model='product.template',
active_ids=[self.p1.product_tmpl_id.id, self.p2.product_tmpl_id.id]
).create({
'orderpoint_template_id': [(6, 0, [self.template.id])]
})
wizard.action_configure()
self.check_orderpoint()
def test_template_variants_orderpoint(self):
self.product_model.create({
'product_tmpl_id': self.p1.product_tmpl_id.id,
'name': 'Unittest P1 variant'
})
wizard = self.wizard_model.with_context(
active_model='product.template',
active_ids=[self.p1.product_tmpl_id.id]
).create({
'orderpoint_template_id': [(6, 0, [self.template.id])]
})
with self.assertRaises(UserError):
wizard.action_configure()

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import orderpoint_generator

View File

@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# © 2012-2016 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import _, api, fields, models
from openerp.exceptions import UserError
_template_register = ['orderpoint_template_id']
class OrderpointGenerator(models.TransientModel):
""" Wizard defining stock.warehouse.orderpoint configurations for selected
products. Those configs are generated using templates
"""
_name = 'stock.warehouse.orderpoint.generator'
_description = 'Orderpoint Generator'
orderpoint_template_id = fields.Many2many(
'stock.warehouse.orderpoint.template',
rel='order_point_generator_rel',
string='Stock rule template'
)
@api.multi
def action_configure(self):
""" Action to retrieve wizard data and launch creation of items.
"""
self.ensure_one()
product_ids = self.env.context.get('active_ids')
assert product_ids and isinstance(product_ids, list)
if self.env.context.get('active_model') == 'product.template':
templates = self.env['product.template'].browse(product_ids)
product_ids = templates.mapped('product_variant_ids.id')
if len(product_ids) != len(templates):
raise UserError(_(
'Cannot apply because some of selected '
'products has multiple variants.'
))
self.orderpoint_template_id.create_orderpoints(product_ids)

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="orderpoint_generator_view" model="ir.ui.view">
<field name="name">stock.warehouse.orderpoint.generator</field>
<field name="model">stock.warehouse.orderpoint.generator</field>
<field name="arch" type="xml">
<form string="Product warehouse config">
<label string="This wizard will apply the following orderpoint to selected product(s)"/>
<group string="Templates" colspan="4">
<field name="orderpoint_template_id" colspan="4"/>
</group>
<footer>
<button name="action_configure" string="Apply" type="object" class="oe_highlight"
icon="gtk-execute"/>
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
<act_window name="Product warehouse config"
res_model="stock.warehouse.orderpoint.generator"
src_model="product.product"
view_mode="form"
target="new"
key2="client_action_multi"
id="act_create_product_conf"/>
<act_window name="Product warehouse config"
res_model="stock.warehouse.orderpoint.generator"
src_model="product.template"
view_mode="form"
target="new"
key2="client_action_multi"
id="act_create_product_template_conf"/>
</odoo>