Skip to content

Commit

Permalink
Merge pull request #89 from yvaucher/8.0-add-sale_owner_stock_sourcing
Browse files Browse the repository at this point in the history
[ADD] module sale_owner_stock_sourcing
  • Loading branch information
gurneyalex committed Jan 21, 2015
2 parents 5ffe859 + 105c063 commit 08b82aa
Show file tree
Hide file tree
Showing 15 changed files with 375 additions and 0 deletions.
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

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>

0 comments on commit 08b82aa

Please sign in to comment.