Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADD] module sale_owner_stock_sourcing #89

Merged
merged 17 commits into from
Jan 21, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ install:
- git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools
- export PATH=${HOME}/maintainer-quality-tools/travis:${PATH}
- travis_install_nightly
- git clone https://github.com/OCA/stock-logistics-workflow -b ${VERSION} $HOME/stock-logistics-workflow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you add the following line to a new text file at the root of the repository called oca-dependencies.txt:

stock-logistics-workflow


script:
- travis_run_tests
Expand Down
1 change: 1 addition & 0 deletions oca-dependencies.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stock-logistics-workflow
26 changes: 26 additions & 0 deletions sale_owner_stock_sourcing/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
Sale Owner Stock Sourcing
=========================

This module allows you to choose for every sale order line the owner of the
stock that will be dispatched. By choosing an owner, the generated deliveries
will then look for stock belonging to the specified partner. If none is
available, then the picking will be waiting availability until stocks with
proper ownership are replenished.

Specifying an owner on a line forces to use the stock of this owner. Leaving
the owner empty allows to use stock (quant) without owner.

Note: pickings and moves both have an owner field. Here we only propagate the
owner to moves. We have integration tests that check that this does end up with
the correct reservation of quants, even if two order lines have different
owners. If we decide instead to propagate the owner_id field of the picking as
well, we will have to split pickings accordingly in case a sale order has lines
with different owners. See the discussion on
https://github.com/odoo/odoo/pull/4548 for details.


Contributors
------------

* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Leonardo Pistone <leonardo.pistone@camptocamp.com>
2 changes: 2 additions & 0 deletions sale_owner_stock_sourcing/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from . import model
39 changes: 39 additions & 0 deletions sale_owner_stock_sourcing/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
#
#
# Author: Yannick Vaucher, Leonardo Pistone
# Copyright 2014-2015 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": "Sale Owner Stock Sourcing",
"summary": "Manage stock ownership on sale order lines",
"version": "0.1",
"author": "Camptocamp",
"license": "AGPL-3",
"category": "Purchase Management",
'complexity': "normal",
"images": [],
"website": "http://www.camptocamp.com",
"depends": ['sale_stock',
'stock_ownership_availability_rules',
],
"demo": [],
"data": ['view/sale_order.xml',
'security/group.xml',
],
'installable': True,
"auto_install": False,
}
32 changes: 32 additions & 0 deletions sale_owner_stock_sourcing/i18n/fr.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_owner_stock_sourcing
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-15 10:18+0000\n"
"PO-Revision-Date: 2015-01-15 10:18+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: sale_owner_stock_sourcing
#: model:ir.model,name:sale_owner_stock_sourcing.model_procurement_order
msgid "Procurement"
msgstr "Procurement"

#. module: sale_owner_stock_sourcing
#: model:ir.model,name:sale_owner_stock_sourcing.model_sale_order_line
msgid "Sales Order Line"
msgstr "Sales Order Line"

#. module: sale_owner_stock_sourcing
#: field:sale.order.line,stock_owner_id:0
msgid "Stock Owner"
msgstr "Stock Owner"

32 changes: 32 additions & 0 deletions sale_owner_stock_sourcing/i18n/sale_owner_stock_sourcing.pot
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * sale_owner_stock_sourcing
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 8.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-01-15 10:19+0000\n"
"PO-Revision-Date: 2015-01-15 10:19+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: sale_owner_stock_sourcing
#: model:ir.model,name:sale_owner_stock_sourcing.model_procurement_order
msgid "Procurement"
msgstr ""

#. module: sale_owner_stock_sourcing
#: model:ir.model,name:sale_owner_stock_sourcing.model_sale_order_line
msgid "Sales Order Line"
msgstr ""

#. module: sale_owner_stock_sourcing
#: field:sale.order.line,stock_owner_id:0
msgid "Stock Owner"
msgstr ""

3 changes: 3 additions & 0 deletions sale_owner_stock_sourcing/model/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import sale_order
from . import procurement
33 changes: 33 additions & 0 deletions sale_owner_stock_sourcing/model/procurement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Author: Leonardo Pistone
# Copyright 2014-2015 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 openerp import models, api


class Procurement(models.Model):
_inherit = 'procurement.order'

@api.model
def _run_move_create(self, procurement):
"""Propagate owner from sale order line to stock move.

This is run when a quotation is validated into a sale order.

"""
res = super(Procurement, self)._run_move_create(procurement)
res['restrict_partner_id'] = procurement.sale_line_id.stock_owner_id.id
return res
29 changes: 29 additions & 0 deletions sale_owner_stock_sourcing/model/sale_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
#
#
# Author: Yannick Vaucher
# Copyright 2014-2015 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 openerp import models, fields


class SaleOrderLine(models.Model):
_inherit = 'sale.order.line'

stock_owner_id = fields.Many2one(
comodel_name='res.partner',
string='Stock Owner')
10 changes: 10 additions & 0 deletions sale_owner_stock_sourcing/security/group.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>

<record model="res.groups" id="base.group_user">
<field name="implied_ids" eval="[(4, ref('sale.group_mrp_properties'))]" />
</record>

</data>
</openerp>
2 changes: 2 additions & 0 deletions sale_owner_stock_sourcing/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import test_propagate_owner_to_move
from . import test_int_sale_to_reservation
96 changes: 96 additions & 0 deletions sale_owner_stock_sourcing/tests/test_int_sale_to_reservation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Author: Leonardo Pistone
# Copyright 2014 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 openerp.tests.common import TransactionCase


class TestIntSaleToReservation(TransactionCase):
"""Integration tests of the propagation of the owner.

Here we check the whole trip from the quotation line to the reservation of
the stock.

"""

def test_one_line_with_owner_reserves_its_stock(self):
self.sol.stock_owner_id = self.owner1
self.so.action_button_confirm()

picking = self.so.picking_ids
picking.action_assign()
self.assertEqual('assigned', picking.state)
self.assertEqual(self.owner1,
picking.move_lines.reserved_quant_ids.owner_id)

def test_one_line_without_owner_reserves_my_stock(self):
self.so.action_button_confirm()

picking = self.so.picking_ids
picking.action_assign()
self.assertEqual('assigned', picking.state)
self.assertEqual(self.my_partner,
picking.move_lines.reserved_quant_ids.owner_id)

def test_two_lines_one_with_owner_reserves_correct_stock(self):
self.sol.copy({'stock_owner_id': self.owner1.id})
self.so.action_button_confirm()

picking = self.so.picking_ids
picking.action_assign()
self.assertEqual('assigned', picking.state)

quant_owners = set([move.reserved_quant_ids.owner_id
for move in picking.move_lines])

self.assertEqual(set([self.my_partner, self.owner1]), quant_owners)

def test_one_line_without_owner_insufficient_stock_respects_stock(self):
self.sol.product_uom_qty = 6000
self.so.action_button_confirm()

picking = self.so.picking_ids
picking.action_assign()
self.assertEqual('partially_available', picking.state)
self.assertEqual(self.my_partner,
picking.move_lines.reserved_quant_ids.owner_id)

def setUp(self):
super(TestIntSaleToReservation, self).setUp()

self.owner1 = self.env.ref('base.res_partner_1')
self.owner2 = self.env.ref('base.res_partner_2')
customer = self.env.ref('base.res_partner_3')
self.my_partner = self.env.user.company_id.partner_id

# this product has no stock in demo data
product = self.env.ref('product.product_product_36')

quant = self.env['stock.quant'].create({
'qty': 5000,
'location_id': self.env.ref('stock.stock_location_stock').id,
'product_id': product.id,
})

quant.copy({'owner_id': self.owner1.id})
quant.copy({'owner_id': self.owner2.id})

self.so = self.env['sale.order'].create({
'partner_id': customer.id,
})
self.sol = self.env['sale.order.line'].create({
'name': '/',
'order_id': self.so.id,
'product_id': product.id,
})
52 changes: 52 additions & 0 deletions sale_owner_stock_sourcing/tests/test_propagate_owner_to_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Author: Leonardo Pistone
# Copyright 2014 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 openerp.tests.common import TransactionCase


class TestPropagateOwner(TransactionCase):

def test_it_propagates_empty_owner_to_the_move(self):
self.so.action_button_confirm()

self.assertEqual(1, len(self.so.picking_ids))
self.assertFalse(self.so.picking_ids.move_lines[0].restrict_partner_id)

def test_it_propagates_owner_to_the_move(self):
self.sol.stock_owner_id = self.partner.id

self.so.action_button_confirm()

self.assertEqual(1, len(self.so.picking_ids))
self.assertEqual(self.so.picking_ids.move_lines[0].restrict_partner_id,
self.partner)

def setUp(self):
super(TestPropagateOwner, self).setUp()
self.SO = self.env['sale.order']
self.SOL = self.env['sale.order.line']

# this product has some stock in demo data
product = self.env.ref('product.product_product_6')
self.partner = self.env.ref('base.res_partner_2')

self.so = self.env['sale.order'].create({
'partner_id': self.env.ref('base.res_partner_2').id,
})
self.sol = self.env['sale.order.line'].create({
'name': '/',
'order_id': self.so.id,
'product_id': product.id,
})
17 changes: 17 additions & 0 deletions sale_owner_stock_sourcing/view/sale_order.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>

<record id="view_order_form_inherit" model="ir.ui.view">
<field name="name">sale.order.form</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale_stock.view_order_form_inherit"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']/form//field[@name='route_id']" position="before">
<field name="stock_owner_id"/>
</xpath>
</field>
</record>

</data>
</openerp>