openerp-community-reviewer team mailing list archive
-
openerp-community-reviewer team
-
Mailing list archive
-
Message #01352
lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows/7.0
Alexis de Lattre has proposed merging lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows/7.0.
Requested reviews:
Stock and Logistic Core Editors (stock-logistic-core-editors)
For more details, see:
https://code.launchpad.net/~akretion-team/stock-logistic-flows/70-product_serial-plus-plus/+merge/195144
This merge proposal is for the module product_serial.
The main new feature of this merge proposal is the ability to select or create prodlots from a text file (simple texte file, 1 line per prodlot)
Other changes :
- Keep the native "split in serial number" button, because it can be usefull for products that need manual split in non-predictable lot size.
- Code cleaning : reduce flake8 warnings in the wizard, remove old code
I hope you will like this merge proposal :-)
--
https://code.launchpad.net/~akretion-team/stock-logistic-flows/70-product_serial-plus-plus/+merge/195144
Your team Stock and Logistic Core Editors is requested to review the proposed merge of lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows/7.0.
=== modified file 'product_serial/__init__.py'
--- product_serial/__init__.py 2013-08-01 12:40:27 +0000
+++ product_serial/__init__.py 2013-11-13 21:39:32 +0000
@@ -19,8 +19,7 @@
#
##############################################################################
-import product
-import stock
-import company
-import wizard
-
+from . import product
+from . import stock
+from . import company
+from . import wizard
=== modified file 'product_serial/stock_view.xml'
--- product_serial/stock_view.xml 2013-05-20 19:39:45 +0000
+++ product_serial/stock_view.xml 2013-11-13 21:39:32 +0000
@@ -32,11 +32,11 @@
<field name="product_packaging" groups="product.group_stock_packaging" domain="[('product_id','=',product_id)]" invisible="context.get('picking_type') != 'out' or False"/>
<field name="location_id" groups="stock.group_locations"/>
</xpath>
- <xpath expr="//button[@name='%(stock.track_line)d']" position="replace">
- <button name="split_move" string="Manual Split"
+ <xpath expr="//button[@name='%(stock.track_line)d']" position="after">
+ <button name="split_move" string="Auto-Split"
groups="stock.group_production_lot"
states="draft,waiting,confirmed,assigned"
- type="object" icon="gtk-justify-fill" />
+ type="object" icon="STOCK_COPY" />
</xpath>
<!-- In the form view of Incoming shipments, add the "new_prodlot_code" field -->
<xpath expr="//field[@name='prodlot_id']" position="before">
@@ -75,83 +75,5 @@
</field>
</record>
- <!-- Wizard to help users input production lots in batch -->
-<!-- TODO Nan-TIc : port to v6
- <record id="view_stock_picking_prodlot_selection" model="ir.ui.view">
- <field name="name">stock.picking.prodlot.selection</field>
- <field name="model">stock.picking.prodlot.selection</field>
- <field name="type">form</field>
- <field name="arch" type="xml">
- <form string="Select Production Lots">
- <field name="product_id" colspan="4"/>
- <field name="first_lot"/>
- <field name="last_lot"/>
- <button type="object" name="action_cancel" string="Cancel" icon="gtk-cancel" special="cancel" colspan="2"/>
- <button type="object" name="action_accept" string="Accept" icon="gtk-ok" colspan="2"/>
- </form>
- </field>
- </record>
-
- <record model="ir.actions.act_window" id="action_prodlot_selection">
- <field name="name">Select Production Lots</field>
- <field name="res_model">stock.picking.prodlot.selection</field>
- <field name="view_type">form</field>
- <field name="view_mode">form</field>
- <field name="target">new</field>
- </record>
-
- <record id="view_picking_form" model="ir.ui.view">
- <field name="name">stock.picking.form.prodlot.selection</field>
- <field name="model">stock.picking</field>
- <field name="type">form</field>
- <field name="inherit_id" ref="stock.view_picking_form"/>
- <field name="arch" type="xml">
- <xpath expr="/form/notebook/page/group/label[@colspan='6']" position="replace">
- <label colspan="5"/>
- <button type="action" name="%(action_prodlot_selection)d" string="Spread Production Lots" states="draft,confirmed,assigned"/>
- </xpath>
- </field>
- </record>
-
- <record id="view_picking_in_form" model="ir.ui.view">
- <field name="name">stock.picking.in.form.prodlot.selection</field>
- <field name="model">stock.picking</field>
- <field name="type">form</field>
- <field name="inherit_id" ref="stock.view_picking_in_form"/>
- <field name="arch" type="xml">
- <xpath expr="/form/notebook/page/group/label[@colspan='5']" position="replace">
- <label colspan="4"/>
- <button type="action" name="%(action_prodlot_selection)d" string="Spread Production Lots" states="draft,confirmed,assigned"/>
- </xpath>
- </field>
- </record>
-
- <record id="view_picking_out_form" model="ir.ui.view">
- <field name="name">stock.picking.out.form</field>
- <field name="model">stock.picking</field>
- <field name="type">form</field>
- <field name="inherit_id" ref="stock.view_picking_out_form"/>
- <field name="arch" type="xml">
- <xpath expr="/form/notebook/page/group/label[@colspan='6']" position="replace">
- <label colspan="5"/>
- <button type="action" name="%(action_prodlot_selection)d" string="Spread Production Lots" states="draft,confirmed,assigned"/>
- </xpath>
- </field>
- </record>
-
- <record id="view_picking_delivery_form" model="ir.ui.view">
- <field name="name">stock.picking.delivery.form</field>
- <field name="model">stock.picking</field>
- <field name="type">form</field>
- <field name="inherit_id" ref="stock.view_picking_delivery_form"/>
- <field name="arch" type="xml">
- <xpath expr="/form/notebook/page/group/label[@colspan='6']" position="replace">
- <label colspan="5"/>
- <button type="action" name="%(action_prodlot_selection)d" string="Spread Production Lots" states="draft,confirmed,assigned"/>
- </xpath>
- </field>
- </record>
--->
-
</data>
</openerp>
=== modified file 'product_serial/wizard/__init__.py'
--- product_serial/wizard/__init__.py 2013-08-01 12:40:27 +0000
+++ product_serial/wizard/__init__.py 2013-11-13 21:39:32 +0000
@@ -20,5 +20,4 @@
#
##############################################################################
-import prodlot_wizard
-
+from . import prodlot_wizard
=== modified file 'product_serial/wizard/prodlot_wizard.py'
--- product_serial/wizard/prodlot_wizard.py 2013-08-01 12:40:27 +0000
+++ product_serial/wizard/prodlot_wizard.py 2013-11-13 21:39:32 +0000
@@ -24,95 +24,163 @@
from openerp.osv import orm, fields
from openerp.tools.translate import _
+import base64
class stock_picking_prodlot_selection_wizard(orm.TransientModel):
_name = 'stock.picking.prodlot.selection'
+ _description = "Select or Create Production Lots"
_columns = {
- 'product_id': fields.many2one('product.product', 'Product', required=True),
+ 'product_id': fields.many2one(
+ 'product.product', 'Product', required=True),
'prefix': fields.char('Prefix', size=256),
'suffix': fields.char('Suffix', size=256),
- 'first_number': fields.char('First Number', size=256, required=True),
- 'last_number': fields.char('Last Number', size=256, required=True),
+ 'first_number': fields.char('First Number', size=256),
+ 'last_number': fields.char('Last Number', size=256),
+ 'prodlot_file': fields.binary(
+ 'Prodlot File',
+ help="The Prodlot file should be a text file with one line per prodlot (all for the same product)."),
'create_prodlots': fields.boolean('Create New Serial Numbers'),
}
+ def _default_create_prodlots(self, cr, uid, context=None):
+ if context is None:
+ context = {}
+ return context.get('default_type') == 'in'
+
_defaults = {
- 'create_prodlots': False,
+ 'create_prodlots': _default_create_prodlots,
}
-
- def select_or_create_prodlots(self, cr, uid, ids, context=None):
- if context is None:
- context = {}
- if not ids:
- return {}
- if not 'active_id' in context:
- return {}
-
- prodlot_obj = self.pool['stock.production.lot']
+ def select_or_create_prodlots_from_file(
+ self, cr, uid, ids, context=None):
+ assert len(ids) == 1, "Only one ID"
+ if context is None:
+ context = {}
+ if not ids:
+ return {}
+ if not 'active_id' in context:
+ return {}
+ picking_id = context['active_id']
+
+ record = self.browse(cr, uid, ids[0], context)
+ if not record.prodlot_file:
+ raise orm.except_orm(
+ _('Error'),
+ _("You should upload a text file containing the production lots."))
+ full_prodlot_str = base64.decodestring(record.prodlot_file)
+ full_prodlot_seq = full_prodlot_str.split('\n')
+ # Remove empty lines
+ prodlot_seq = [prodlot for prodlot in full_prodlot_seq if prodlot]
+ return self._select_or_create_prodlots(
+ cr, uid, picking_id, record.product_id, prodlot_seq,
+ record.create_prodlots, context=context)
+
+ def select_or_create_prodlots_from_interval(
+ self, cr, uid, ids, context=None):
+ assert len(ids) == 1, "Only one ID"
+ if context is None:
+ context = {}
+ if not ids:
+ return {}
+ if not 'active_id' in context:
+ return {}
+ picking_id = context['active_id']
+
record = self.browse(cr, uid, ids[0], context)
prefix = record.prefix or ''
suffix = record.suffix or ''
+ if not record.first_number or not record.last_number:
+ raise orm.except_orm(
+ _('Missing parameters'),
+ _('You should enter a value for the First Number and the Last Number'))
try:
first_number = int(record.first_number)
except:
- raise orm.except_orm(_('Invalid First Number'), _("The field 'First Number' should only contain digits."))
+ raise orm.except_orm(
+ _('Invalid First Number'),
+ _("The field 'First Number' should only contain digits."))
try:
last_number = int(record.last_number)
except:
- raise orm.except_orm(_('Invalid Last Number'), _("The field 'Last Number' should only contain digits."))
+ raise orm.except_orm(
+ _('Invalid Last Number'),
+ _("The field 'Last Number' should only contain digits."))
if last_number < first_number:
- raise orm.except_orm(_('Invalid Numbers'), _('The First Number must be lower than the Last Number.'))
+ raise orm.except_orm(
+ _('Invalid Numbers'),
+ _('The First Number must be lower than the Last Number.'))
if len(record.first_number) != len(record.last_number):
- raise orm.except_orm(_('Invalid Lot Numbers'), _('First and Last Numbers must have the same length.'))
+ raise orm.except_orm(
+ _('Invalid Lot Numbers'),
+ _('First and Last Numbers must have the same length.'))
number_length = len(record.first_number)
+ prodlot_seq = ['%%s%%0%dd%%s' % number_length % (
+ prefix, current_number, suffix) for current_number in
+ range(first_number, last_number+1)
+ ]
+ return self._select_or_create_prodlots(
+ cr, uid, picking_id, record.product_id, prodlot_seq,
+ record.create_prodlots, context=context)
- picking_id = context['active_id']
- current_number = first_number
- picking = self.pool['stock.picking'].browse(cr, uid, picking_id, context=context)
+ def _select_or_create_prodlots(
+ self, cr, uid, picking_id, product, prodlot_seq,
+ create_prodlots, context=None):
+ assert prodlot_seq and isinstance(prodlot_seq, list)
+ prodlot_obj = self.pool['stock.production.lot']
+ picking = self.pool['stock.picking'].browse(
+ cr, uid, picking_id, context=context)
company_id = picking.company_id.id
for move in picking.move_lines:
- if move.prodlot_id or move.product_id != record.product_id:
+ if move.prodlot_id or move.product_id != product:
continue
-
- current_lot = '%%s%%0%dd%%s' % number_length % (prefix, current_number, suffix)
- if record.create_prodlots:
+ try:
+ current_lot = prodlot_seq.pop(0)
+ except:
+ break
+ if create_prodlots:
# Create new prodlot
- lot_id_on_move = prodlot_obj.create(cr, uid, {
- 'product_id': record.product_id.id,
- 'name': current_lot,
- 'company_id': company_id,
- }, context=context)
+ lot_id_on_move = prodlot_obj.create(
+ cr, uid, {
+ 'product_id': product.id,
+ 'name': current_lot,
+ 'company_id': company_id,
+ }, context=context)
else:
# Search existing prodlots
- lot_ids = prodlot_obj.search(cr, uid, [('name','=',current_lot)], limit=1, context=context)
+ lot_ids = prodlot_obj.search(
+ cr, uid, [('name', '=', current_lot)], limit=1,
+ context=context)
if not lot_ids:
- raise orm.except_orm(_('Invalid lot numbers'), _('Production lot %s not found.') % current_lot)
+ raise orm.except_orm(
+ _('Invalid lot numbers'),
+ _('Production lot %s not found.') % current_lot)
ctx = context.copy()
ctx['location_id'] = move.location_id.id
- prodlot = self.pool.get('stock.production.lot').browse(cr, uid, lot_ids[0], ctx)
+ prodlot = self.pool.get('stock.production.lot').browse(
+ cr, uid, lot_ids[0], ctx)
- if prodlot.product_id != record.product_id:
- raise orm.except_orm(_('Invalid lot numbers'), _('Production lot %s exists but not for product %s.') % (current_lot, record.product_id.name))
+ if prodlot.product_id != product:
+ raise orm.except_orm(
+ _('Invalid lot numbers'),
+ _('Production lot %s exists but not for product %s.')
+ % (current_lot, product.name))
if prodlot.stock_available < move.product_qty:
- raise orm.except_orm(_('Invalid lot numbers'), _('Not enough stock available of production lot %s.') % current_lot)
+ raise orm.except_orm(
+ _('Invalid lot numbers'),
+ _('Not enough stock available of production lot %s.')
+ % current_lot)
lot_id_on_move = lot_ids[0]
- self.pool.get('stock.move').write(cr, uid, [move.id], {
- 'prodlot_id': lot_id_on_move,
- }, context=context)
-
- current_number += 1
- if current_number > last_number:
- break
+ self.pool.get('stock.move').write(
+ cr, uid, [move.id], {'prodlot_id': lot_id_on_move},
+ context=context)
return True
-
=== modified file 'product_serial/wizard/prodlot_wizard_view.xml'
--- product_serial/wizard/prodlot_wizard_view.xml 2013-08-01 12:40:27 +0000
+++ product_serial/wizard/prodlot_wizard_view.xml 2013-11-13 21:39:32 +0000
@@ -7,17 +7,23 @@
<field name="name">stock.picking.prodlot.selection</field>
<field name="model">stock.picking.prodlot.selection</field>
<field name="arch" type="xml">
- <form string="Select Production Lots" version="7.0">
- <group>
+ <form string="Select or Create Production Lots" version="7.0">
+ <group name="common">
<field name="product_id" />
+ <field name="create_prodlots"/>
+ </group>
+ <group name="from_interval" string="From Interval">
<field name="prefix"/>
<field name="first_number"/>
<field name="last_number"/>
<field name="suffix"/>
- <field name="create_prodlots"/>
+ <button type="object" name="select_or_create_prodlots_from_interval" string="Run From Interval" class="oe_highlight"/>
+ </group>
+ <group name="from_file" string="From File">
+ <field name="prodlot_file"/>
+ <button type="object" name="select_or_create_prodlots_from_file" string="Run From File" class="oe_highlight"/>
</group>
<footer>
- <button type="object" name="select_or_create_prodlots" string="Validate" class="oe_highlight"/>
<button string="Cancel" special="cancel" class="oe_link"/>
</footer>
</form>
@@ -26,7 +32,7 @@
<record id="action_prodlot_selection" model="ir.actions.act_window">
- <field name="name">Select Production Lots</field>
+ <field name="name">Select or Create Production Lots</field>
<field name="res_model">stock.picking.prodlot.selection</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
Follow ups
-
Re: [Merge] lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Alexis de Lattre, 2014-07-03
-
Re: [Merge] lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Alexandre Fayolle - camptocamp, 2014-07-03
-
Re: [Merge] lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Joël Grand-Guillaume, 2014-05-09
-
Re: [Merge] lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Alexis de Lattre, 2014-04-10
-
Re: [Merge] lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Alexis de Lattre, 2014-03-19
-
Re: [Merge] lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Alexandre Fayolle - camptocamp, 2014-03-19
-
Re: [Merge] lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Alexis de Lattre, 2014-02-21
-
Re: [Merge] lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Yannick Vaucher @ Camptocamp, 2014-02-21
-
Re: lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Lionel Sausin - Numérigraphe, 2014-01-24
-
Re: lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Lionel Sausin - Numérigraphe, 2014-01-24
-
Re: lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Alexis de Lattre, 2014-01-24
-
Re: lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows
From: Lionel Sausin - Numérigraphe, 2014-01-24
-
Re: lp:~akretion-team/stock-logistic-flows/70-product_serial-plus-plus into lp:stock-logistic-flows/7.0
From: Raphaël Valyi - http : //www . akretion . com, 2014-01-10