diff options
| author | Odoo's Mergebot <xmo+roboto@odoo.com> | 2020-02-05 17:00:16 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-02-05 17:00:16 +0100 |
| commit | e753bb1a85bac83b0e4dc536ae6d4992cbe4907f (patch) | |
| tree | 28816b1b06cb56a020c9a5b59a4da3b28976e8d6 | |
| parent | 90053ca72f6cf17b21507fc5df42dd1577e157d1 (diff) | |
| parent | a0aac8a50ac42a72c72b2f0f208278be8cde1a60 (diff) | |
[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>
| -rw-r--r-- | addons/hr/views/res_users.xml | 6 | ||||
| -rw-r--r-- | addons/sale_stock/__manifest__.py | 1 | ||||
| -rw-r--r-- | addons/sale_stock/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/sale_stock/models/res_users.py | 31 | ||||
| -rw-r--r-- | addons/sale_stock/models/sale_order.py | 18 | ||||
| -rw-r--r-- | addons/sale_stock/tests/__init__.py | 1 | ||||
| -rw-r--r-- | addons/sale_stock/tests/test_sale_stock_multicompany.py | 81 | ||||
| -rw-r--r-- | addons/sale_stock/views/res_users_views.xml | 44 | ||||
| -rw-r--r-- | addons/sale_timesheet/wizard/project_create_sale_order.py | 3 | ||||
| -rw-r--r-- | addons/sale_timesheet/wizard/project_task_create_sale_order.py | 3 | ||||
| -rw-r--r-- | odoo/addons/base/views/res_users_views.xml | 2 |
11 files changed, 177 insertions, 14 deletions
diff --git a/addons/hr/views/res_users.xml b/addons/hr/views/res_users.xml index c5724bd8fc1..17c7f58cd1e 100644 --- a/addons/hr/views/res_users.xml +++ b/addons/hr/views/res_users.xml @@ -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> diff --git a/addons/sale_stock/__manifest__.py b/addons/sale_stock/__manifest__.py index e685e966c69..0992ac081e0 100644 --- a/addons/sale_stock/__manifest__.py +++ b/addons/sale_stock/__manifest__.py @@ -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', diff --git a/addons/sale_stock/models/__init__.py b/addons/sale_stock/models/__init__.py index b2be36f44b9..e2983e92c78 100644 --- a/addons/sale_stock/models/__init__.py +++ b/addons/sale_stock/models/__init__.py @@ -7,3 +7,4 @@ from . import sale_order from . import res_config_settings from . import stock from . import product_template +from . import res_users diff --git a/addons/sale_stock/models/res_users.py b/addons/sale_stock/models/res_users.py new file mode 100644 index 00000000000..053fee222da --- /dev/null +++ b/addons/sale_stock/models/res_users.py @@ -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 diff --git a/addons/sale_stock/models/sale_order.py b/addons/sale_stock/models/sale_order.py index 59234024ceb..2fef194d436 100644 --- a/addons/sale_stock/models/sale_order.py +++ b/addons/sale_stock/models/sale_order.py @@ -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): diff --git a/addons/sale_stock/tests/__init__.py b/addons/sale_stock/tests/__init__.py index 43ad813d2c3..c9c90165757 100644 --- a/addons/sale_stock/tests/__init__.py +++ b/addons/sale_stock/tests/__init__.py @@ -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 diff --git a/addons/sale_stock/tests/test_sale_stock_multicompany.py b/addons/sale_stock/tests/test_sale_stock_multicompany.py new file mode 100644 index 00000000000..307dc23a6e9 --- /dev/null +++ b/addons/sale_stock/tests/test_sale_stock_multicompany.py @@ -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) diff --git a/addons/sale_stock/views/res_users_views.xml b/addons/sale_stock/views/res_users_views.xml new file mode 100644 index 00000000000..d141c4ea7e0 --- /dev/null +++ b/addons/sale_stock/views/res_users_views.xml @@ -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>
\ No newline at end of file diff --git a/addons/sale_timesheet/wizard/project_create_sale_order.py b/addons/sale_timesheet/wizard/project_create_sale_order.py index 9a91e329fc7..7b6029e8296 100644 --- a/addons/sale_timesheet/wizard/project_create_sale_order.py +++ b/addons/sale_timesheet/wizard/project_create_sale_order.py @@ -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) diff --git a/addons/sale_timesheet/wizard/project_task_create_sale_order.py b/addons/sale_timesheet/wizard/project_task_create_sale_order.py index 27d9213268d..e697f95592c 100644 --- a/addons/sale_timesheet/wizard/project_task_create_sale_order.py +++ b/addons/sale_timesheet/wizard/project_task_create_sale_order.py @@ -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, diff --git a/odoo/addons/base/views/res_users_views.xml b/odoo/addons/base/views/res_users_views.xml index bc08c1ec562..f3b24835761 100644 --- a/odoo/addons/base/views/res_users_views.xml +++ b/odoo/addons/base/views/res_users_views.xml @@ -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"> |
