openerp-community-reviewer team mailing list archive
-
openerp-community-reviewer team
-
Mailing list archive
-
Message #00154
lp:~camptocamp/stock-logistic-flows/7.0-add-stock_recompute_availability_on_force-afe into lp:stock-logistic-flows
Alexandre Fayolle - camptocamp has proposed merging lp:~camptocamp/stock-logistic-flows/7.0-add-stock_recompute_availability_on_force-afe into lp:stock-logistic-flows.
Requested reviews:
Stock and Logistic Core Editors (stock-logistic-core-editors)
For more details, see:
https://code.launchpad.net/~camptocamp/stock-logistic-flows/7.0-add-stock_recompute_availability_on_force-afe/+merge/187768
[ADD] stock_recompute_availability_on_force
when forcing a picking availability, recompute availability of other pickings which could be affected by the forcing
--
https://code.launchpad.net/~camptocamp/stock-logistic-flows/7.0-add-stock_recompute_availability_on_force-afe/+merge/187768
Your team Stock and Logistic Core Editors is requested to review the proposed merge of lp:~camptocamp/stock-logistic-flows/7.0-add-stock_recompute_availability_on_force-afe into lp:stock-logistic-flows.
=== modified file 'picking_dispatch/__init__.py'
--- picking_dispatch/__init__.py 2012-07-12 13:51:36 +0000
+++ picking_dispatch/__init__.py 2013-09-26 13:01:57 +0000
@@ -18,7 +18,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
-
from . import picking_dispatch
import wizard
-import report
\ No newline at end of file
+import report
=== modified file 'picking_dispatch/__openerp__.py'
--- picking_dispatch/__openerp__.py 2012-12-10 10:51:23 +0000
+++ picking_dispatch/__openerp__.py 2013-09-26 13:01:57 +0000
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
##############################################################################
#
-# Author: Alexandre Fayolle
+# Author: Alexandre Fayolle
# Copyright 2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
@@ -19,14 +19,14 @@
#
##############################################################################
{'name' : 'Picking dispatch',
- 'version' : '0.4',
+ 'version' : '1.0',
'author' : 'Camptocamp',
'maintainer': 'Camptocamp',
'category': 'Products',
'complexity': "normal", # easy, normal, expert
'depends' : ['stock',
'base',
- 'report_webkit',
+ 'report_webkit',
'base_headers_webkit',
],
'description': """Dispatch pickings to employees working in warehouses
=== modified file 'picking_dispatch/picking_dispatch_view.xml'
--- picking_dispatch/picking_dispatch_view.xml 2012-12-10 10:51:23 +0000
+++ picking_dispatch/picking_dispatch_view.xml 2013-09-26 13:01:57 +0000
@@ -6,36 +6,43 @@
<field name="type">form</field>
<field name="model">picking.dispatch</field>
<field name="arch" type="xml">
- <form string="Picking Dispatch">
- <field name="name" />
- <field name="picker_id"/>
- <field name="date"/>
- <field name="backorder_id"/>
- <notebook colspan="4">
- <page string="Stock Moves">
- <field name="move_ids" nolabel="1" colspan='4' widget="many2many"
- domain="[('dispatch_id','=',False), ('state','in',('confirmed','waiting','assigned'))]" />
- </page>
- <page string="Related Picking">
- <field name="related_picking_ids" colspan="4" nolabel="1"/>
- </page>
- <page string='Notes'>
- <field name='notes' colspan='4' nolabel='1'/>
- </page>
- </notebook>
- <separator colspan="4"/>
-
- <field name="state" widget="statusbar" statusbar_visible="draft,assigned,progress,done"/>
- <group col="4" colspan="2">
+ <form string="Picking Dispatch" version="7.0">
+ <header>
<button name="action_assign" states="draft" string="Assign"
- type="object" icon="gtk-apply"/>
+ class="oe_highlight" type="object"/>
<button name="action_progress" states="assigned" string="Start"
- type="object" icon="gtk-apply"/>
+ class="oe_highlight" type="object"/>
<button name="action_done" states="progress" string="Done"
- type="object" icon="gtk-jump-to"/>
+ class="oe_highlight" type="object"/>
<button name="action_cancel" states="draft,assigned,progress" string="Cancel"
- type="object" icon="gtk-cancel"/>
- </group>
+ type="object"/>
+ <field name="state" widget="statusbar" statusbar_visible="draft,confirm"/>
+ </header>
+ <sheet>
+ <div class="oe_title">
+ <div class="oe_edit_only">
+ <label for="name" string="Picking Dispatch Name"/>
+ </div>
+ <h1>
+ <field name="name"/>
+ </h1>
+ </div>
+ <field name="picker_id"/>
+ <field name="date"/>
+ <field name="backorder_id"/>
+ <notebook colspan="4">
+ <page string="Stock Moves">
+ <field name="move_ids" nolabel="1" colspan='4' widget="many2many"
+ domain="[('dispatch_id','=',False), ('state','in',('confirmed','waiting','assigned'))]" />
+ </page>
+ <page string="Related Picking">
+ <field name="related_picking_ids" colspan="4" nolabel="1"/>
+ </page>
+ <page string='Notes'>
+ <field name='notes' colspan='4' nolabel='1'/>
+ </page>
+ </notebook>
+ </sheet>
</form>
</field>
</record>
@@ -69,8 +76,7 @@
domain="[('state','=','done')]" help="Dispatch order already processed"/>
<separator orientation="vertical" />
<filter icon="terp-accessories-archiver-minus" string="Back Orders"
- domain="[('backorder_id', '!=', False)]" help="Is a Back Order"
- groups="base.group_extended"/>
+ domain="[('backorder_id', '!=', False)]" help="Is a Back Order"/>
<separator orientation="vertical"/>
<field name="name"/>
<field name="picker_id">
@@ -79,7 +85,7 @@
</field>
<field name="state"/>
<field name="date"/>
- <field name="backorder_id" groups="base.group_extended" widget="selection"/>
+ <field name="backorder_id" widget="selection"/>
</group>
<newline/>
<group expand="0" string="Group By...">
@@ -108,8 +114,7 @@
<menuitem action='action_picking_dispatch_tree'
parent='stock.menu_stock_warehouse_mgmt'
id='menu_action_picking_dispatch'
- sequence='10'
- groups="base.group_extended" />
+ sequence='10'/>
<!-- Stock Out picking : add dispatch infos -->
<record id="view_picking_form" model="ir.ui.view">
@@ -118,11 +123,11 @@
<field name="inherit_id" ref="stock.view_picking_out_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
- <xpath expr="/form/notebook/page[@string='Additional info']" position="after">
+ <page string="Additional Info" position="after">
<page string="Related Dispatch">
<field name="related_dispatch_ids" nolabel="1" colspan="4"/>
</page>
- </xpath>
+ </page>
</field>
</record>
@@ -133,11 +138,11 @@
<field name="inherit_id" ref="stock.view_picking_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
- <xpath expr="/form/notebook/page[@string='Additional info']" position="after">
+ <page string="Additional Info" position="after">
<page string="Related Dispatch">
<field name="related_dispatch_ids" nolabel="1" colspan="4"/>
</page>
- </xpath>
+ </page>
</field>
</record>
@@ -163,11 +168,11 @@
<field name="type">form</field>
<field name="inherit_id" ref="product.product_normal_form_view"/>
<field name="arch" type="xml">
- <xpath expr="/form/notebook/page[@string='Descriptions']/field[@name='description_purchase']"
+ <field name="description_purchase"
position="after">
<separator string="Warehouse Description"/>
<field colspan="4" name="description_warehouse" nolabel="1"/>
- </xpath>
+ </field>
</field>
</record>
@@ -183,6 +188,7 @@
</field>
</field>
</record>
+
<record id="view_move_form" model="ir.ui.view">
<field name="name">stock.move.form</field>
<field name="model">stock.move</field>
@@ -194,6 +200,7 @@
</field>
</field>
</record>
+
<record id="view_move_search" model="ir.ui.view">
<field name="name">stock.move.search</field>
<field name="model">stock.move</field>
@@ -205,42 +212,20 @@
</field>
</field>
</record>
- <record id="view_move_search_reception_outcoming_picking" model="ir.ui.view">
- <field name="name">stock.move.outgoing.search2</field>
- <field name="model">stock.move</field>
- <field name="type">search</field>
- <field name="inherit_id" ref="stock.view_move_search_reception_outcoming_picking"/>
- <field name="arch" type="xml">
- <field name="prodlot_id" position="after">
- <field name="dispatch_id" widget="selection"/>
- </field>
- </field>
- </record>
- <record id="view_move_search_reception_incoming_picking" model="ir.ui.view">
- <field name="name">stock.move.incoming.search2</field>
- <field name="model">stock.move</field>
- <field name="type">search</field>
- <field name="inherit_id" ref="stock.view_move_search_reception_incoming_picking"/>
- <field name="arch" type="xml">
- <field name="prodlot_id" position="after">
- <field name="dispatch_id" widget="selection" />
- </field>
- </field>
- </record>
- <record model="ir.ui.view" id="view_company_form_inherit">
- <field name="name">res.company.form.inherit</field>
- <field name="model">res.company</field>
- <field name="type">form</field>
- <field name="inherit_id" ref="base.view_company_form" />
- <field name="arch" type="xml">
- <notebook position="inside">
- <page string="Pickings" position="inside">
- <field name="default_picker_id" colspan ="4" />
- </page>
- </notebook>
- </field>
- </record>
+ <record model="ir.ui.view" id="view_company_form_inherit">
+ <field name="name">res.company.form.inherit</field>
+ <field name="model">res.company</field>
+ <field name="type">form</field>
+ <field name="inherit_id" ref="base.view_company_form"/>
+ <field name="arch" type="xml">
+ <notebook position="inside">
+ <page string="Pickings" position="inside">
+ <field name="default_picker_id" colspan ="4"/>
+ </page>
+ </notebook>
+ </field>
+ </record>
</data>
</openerp>
=== modified file 'picking_dispatch/report/dispatch_report.py'
--- picking_dispatch/report/dispatch_report.py 2012-12-10 10:51:23 +0000
+++ picking_dispatch/report/dispatch_report.py 2013-09-26 13:01:57 +0000
@@ -1,28 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
-# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)
-# @author Nicolas Bessi, Joel Grand-Guillaume, Alexandre Fayolle
-#
-# WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
-# consequences resulting from its eventual inadequacies and bugs
-# End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly adviced to contract a Free Software
-# Service Company
-#
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# 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 General Public License for more details.
-#
-## -*- coding: utf-8 -*-
-
+# Authors: Nicolas Bessi, Joel Grand-Guillaume, Alexandre Fayolle
+# 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/>.
+#
+##############################################################################
import operator
from report import report_sxw
import pooler
=== modified file 'picking_dispatch/wizard/__init__.py'
--- picking_dispatch/wizard/__init__.py 2012-07-12 13:51:36 +0000
+++ picking_dispatch/wizard/__init__.py 2013-09-26 13:01:57 +0000
@@ -1,29 +1,21 @@
# -*- coding: utf-8 -*-
##############################################################################
#
-# Copyright (c) 2011 Camptocamp SA (http://www.camptocamp.com)
-# @author Joel Grand-Guillaume
-#
-# WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
-# consequences resulting from its eventual inadequacies and bugs
-# End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly adviced to contract a Free Software
-# Service Company
-#
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Author: Joel Grand-Guillaume
+# 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 create_dispatch
=== modified file 'picking_dispatch/wizard/create_dispatch.py'
--- picking_dispatch/wizard/create_dispatch.py 2012-12-10 10:51:23 +0000
+++ picking_dispatch/wizard/create_dispatch.py 2013-09-26 13:01:57 +0000
@@ -18,7 +18,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
-
from openerp.osv.orm import TransientModel
from openerp.osv import osv, fields
from openerp.tools.translate import _
@@ -44,7 +43,7 @@
user_obj = self.pool.get('res.users')
company = user_obj.browse(cr, uid, uid).company_id
return company.default_picker_id.id if company.default_picker_id else False
-
+
_defaults = {
'name': lambda obj, cr, uid, ctxt: obj.pool.get('ir.sequence').get(cr, uid, 'picking.dispatch'),
'date': fields.date.context_today,
@@ -80,7 +79,7 @@
"confirm, waiting or assigned state can be used)")]
for dispatch_name, mvs in already_dispatched_ids.iteritems():
mvs.sort()
-
+
problems.append(_('Dispatch %s already covers moves %s') % \
(dispatch_name,
u', '.join(['%s [%s]' % (mv, pck) for mv, pck in mvs]))
=== modified file 'picking_dispatch/wizard/create_dispatch_view.xml'
--- picking_dispatch/wizard/create_dispatch_view.xml 2012-12-10 10:51:23 +0000
+++ picking_dispatch/wizard/create_dispatch_view.xml 2013-09-26 13:01:57 +0000
@@ -6,30 +6,35 @@
<field name="model">picking.dispatch.creator</field>
<field name="type">form</field>
<field name="arch" type="xml">
- <form string="Picking Dispatch properties">
- <group colspan="4">
- <field name="name" colspan='4'/>
+ <form string="Picking Dispatch properties" version="7.0">
+ <separator string="Picking Dispatch"/>
+ <label string="Assign picking operations to an employee."/> <br/>
+ <br/>
+ <label for="name"/>
+ <field name="name"/>
+ <group>
<field name="date" />
<field name="picker_id"/>
- <field name="notes" colspan='4'/>
- </group>
- <separator string="" colspan="4"/>
- <group colspan="4" col="6">
- <button special="cancel" string="_Cancel" icon="gtk-cancel" />
- <button icon="terp-gtk-go-back-rtl" string="_Create" name="action_create_dispatch" type="object"/>
- </group>
+ </group>
+ <label for="notes"/>
+ <field name="notes"/>
+ <footer>
+ <button name="action_create_dispatch" string="_Create" type="object" class="oe_highlight"/>
+ or
+ <button string="Cancel" class="oe_link" special="cancel"/>
+ </footer>
</form>
</field>
</record>
<act_window name="Create Picking Dispatch"
res_model="picking.dispatch.creator"
- src_model="stock.picking"
+ src_model="stock.picking.in"
view_mode="form"
target="new"
key2="client_action_multi"
id="picking_dispatch_create_act_window"/>
-
+
<!-- <act_window name="Create Dispatch Order"
res_model="picking.dispatch"
src_model="stock.picking"
=== modified file 'product_serial/__init__.py'
--- product_serial/__init__.py 2011-10-25 13:57:35 +0000
+++ product_serial/__init__.py 2013-09-26 13:01:57 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
# -*- encoding: utf-8 -*-
##############################################################################
#
@@ -24,3 +25,31 @@
import company
import prodlot_wizard
+=======
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Product serial module for OpenERP
+# Copyright (C) 2008 Raphaël Valyi
+#
+# 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/>.
+#
+##############################################################################
+
+import product
+import stock
+import company
+import wizard
+
+>>>>>>> MERGE-SOURCE
=== modified file 'product_serial/__openerp__.py'
--- product_serial/__openerp__.py 2013-05-08 13:14:45 +0000
+++ product_serial/__openerp__.py 2013-09-26 13:01:57 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
# -*- encoding: utf-8 -*-
##############################################################################
#
@@ -45,3 +46,55 @@
"installable": True
}
+=======
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2008 Raphaël Valyi
+# Copyright (C) 2013 Akretion (http://www.akretion.com/)
+#
+# 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" : "Product Serial",
+ "summary": "Enhance Serial Number management",
+ "version" : "1.0",
+ "author" : "Akretion, NaN·tic",
+ "website" : "http://www.akretion.com",
+ "depends" : ["stock"],
+ "category" : "Generic Modules/Inventory Control",
+ "license": "AGPL-3",
+ "description":"""Enhance the management of Production Lots (Serial Numbers) in OpenERP.
+
+ Here are the additionnal features proposed by this module:
+ 1) Add a new selection field 'Lot split type' on the product form under the 'Inventory' tab to specify how the Production Lots should be split on the Pickings (you should also enable 'Track Incoming/Outgoing Lots').
+ 2) If the option 'Active auto split' is active for the Company, OpenERP will automagically splits up picking list movements into one movement per product instance or logistical unit packing quantity (in that case, only the first logistical unit is taken into account at the present time. Improvement to take them all to be done !).
+ 3) Turns Incoming Pickings into an editable grid where you can directly type the codes of a new production lot and/or tracking number to create and associate to the move (it also checks it doesn't exist yet).
+ 4) If the option 'Group invoice lines' is active for the Company, OpenERP will group the invoice lines to make it look like the Sale/Purchase Order when generating an Invoice from a Picking.
+
+ """,
+ "demo" : ["product_demo.xml"],
+ "data" : [
+ "product_view.xml",
+ "company_view.xml",
+ "stock_view.xml",
+ "wizard/prodlot_wizard_view.xml",
+ ],
+ "active": False,
+ "installable": True
+}
+
+>>>>>>> MERGE-SOURCE
=== modified file 'product_serial/company.py'
--- product_serial/company.py 2011-10-25 13:57:35 +0000
+++ product_serial/company.py 2013-09-26 13:01:57 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
# -*- encoding: utf-8 -*-
##############################################################################
#
@@ -37,3 +38,44 @@
company()
+=======
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Product serial module for OpenERP
+# Copyright (C) 2010-2011 Anevia. All Rights Reserved
+# Copyright (C) 2013 Akretion
+# @author: Sebastien Beau <sebastien.beau@xxxxxxxxxxxx>
+# @author: Alexis de Lattre <alexis.delattre@xxxxxxxxxxxx>
+#
+# 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 openerp.osv import orm, fields
+
+class company(orm.Model):
+ _inherit = 'res.company'
+
+ _columns = {
+ 'autosplit_is_active': fields.boolean('Active auto split', help="Active the automatic split of move lines on the pickings."),
+ 'is_group_invoice_line': fields.boolean('Group invoice lines', help="If active, OpenERP will group the identical invoice lines when generating an invoice from a picking. If inactive, each move line will generate one invoice line."),
+ }
+
+ _defaults = {
+ 'autosplit_is_active': True,
+ 'is_group_invoice_line': True,
+ }
+
+>>>>>>> MERGE-SOURCE
=== modified file 'product_serial/company_view.xml'
--- product_serial/company_view.xml 2011-10-25 13:57:35 +0000
+++ product_serial/company_view.xml 2013-09-26 13:01:57 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
<?xml version="1.0" ?>
<openerp>
<data>
@@ -19,3 +20,25 @@
</data>
</openerp>
+=======
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data>
+
+<record id="autosplit_company" model="ir.ui.view">
+ <field name="name">res.company.autosplit.config</field>
+ <field name="model">res.company</field>
+ <field name="inherit_id" ref="base.view_company_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//group[@name='account_grp']" position="after">
+ <group name="product_serial" string="Product serial">
+ <field name="autosplit_is_active"/>
+ <field name="is_group_invoice_line"/>
+ </group>
+ </xpath>
+ </field>
+</record>
+
+</data>
+</openerp>
+>>>>>>> MERGE-SOURCE
=== modified file 'product_serial/product.py'
--- product_serial/product.py 2011-10-25 13:57:35 +0000
+++ product_serial/product.py 2013-09-26 13:01:57 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
# -*- encoding: utf-8 -*-
##############################################################################
#
@@ -35,3 +36,45 @@
product_product()
+=======
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Product serial module for OpenERP
+# Copyright (C) 2008 Raphaël Valyi
+# Copyright (C) 2013 Akretion (http://www.akretion.com/)
+#
+# 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 openerp.osv import fields, orm
+
+class product_product(orm.Model):
+ _inherit = "product.product"
+
+ _columns = {
+ 'lot_split_type': fields.selection([
+ ('none','None'),
+ ('single','Single'),
+ ('lu','Logistical Unit')
+ ], 'Lot split type', required=True,
+ help="None: no split ; single: 1 line/product unit ; Logistical Unit: split using the first Logistical Unit quantity of the product form packaging tab (to be improved to take into account all LU)"),
+ }
+
+ _defaults = {
+ 'lot_split_type': 'none',
+ }
+
+>>>>>>> MERGE-SOURCE
=== added file 'product_serial/product_demo.xml'
--- product_serial/product_demo.xml 1970-01-01 00:00:00 +0000
+++ product_serial/product_demo.xml 2013-09-26 13:01:57 +0000
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Product serial module for OpenERP
+ Copyright (C) 2013 Akretion (http://www.akretion.com/)
+ @author: Alexis de Lattre <alexis.delattre@xxxxxxxxxxxx>
+ The licence is in the file __openerp__.py
+-->
+
+<openerp>
+<data noupdate="1">
+
+<!-- Laptop E5023 -->
+<record id="product.product_product_25" model="product.product">
+ <field name="lot_split_type">single</field>
+ <field name="track_production">True</field>
+ <field name="track_incoming">True</field>
+ <field name="track_outgoing">True</field>
+</record>
+
+</data>
+</openerp>
=== modified file 'product_serial/product_view.xml'
--- product_serial/product_view.xml 2013-05-08 13:11:33 +0000
+++ product_serial/product_view.xml 2013-09-26 13:01:57 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
@@ -16,3 +17,24 @@
</data>
</openerp>
+=======
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data>
+
+
+<record id="view_normal_unique_production_number_form" model="ir.ui.view">
+ <field name="name">product.normal.stock.form.unique_production_number.inherit</field>
+ <field name="model">product.product</field>
+ <field name="inherit_id" ref="stock.view_normal_procurement_locations_form"/>
+ <field name="arch" type="xml">
+ <field name="track_outgoing" position="after">
+ <field name="lot_split_type" groups="stock.group_tracking_lot"/>
+ </field>
+ </field>
+</record>
+
+
+</data>
+</openerp>
+>>>>>>> MERGE-SOURCE
=== modified file 'product_serial/stock.py'
--- product_serial/stock.py 2013-05-14 16:34:57 +0000
+++ product_serial/stock.py 2013-09-26 13:01:57 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
# -*- encoding: utf-8 -*-
##############################################################################
#
@@ -295,3 +296,295 @@
stock_production_lot()
+=======
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Product serial module for OpenERP
+# Copyright (C) 2008 Raphaël Valyi
+# Copyright (C) 2011 Anevia S.A. - Ability to group invoice lines
+# written by Alexis Demeaulte <alexis.demeaulte@xxxxxxxxxx>
+# Copyright (C) 2011-2013 Akretion - Ability to split lines on logistical units
+# written by Emmanuel Samyn
+#
+# 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 openerp.osv import fields, orm
+import hashlib
+from openerp.tools.translate import _
+
+
+class stock_move(orm.Model):
+ _inherit = "stock.move"
+ # We order by product name because otherwise, after the split,
+ # the products are "mixed" and not grouped by product name any more
+ _order = "picking_id, name, id"
+
+ def copy(self, cr, uid, id, default=None, context=None):
+ if not default:
+ default = {}
+ default['new_prodlot_code'] = False
+ return super(stock_move, self).copy(cr, uid, id, default, context=context)
+
+ def _get_prodlot_code(self, cr, uid, ids, field_name, arg, context=None):
+ res = {}
+ for move in self.browse(cr, uid, ids):
+ res[move.id] = move.prodlot_id and move.prodlot_id.name or False
+ return res
+
+ def _set_prodlot_code(self, cr, uid, ids, name, value, arg, context=None):
+ if not value: return False
+
+ if isinstance(ids, (int, long)):
+ ids = [ids]
+
+ for move in self.browse(cr, uid, ids, context=context):
+ product_id = move.product_id.id
+ existing_prodlot = move.prodlot_id
+ if existing_prodlot: #avoid creating a prodlot twice
+ self.pool.get('stock.production.lot').write(cr, uid, existing_prodlot.id, {'name': value})
+ else:
+ prodlot_id = self.pool.get('stock.production.lot').create(cr, uid, {
+ 'name': value,
+ 'product_id': product_id,
+ })
+ move.write({'prodlot_id' : prodlot_id})
+
+ def _get_tracking_code(self, cr, uid, ids, field_name, arg, context=None):
+ res = {}
+ for move in self.browse(cr, uid, ids):
+ res[move.id] = move.tracking_id and move.tracking_id.name or False
+ return res
+
+ def _set_tracking_code(self, cr, uid, ids, name, value, arg, context=None):
+ if not value: return False
+
+ if isinstance(ids, (int, long)):
+ ids = [ids]
+
+ for move in self.browse(cr, uid, ids, context=context):
+ product_id = move.product_id.id
+ existing_tracking = move.tracking_id
+ if existing_tracking: #avoid creating a tracking twice
+ self.pool.get('stock.tracking').write(cr, uid, existing_tracking.id, {'name': value})
+ else:
+ tracking_id = self.pool.get('stock.tracking').create(cr, uid, {
+ 'name': value,
+ })
+ move.write({'tracking_id' : tracking_id})
+
+ _columns = {
+ 'new_prodlot_code': fields.function(_get_prodlot_code, fnct_inv=_set_prodlot_code,
+ method=True, type='char', size=64,
+ string='Create Serial Number', select=1
+ ),
+ 'new_tracking_code': fields.function(_get_tracking_code, fnct_inv=_set_tracking_code,
+ method=True, type='char', size=64,
+ string='Create Tracking', select=1
+ ),
+ }
+
+ def action_done(self, cr, uid, ids, context=None):
+ """
+ If we autosplit moves without reconnecting them 1 by 1, at least when some move which has descendants is split
+ The following situation would happen (alphabetical order is order of creation, initially b and a pre-exists, then a is split, so a might get assigned and then split too):
+ Incoming moves b, c, d
+ Outgoing moves a, e, f
+ Then we have those links: b->a, c->a, d->a
+ and: b->, b->e, b->f
+ The following code will detect this situation and reconnect properly the moves into only: b->a, c->e and d->f
+ """
+ result = super(stock_move, self).action_done(cr, uid, ids, context)
+ for move in self.browse(cr, uid, ids):
+ if move.product_id.lot_split_type and move.move_dest_id and move.move_dest_id.id:
+ cr.execute("select stock_move.id from stock_move_history_ids left join stock_move on stock_move.id = stock_move_history_ids.child_id where parent_id=%s and stock_move.product_qty=1", (move.id,))
+ unitary_out_moves = cr.fetchall()
+ if unitary_out_moves and len(unitary_out_moves) > 1:
+ unitary_in_moves = []
+ out_node = False
+ counter = 0
+ while len(unitary_in_moves) != len(unitary_out_moves) and counter < len(unitary_out_moves):
+ out_node = unitary_out_moves[counter][0]
+ cr.execute("select stock_move.id from stock_move_history_ids left join stock_move on stock_move.id = stock_move_history_ids.parent_id where child_id=%s and stock_move.product_qty=1", (out_node,))
+ unitary_in_moves = cr.fetchall()
+ counter += 1
+
+ if len(unitary_in_moves) == len(unitary_out_moves):
+ unitary_out_moves.reverse()
+ unitary_out_moves.pop()
+ unitary_in_moves.reverse()
+ unitary_in_moves.pop()
+ counter = 0
+ for unitary_in_move in unitary_in_moves:
+ cr.execute("delete from stock_move_history_ids where parent_id=%s and child_id=%s", (unitary_in_moves[counter][0], out_node))
+ cr.execute("update stock_move_history_ids set parent_id=%s where parent_id=%s and child_id=%s", (unitary_in_moves[counter][0], move.id, unitary_out_moves[counter][0]))
+ counter += 1
+
+ return result
+
+ def split_move(self, cr, uid, ids, context=None):
+ all_ids = list(ids)
+ for move in self.browse(cr, uid, ids, context=context):
+ qty = move.product_qty
+ lu_qty = False
+ if move.product_id.lot_split_type == 'lu':
+ if not move.product_id.packaging:
+ raise orm.except_orm(_('Error :'), _("Product '%s' has 'Lot split type' = 'Logistical Unit' but is missing packaging information.") % (move.product_id.name))
+ lu_qty = move.product_id.packaging[0].qty
+ elif move.product_id.lot_split_type == 'single':
+ lu_qty = 1
+ if lu_qty and qty > 1:
+ # Set existing move to LU quantity
+ self.write(cr, uid, move.id, {'product_qty': lu_qty, 'product_uos_qty': move.product_id.uos_coeff})
+ qty -= lu_qty
+ # While still enough qty to create a new move, create it
+ while qty >= lu_qty:
+ all_ids.append( self.copy(cr, uid, move.id, {'state': move.state, 'prodlot_id': None}) )
+ qty -= lu_qty
+ # Create a last move for the remainder qty
+ if qty > 0:
+ all_ids.append( self.copy(cr, uid, move.id, {'state': move.state, 'prodlot_id': None, 'product_qty': qty}) )
+ return all_ids
+
+
+class stock_picking(orm.Model):
+ _inherit = "stock.picking"
+
+ def action_assign_wkf(self, cr, uid, ids, context=None):
+ result = super(stock_picking, self).action_assign_wkf(cr, uid, ids, context=context)
+
+ for picking in self.browse(cr, uid, ids):
+ if picking.company_id.autosplit_is_active:
+ for move in picking.move_lines:
+ # Auto split
+ if ((move.product_id.track_production and move.location_id.usage == 'production') or \
+ (move.product_id.track_production and move.location_dest_id.usage == 'production') or \
+ (move.product_id.track_incoming and move.location_id.usage == 'supplier') or \
+ (move.product_id.track_outgoing and move.location_dest_id.usage == 'customer')):
+ self.pool.get('stock.move').split_move(cr, uid, [move.id])
+
+ return result
+
+ # Because stock move line can be splitted by the module, we merge
+ # invoice lines (if option 'is_group_invoice_line' is activated for the company)
+ # at the following conditions :
+ # - the product is the same and
+ # - the discount is the same and
+ # - the unit price is the same and
+ # - the description is the same and
+ # - taxes are the same
+ # - they are from the same sale order lines (requires extra-code)
+ # we merge invoice line together and do the sum of quantity and
+ # subtotal.
+ def action_invoice_create(self, cursor, user, ids, journal_id=False,
+ group=False, type='out_invoice', context=None):
+ invoice_dict = super(stock_picking, self).action_invoice_create(cursor, user,
+ ids, journal_id, group, type, context=context)
+
+ for picking_key in invoice_dict:
+ invoice = self.pool.get('account.invoice').browse(cursor, user, invoice_dict[picking_key], context=context)
+ if not invoice.company_id.is_group_invoice_line:
+ continue
+
+ new_line_list = {}
+
+ for line in invoice.invoice_line:
+
+ # Build a key
+ key = unicode(line.product_id.id) + ";" \
+ + unicode(line.discount) + ";" \
+ + unicode(line.price_unit) + ";" \
+ + line.name + ";"
+
+ # Add the tax key part
+ tax_tab = []
+ for tax in line.invoice_line_tax_id:
+ tax_tab.append(tax.id)
+ tax_tab.sort()
+ for tax in tax_tab:
+ key = key + unicode(tax) + ";"
+
+ # Add the sale order line part but check if the field exist because
+ # it's install by a specific module (not from addons)
+ if self.pool.get('ir.model.fields').search(cursor, user,
+ [('name', '=', 'sale_order_lines'), ('model', '=', 'account.invoice.line')], context=context) != []:
+ order_line_tab = []
+ for order_line in line.sale_order_lines:
+ order_line_tab.append(order_line.id)
+ order_line_tab.sort()
+ for order_line in order_line_tab:
+ key = key + unicode(order_line) + ";"
+
+
+ # Get the hash of the key
+ hash_key = hashlib.sha224(key.encode('utf8')).hexdigest()
+
+ # if the key doesn't already exist, we keep the invoice line
+ # and we add the key to new_line_list
+ if not new_line_list.has_key(hash_key):
+ new_line_list[hash_key] = {
+ 'id': line.id,
+ 'quantity': line.quantity,
+ 'price_subtotal': line.price_subtotal,
+ }
+ # if the key already exist, we update new_line_list and
+ # we delete the invoice line
+ else:
+ new_line_list[hash_key]['quantity'] = new_line_list[hash_key]['quantity'] + line.quantity
+ new_line_list[hash_key]['price_subtotal'] = new_line_list[hash_key]['price_subtotal'] \
+ + line.price_subtotal
+ self.pool.get('account.invoice.line').unlink(cursor, user, line.id, context=context)
+
+ # Write modifications made on invoice lines
+ for hash_key in new_line_list:
+ line_id = new_line_list[hash_key]['id']
+ del new_line_list[hash_key]['id']
+ self.pool.get('account.invoice.line').write(cursor, user, line_id, new_line_list[hash_key], context=context)
+
+ return invoice_dict
+
+
+class stock_production_lot(orm.Model):
+ _inherit = "stock.production.lot"
+
+ def _last_location_id(self, cr, uid, ids, field_name, arg, context=None):
+ """Retrieves the last location where the product with given serial is.
+ Instead of using dates we assume the product is in the location having the
+ highest number of products with the given serial (should be 1 if no mistake). This
+ is better than using move dates because moves can easily be encoded at with wrong dates."""
+ res = {}
+
+ for prodlot_id in ids:
+ cr.execute(
+ "select location_dest_id " \
+ "from stock_move inner join stock_report_prodlots on stock_report_prodlots.location_id = location_dest_id and stock_report_prodlots.prodlot_id = %s " \
+ "where stock_move.prodlot_id = %s and stock_move.state=%s "\
+ "order by stock_report_prodlots.qty DESC ",
+ (prodlot_id, prodlot_id, 'done'))
+ results = cr.fetchone()
+
+ res[prodlot_id] = results and results[0] or False
+
+ return res
+
+ _columns = {
+ 'last_location_id': fields.function(_last_location_id,
+ type="many2one", relation="stock.location",
+ string="Last location",
+ help="Display the current stock location of this production lot"),
+ }
+
+>>>>>>> MERGE-SOURCE
=== modified file 'product_serial/stock_view.xml'
--- product_serial/stock_view.xml 2013-03-27 15:01:58 +0000
+++ product_serial/stock_view.xml 2013-09-26 13:01:57 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
<?xml version="1.0"?>
<openerp>
<data>
@@ -297,3 +298,162 @@
</data>
</openerp>
+=======
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data>
+
+ <!-- In the form view of Incoming/Outgoing products, make the tree view of move lines editable and set the parameters that are only present in the form view to the fields of the tree view-->
+
+<record id="view_stock_move_tree_editable" model="ir.ui.view">
+ <field name="name">view_stock_move_tree_editable</field>
+ <field name="model">stock.move</field>
+ <field name="inherit_id" ref="stock.view_move_picking_tree" />
+ <field name="arch" type="xml">
+ <xpath expr="/tree[@string='Stock Moves']" position="attributes">
+ <attribute name="editable">bottom</attribute>
+ </xpath>
+ <xpath expr="//field[@name='product_id']" position="attributes">
+ <attribute name="on_change">onchange_product_id(product_id,location_id,location_dest_id, False)</attribute>
+ </xpath>
+ <!-- we need to have the "name" field in the tree view, because it is a required field that is set by the on_change on product_id... otherwise we get an error message when adding a new move line via the editable tree view -->
+ <xpath expr="//field[@name='product_id']" position="after">
+ <field name="name" invisible="1"/>
+ </xpath>
+ <!-- For the field product_qty, the on_change is already in the original tree view -->
+ <xpath expr="//field[@name='prodlot_id']" position="attributes">
+ <attribute name="context">{'location_id':location_id, 'product_id':product_id}</attribute>
+ <attribute name="domain">[('product_id','=?',product_id)]</attribute>
+ <attribute name="on_change">onchange_lot_id(prodlot_id, product_qty, location_id, product_id, product_uom)</attribute>
+ </xpath>
+ <!-- I want to have the field location_id visible and placed before dest_location_id -->
+ <xpath expr="//field[@name='location_id']" position="replace">
+ </xpath>
+ <xpath expr="//field[@name='location_dest_id']" position="before">
+ <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"
+ groups="stock.group_production_lot"
+ states="draft,waiting,confirmed,assigned"
+ type="object" icon="gtk-justify-fill" />
+ </xpath>
+ <!-- In the form view of Incoming shipments, add the "new_prodlot_code" field -->
+ <xpath expr="//field[@name='prodlot_id']" position="before">
+ <field name="new_prodlot_code" groups="stock.group_production_lot" invisible="context.get('picking_type') != 'in' or False" />
+ </xpath>
+ <!-- In the form view of Incoming shipments, add the "new_tracking_code" field -->
+ <xpath expr="//field[@name='tracking_id']" position="before">
+ <field name="new_tracking_code" groups="stock.group_tracking_lot" invisible="context.get('picking_type') != 'in' or False" />
+ </xpath>
+ </field>
+</record>
+
+<record id="view_move_form_manual_split" model="ir.ui.view">
+ <field name="name">view.stock.move.form.manual_split</field>
+ <field name="model">stock.move</field>
+ <field name="inherit_id" ref="stock.view_move_form" />
+ <field name="priority">24</field> <!-- inherit after product_hardware_revision -->
+ <field name="arch" type="xml">
+ <xpath expr="//button[@string='Split']" position="after">
+ <button name="split_move" string="Manual split"
+ states="draft,waiting,confirmed,assigned"
+ type="object" icon="gtk-justify-fill" />
+ </xpath>
+ </field>
+</record>
+
+<!-- Add "last_location_id" on prod lot view -->
+<record id="view_production_lot_form_unique_production_number" model="ir.ui.view">
+ <field name="name">view_production_lot_form_unique_production_number</field>
+ <field name="model">stock.production.lot</field>
+ <field name="inherit_id" ref="stock.view_production_lot_form"/>
+ <field name="arch" type="xml">
+ <field name="stock_available" position="after">
+ <field name="last_location_id"/>
+ </field>
+ </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>
+>>>>>>> MERGE-SOURCE
=== added directory 'product_serial/wizard'
=== added file 'product_serial/wizard/__init__.py'
--- product_serial/wizard/__init__.py 1970-01-01 00:00:00 +0000
+++ product_serial/wizard/__init__.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,24 @@
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Product serial module for OpenERP
+# Copyright (C) 2013 Akretion (http://www.akretion.com)
+# @author Alexis de Lattre <alexis.delattre@xxxxxxxxxxxx>
+#
+# 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/>.
+#
+##############################################################################
+
+import prodlot_wizard
+
=== renamed file 'product_serial/prodlot_wizard.py' => 'product_serial/wizard/prodlot_wizard.py'
--- product_serial/prodlot_wizard.py 2011-10-25 13:57:35 +0000
+++ product_serial/wizard/prodlot_wizard.py 2013-09-26 13:01:57 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
# -*- encoding: utf-8 -*-
##############################################################################
#
@@ -117,3 +118,123 @@
stock_picking_prodlot_selection_wizard()
+=======
+# -*- encoding: utf-8 -*-
+##############################################################################
+#
+# Product serial module for OpenERP
+# Copyright (C) 2010 NaN Projectes de Programari Lliure, S.L.
+# http://www.NaN-tic.com
+# Copyright (C) 2013 Akretion (http://www.akretion.com)
+# @author Alexis de Lattre <alexis.delattre@xxxxxxxxxxxx>
+#
+# 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 openerp.osv import orm, fields
+from openerp.tools.translate import _
+
+
+class stock_picking_prodlot_selection_wizard(orm.TransientModel):
+ _name = 'stock.picking.prodlot.selection'
+
+ _columns = {
+ '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),
+ 'create_prodlots': fields.boolean('Create New Serial Numbers'),
+ }
+
+ _defaults = {
+ 'create_prodlots': False,
+ }
+
+
+ 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']
+ record = self.browse(cr, uid, ids[0], context)
+ prefix = record.prefix or ''
+ suffix = record.suffix or ''
+ try:
+ first_number = int(record.first_number)
+ except:
+ 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."))
+
+ if last_number < first_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.'))
+
+ number_length = len(record.first_number)
+
+ picking_id = context['active_id']
+ current_number = first_number
+ 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:
+ continue
+
+ current_lot = '%%s%%0%dd%%s' % number_length % (prefix, current_number, suffix)
+ if record.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)
+ else:
+ # Search existing prodlots
+ 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)
+
+ ctx = context.copy()
+ ctx['location_id'] = move.location_id.id
+ 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.stock_available < move.product_qty:
+ 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
+
+ return True
+
+>>>>>>> MERGE-SOURCE
=== added file 'product_serial/wizard/prodlot_wizard_view.xml'
--- product_serial/wizard/prodlot_wizard_view.xml 1970-01-01 00:00:00 +0000
+++ product_serial/wizard/prodlot_wizard_view.xml 2013-09-26 13:01:57 +0000
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data>
+
+
+<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="arch" type="xml">
+ <form string="Select Production Lots" version="7.0">
+ <group>
+ <field name="product_id" />
+ <field name="prefix"/>
+ <field name="first_number"/>
+ <field name="last_number"/>
+ <field name="suffix"/>
+ <field name="create_prodlots"/>
+ </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>
+ </field>
+</record>
+
+
+<record id="action_prodlot_selection" model="ir.actions.act_window">
+ <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="inherit_id" ref="stock.view_picking_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='origin']/.." position="after">
+ <group name="prodlot_wizard">
+ <button type="action" name="%(action_prodlot_selection)d" string="Spread Production Lots" states="draft,confirmed,assigned"/>
+ </group>
+ </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.in</field>
+ <field name="inherit_id" ref="stock.view_picking_in_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='origin']/.." position="after">
+ <group name="prodlot_wizard">
+ <button type="action" name="%(action_prodlot_selection)d" string="Spread Production Lots" states="draft,confirmed,assigned"/>
+ </group>
+ </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.out</field>
+ <field name="inherit_id" ref="stock.view_picking_out_form"/>
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='origin']/.." position="after">
+ <group name="prodlot_wizard">
+ <button type="action" name="%(action_prodlot_selection)d" string="Spread Production Lots" states="draft,confirmed,assigned"/>
+ </group>
+ </xpath>
+ </field>
+</record>
+
+
+</data>
+</openerp>
=== modified file 'stock_location_flow_creator/__openerp__.py'
--- stock_location_flow_creator/__openerp__.py 2012-05-24 14:07:16 +0000
+++ stock_location_flow_creator/__openerp__.py 2013-09-26 13:01:57 +0000
@@ -36,7 +36,7 @@
"security/ir.model.access.csv"],
'demo_xml': [],
'test': [],
- 'installable': True,
+ 'installable': False,
'images': [],
'auto_install': True,
'license': 'AGPL-3',
=== added directory 'stock_move_backdating'
=== added file 'stock_move_backdating/AUTHORS.txt'
--- stock_move_backdating/AUTHORS.txt 1970-01-01 00:00:00 +0000
+++ stock_move_backdating/AUTHORS.txt 2013-09-26 13:01:57 +0000
@@ -0,0 +1,3 @@
+Marco Dieckhoff < marco.dieckhoff@xxxxxxxxxxxx>
+Alex Comba <alex.comba@xxxxxxxxxxx>
+Lorenzo Battistini <lorenzo.battistini@xxxxxxxxxxx>
=== added file 'stock_move_backdating/__init__.py'
--- stock_move_backdating/__init__.py 1970-01-01 00:00:00 +0000
+++ stock_move_backdating/__init__.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2012 BREMSKERL-REIBBELAGWERKE EMMERLING GmbH & Co. KG
+# Author Marco Dieckhoff
+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
+#
+# 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/>.
+#
+##############################################################################
+import stock
+import wizard
+import account
=== added file 'stock_move_backdating/__openerp__.py'
--- stock_move_backdating/__openerp__.py 1970-01-01 00:00:00 +0000
+++ stock_move_backdating/__openerp__.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2012+ BREMSKERL-REIBBELAGWERKE EMMERLING GmbH & Co. KG
+# Author Marco Dieckhoff
+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
+#
+# 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": "Stock Move Backdating",
+ "version": "1.0",
+ 'author' : ['Marco Dieckhoff, BREMSKERL', 'Agile Business Group'],
+ "category": "Stock Logistics",
+ 'website' : 'www.bremskerl.com',
+ "depends": ["stock"],
+ "summary": "Allows back-dating of stock moves",
+ "description": """This module allows to register old stock moves (with date != now).
+On stock moves, user can specify the "Actual Movement Date", that will be used as movement date""",
+ 'data': [
+ "view/stock_view.xml",
+ "wizard/stock_partial_picking_view.xml",
+ ],
+ 'demo': [],
+ 'installable': True,
+}
=== added file 'stock_move_backdating/account.py'
--- stock_move_backdating/account.py 1970-01-01 00:00:00 +0000
+++ stock_move_backdating/account.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
+#
+# 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 openerp.osv import fields, orm
+
+class account_move(orm.Model):
+ _inherit = "account.move"
+
+ def create(self, cr, uid, vals, context=None):
+ if context is None:
+ context = {}
+ period_obj = self.pool.get('account.period')
+ if context.get('move_date'):
+ period_ids = period_obj.find(cr, uid, dt=context['move_date'], context=context)
+ if period_ids:
+ vals['period_id'] = period_ids[0]
+ vals['date'] = context['move_date']
+ return super(account_move,self).create(cr, uid, vals, context=context)
=== added directory 'stock_move_backdating/i18n'
=== added file 'stock_move_backdating/stock.py'
--- stock_move_backdating/stock.py 1970-01-01 00:00:00 +0000
+++ stock_move_backdating/stock.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,97 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2012 BREMSKERL-REIBBELAGWERKE EMMERLING GmbH & Co. KG
+# Author Marco Dieckhoff
+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
+#
+# 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 openerp.osv import fields, orm
+from datetime import datetime
+from openerp.tools.translate import _
+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
+
+
+class stock_move(orm.Model):
+ _name = "stock.move"
+ _inherit = _name
+
+ _columns = {
+ 'date_backdating': fields.datetime(
+ "Actual Movement Date", readonly=False,
+ states={'done': [('readonly', True)], 'cancel': [('readonly', True)]},
+ help="Date when the move action was committed. "
+ "Will set the move date to this date instead "
+ "of current date when processing to done."
+ ),
+ }
+
+ def action_done(self, cr, uid, ids, context=None):
+ # look at previous state and find date_backdating
+ backdating_dates = {}
+
+ for move in self.browse(cr, uid, ids, context=context):
+ # if not already in done and date is given
+ if (move.state != 'done') and (move.date_backdating):
+ backdating_dates[move.id] = move.date_backdating
+
+ # do actual processing
+ result = super(stock_move, self).action_done(cr, uid, ids, context)
+
+ # overwrite date field where applicable
+ for move in self.browse(cr, uid, backdating_dates.keys(), context=context):
+ self.write(cr, uid, [move.id], {'date': backdating_dates[move.id]}, context=context)
+
+ return result
+
+ def on_change_date_backdating(self, cr, uid, ids, date_backdating, context=None):
+ """ Test if date is in the past
+ @param date_backdating: date
+ """
+ # do nothing if empty
+ if (not date_backdating):
+ return {}
+
+ dt = datetime.strptime(date_backdating, DEFAULT_SERVER_DATETIME_FORMAT)
+ NOW = datetime.now()
+
+ if (dt > NOW):
+ warning = {'title': _('Error!'), 'message': _('You can not process an actual movement date in the future.')}
+ values = {'date_backdating': NOW.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}
+ return {'warning': warning, 'value': values}
+
+ # otherwise, ok
+ return {}
+
+ def _create_account_move_line(self, cr, uid, move, src_account_id, dest_account_id, reference_amount, reference_currency_id, context=None):
+ if context is None:
+ context = {}
+ res = super(stock_move, self)._create_account_move_line(
+ cr, uid, move,
+ src_account_id, dest_account_id, reference_amount,
+ reference_currency_id, context=context
+ )
+ for o2m_tuple in res:
+ date = False
+ if move.date_backdating:
+ date = move.date_backdating[:10]
+ elif move.date:
+ date = move.date[:10]
+ o2m_tuple[2]['date'] = date
+ if 'move_date' not in context:
+ context['move_date'] = date
+ return res
=== added directory 'stock_move_backdating/view'
=== added file 'stock_move_backdating/view/stock_view.xml'
--- stock_move_backdating/view/stock_view.xml 1970-01-01 00:00:00 +0000
+++ stock_move_backdating/view/stock_view.xml 2013-09-26 13:01:57 +0000
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openerp>
+ <data>
+
+ <!-- views -->
+ <record id="view_move_form" model="ir.ui.view">
+ <field name="name">stock.move.backdating.form</field>
+ <field name="model">stock.move</field>
+ <field name="inherit_id" ref="stock.view_move_form" />
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='date_expected']" position="after">
+ <field name="date_backdating" on_change="on_change_date_backdating(date_backdating)" />
+ </xpath>
+ </field>
+ </record>
+
+ <record id="view_move_picking_form" model="ir.ui.view">
+ <field name="name">stock.move.picking.backdating.form</field>
+ <field name="model">stock.move</field>
+ <field name="inherit_id" ref="stock.view_move_picking_form" />
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='date_expected']" position="after">
+ <field name="date_backdating" on_change="on_change_date_backdating(date_backdating)" />
+ </xpath>
+ </field>
+ </record>
+
+ </data>
+</openerp>
=== added directory 'stock_move_backdating/wizard'
=== added file 'stock_move_backdating/wizard/__init__.py'
--- stock_move_backdating/wizard/__init__.py 1970-01-01 00:00:00 +0000
+++ stock_move_backdating/wizard/__init__.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
+#
+# 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/>.
+#
+##############################################################################
+import stock_partial_picking
+
=== added file 'stock_move_backdating/wizard/stock_partial_picking.py'
--- stock_move_backdating/wizard/stock_partial_picking.py 1970-01-01 00:00:00 +0000
+++ stock_move_backdating/wizard/stock_partial_picking.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
+#
+# 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 osv import fields, osv
+
+class stock_partial_picking_line(osv.TransientModel):
+ _inherit = 'stock.partial.picking.line'
+ _name = "stock.partial.picking.line"
+ _columns = {
+ 'date_backdating': fields.datetime("Actual Movement Date"),
+ }
+
+ def on_change_date_backdating(self, cr, uid, ids, date_backdating, context=None):
+ move_obj = self.pool.get('stock.move')
+ return move_obj.on_change_date_backdating(cr, uid, ids, date_backdating, context=context)
+
+
+class stock_partial_picking(osv.TransientModel):
+ _inherit = 'stock.partial.picking'
+ name = 'stock.partial.picking'
+
+ def _partial_move_for(self, cr, uid, move):
+ partial_move = super(stock_partial_picking, self)._partial_move_for(cr, uid, move)
+ partial_move.update({'date_backdating': move.date_backdating},)
+ return partial_move
+
+ def do_partial(self, cr, uid, ids, context=None):
+ partial = self.browse(cr, uid, ids[0], context=context)
+ for wizard_line in partial.move_ids:
+ date_backdating = wizard_line.date_backdating
+ wizard_line.move_id.write({'date_backdating': date_backdating,})
+ return super(stock_partial_picking, self).do_partial(cr, uid, ids, context=context)
+
+
+
=== added file 'stock_move_backdating/wizard/stock_partial_picking_view.xml'
--- stock_move_backdating/wizard/stock_partial_picking_view.xml 1970-01-01 00:00:00 +0000
+++ stock_move_backdating/wizard/stock_partial_picking_view.xml 2013-09-26 13:01:57 +0000
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+ <record id="stock_partial_picking_form" model="ir.ui.view">
+ <field name="name">stock.partial.picking.form</field>
+ <field name="model">stock.partial.picking</field>
+ <field name="inherit_id" ref="stock.stock_partial_picking_form" />
+ <field name="arch" type="xml">
+ <xpath expr="//field[@name='move_ids']/tree//field[@name='product_uom']" position="after">
+ <field name="date_backdating" on_change="on_change_date_backdating(date_backdating)"/>
+ </xpath>
+ </field>
+ </record>
+
+ </data>
+</openerp>
=== modified file 'stock_move_on_hold/__openerp__.py'
--- stock_move_on_hold/__openerp__.py 2012-03-07 12:56:37 +0000
+++ stock_move_on_hold/__openerp__.py 2013-09-26 13:01:57 +0000
@@ -40,7 +40,7 @@
'stock_workflow.xml',
],
'test': [],
- 'installable': True,
+ 'installable': False,
'active': False,
'certificate': '',
}
=== added directory 'stock_picking_invoice_link'
=== added file 'stock_picking_invoice_link/AUTHORS.txt'
--- stock_picking_invoice_link/AUTHORS.txt 1970-01-01 00:00:00 +0000
+++ stock_picking_invoice_link/AUTHORS.txt 2013-09-26 13:01:57 +0000
@@ -0,0 +1,1 @@
+Lorenzo Battistini <lorenzo.battistini@xxxxxxxxxxx>
=== added file 'stock_picking_invoice_link/__init__.py'
--- stock_picking_invoice_link/__init__.py 1970-01-01 00:00:00 +0000
+++ stock_picking_invoice_link/__init__.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
+#
+# 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/>.
+#
+##############################################################################
+
+import stock
=== added file 'stock_picking_invoice_link/__openerp__.py'
--- stock_picking_invoice_link/__openerp__.py 1970-01-01 00:00:00 +0000
+++ stock_picking_invoice_link/__openerp__.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
+#
+# 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': "Picking Invoice Link",
+ 'version': '0.1',
+ 'category': 'Warehouse Management',
+ 'summary': 'Adds link between pickings and generated invoices',
+ 'description': """
+This module adds a link between pickings and generated invoices. So that user can easly reach the invoice related to the picking.
+
+Probably to be modified according to bug 1169998 (when solution will come)
+https://bugs.launchpad.net/openobject-addons/+bug/1169998
+""",
+ 'author': 'Agile Business Group',
+ 'website': 'http://www.agilebg.com',
+ 'license': 'AGPL-3',
+ "depends": ['stock'],
+ "data": [
+ "stock_view.xml",
+ ],
+ "demo": [],
+ 'test': [
+ ],
+ "active": False,
+ "installable": True
+}
=== added file 'stock_picking_invoice_link/stock.py'
--- stock_picking_invoice_link/stock.py 1970-01-01 00:00:00 +0000
+++ stock_picking_invoice_link/stock.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>)
+#
+# 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 openerp.osv import fields, orm
+from openerp.tools.translate import _
+
+class stock_picking(orm.Model):
+ _inherit = "stock.picking"
+
+ _columns = {
+ 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True),
+ }
+
+ def _invoice_hook(self, cr, uid, picking, invoice_id):
+ res = super(stock_picking,self)._invoice_hook(cr, uid, picking, invoice_id)
+ picking.write({'invoice_id': invoice_id})
+ return res
+
+class stock_picking_out(orm.Model):
+ _inherit = "stock.picking.out"
+
+ _columns = {
+ 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True),
+ }
+
+class stock_picking_in(orm.Model):
+ _inherit = "stock.picking.in"
+
+ _columns = {
+ 'invoice_id': fields.many2one('account.invoice', 'Invoice', readonly=True),
+ }
=== added file 'stock_picking_invoice_link/stock_view.xml'
--- stock_picking_invoice_link/stock_view.xml 1970-01-01 00:00:00 +0000
+++ stock_picking_invoice_link/stock_view.xml 2013-09-26 13:01:57 +0000
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+<data>
+ <record id="view_picking_out_form" model="ir.ui.view">
+ <field name="name">stock.picking.out.form</field>
+ <field name="model">stock.picking.out</field>
+ <field name="inherit_id" ref="stock.view_picking_out_form"></field>
+ <field name="arch" type="xml">
+ <field name="invoice_state" position="after">
+ <field name="invoice_id" string="Invoice" groups="account.group_account_invoice" attrs="{'invisible':[('invoice_state', '=', 'none')]}"/>
+ </field>
+ </field>
+ </record>
+ <record id="view_picking_in_form" model="ir.ui.view">
+ <field name="name">stock.picking.in.form</field>
+ <field name="model">stock.picking.in</field>
+ <field name="inherit_id" ref="stock.view_picking_in_form"></field>
+ <field name="arch" type="xml">
+ <field name="invoice_state" position="after">
+ <field name="invoice_id" string="Invoice" groups="account.group_account_invoice" attrs="{'invisible':[('invoice_state', '=', 'none')]}"/>
+ </field>
+ </field>
+ </record>
+</data>
+</openerp>
=== added directory 'stock_picking_invoice_link/test'
=== added directory 'stock_recompute_availability_on_force'
=== added file 'stock_recompute_availability_on_force/__init__.py'
--- stock_recompute_availability_on_force/__init__.py 1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/__init__.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Alexandre Fayolle
+# Copyright 2013 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 stock
=== added file 'stock_recompute_availability_on_force/__openerp__.py'
--- stock_recompute_availability_on_force/__openerp__.py 1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/__openerp__.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Alexandre Fayolle
+# Copyright 2013 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' : 'Recompute stock move state on Force Availability',
+ 'version' : '0.1',
+ 'category' : 'Warehouse Management',
+ 'description': '''
+ When the user forced availability of a stock.picking, recompute the availability of other pickings which could be affected by this forcing.
+ ''',
+ 'author' : 'Camptocamp',
+ 'website' : 'http://www.camptocamp.com',
+ 'depends' : ['stock', 'procurement'],
+ 'data' : ['procurement_workflow.xml',
+ ],
+ 'demo' : [],
+ 'test' : [],
+ 'installable': True,
+ 'auto_install' : False,
+ 'application' : False,
+}
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== added file 'stock_recompute_availability_on_force/procurement_workflow.xml'
--- stock_recompute_availability_on_force/procurement_workflow.xml 1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/procurement_workflow.xml 2013-09-26 13:01:57 +0000
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+ <record id="trans_ready_confirm" model="workflow.transition">
+ <field name="act_from" ref="procurement.act_make_done"/>
+ <field name="act_to" ref="procurement.act_confirm"/>
+ <field name="condition">check_cancel_assigned_moves()</field>
+ <field name="signal">button_recompute_availability</field>
+ </record>
+
+ </data>
+</openerp>
=== added file 'stock_recompute_availability_on_force/stock.py'
--- stock_recompute_availability_on_force/stock.py 1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/stock.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Author: Alexandre Fayolle
+# Copyright 2013 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/>.
+#
+##############################################################################
+import logging
+from openerp.osv import orm
+from openerp import netsvc
+
+_logger = logging.getLogger(__name__)
+
+class stock_picking(orm.Model):
+ _inherit = 'stock.picking'
+
+ def force_assign(self, cr, uid, ids, *args):
+ """
+ when forcing availability on a picking, get the procurements
+ for all the assigned moves for the same product on the same
+ stock location, and send the recompute_availability signal to
+ reset them in confirmed state. The scheduler can then be run
+ to recompute availability.
+ """
+ _logger.debug('stock.picking force_assign %s', ids)
+ move_obj = self.pool.get('stock.move')
+ procurement_obj = self.pool.get('procurement.order')
+ product_locations = set()
+ for pick in self.browse(cr, uid, ids):
+ for move in pick.move_lines:
+ product_locations.add((move.product_id.id, move.location_id.id))
+ res = super(stock_picking, self).force_assign(cr, uid, ids, *args)
+ other_move_ids = []
+ for product_id, location_id in product_locations:
+ other_move_ids += move_obj.search(cr, uid,
+ [('product_id', '=', product_id),
+ ('location_id', '=', location_id),
+ ('state', '=', 'assigned'),
+ ('picking_id', 'not in', ids),
+ ])
+ procurement_ids = procurement_obj.search(cr, uid,
+ [('move_id', 'in', other_move_ids),
+ ('procure_method', '=', 'make_to_stock'),
+ ])
+ move_ids = [proc.move_id.id for proc in procurement_obj.browse(cr, uid, procurement_ids)]
+ move_obj.cancel_assign(cr, uid, move_ids)
+ wf_service = netsvc.LocalService("workflow")
+ for proc_id in procurement_ids:
+ _logger.debug('button_recompute_availability on procurement.order %d', proc_id)
+ wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_recompute_availability', cr)
+ our_procurement_ids = procurement_obj.search(cr, uid, [('move_id.picking_id', 'in', ids)])
+ for proc_id in our_procurement_ids:
+ _logger.debug('button_check on procurement.order %d', proc_id)
+ wf_service.trg_validate(uid, 'procurement.order', proc_id, 'button_check', cr)
+ return res
+
+class stock_picking_in(orm.Model):
+ _inherit = 'stock.picking.in'
+ def force_assign(self, cr, uid, ids, *args):
+ return self.pool.get('stock.picking').force_assign(cr, uid, ids, *args)
+
+class stock_picking_out(orm.Model):
+ _inherit = 'stock.picking.out'
+ def force_assign(self, cr, uid, ids, *args):
+ return self.pool.get('stock.picking').force_assign(cr, uid, ids, *args)
+
+
+class procurement_order(orm.Model):
+ _inherit = 'procurement.order'
+
+ def check_cancel_assigned_moves(self, cr, uid, ids, context=None):
+ return all(procurement.move_id.state == 'confirmed' for procurement in self.browse(cr, uid, ids, context=context))
=== added file 'stock_recompute_availability_on_force/stock_view.xml'
--- stock_recompute_availability_on_force/stock_view.xml 1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/stock_view.xml 2013-09-26 13:01:57 +0000
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<openerp>
+ <data>
+
+
+ </data>
+</openerp>
=== added directory 'stock_recompute_availability_on_force/test'
=== added directory 'stock_recompute_availability_on_force/tests'
=== added file 'stock_recompute_availability_on_force/tests/__init__.py'
--- stock_recompute_availability_on_force/tests/__init__.py 1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/tests/__init__.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,5 @@
+from . import test_force_recompute
+
+checks = [
+ test_force_recompute,
+]
=== added file 'stock_recompute_availability_on_force/tests/test_force_recompute.py'
--- stock_recompute_availability_on_force/tests/test_force_recompute.py 1970-01-01 00:00:00 +0000
+++ stock_recompute_availability_on_force/tests/test_force_recompute.py 2013-09-26 13:01:57 +0000
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+
+import logging
+
+from openerp import tools
+from openerp.tests import common
+from openerp import netsvc
+
+_logger = logging.getLogger('openerp.test.force_recompute')
+
+class TestForceRecompute(common.TransactionCase):
+
+ def setUp(self):
+ super(TestForceRecompute, self).setUp()
+ cr, uid = self.cr, self.uid
+ self.procurement = self.registry('procurement.order')
+ self.stock_move = self.registry('stock.move')
+ self.stock_picking = self.registry('stock.picking.out')
+ self.stock_location = self.registry('stock.location')
+ self.product_product = self.registry('product.product')
+ self.res_partner = self.registry('res.partner')
+ self.wf_service = netsvc.LocalService("workflow")
+
+ #
+ # create picking and procurement for CARD
+ #
+
+ prod_id = self.product_product.search(cr, uid, [('default_code', '=', 'CARD')])[0]
+ location_data = self.stock_location.default_get(cr, uid,['active', 'usage',
+ 'chained_location_type',
+ 'chainged_auto_packing',
+ 'company_id',
+ 'posx', 'posy', 'posz', 'icon',
+ 'scrap_location'])
+ location_data.update({'name': 'test', 'usage': 'internal'})
+ location_id = self.stock_location.create(cr, uid, location_data)
+ location_dest_id = self.stock_location.search(cr, uid, [('name', '=', 'Customers')])[0]
+ location_supplier_id = self.stock_location.search(cr, uid, [('name', '=', 'Suppliers')])[0]
+ move_data = self.stock_move.default_get(cr, uid,
+ ['state', 'company_id', 'priority', 'scrapped', 'date', 'date_expected'])
+ move_data.update({'product_id': prod_id,
+ 'product_qty': 15,
+ 'product_uom': 1,
+ 'location_dest_id': location_id,
+ 'location_id': location_supplier_id,
+ 'name': 'initial inventory'})
+ move_id = self.stock_move.create(cr, uid, move_data)
+ self.stock_move.action_done(cr, uid, [move_id])
+ self.picking_ids = []
+ self.procurement_ids = []
+ for i in range(2):
+ picking_data = self.stock_picking.default_get(cr, uid,
+ ['company_id', 'name', 'state', 'move_type', 'type', 'invoice_state', 'date'])
+ move_data = self.stock_move.default_get(cr, uid,
+ ['state', 'company_id', 'priority', 'scrapped', 'date', 'date_expected'])
+ proc_data = self.procurement.default_get(cr, uid, ['state', 'priority', 'date_planned', 'close_move', 'company_id'])
+ picking_id = self.stock_picking.create(cr, uid,
+ picking_data)
+ self.picking_ids.append(picking_id)
+ move_data.update({'location_id': location_id,
+ 'location_dest_id': location_dest_id,
+ 'product_id': prod_id,
+ 'product_qty': 10,
+ 'product_uom': 1,
+ 'partner_id': self.res_partner.search(cr, uid, [('name', '=', 'Camptocamp')])[0],
+ 'picking_id': picking_id,
+ 'name': 'test move card',
+ })
+ move_id = self.stock_move.create(cr, uid, move_data)
+ proc_data.update({'name': 'test procurement',
+ 'product_id': prod_id,
+ 'product_qty': 10,
+ 'product_uom': 1,
+ 'procure_method': 'make_to_stock',
+ 'location_id': location_id,
+ 'move_id': move_id
+ })
+ procurement_id = self.procurement.create(cr, uid, proc_data)
+ self.procurement_ids.append(procurement_id)
+ self.wf_service.trg_validate(uid, 'stock.picking', picking_id, 'button_confirm', cr)
+ self.wf_service.trg_validate(uid, 'procurement.order', procurement_id, 'button_confirm', cr)
+ self.wf_service.trg_validate(uid, 'procurement.order', procurement_id, 'button_check', cr)
+
+ def test_setup(self):
+ cr, uid = self.cr, self.uid
+ picking1 = self.stock_picking.browse(cr, uid, self.picking_ids[0])
+ self.assertEqual(picking1.state, 'assigned')
+ procurement1 = self.procurement.browse(cr, uid, self.procurement_ids[0])
+ self.assertEqual(procurement1.state, 'ready')
+ picking2 = self.stock_picking.browse(cr, uid, self.picking_ids[1])
+ self.assertEqual(picking2.state, 'confirmed')
+ procurement2 = self.procurement.browse(cr, uid, self.procurement_ids[1])
+ self.assertEqual(procurement2.state, 'exception')
+
+ def test_force_assign(self):
+ cr, uid = self.cr, self.uid
+ self.stock_picking.force_assign(cr, uid, [self.picking_ids[1]])
+ # tests
+ picking2 = self.stock_picking.browse(cr, uid, self.picking_ids[1])
+ for move in picking2.move_lines:
+ self.assertEqual(move.state, 'assigned')
+ self.assertEqual(picking2.state, 'assigned')
+ procurement2 = self.procurement.browse(cr, uid, self.procurement_ids[1])
+ self.assertEqual(procurement2.state, 'ready')
+ picking1 = self.stock_picking.browse(cr, uid, self.picking_ids[0])
+ for move in picking1.move_lines:
+ self.assertEqual(move.state, 'confirmed')
+ self.assertEqual(picking1.state, 'confirmed')
+ procurement1 = self.procurement.browse(cr, uid, self.procurement_ids[0])
+ self.assertEqual(procurement1.state, 'confirmed')
+
=== modified file 'stock_sale_filters/__openerp__.py'
--- stock_sale_filters/__openerp__.py 2012-08-10 13:29:34 +0000
+++ stock_sale_filters/__openerp__.py 2013-09-26 13:01:57 +0000
@@ -33,5 +33,5 @@
"demo_xml" : [],
"update_xml" : ["stock_view.xml", ],
"active": False,
- "installable": True
+ "installable": False
}
Follow ups