[MERGE] sale_stock : add a default warehouse on the user

Purpose
======
Our current integration between FSM and Stock works well for basic flows, but quickly shows limitations. We don't handle warehouse by employee. Use-case: each worker has a dedicated van with a dedicated stock. I want to track the material used for each intervention to make sure that nothing is stolen.

+ This is also useful in usecases outside of FSM. Example: I have 3 shops (= 3 warehouses), each employee works in a different shop, the default warehouse set on the Sales Order should be the default warehouse of the salesperson. So this should be done sale_stock (and not in FSM)

Specifications
===========
Add a 'Default Warehouse' field on the user only visible if :

- sale_stock is installed
- the 'multi-warehouses' feature is enabled
- domain should only include warehouses which belong to a company the user has access to
- not required

The field should be editable by a user on his own profile and by the admin on all users.

When creating a FSM task from scratch, adding some products creates a SO, the warehouse on the SO should be the default warehouse of the user set on the FSM task.

On SO creation,  set the 'default warehouse' selected on the user assigned to the SO. If there isn't any 'default warehouse' set on the user, fallback on the current behavior (warehouse with
lowest sequence)

The field on the res.user should be a property field to allow multicompany usage.

Task : 2166382

closes odoo/odoo#44257

Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
Odoo's Mergebot
2020-02-05 17:00:16 +01:00
committed by GitHub
11 changed files with 177 additions and 14 deletions
-6
View File
@@ -16,12 +16,6 @@
<h1 position="replace"/>
<widget name="notification_alert" position="replace"/>
<xpath expr="//field[@name='image_1920']" position="replace"/>
<field name="signature" position="replace" />
<xpath expr="//group[@name='preference_contact']" position="before">
<group>
<field name="signature" />
</group>
</xpath>
<xpath expr="//field[@name='company_id']" position="attributes">
<attribute name="invisible">1</attribute>
</xpath>
+1
View File
@@ -29,6 +29,7 @@ Preferences
'views/sale_stock_portal_template.xml',
'views/stock_production_lot_views.xml',
'views/report_invoice.xml',
'views/res_users_views.xml',
'report/sale_order_report_templates.xml',
'report/stock_report_deliveryslip.xml',
'data/sale_stock_data.xml',
+1
View File
@@ -7,3 +7,4 @@ from . import sale_order
from . import res_config_settings
from . import stock
from . import product_template
from . import res_users
+31
View File
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models, fields
class Users(models.Model):
_inherit = ['res.users']
property_warehouse_id = fields.Many2one('stock.warehouse', string='Default Warehouse', company_dependent=True, check_company=True)
def _get_default_warehouse_id(self):
if self.property_warehouse_id:
return self.property_warehouse_id
return self.env['stock.warehouse'].search([('company_id', '=', self.env.company.id)], limit=1)
def __init__(self, pool, cr):
""" Override of __init__ to add access rights.
Access rights are disabled by default, but allowed
on some specific fields defined in self.SELF_{READ/WRITE}ABLE_FIELDS.
"""
sale_stock_writeable_fields = [
'property_warehouse_id',
]
init_res = super().__init__(pool, cr)
# duplicate list to avoid modifying the original reference
type(self).SELF_READABLE_FIELDS = type(self).SELF_READABLE_FIELDS + sale_stock_writeable_fields
type(self).SELF_WRITEABLE_FIELDS = type(self).SELF_WRITEABLE_FIELDS + sale_stock_writeable_fields
return init_res
+11 -7
View File
@@ -15,9 +15,7 @@ class SaleOrder(models.Model):
@api.model
def _default_warehouse_id(self):
company = self.env.company.id
warehouse_ids = self.env['stock.warehouse'].search([('company_id', '=', company)], limit=1)
return warehouse_ids
return self.user_id._get_default_warehouse_id()
incoterm = fields.Many2one(
'account.incoterms', 'Incoterm',
@@ -66,9 +64,10 @@ class SaleOrder(models.Model):
@api.model
def create(self, vals):
if 'warehouse_id' not in vals and 'company_id' in vals and vals.get('company_id') != self.env.company.id:
vals['warehouse_id'] = self.env['stock.warehouse'].search([('company_id', '=', vals.get('company_id'))], limit=1).id
return super(SaleOrder, self).create(vals)
if 'warehouse_id' not in vals and 'company_id' in vals:
user = self.env['res.users'].browse(vals.get('user_id', False))
vals['warehouse_id'] = user.with_company(vals.get('company_id'))._get_default_warehouse_id().id
return super().create(vals)
def write(self, values):
if values.get('order_line') and self.state == 'sale':
@@ -123,7 +122,12 @@ class SaleOrder(models.Model):
@api.onchange('company_id')
def _onchange_company_id(self):
if self.company_id:
self.warehouse_id = self.env['stock.warehouse'].search([('company_id', '=', self.company_id.id)], limit=1)
self.warehouse_id = self.user_id.with_company(self.company_id.id)._get_default_warehouse_id().id
@api.onchange('user_id')
def onchange_user_id(self):
super().onchange_user_id()
self.warehouse_id = self.user_id.with_company(self.company_id.id)._get_default_warehouse_id().id
@api.onchange('partner_shipping_id')
def _onchange_partner_shipping_id(self):
+1
View File
@@ -6,3 +6,4 @@ from . import test_anglo_saxon_valuation_reconciliation
from . import test_sale_stock
from . import test_sale_stock_lead_time
from . import test_sale_order_dates
from . import test_sale_stock_multicompany
@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, timedelta
from odoo.addons.sale.tests.test_sale_common import TestSaleCommon
from odoo.tests import tagged
from odoo.tests.common import new_test_user
@tagged('post_install', '-at_install')
class TestSaleStockMultiCompany(TestSaleCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.company_B = cls.env['res.company'].create({'name': 'Company B'})
cls.warehouse_A = cls.env['stock.warehouse'].create({'name': 'WH A', 'code': 'WHA', 'company_id': cls.env.company.id, 'partner_id': cls.env.company.partner_id.id})
cls.warehouse_A2 = cls.env['stock.warehouse'].create({'name': 'WH A 2', 'code': 'WHA2', 'company_id': cls.env.company.id, 'partner_id': cls.env.company.partner_id.id, 'sequence': 5})
cls.warehouse_B = cls.env['stock.warehouse'].create({'name': 'WH B', 'code': 'WHB', 'company_id': cls.company_B.id, 'partner_id': cls.company_B.partner_id.id})
cls.warehouse_user = new_test_user(cls.env, 'WarehouseUser', groups='base.group_user,stock.group_stock_user,stock.group_stock_multi_locations,sales_team.group_sale_salesman', company_ids=[(6, 0, (cls.env.company | cls.company_B).ids)])
cls.warehouse_user.with_company(cls.env.company).property_warehouse_id = cls.warehouse_A.id
cls.warehouse_user.with_company(cls.company_B).property_warehouse_id = cls.warehouse_B.id
def test_warehouse_definition_on_so(self):
partner = self.partner
product = self.products['prod_del']
sale_order_vals = {
'partner_id': partner.id,
'partner_invoice_id': partner.id,
'partner_shipping_id': partner.id,
'user_id': False,
'company_id': self.env.company.id,
'order_line': [(0, 0, {
'name': product.name,
'product_id': product.id,
'product_uom_qty': 10,
'product_uom': product.uom_id.id,
'price_unit': product.list_price})],
'pricelist_id': self.env.ref('product.list0').id,
}
sale_order = self.env['sale.order']
so_no_user = sale_order.create(sale_order_vals)
self.assertFalse(so_no_user.user_id.property_warehouse_id)
self.assertEqual(so_no_user.warehouse_id.id, self.warehouse_A2.id)
sale_order_vals2 = {
'partner_id': partner.id,
'partner_invoice_id': partner.id,
'partner_shipping_id': partner.id,
'user_id': self.warehouse_user.id,
'company_id': self.env.company.id,
'order_line': [(0, 0, {
'name': product.name,
'product_id': product.id,
'product_uom_qty': 10,
'product_uom': product.uom_id.id,
'price_unit': product.list_price})],
'pricelist_id': self.env.ref('product.list0').id,
}
so_company_A = sale_order.with_company(self.env.company).create(sale_order_vals2)
self.assertEqual(so_company_A.warehouse_id.id, self.warehouse_A.id)
sale_order_vals3 = {
'partner_id': partner.id,
'partner_invoice_id': partner.id,
'partner_shipping_id': partner.id,
'user_id': self.warehouse_user.id,
'company_id': self.company_B.id,
'order_line': [(0, 0, {
'name': product.name,
'product_id': product.id,
'product_uom_qty': 10,
'product_uom': product.uom_id.id,
'price_unit': product.list_price})],
'pricelist_id': self.env.ref('product.list0').id,
}
so_company_B = sale_order.with_company(self.company_B).create(sale_order_vals3)
self.assertEqual(so_company_B.warehouse_id.id, self.warehouse_B.id)
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_users_view_form_preferences" model="ir.ui.view">
<field name="name">res.users.preferences.form.inherit</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form_simple_modif"/>
<field name="arch" type="xml">
<group name="signature" position="after">
<group name="Warehouses">
<field name="property_warehouse_id" groups="stock.group_stock_multi_warehouses"/>
</group>
</group>
</field>
</record>
<record id="res_users_view_simple_form" model="ir.ui.view">
<field name="name">res.users.simple.form.inherit</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_simple_form"/>
<field name="arch" type="xml">
<group name="phone_numbers" position="after">
<group name="Warehouses">
<field name="property_warehouse_id" groups="stock.group_stock_multi_warehouses"/>
</group>
</group>
</field>
</record>
<record id="res_users_view_form" model="ir.ui.view">
<field name="name">res.users.form.inherit</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<group name="messaging" position="after">
<group name="Warehouses" string="Inventory" groups="stock.group_stock_multi_warehouses">
<field name="property_warehouse_id" groups="stock.group_stock_multi_warehouses"/>
</group>
</group>
</field>
</record>
</data>
</odoo>
@@ -94,6 +94,9 @@ class ProjectCreateSalesOrder(models.TransientModel):
})
sale_order.onchange_partner_id()
sale_order.onchange_partner_shipping_id()
# rewrite the user as the onchange_partner_id erases it
sale_order.write({'user_id': self.project_id.user_id.id})
sale_order.onchange_user_id()
# create the sale lines, the map (optional), and assign existing timesheet to sale lines
self._make_billable(sale_order)
@@ -67,6 +67,9 @@ class ProjectTaskCreateSalesOrder(models.TransientModel):
})
sale_order.onchange_partner_id()
sale_order.onchange_partner_shipping_id()
# rewrite the user as the onchange_partner_id erases it
sale_order.write({'user_id': self.task_id.user_id.id})
sale_order.onchange_user_id()
sale_order_line = self.env['sale.order.line'].create({
'order_id': sale_order.id,
+1 -1
View File
@@ -137,7 +137,7 @@
<label for="company_id" class="oe_edit_only" groups="base.group_multi_company"/>
<field name="company_id" context="{'user_preference': 0}" groups="base.group_multi_company"/>
</div>
<group>
<group name="phone_numbers">
<label for="groups_id" string="Access Rights"
attrs="{'invisible': [('id', '>', 0)]}" groups="base.group_no_one"/>
<div attrs="{'invisible': [('id', '>', 0)]}" groups="base.group_no_one">