Initialize v18 branch

Rename *_usability modules to *_usability_akretion
This commit is contained in:
Alexis de Lattre
2024-12-24 10:11:21 +01:00
parent 9913924202
commit 13744fc404
264 changed files with 50 additions and 87 deletions

View File

@@ -0,0 +1,2 @@
from . import models
from .post_install import create_config_parameter_immediate_tranfer

View File

@@ -0,0 +1,45 @@
# Copyright 2014-2022 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Stock Usability',
'version': '16.0.1.0.0',
'category': 'Inventory, Logistic, Storage',
'license': 'AGPL-3',
'summary': 'Several usability enhancements in Warehouse management',
'description': """
Stock Usability
===============
The usability enhancements include:
* display the source location on the tree view of the move lines of the pickings (by default, only the destination location is displayed).
* always display the field *Backorder* on the form view of picking (by default, this field is only displayed when it has a value, so the user doesn't know when the field has no value because he doesn't see the field !)
* add a group by Partner in the picking search view (particularly usefull for receptions)
* add graph view for pickings
* remove ability to translate stock.location, stock.location.route and stock.picking.type
This module has been written by Alexis de Lattre from Akretion <alexis.delattre@akretion.com>.
""",
'author': 'Akretion',
'website': 'http://www.akretion.com',
'depends': ['stock'],
'data': [
'views/stock_quant.xml',
'views/stock_location.xml',
'views/stock_move.xml',
'views/stock_move_line.xml',
'views/stock_picking.xml',
'views/stock_picking_type.xml',
'views/stock_warehouse.xml',
'views/stock_warehouse_orderpoint.xml',
'views/product.xml',
'views/procurement_group.xml',
'views/stock_lot.xml',
'views/procurement_scheduler_log.xml',
'security/ir.model.access.csv',
],
'post_init_hook': 'create_config_parameter_immediate_tranfer',
'installable': False,
}

View File

@@ -0,0 +1,393 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_usability
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-07-04 16:23+0000\n"
"PO-Revision-Date: 2024-07-04 16:24+0000\n"
"Last-Translator: Alexis de Lattre <alexis.delattre@akretion.com>\n"
"Language-Team: \n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: stock_usability
#: model:ir.model.fields,help:stock_usability.field_product_product__detailed_type
#: model:ir.model.fields,help:stock_usability.field_product_template__detailed_type
msgid ""
"A storable product is a product for which you manage stock. The Inventory "
"app has to be installed.\n"
"A consumable product is a product for which stock is not managed.\n"
"A service is a non-material product you provide."
msgstr ""
"Un produit stockable est un produit dont on gère le stock. L'application "
"\"Inventaire\" doit être installée. \n"
"Un produit consommable est un produit pour lequel le stock n'est pas géré.\n"
"Un service est un produit immatériel que vous fournissez."
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_move_form
msgid "Advanced"
msgstr "Avancé"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_picking_form
msgid "Are you sure you want to cancel this picking?"
msgstr "Êtes-vous sûr de vouloir annuler ce bon de préparation ?"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.stock_quant_tree_simple
msgid "Available"
msgstr "Disponible"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_scheduler_log__company_id
msgid "Company"
msgstr "Société"
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_res_partner
#: model:ir.model.fields,field_description:stock_usability.field_stock_picking__partner_id
msgid "Contact"
msgstr "Contact"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_scheduler_log__create_uid
msgid "Created by"
msgstr "Créé par"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_scheduler_log__create_date
msgid "Created on"
msgstr "Créé le"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_product_product__sale_delay
#: model:ir.model.fields,field_description:stock_usability.field_product_template__sale_delay
msgid "Customer Lead Time"
msgstr "Délai de livraison au client"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_picking_internal_search
msgid "Date Done"
msgstr "Date du transfert"
#. module: stock_usability
#: model:ir.model.fields,help:stock_usability.field_product_product__sale_delay
#: model:ir.model.fields,help:stock_usability.field_product_template__sale_delay
msgid ""
"Delivery lead time, in days. It's the number of days, promised to the "
"customer, between the confirmation of the sales order and the delivery."
msgstr ""
"Délai de livraison, en jours. C'est le nombre de jours, promis au client, "
"entre la confirmation de la commande client et la livraison."
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_scheduler_log__display_name
msgid "Display Name"
msgstr "Nom affiché"
#. module: stock_usability
#: model:ir.model.fields,help:stock_usability.field_product_product__tracking
#: model:ir.model.fields,help:stock_usability.field_product_template__tracking
msgid "Ensure the traceability of a storable product in your warehouse."
msgstr "Assurer la traçabilité d'un produit stockable dans votre entrepôt."
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_location_search
msgid "Group By"
msgstr "Grouper par"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_scheduler_log__id
msgid "ID"
msgstr "ID"
#. module: stock_usability
#: model:ir.model.fields,help:stock_usability.field_stock_move__product_barcode
#: model:ir.model.fields,help:stock_usability.field_stock_move_line__product_barcode
#: model:ir.model.fields,help:stock_usability.field_stock_quant__product_barcode
msgid "International Article Number used for product identification."
msgstr ""
"Numéro d'article international utilisé pour l'identification du produit."
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_stock_picking_type__is_dropship
msgid "Is Dropship"
msgstr "Est un dropship"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_stock_picking__is_locked
msgid "Is Locked"
msgstr "Est verrouillé"
#. module: stock_usability
#: model:ir.model.fields,help:stock_usability.field_stock_picking__move_type
msgid "It specifies goods to be deliver partially or all at once"
msgstr ""
"Indique si les marchandises doivent être livrées partiellement ou en une "
"seule fois."
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_scheduler_log____last_update
msgid "Last Modified on"
msgstr "Dernière modification le"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_scheduler_log__write_uid
msgid "Last Updated by"
msgstr "Dernière mise à jour par"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_scheduler_log__write_date
msgid "Last Updated on"
msgstr "Dernière mise à jour le"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_location_search
msgid "Location Type"
msgstr "Type d'emplacement"
#. module: stock_usability
#: model:ir.ui.menu,name:stock_usability.stock_location_menu
msgid "Locations"
msgstr "Emplacements"
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_procurement_scheduler_log
msgid "Logs of the Procurement Scheduler"
msgstr "Journaux du planificateur des approvisionnements"
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_stock_warehouse_orderpoint
msgid "Minimum Inventory Rule"
msgstr "Règle d'inventaire minimum"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_stock_picking__picking_type_id
msgid "Operation Type"
msgstr "Type d'opération"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_picking_internal_search
msgid "Partner"
msgstr "Partenaire"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_move_form
msgid "Picking"
msgstr "Transfert"
#. module: stock_usability
#. odoo-python
#: code:addons/stock_usability/models/stock_picking.py:0
#, python-format
msgid "Picking <b>unreserved</b>."
msgstr "Bon de préparation <b>déréservé</b>."
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_stock_picking_type
msgid "Picking Type"
msgstr "Type de transfert"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_group__picking_ids
msgid "Pickings"
msgstr "Transferts"
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_procurement_group
msgid "Procurement Group"
msgstr "Groupe d'approvisionnement"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.procurement_scheduler_log_tree
msgid "Procurement Scheduler Logs"
msgstr "Journal du planificateur des approvisionnements"
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_product_template
msgid "Product"
msgstr "Produit"
#. module: stock_usability
#. odoo-python
#: code:addons/stock_usability/models/stock_move.py:0
#: code:addons/stock_usability/models/stock_move_line.py:0
#, python-format
msgid ""
"Product <a href=# data-oe-model=product.product data-oe-id=%d>%s</a> qty %s "
"%s <b>unreserved</b>"
msgstr ""
"Produit <a href=# data-oe-model=product.product data-oe-id=%d>%s</a> "
"qté %s %s <b>déréservée</b>"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_stock_move__product_barcode
#: model:ir.model.fields,field_description:stock_usability.field_stock_move_line__product_barcode
#: model:ir.model.fields,field_description:stock_usability.field_stock_quant__product_barcode
msgid "Product Barcode"
msgstr "Code-barres du produit"
#. module: stock_usability
#: model:ir.actions.act_window,name:stock_usability.stock_move_line_from_lot_action
#: model_terms:ir.ui.view,arch_db:stock_usability.view_move_form
#: model_terms:ir.ui.view,arch_db:stock_usability.view_production_lot_form
msgid "Product Moves"
msgstr "Mouvements de produits"
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_stock_move_line
msgid "Product Moves (Stock Move Line)"
msgstr "Mouvements de produit (Ligne de mouvement de stock)"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_product_product__detailed_type
#: model:ir.model.fields,field_description:stock_usability.field_product_template__detailed_type
msgid "Product Type"
msgstr "Type de produit"
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_product_product
msgid "Product Variant"
msgstr "Variante de produit"
#. module: stock_usability
#: model:ir.actions.act_window,name:stock_usability.location_open_quants_regular_tree
#: model:ir.model,name:stock_usability.model_stock_quant
#: model_terms:ir.ui.view,arch_db:stock_usability.view_location_form
msgid "Quants"
msgstr "Quants"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_location_search
msgid "Removal Strategy"
msgstr "Stratégie d'enlèvement"
#. module: stock_usability
#: model:ir.actions.act_window,name:stock_usability.location_open_orderpoint
#: model_terms:ir.ui.view,arch_db:stock_usability.view_location_form
msgid "Reordering Rules"
msgstr "Règles de réapprovisionnement"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.stock_quant_tree_simple
#: model_terms:ir.ui.view,arch_db:stock_usability.view_stock_quant_tree_inventory_editable
msgid "Reservations"
msgstr "Réservations"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.stock_quant_tree_simple
msgid "Reserved"
msgstr "Réservé"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_warehouse
msgid "Routes"
msgstr "Routes"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.procurement_scheduler_log_tree
msgid "Scheduler End Time"
msgstr "Heure de fin du planificateur"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.procurement_scheduler_log_tree
msgid "Scheduler Executed by"
msgstr "Planificateur exécuté par"
#. module: stock_usability
#: model:ir.actions.act_window,name:stock_usability.procurement_scheduler_log_action
#: model:ir.ui.menu,name:stock_usability.procurement_scheduler_log_menu
msgid "Scheduler Logs"
msgstr "Journal du planificateur"
#. module: stock_usability
#: model:ir.model.fields,help:stock_usability.field_res_partner__picking_warn
#: model:ir.model.fields,help:stock_usability.field_res_users__picking_warn
msgid ""
"Selecting the \"Warning\" option will notify user with the message, "
"Selecting \"Blocking Message\" will throw an exception with the message and "
"block the flow. The Message has to be written in the next field."
msgstr ""
"Sélectionner l'option 'Avertissement' notifiera l'utilisateur avec le "
"message. Sélectionner 'Message bloquant' lancera une exception avec le "
"message et bloquera le flux. Le message doit être encodé dans le champ "
"suivant."
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_stock_picking__move_type
msgid "Shipping Policy"
msgstr "Politique d'expédition"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_procurement_scheduler_log__start_datetime
msgid "Start Date"
msgstr "Date de début"
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_stock_move
msgid "Stock Move"
msgstr "Mouvement de stock"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.product_form_view_procurement_button
#: model_terms:ir.ui.view,arch_db:stock_usability.product_template_form_view_procurement_button
msgid "Stock Moves"
msgstr "Mouvements de stock"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_res_partner__picking_warn
#: model:ir.model.fields,field_description:stock_usability.field_res_users__picking_warn
msgid "Stock Picking"
msgstr "Transfert"
#. module: stock_usability
#: model:ir.model.fields,field_description:stock_usability.field_product_product__tracking
#: model:ir.model.fields,field_description:stock_usability.field_product_template__tracking
msgid "Tracking"
msgstr "Suivi"
#. module: stock_usability
#: model:ir.model,name:stock_usability.model_stock_picking
msgid "Transfer"
msgstr "Transfert"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.stock_picking_pivot
msgid "Transfers"
msgstr "Transferts"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.stock_quant_tree_simple
msgid "Unit"
msgstr "Unité"
#. module: stock_usability
#: model_terms:ir.ui.view,arch_db:stock_usability.view_move_line_tree
#: model_terms:ir.ui.view,arch_db:stock_usability.view_move_tree
#: model_terms:ir.ui.view,arch_db:stock_usability.view_picking_form
msgid "Unreserve"
msgstr "Déréserver"
#. module: stock_usability
#: model:ir.model.fields,help:stock_usability.field_stock_picking__is_locked
msgid ""
"When the picking is not done this allows changing the initial demand. When "
"the picking is done this allows changing the done quantities."
msgstr ""
"Quand le bon de préparation n'est pas à l'état terminé, cela permet de modifier la "
"demande initiale. Lorsque le transfert est terminé, cela permet de "
"modifier les quantités traitées."
#. module: stock_usability
#. odoo-python
#: code:addons/stock_usability/models/stock_move_line.py:0
#, python-format
msgid "You cannot unreserve a move line in done state."
msgstr "Il n'est pas possible de déréserver un mouvement de stock à l'état terminé."

View File

@@ -0,0 +1,11 @@
from . import stock_move
from . import stock_move_line
from . import stock_picking
from . import stock_picking_type
from . import stock_warehouse_orderpoint
from . import stock_quant
from . import stock_lot
from . import procurement_group
from . import procurement_scheduler_log
from . import product
from . import res_partner

View File

@@ -0,0 +1,46 @@
# Copyright 2015-2022 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
class ProcurementGroup(models.Model):
_inherit = 'procurement.group'
picking_ids = fields.One2many(
'stock.picking', 'group_id', string='Pickings', readonly=True)
@api.model
def run_scheduler(self, use_new_cursor=False, company_id=False):
'''Inherit to add info logs'''
logger.info(
'START procurement scheduler '
'(company ID=%d, uid=%d, use_new_cursor=%s)',
company_id, self._uid, use_new_cursor)
start_datetime = datetime.now()
res = super().run_scheduler(
use_new_cursor=use_new_cursor, company_id=company_id)
logger.info(
'END procurement scheduler '
'(company ID=%d, uid=%d, use_new_cursor=%s)',
company_id, self._uid, use_new_cursor)
try:
# I put it in a try/except, to be sure that, even if the user
# the execute the scheduler doesn't have create right on
# procurement.scheduler.log
self.env['procurement.scheduler.log'].create({
'company_id': company_id,
'start_datetime': start_datetime,
})
# If I don't do an explicit cr.commit(), it doesn't create
# the procurement.scheduler.log... I don't know why
self._cr.commit()
except Exception as e:
logger.warning(
'Could not create procurement.scheduler.log (error: %s)', e)
return res

View File

@@ -0,0 +1,15 @@
# Copyright 2015-2022 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ProcurementSchedulerLog(models.Model):
_name = 'procurement.scheduler.log'
_description = 'Logs of the Procurement Scheduler'
_order = 'create_date desc'
company_id = fields.Many2one(
'res.company', string='Company', readonly=True)
start_datetime = fields.Datetime(string='Start Date', readonly=True)

View File

@@ -0,0 +1,31 @@
# Copyright 2016-2022 Akretion France
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ProductTemplate(models.Model):
_inherit = 'product.template'
tracking = fields.Selection(tracking=True)
sale_delay = fields.Float(tracking=True)
# the 'stock' module adds 'product' in detailed_type...
# but forgets to make it the default
detailed_type = fields.Selection(default='product')
def action_view_stock_move(self):
action = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_action")
action['domain'] = [('product_id.product_tmpl_id', 'in', self.ids)]
action['context'] = {'search_default_done': True}
return action
class ProductProduct(models.Model):
_inherit = 'product.product'
def action_view_stock_move(self):
action = self.env["ir.actions.actions"]._for_xml_id("stock.stock_move_action")
action['domain'] = [('product_id', 'in', self.ids)]
action['context'] = {'search_default_done': True}
return action

View File

@@ -0,0 +1,11 @@
# Copyright 2017-2022 Akretion France (https://akretion.com/)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ResPartner(models.Model):
_inherit = 'res.partner'
picking_warn = fields.Selection(tracking=True)

View File

@@ -0,0 +1,14 @@
# Copyright 2024 Akretion France (https://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
class StockLot(models.Model):
_inherit = 'stock.lot'
name = fields.Char(tracking=True)
ref = fields.Char(tracking=True)
product_id = fields.Many2one(tracking=True)
company_id = fields.Many2one(tracking=True)

View File

@@ -0,0 +1,30 @@
# Copyright 2014-2022 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models, _
import logging
logger = logging.getLogger(__name__)
class StockMove(models.Model):
_inherit = 'stock.move'
# for optional display in tree view
product_barcode = fields.Char(
related='product_id.barcode', string="Product Barcode")
def button_do_unreserve(self):
for move in self:
move._do_unreserve()
picking = move.picking_id
if picking:
product = move.product_id
picking.message_post(body=_(
"Product <a href=# data-oe-model=product.product "
"data-oe-id=%d>%s</a> qty %s %s <b>unreserved</b>")
% (product.id, product.display_name,
move.product_qty, product.uom_id.name))
# Copied from do_unreserved of stock.picking
picking.package_level_ids.filtered(lambda p: not p.move_ids).unlink()

View File

@@ -0,0 +1,37 @@
# Copyright 2014-2022 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models, _
from odoo.exceptions import UserError
import logging
logger = logging.getLogger(__name__)
class StockMoveLine(models.Model):
_inherit = 'stock.move.line'
# for optional display in tree view
product_barcode = fields.Char(
related='product_id.barcode', string="Product Barcode")
# TODO: I think it's not complete
def button_do_unreserve(self):
for moveline in self:
if moveline.state == 'cancel':
continue
elif moveline.state == 'done':
raise UserError(_(
"You cannot unreserve a move line in done state."))
picking = moveline.move_id.picking_id
if picking:
product = moveline.product_id
picking.message_post(body=_(
"Product <a href=# data-oe-model=product.product "
"data-oe-id=%d>%s</a> qty %s %s <b>unreserved</b>")
% (product.id, product.display_name,
moveline.reserved_qty, product.uom_id.name))
# Copied from do_unreserved of stock.picking
picking.package_level_ids.filtered(lambda p: not p.move_ids).unlink()
moveline.unlink()

View File

@@ -0,0 +1,26 @@
# Copyright 2014-2022 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models, _
import logging
logger = logging.getLogger(__name__)
class StockPicking(models.Model):
_inherit = 'stock.picking'
# _order = 'id desc'
# In the stock module: _order = "priority desc, scheduled_date asc, id desc"
# The problem is date asc
partner_id = fields.Many2one(tracking=True)
picking_type_id = fields.Many2one(tracking=True)
move_type = fields.Selection(tracking=True)
is_locked = fields.Boolean(tracking=True)
def do_unreserve(self):
res = super().do_unreserve()
for pick in self:
pick.message_post(body=_("Picking <b>unreserved</b>."))
return res

View File

@@ -0,0 +1,26 @@
# Copyright 2023 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class StockPickingType(models.Model):
_inherit = 'stock.picking.type'
is_dropship = fields.Boolean(compute="_compute_is_dropship", store=True)
@api.depends("code", "warehouse_id", "default_location_src_id", "default_location_dest_id")
def _compute_is_dropship(self):
supplier_loc_id = self.env.ref("stock.stock_location_suppliers").id
customer_loc_id = self.env.ref("stock.stock_location_customers").id
for picktype in self:
is_dropship = False
if (
picktype.code == 'incoming'
and not picktype.warehouse_id
and picktype.default_location_src_id.id == supplier_loc_id
and picktype.default_location_dest_id.id == customer_loc_id
):
is_dropship = True
picktype.is_dropship = is_dropship

View File

@@ -0,0 +1,51 @@
# Copyright 2014-2022 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class StockQuant(models.Model):
_inherit = 'stock.quant'
product_barcode = fields.Char(
related='product_id.barcode', string="Product Barcode")
def action_stock_move_lines_reserved(self):
self.ensure_one()
action = self.env["ir.actions.actions"]._for_xml_id(
"stock.stock_move_line_action")
action['domain'] = [
('state', 'not in', ('draft', 'done', 'cancel')),
('reserved_uom_qty', '!=', 0),
('product_id', '=', self.product_id.id),
('location_id', '=', self.location_id.id),
('lot_id', '=', self.lot_id.id or False),
'|',
('package_id', '=', self.package_id.id or False),
('result_package_id', '=', self.package_id.id or False),
]
action['context'] = {'create': 0}
return action
@api.model
def default_get(self, fields_list):
res = super().default_get(fields_list)
if (
not res.get('location_id') and
self._context.get('search_location') and
isinstance(self._context['search_location'], list) and
len(self._context['search_location']) == 1):
res['location_id'] = self._context['search_location'][0]
return res
@api.model
def action_view_inventory(self):
action = super().action_view_inventory()
# Remove filter 'My Counts' set by default for Stock Users
if (
action.get('context') and
isinstance(action['context'], dict) and
action['context'].get('search_default_my_count')):
action['context'].pop('search_default_my_count')
return action

View File

@@ -0,0 +1,30 @@
# Copyright 2015-2022 Akretion (http://www.akretion.com)
# @author Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import fields, models
import logging
logger = logging.getLogger(__name__)
class StockWarehouseOrderpoint(models.Model):
_inherit = 'stock.warehouse.orderpoint'
# In the 'stock' module, the default value for 'trigger' is 'auto'
# but all the Odoo deployments I've seen so far need 'manual' by default
trigger = fields.Selection(default='manual')
product_barcode = fields.Char(related='product_id.barcode', string="Product Barcode")
def _procure_orderpoint_confirm(
self, use_new_cursor=False, company_id=None, raise_user_error=True):
logger.info(
'procurement scheduler: START to create moves from '
'orderpoints')
res = super()._procure_orderpoint_confirm(
use_new_cursor=use_new_cursor, company_id=company_id,
raise_user_error=raise_user_error)
logger.info(
'procurement scheduler: END creation of moves from '
'orderpoints')
return res

View File

@@ -0,0 +1,25 @@
# Copyright 2021-2022 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from odoo import SUPERUSER_ID, api
logger = logging.getLogger(__name__)
def create_config_parameter_immediate_tranfer(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
ico = env["ir.config_parameter"]
conf_param = ico.search([('key', '=', 'stock.no_default_immediate_tranfer')])
if not conf_param:
ico.create({
'key': 'stock.no_default_immediate_tranfer',
'value': 'True',
})
logger.info(
'ir.config_parameter stock.no_default_immediate_tranfer created')
else:
logger.info(
'ir.config_parameter stock.no_default_immediate_tranfer '
'already exists')

View File

@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_procurement_scheduler_log_full,Full access on procurement.scheduler.log to System group,model_procurement_scheduler_log,base.group_system,1,1,1,1
access_procurement_scheduler_log_user,Read/Create procurement.scheduler.log to Stock user,model_procurement_scheduler_log,stock.group_stock_user,1,0,1,0
access_procurement_scheduler_log_read,Read access on procurement.scheduler.log to Employee,model_procurement_scheduler_log,base.group_user,1,0,0,0
access_stock_warehouse_orderpoint_employee,Read access on stock.warehouse.orderpoint to employee (needed to open product form view with employee-only group),stock.model_stock_warehouse_orderpoint,base.group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_procurement_scheduler_log_full Full access on procurement.scheduler.log to System group model_procurement_scheduler_log base.group_system 1 1 1 1
3 access_procurement_scheduler_log_user Read/Create procurement.scheduler.log to Stock user model_procurement_scheduler_log stock.group_stock_user 1 0 1 0
4 access_procurement_scheduler_log_read Read access on procurement.scheduler.log to Employee model_procurement_scheduler_log base.group_user 1 0 0 0
5 access_stock_warehouse_orderpoint_employee Read access on stock.warehouse.orderpoint to employee (needed to open product form view with employee-only group) stock.model_stock_warehouse_orderpoint base.group_user 1 0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@@ -0,0 +1,238 @@
diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py
index bbb6f301834..48d016010dc 100644
--- a/addons/stock/models/product.py
+++ b/addons/stock/models/product.py
@@ -36,6 +36,18 @@ class Product(models.Model):
"or any of its children.\n"
"Otherwise, this includes goods stored in any Stock Location "
"with 'internal' type.")
+ reserved_qty = fields.Float(
+ compute='_compute_quantities',
+ digits=dp.get_precision('Product Unit of Measure'),
+ search='_search_reserved_qty',
+ string='Reserved Quantity')
+ free_qty = fields.Float(
+ compute='_compute_quantities',
+ search='_search_free_qty',
+ digits=dp.get_precision('Product Unit of Measure'),
+ string='Free To Use Quantity',
+ help="The free to use quantity corresponds to the quantity on hand "
+ "- reserved quantity")
virtual_available = fields.Float(
'Forecast Quantity', compute='_compute_quantities', search='_search_virtual_available',
digits=dp.get_precision('Product Unit of Measure'),
@@ -84,6 +96,8 @@ class Product(models.Model):
product.incoming_qty = res[product.id]['incoming_qty']
product.outgoing_qty = res[product.id]['outgoing_qty']
product.virtual_available = res[product.id]['virtual_available']
+ product.reserved_qty = res[product.id]['reserved_qty']
+ product.free_qty = res[product.id]['free_qty']
def _product_available(self, field_names=None, arg=False):
""" Compatibility method """
@@ -124,7 +138,7 @@ class Product(models.Model):
domain_move_out_todo = [('state', 'in', ('waiting', 'confirmed', 'assigned', 'partially_available'))] + domain_move_out
moves_in_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_in_todo, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
moves_out_res = dict((item['product_id'][0], item['product_qty']) for item in Move.read_group(domain_move_out_todo, ['product_id', 'product_qty'], ['product_id'], orderby='id'))
- quants_res = dict((item['product_id'][0], item['quantity']) for item in Quant.read_group(domain_quant, ['product_id', 'quantity'], ['product_id'], orderby='id'))
+ quants_res = dict((item['product_id'][0], (item['quantity'], item['reserved_quantity'])) for item in Quant.read_group(domain_quant, ['product_id', 'quantity', 'reserved_quantity'], ['product_id'], orderby='id'))
if dates_in_the_past:
# Calculate the moves that were done before now to calculate back in time (as most questions will be recent ones)
domain_move_in_done = [('state', '=', 'done'), ('date', '>', to_date)] + domain_move_in_done
@@ -138,10 +152,13 @@ class Product(models.Model):
rounding = product.uom_id.rounding
res[product_id] = {}
if dates_in_the_past:
- qty_available = quants_res.get(product_id, 0.0) - moves_in_res_past.get(product_id, 0.0) + moves_out_res_past.get(product_id, 0.0)
+ qty_available = quants_res.get(product_id, [0.0])[0] - moves_in_res_past.get(product_id, 0.0) + moves_out_res_past.get(product_id, 0.0)
else:
- qty_available = quants_res.get(product_id, 0.0)
+ qty_available = quants_res.get(product_id, [0.0])[0]
+ reserved_quantity = quants_res.get(product_id, [False, 0.0])[1]
res[product_id]['qty_available'] = float_round(qty_available, precision_rounding=rounding)
+ res[product_id]['reserved_qty'] = float_round(reserved_quantity, precision_rounding=rounding)
+ res[product_id]['free_qty'] = float_round(qty_available - reserved_quantity, precision_rounding=rounding)
res[product_id]['incoming_qty'] = float_round(moves_in_res.get(product_id, 0.0), precision_rounding=rounding)
res[product_id]['outgoing_qty'] = float_round(moves_out_res.get(product_id, 0.0), precision_rounding=rounding)
res[product_id]['virtual_available'] = float_round(
@@ -261,10 +278,16 @@ class Product(models.Model):
# TDE FIXME: should probably clean the search methods
return self._search_product_quantity(operator, value, 'outgoing_qty')
+ def _search_reserved_qty(self, operator, value):
+ return self._search_product_quantity(operator, value, 'reserved_qty')
+
+ def _search_free_qty(self, operator, value):
+ return self._search_product_quantity(operator, value, 'free_qty')
+
def _search_product_quantity(self, operator, value, field):
# TDE FIXME: should probably clean the search methods
# to prevent sql injections
- if field not in ('qty_available', 'virtual_available', 'incoming_qty', 'outgoing_qty'):
+ if field not in ('qty_available', 'virtual_available', 'incoming_qty', 'outgoing_qty', 'reserved_qty', 'free_qty'):
raise UserError(_('Invalid domain left operand %s') % field)
if operator not in ('<', '>', '=', '!=', '<=', '>='):
raise UserError(_('Invalid domain operator %s') % operator)
@@ -387,6 +410,12 @@ class Product(models.Model):
action['context'] = {'search_default_internal_loc': 1}
return action
+ def action_open_move_lines(self):
+ action = self.env.ref('stock.stock_move_line_action').read()[0]
+ action['domain'] = [('product_id', '=', self.id), ('state', 'not in', ('done', 'cancel'))]
+ action['context'] = {'default_product_id': self.id}
+ return action
+
def action_open_product_lot(self):
self.ensure_one()
action = self.env.ref('stock.action_production_lot_form').read()[0]
@@ -451,6 +480,18 @@ class ProductTemplate(models.Model):
qty_available = fields.Float(
'Quantity On Hand', compute='_compute_quantities', search='_search_qty_available',
digits=dp.get_precision('Product Unit of Measure'))
+ reserved_qty = fields.Float(
+ compute='_compute_quantities',
+ digits=dp.get_precision('Product Unit of Measure'),
+ search='_search_reserved_qty',
+ string='Reserved Quantity')
+ free_qty = fields.Float(
+ compute='_compute_quantities',
+ search='_search_free_qty',
+ digits=dp.get_precision('Product Unit of Measure'),
+ string='Free To Use Quantity',
+ help="The free to use quantity corresponds to the quantity on hand "
+ "- reserved quantity")
virtual_available = fields.Float(
'Forecasted Quantity', compute='_compute_quantities', search='_search_virtual_available',
digits=dp.get_precision('Product Unit of Measure'))
@@ -490,6 +531,8 @@ class ProductTemplate(models.Model):
res = self._compute_quantities_dict()
for template in self:
template.qty_available = res[template.id]['qty_available']
+ template.reserved_qty = res[template.id]['reserved_qty']
+ template.free_qty = res[template.id]['free_qty']
template.virtual_available = res[template.id]['virtual_available']
template.incoming_qty = res[template.id]['incoming_qty']
template.outgoing_qty = res[template.id]['outgoing_qty']
@@ -503,16 +546,22 @@ class ProductTemplate(models.Model):
prod_available = {}
for template in self:
qty_available = 0
+ reserved_qty = 0
+ free_qty = 0
virtual_available = 0
incoming_qty = 0
outgoing_qty = 0
for p in template.product_variant_ids:
qty_available += variants_available[p.id]["qty_available"]
+ reserved_qty += variants_available[p.id]["reserved_qty"]
+ free_qty += variants_available[p.id]["free_qty"]
virtual_available += variants_available[p.id]["virtual_available"]
incoming_qty += variants_available[p.id]["incoming_qty"]
outgoing_qty += variants_available[p.id]["outgoing_qty"]
prod_available[template.id] = {
"qty_available": qty_available,
+ "reserved_qty": reserved_qty,
+ "free_qty": free_qty,
"virtual_available": virtual_available,
"incoming_qty": incoming_qty,
"outgoing_qty": outgoing_qty,
@@ -524,6 +573,16 @@ class ProductTemplate(models.Model):
product_variant_ids = self.env['product.product'].search(domain)
return [('product_variant_ids', 'in', product_variant_ids.ids)]
+ def _search_reserved_qty(self, operator, value):
+ domain = [('reserved_qty', operator, value)]
+ product_variant_ids = self.env['product.product'].search(domain)
+ return [('product_variant_ids', 'in', product_variant_ids.ids)]
+
+ def _search_free_qty(self, operator, value):
+ domain = [('free_qty', operator, value)]
+ product_variant_ids = self.env['product.product'].search(domain)
+ return [('product_variant_ids', 'in', product_variant_ids.ids)]
+
def _search_virtual_available(self, operator, value):
domain = [('virtual_available', operator, value)]
product_variant_ids = self.env['product.product'].search(domain)
@@ -609,6 +668,13 @@ class ProductTemplate(models.Model):
action['context'] = {'search_default_internal_loc': 1}
return action
+ def action_open_move_lines(self):
+ products = self.mapped('product_variant_ids')
+ action = self.env.ref('stock.stock_move_line_action').read()[0]
+ action['domain'] = [('product_id', 'in', products.ids), ('state', 'not in', ('done', 'cancel'))]
+ action['context'] = {}
+ return action
+
def action_view_orderpoints(self):
products = self.mapped('product_variant_ids')
action = self.env.ref('stock.product_open_orderpoint').read()[0]
diff --git a/addons/stock/views/product_views.xml b/addons/stock/views/product_views.xml
index 70321544e00..99bd47b4e56 100644
--- a/addons/stock/views/product_views.xml
+++ b/addons/stock/views/product_views.xml
@@ -36,6 +36,8 @@
</tree>
<field name="price" position="after">
<field name="qty_available" attrs="{'invisible':[('type', '!=', 'product')]}"/>
+ <field name="reserved_qty" attrs="{'invisible': [('type', '!=', 'product')]}"/>
+ <field name="free_qty" attrs="{'invisible': [('type', '!=', 'product')]}"/>
<field name="virtual_available" attrs="{'invisible':[('type', '!=', 'product')]}"/>
</field>
</field>
@@ -52,6 +54,8 @@
</tree>
<field name="uom_id" position="before">
<field name="qty_available" attrs="{'invisible':[('type', '!=', 'product')]}"/>
+ <field name="reserved_qty" attrs="{'invisible': [('type', '!=', 'product')]}"/>
+ <field name="free_qty" attrs="{'invisible': [('type', '!=', 'product')]}"/>
<field name="virtual_available" attrs="{'invisible':[('type', '!=', 'product')]}"/>
</field>
</field>
@@ -140,6 +144,7 @@
</field>
<ul position="inside">
<li t-if="record.type.raw_value == 'product'">On hand: <field name="qty_available"/> <field name="uom_id"/></li>
+ <li t-if="record.type.raw_value == 'product'">Reserved: <field name="reserved_qty"/> <field name="uom_id"/></li>
</ul>
</field>
</record>
@@ -208,6 +213,18 @@
<span class="o_stat_text">On Hand</span>
</div>
</button>
+ <button class="oe_stat_button"
+ name="action_open_move_lines"
+ icon="fa-building-o"
+ type="object" attrs="{'invisible': [('type', '!=', 'product')]}">
+ <div class="o_field_widget o_stat_info">
+ <span class="o_stat_value">
+ <field name="reserved_qty" widget="statinfo" nolabel="1" class="mr4"/>
+ <field name="uom_name"/>
+ </span>
+ <span class="o_stat_text">Reserved</span>
+ </div>
+ </button>
<button type="action"
name="%(stock.action_stock_level_forecast_report_product)d"
attrs="{'invisible':[('type', '!=', 'product')]}"
@@ -291,6 +308,18 @@
<span class="o_stat_text">On Hand</span>
</div>
</button>
+ <button type="object"
+ name="action_open_move_lines"
+ attrs="{'invisible': [('type', '!=', 'product')]}"
+ class="oe_stat_button" icon="fa-building-o">
+ <div class="o_field_widget o_stat_info">
+ <span class="o_stat_value" widget="statinfo">
+ <field name="reserved_qty" widget="statinfo" nolabel="1" class="mr4"/>
+ <field name="uom_name"/>
+ </span>
+ <span class="o_stat_text">Reserved</span>
+ </div>
+ </button>
<button type="action"
name="%(stock.action_stock_level_forecast_report_template)d"
attrs="{'invisible':[('type', '!=', 'product')]}"

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2015-2022 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="procurement_group_form_view" model="ir.ui.view">
<field name="name">stock_usability.procurement.group.form</field>
<field name="model">procurement.group</field>
<field name="inherit_id" ref="stock.procurement_group_form_view"/>
<field name="arch" type="xml">
<field name="move_type" position="after">
<field name="partner_id" readonly="1"/>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2015-2020 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="procurement_scheduler_log_tree" model="ir.ui.view">
<field name="name">procurement_scheduler_log_tree</field>
<field name="model">procurement.scheduler.log</field>
<field name="arch" type="xml">
<tree string="Procurement Scheduler Logs">
<field name="start_datetime"/>
<field name="create_date" string="Scheduler End Time"/>
<field name="create_uid" string="Scheduler Executed by"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
<record id="procurement_scheduler_log_action" model="ir.actions.act_window">
<field name="name">Scheduler Logs</field>
<field name="res_model">procurement.scheduler.log</field>
<field name="view_mode">tree</field>
</record>
<menuitem id="procurement_scheduler_log_menu"
action="procurement_scheduler_log_action"
parent="stock.menu_warehouse_report" sequence="300"/>
</odoo>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2021-2022 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_stock_product_template_tree" model="ir.ui.view">
<field name="name">stock.usability.product.template.tree</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="stock.view_stock_product_template_tree" />
<field name="arch" type="xml">
<field name="responsible_id" position="attributes">
<attribute name="optional">hide</attribute>
</field>
<field name="virtual_available" position="before">
<field name="incoming_qty" optional="hide" sum="1"/>
<field name="outgoing_qty" optional="hide" sum="1"/>
<!-- we would like to also have free_qty, as on product.product, but this field doesn't exist on product.template -->
</field>
<!-- we have sum=1 on product.product qty fields, but not on product.template...so I add it on product.template -->
<field name="virtual_available" position="attributes">
<attribute name="sum">1</attribute>
</field>
<field name="qty_available" position="attributes">
<attribute name="sum">1</attribute>
</field>
</field>
</record>
<record id="product_template_form_view_procurement_button" model="ir.ui.view">
<field name="name">stock_usability.product.template.form</field>
<field name="model">product.template</field>
<field name="inherit_id" ref="stock.product_template_form_view_procurement_button"/>
<field name="arch" type="xml">
<button name="action_view_stock_move_lines" position="before">
<button string="Stock Moves"
type="object"
name= "action_view_stock_move"
attrs="{'invisible': [('type', 'not in', ('product', 'consu'))]}"
groups="stock.group_stock_user"
class="oe_stat_button" icon="fa-exchange"
/>
</button>
</field>
</record>
<record id="product_form_view_procurement_button" model="ir.ui.view">
<field name="name">stock_usability.product.product.form</field>
<field name="model">product.product</field>
<field name="inherit_id" ref="stock.product_form_view_procurement_button"/>
<field name="arch" type="xml">
<button name="action_view_stock_move_lines" position="before">
<button string="Stock Moves"
type="object"
name= "action_view_stock_move"
attrs="{'invisible': [('type', 'not in', ('product', 'consu'))]}"
groups="stock.group_stock_user"
class="oe_stat_button" icon="fa-exchange"
/>
</button>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2022 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_location_search" model="ir.ui.view">
<field name="name">stock.usability.stock.location.search</field>
<field name="model">stock.location</field>
<field name="inherit_id" ref="stock.view_location_search" />
<field name="arch" type="xml">
<filter name="inactive" position="after">
<group string="Group By" name="groupby">
<filter name="usage_groupby" string="Location Type"
context="{'group_by': 'usage'}"/>
<filter name="removal_strategy_groupby" string="Removal Strategy"
context="{'group_by': 'removal_strategy_id'}"/>
</group>
</filter>
</field>
</record>
<record id="location_open_orderpoint" model="ir.actions.act_window">
<field name="name">Reordering Rules</field>
<field name="res_model">stock.warehouse.orderpoint</field>
<field name="context">{'default_location_id': active_id, 'search_default_location_id': active_id}</field>
</record>
<record id="location_open_quants_regular_tree" model="ir.actions.act_window">
<field name="name">Quants</field>
<field name="res_model">stock.quant</field>
<field name="domain">[('location_id', 'child_of', active_ids)]</field>
<field name="view_id" ref="stock_usability.stock_quant_tree_simple"/>
</record>
<record id="view_location_form" model="ir.ui.view">
<field name="name">stock.usability.stock.location.form</field>
<field name="model">stock.location</field>
<field name="inherit_id" ref="stock.view_location_form"/>
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button type="action" name="%(location_open_quants_regular_tree)d"
string="Quants"
class="oe_stat_button" icon="fa-cubes"/>
<button type="action" name="%(location_open_orderpoint)d"
string="Reordering Rules"
class="oe_stat_button" icon="fa-refresh"/>
</div>
</field>
</record>
<record id="view_location_tree2" model="ir.ui.view">
<field name="model">stock.location</field>
<field name="inherit_id" ref="stock.view_location_tree2"/>
<field name="arch" type="xml">
<field name="storage_category_id" position="after">
<field name="removal_strategy_id" optional="hide"/>
</field>
</field>
</record>
<!-- By default, the menu entry for stock location is only under
Configuration > Warehouse management
But, the view of stock location is very useful to be able to list
the items present on a particular stock location => so every user
should be able to access it. So I add a menu entry under Inventory Control.
The problem is that, in addons/stock/views/stock_quant_views.xml,
there is a menu entry XMLID stock.menu_valuation pointing to stock.quant
with name="Locations"... so we end up having 2 different menu entries "Locations"
pointing to different models.
Until I find a proper solution to that, I decided to remove the menu entry
<menuitem id="stock_location_menu" action="stock.action_location_form"
parent="stock.menu_warehouse_report"
groups="stock.group_stock_multi_locations"
sequence="160"/>
-->
</odoo>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2022 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="stock.action_production_lot_form" model="ir.actions.act_window">
<!-- remove from context 'search_default_group_by_product': 1-->
<field name="context">{'display_complete': True, 'default_company_id': allowed_company_ids[0]}</field>
</record>
<record id="stock_move_line_from_lot_action" model="ir.actions.act_window">
<field name="name">Product Moves</field>
<field name="res_model">stock.move.line</field>
<field name="view_mode">tree,kanban,pivot,form</field>
<field name="domain">[('lot_id', '=', active_id)]</field>
<field name="context">{'search_default_done': 1, 'create': 0}</field>
</record>
<record id="view_production_lot_form" model="ir.ui.view">
<field name="model">stock.lot</field>
<field name="inherit_id" ref="stock.view_production_lot_form"/>
<field name="arch" type="xml">
<button name="action_lot_open_quants" position="after">
<button name="%(stock_move_line_from_lot_action)d" string="Product Moves" type="action" icon="fa-exchange"/>
</button>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2022 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<!-- Display advanced fields in stock moves form view -->
<record id="view_move_form" model="ir.ui.view">
<field name="name">stock.usability.stock.move.form</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_form" />
<field name="arch" type="xml">
<!--
<field name="state" position="before">
<button type="object" name="button_do_unreserve" string="Unreserve"
groups="stock.group_stock_user"
attrs="{'invisible': [('reserved_quant_ids', '=', [])]}"/>
</field> -->
<field name="origin" position="after">
<field name="picking_id" readonly="1" string="Picking"/>
</field>
<group name="origin_grp" position="after">
<group name="advanced" string="Advanced" groups="stock.group_stock_manager">
<field name="warehouse_id" readonly="1"/>
<field name="route_ids" widget="many2many_tags" readonly="1"/>
<field name="rule_id" readonly="1"/>
<field name="propagate_cancel" readonly="1"/>
<field name="price_unit" readonly="1"/>
<field name="partner_id" readonly="1"/>
<field name="restrict_partner_id" readonly="1"/>
</group>
</group>
<group name="linked_group" position="after">
<group name="move_line_ids" string="Product Moves" colspan="2">
<field name="move_line_ids" nolabel="1" readonly="1" colspan="2"/>
</group>
</group>
</field>
</record>
<record id="view_move_tree" model="ir.ui.view">
<field name="name">stock_usability.move.tree.better.order</field>
<field name="model">stock.move</field>
<field name="inherit_id" ref="stock.view_move_tree" />
<field name="arch" type="xml">
<field name="state" position="after">
<button type="object" name="button_do_unreserve" string="Unreserve"
groups="stock.group_stock_user"
states="partially_available,assigned"
icon="fa-ban"/>
</field>
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
<field name="reference" position="after">
<field name="origin" optional="hide"/>
<field name="partner_id" optional="hide"/>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2022 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_move_line_tree" model="ir.ui.view">
<field name="name">stock_usability.stock.move.line.tree</field>
<field name="model">stock.move.line</field>
<field name="inherit_id" ref="stock.view_move_line_tree" />
<field name="arch" type="xml">
<field name="qty_done" position="before">
<field name="reserved_uom_qty" sum="1"/>
</field>
<field name="qty_done" position="attributes">
<attribute name="sum">1</attribute>
</field>
<field name="state" position="after">
<button type="object" name="button_do_unreserve" string="Unreserve"
groups="stock.group_stock_user"
states="partially_available,assigned"
icon="fa-ban"/>
</field>
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
</field>
</record>
<!-- View embedded in picking -->
<record id="view_stock_move_line_detailed_operation_tree" model="ir.ui.view">
<field name="model">stock.move.line</field>
<field name="inherit_id" ref="stock.view_stock_move_line_detailed_operation_tree" />
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
<field name="qty_done" position="attributes">
<attribute name="sum">1</attribute>
</field>
<field name="reserved_uom_qty" position="attributes">
<attribute name="sum">1</attribute>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2020 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_picking_form" model="ir.ui.view">
<field name="name">stock_usability.view_picking_form</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form" />
<field name="arch" type="xml">
<field name="backorder_id" position="attributes">
<attribute name="attrs">{}</attribute>
</field>
<field name="partner_id" position="attributes">
<attribute name="context">{'show_address': 1}</attribute>
<attribute name="options">{'always_reload': True}</attribute>
</field>
<button name="action_cancel" type="object" position="attributes">
<attribute name="confirm">Are you sure you want to cancel this picking?</attribute>
</button>
<!-- STOCK MOVE -->
<!-- This sum is useful to check the 'number of items' to transfer... -->
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='product_uom_qty']" position="attributes">
<attribute name="sum">1</attribute>
</xpath>
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='quantity_done']" position="attributes">
<attribute name="sum">1</attribute>
</xpath>
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='location_id']" position="replace"/>
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='location_dest_id']" position="replace"/>
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='name']" position="replace"/>
<xpath expr="//field[@name='move_ids_without_package']/tree/field[@name='product_id']" position="after">
<field name="product_barcode" optional="hide"/>
<field name="name" optional="hide"/>
<field name="location_id" groups="stock.group_stock_multi_locations" optional="show" domain="[('id', 'child_of', 'parent.location_id')]" options="{'no_create': True}"/>
<field name="location_dest_id" groups="stock.group_stock_multi_locations" optional="show" domain="[('id', 'child_of', 'parent.location_dest_id')]" options="{'no_create': True}"/>
</xpath>
<xpath expr="//field[@name='move_ids_without_package']/tree/button[@name='action_assign_serial']" position="after">
<button type="object" name="button_do_unreserve" string="Unreserve"
groups="stock.group_stock_user"
states="partially_available,assigned"
icon="fa-ban"/>
</xpath>
<xpath expr="//field[@name='move_ids_without_package']/form//field[@name='product_uom']" position="after">
<field name="location_id" groups="stock.group_stock_multi_locations" domain="[('id', 'child_of', 'parent.location_id')]" options="{'no_create': True}"/>
<field name="location_dest_id" groups="stock.group_stock_multi_locations" domain="[('id', 'child_of', 'parent.location_dest_id')]" options="{'no_create': True}"/>
</xpath>
<xpath expr="//sheet/group/group/field[@name='location_id' and @groups='stock.group_stock_multi_locations']" position="attributes">
<attribute name="attrs">{}</attribute>
</xpath>
<xpath expr="//sheet/group/group/field[@name='location_dest_id' and @groups='stock.group_stock_multi_locations']" position="attributes">
<attribute name="attrs">{}</attribute>
</xpath>
<group name="other_infos" position="inside">
<!-- the field picking_type_id is shown at the top of the form view when hide_picking_type = False, which depend on a context key. I want to always show it in the last tab -->
<field name="picking_type_id" readonly="1"/>
</group>
</field>
</record>
<record id="view_picking_internal_search" model="ir.ui.view">
<field name="name">stock_usability.view_picking_search</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_internal_search" />
<field name="arch" type="xml">
<filter name="picking_type" position="after">
<filter string="Partner" name="partner_groupby" context="{'group_by': 'partner_id'}"/>
</filter>
<filter name="origin" position="replace"/>
<filter name="expected_date" position="after">
<filter name="date_done_groupby" string="Date Done"
context="{'group_by': 'date_done:day'}"/>
</filter>
<filter name="expected_date" position="attributes">
<!-- group per day -->
<attribute name="context">{'group_by': 'scheduled_date:day'}</attribute>
</filter>
</field>
</record>
<record id="stock_picking_pivot" model="ir.ui.view">
<field name="name">stock_usability.picking_pivot</field>
<field name="model">stock.picking</field>
<field name="arch" type="xml">
<pivot string="Transfers">
<field name="date_done" type="row" interval="month"/>
</pivot>
</field>
</record>
<record id="stock.action_picking_tree_all" model="ir.actions.act_window">
<field name="view_mode">tree,kanban,form,calendar,pivot</field> <!-- add pivot -->
</record>
<record id="stock.stock_picking_action_picking_type" model="ir.actions.act_window">
<field name="view_mode">tree,kanban,form,calendar,pivot</field> <!-- add pivot -->
</record>
<record id="stock.action_picking_tree_ready" model="ir.actions.act_window">
<field name="view_mode">tree,kanban,form,calendar,pivot</field> <!-- add pivot -->
</record>
<record id="stock.action_picking_tree_waiting" model="ir.actions.act_window">
<field name="view_mode">tree,kanban,form,calendar,pivot</field> <!-- add pivot -->
</record>
<record id="stock.action_picking_tree_late" model="ir.actions.act_window">
<field name="view_mode">tree,kanban,form,calendar,pivot</field> <!-- add pivot -->
</record>
<record id="stock.action_picking_tree_backorder" model="ir.actions.act_window">
<field name="view_mode">tree,kanban,form,calendar,pivot</field> <!-- add pivot -->
</record>
</odoo>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2023 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_picking_type_form" model="ir.ui.view">
<field name="model">stock.picking.type</field>
<field name="inherit_id" ref="stock.view_picking_type_form" />
<field name="arch" type="xml">
<field name="return_picking_type_id" position="before">
<field name="is_dropship"/>
</field>
</field>
</record>
<record id="view_picking_type_tree" model="ir.ui.view">
<field name="name">usability.stock.picking.type.tree</field>
<field name="model">stock.picking.type</field>
<field name="inherit_id" ref="stock.view_picking_type_tree"/>
<field name="arch" type="xml">
<field name="warehouse_id" position="after">
<field name="default_location_src_id"/>
<field name="default_location_dest_id"/>
</field>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2022 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_stock_quant_tree" model="ir.ui.view">
<field name="name">stock.usability.quant.tree</field>
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.view_stock_quant_tree"/>
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
<!-- Move available_quantity AFTER quantity -->
<field name="quantity" position="after">
<field name="reserved_quantity" sum="1" optional="show"/>
<field name="available_quantity" position="move"/>
</field>
<field name="quantity" position="attributes">
<attribute name="sum">1</attribute>
</field>
<field name="available_quantity" position="attributes">
<attribute name="sum">1</attribute>
<attribute name="optional">show</attribute>
</field>
</field>
</record>
<!-- view used from product form -->
<record id="view_stock_quant_tree_inventory_editable" model="ir.ui.view">
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.view_stock_quant_tree_inventory_editable"/>
<field name="arch" type="xml">
<field name="quantity" position="after">
<field name="reserved_quantity" sum="1" optional="show"/>
<button type="object" name="action_stock_move_lines_reserved" string="Reservations" attrs="{'invisible': [('reserved_quantity', '=', 0)]}"/>
<field name="available_quantity" position="move"/>
</field>
<field name="quantity" position="attributes">
<attribute name="sum">1</attribute>
</field>
<field name="available_quantity" position="attributes">
<attribute name="sum">1</attribute>
<attribute name="optional">show</attribute>
</field>
</field>
</record>
<record id="quant_search_view" model="ir.ui.view">
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.quant_search_view"/>
<field name="arch" type="xml">
<!-- With the context set via the field location_id
odoo will set default_location_id to [self]
So, to make it work, we also inherit default_get
to convert from list of 1 ID to an ID -->
<field name="location_id" position="attributes">
<attribute name="context">{'search_location': self}</attribute>
</field>
</field>
</record>
<record id="stock_quant_tree_simple" model="ir.ui.view">
<field name="model">stock.quant</field>
<field name="priority">100</field>
<field name="arch" type="xml">
<tree edit="0" create="0" delete="0">
<field name="location_id"/>
<field name="product_id" />
<field name="lot_id" groups="stock.group_production_lot"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<field name="quantity" sum="1"/>
<field name="product_uom_id" string="Unit" groups="uom.group_uom"/>
<field name="reserved_quantity" string="Reserved" sum="1" optional="show"/>
<button type="object" name="action_stock_move_lines_reserved" string="Reservations" attrs="{'invisible': [('reserved_quantity', '=', 0)]}"/>
<field name="available_quantity" string="Available" sum="1" optional="show"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
<!--
<record id="view_stock_quant_tree_editable" model="ir.ui.view">
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.view_stock_quant_tree_editable"/>
<field name="arch" type="xml">
<field name="reserved_quantity" position="after">
<button type="object" name="action_stock_move_lines_reserved" string="Reservations" attrs="{'invisible': [('reserved_quantity', '=', 0)]}"/>
<field name="available_quantity" sum="1" optional="show"/>
</field>
</field>
</record>
-->
<!-- mig to v16 ?
<record id="view_stock_quant_form" model="ir.ui.view">
<field name="name">stock.usability.quant.form</field>
<field name="model">stock.quant</field>
<field name="inherit_id" ref="stock.view_stock_quant_form"/>
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button class="oe_stat_button" icon="fa-arrows-v" type="object" name="action_stock_move_lines_reserved" string="Reservations"/>
</div>
<xpath expr="//field[@name='reserved_quantity']/.." position="after">
<label for="available_quantity" />
<div class="o_row">
<field name="available_quantity"/>
<field name="product_uom_id" groups="uom.group_uom"/>
</div>
</xpath>
</field>
</record>
-->
<!-- more detailed stock.move tree view when using the button from product form -->
<!-- TODO TEST
<record id="stock.act_product_stock_move_open" model="ir.actions.act_window">
<field name="view_id" ref="stock.view_move_tree"/>
</record> -->
<!-- Rename menu entry Locations -> Quants -->
<record id="stock.menu_valuation" model="ir.ui.menu">
<field name="name">Quants</field>
</record>
</odoo>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2022 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_warehouse" model="ir.ui.view">
<field name="name">stock.usability.warehouse.form</field>
<field name="model">stock.warehouse</field>
<field name="inherit_id" ref="stock.view_warehouse" />
<field name="arch" type="xml">
<xpath expr="//field[@name='out_type_id']/.." position="after">
<group name="routes" string="Routes">
<field name="route_ids" widget="many2many_tags"/>
<field name="crossdock_route_id"/>
<field name="reception_route_id"/>
<field name="delivery_route_id"/>
<field name="resupply_route_ids"/>
</group>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2014-2022 Akretion (http://www.akretion.com/)
@author Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="view_warehouse_orderpoint_form" model="ir.ui.view">
<field name="model">stock.warehouse.orderpoint</field>
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_form" />
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="trigger"/>
</field>
</field>
</record>
<record id="view_warehouse_orderpoint_tree_editable" model="ir.ui.view">
<field name="model">stock.warehouse.orderpoint</field>
<field name="inherit_id" ref="stock.view_warehouse_orderpoint_tree_editable" />
<field name="arch" type="xml">
<field name="trigger" position="attributes">
<attribute name="optional">show</attribute>
</field>
<field name="product_id" position="after">
<field name="product_barcode" optional="hide"/>
</field>
</field>
</record>
</odoo>