[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:
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -7,3 +7,4 @@ from . import sale_order
|
||||
from . import res_config_settings
|
||||
from . import stock
|
||||
from . import product_template
|
||||
from . import res_users
|
||||
|
||||
@@ -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
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user