Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[15.0][FIX/IMP] stock_inventory: new small features and ux improvements #1991

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions stock_inventory/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
"depends": ["stock"],
"data": [
"security/ir.model.access.csv",
"security/security.xml",
"views/stock_inventory.xml",
"views/stock_quant.xml",
"views/stock_move_line.xml",
"views/res_config_settings_view.xml",
],
"installable": True,
"application": False,
Expand Down
2 changes: 2 additions & 0 deletions stock_inventory/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from . import stock_inventory
from . import stock_quant
from . import stock_move_line
from . import res_company
from . import res_config_settings
15 changes: 15 additions & 0 deletions stock_inventory/models/res_company.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2024 ForgeFlow S.L. (http://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).


from odoo import fields, models


class ResCompany(models.Model):
_inherit = "res.company"

stock_inventory_auto_complete = fields.Boolean(
help="If enabled, when all the quants prepared for the adjustment "
"are done, the adjustment is automatically set to done.",
default=False,
)
12 changes: 12 additions & 0 deletions stock_inventory/models/res_config_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2024 ForgeFlow S.L. (http://www.forgeflow.com)
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).

from odoo import fields, models


class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"

stock_inventory_auto_complete = fields.Boolean(
related="company_id.stock_inventory_auto_complete", readonly=False
)
143 changes: 119 additions & 24 deletions stock_inventory/models/stock_inventory.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,28 @@ class InventoryAdjustmentsGroup(models.Model):
_description = "Inventory Adjustment Group"
_order = "date desc, id desc"

name = fields.Char(required=True, default="Inventory", string="Inventory Reference")
name = fields.Char(
required=True,
default="Inventory",
string="Inventory Reference",
states={"draft": [("readonly", False)]},
readonly=True,
)

date = fields.Datetime(default=lambda self: fields.Datetime.now())
date = fields.Datetime(
default=lambda self: fields.Datetime.now(),
states={"draft": [("readonly", False)]},
readonly=True,
)

company_id = fields.Many2one(
comodel_name="res.company",
readonly=True,
index=True,
states={"draft": [("readonly", False)]},
default=lambda self: self.env.company,
LoisRForgeFlow marked this conversation as resolved.
Show resolved Hide resolved
required=True,
)

state = fields.Selection(
[("draft", "Draft"), ("in_progress", "In Progress"), ("done", "Done")],
Expand All @@ -22,7 +41,10 @@ class InventoryAdjustmentsGroup(models.Model):
)

location_ids = fields.Many2many(
"stock.location", string="Locations", domain="[('usage', '=', 'internal')]"
"stock.location",
string="Locations",
domain="[('usage', '=', 'internal'), "
"'|', ('company_id', '=', company_id), ('company_id', '=', False)]",
)

product_selection = fields.Selection(
Expand All @@ -37,15 +59,24 @@ class InventoryAdjustmentsGroup(models.Model):
required=True,
)

product_ids = fields.Many2many("product.product", string="Products")
product_ids = fields.Many2many(
"product.product",
string="Products",
domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]",
)

stock_quant_ids = fields.Many2many("stock.quant", string="Inventory Adjustment")
stock_quant_ids = fields.Many2many(
"stock.quant",
string="Inventory Adjustment",
domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]",
)

category_id = fields.Many2one("product.category", string="Product Category")

lot_ids = fields.Many2many(
"stock.production.lot",
string="Lot/Serial Numbers",
domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]",
)

stock_move_ids = fields.One2many(
Expand All @@ -66,14 +97,24 @@ class InventoryAdjustmentsGroup(models.Model):
compute="_compute_count_stock_moves", string="Stock Moves Lines"
)

exclude_sublocation = fields.Boolean(
help="If enabled, it will only take into account "
"the locations selected, and not their children."
)

responsible_id = fields.Many2one(
comodel_name="res.users",
string="Assigned to",
states={"draft": [("readonly", False)]},
readonly=True,
tracking=True,
help="Specific responsible of Inventory Adjustment.",
)

@api.depends("stock_quant_ids")
def _compute_count_stock_quants(self):
self.count_stock_quants = len(self.stock_quant_ids)
count_todo = len(
self.stock_quant_ids.search(
[("id", "in", self.stock_quant_ids.ids), ("to_do", "=", "True")]
)
)
count_todo = len(self.stock_quant_ids.filtered(lambda sq: sq.to_do))
self.count_stock_quants_string = "{} / {}".format(
count_todo, self.count_stock_quants
)
Expand All @@ -99,11 +140,15 @@ def _get_quants(self, locations):
return self.env["stock.quant"].search(domain)

def _get_base_domain(self, locations):
return [
"|",
("location_id", "in", locations.mapped("id")),
("location_id", "in", locations.child_ids.ids),
]
return (
[
("location_id", "in", locations.mapped("id")),
]
if self.exclude_sublocation
else [
("location_id", "child_of", locations.child_internal_location_ids.ids),
]
)

def _get_domain_all_quants(self, base_domain):
return base_domain
Expand Down Expand Up @@ -146,13 +191,15 @@ def _get_domain_category_quants(self, base_domain):
]
)

def refresh_stock_quant_ids(self):
for rec in self:
rec.stock_quant_ids = rec._get_quants(rec.location_ids)

def action_state_to_in_progress(self):
active_rec = self.env["stock.inventory"].search(
[
("state", "=", "in_progress"),
"|",
("location_ids", "in", self.location_ids.mapped("id")),
("location_ids", "in", self.location_ids.child_ids.ids),
("location_ids", "child_of", self.location_ids.ids),
LoisRForgeFlow marked this conversation as resolved.
Show resolved Hide resolved
],
limit=1,
)
Expand All @@ -164,25 +211,48 @@ def action_state_to_in_progress(self):
% active_rec.name
)
self.state = "in_progress"
self.stock_quant_ids = self._get_quants(self.location_ids)
self.stock_quant_ids.update({"to_do": True})
self.refresh_stock_quant_ids()
self.stock_quant_ids.update(
{
"to_do": True,
"user_id": self.responsible_id,
"inventory_date": self.date,
}
)
return

def action_state_to_done(self):
self.state = "done"
self.stock_quant_ids.update({"to_do": True})
self.stock_quant_ids.update(
{
"to_do": True,
"user_id": False,
"inventory_date": False,
}
)
return

def action_auto_state_to_done(self):
self.ensure_one()
if not any(self.stock_quant_ids.filtered(lambda sq: sq.to_do)):
self.action_state_to_done()
return

def action_state_to_draft(self):
self.state = "draft"
self.stock_quant_ids.update({"to_do": True})
self.stock_quant_ids.update(
{
"to_do": True,
"user_id": False,
"inventory_date": False,
}
)
self.stock_quant_ids = None
return

def action_view_inventory_adjustment(self):
result = self.env["stock.quant"].action_view_inventory()
ia_ids = self.mapped("stock_quant_ids").ids
result["domain"] = [("id", "in", ia_ids)]
result["domain"] = [("id", "in", self.stock_quant_ids.ids)]
result["search_view_id"] = self.env.ref("stock.quant_search_view").id
result["context"]["search_default_to_do"] = 1
return result
Expand All @@ -196,6 +266,31 @@ def action_view_stock_moves(self):
result["context"] = []
return result

@api.constrains("state", "location_ids")
def _check_inventory_in_progress_not_override(self):
inventories = self.search([("state", "=", "in_progress")])
for rec in inventories:
inventory = inventories.filtered(
lambda x: x.id != rec.id
and (
any(i in x.location_ids for i in rec.location_ids)
or (
any(
i in x.location_ids.child_internal_location_ids
for i in rec.location_ids
)
and not x.exclude_sublocation
)
)
)
if len(inventory) > 0:
raise ValidationError(
_(
"Cannot be more than one in progress inventory adjustment "
"affecting the same location at the same time."
)
)

@api.constrains("product_selection", "product_ids")
def _check_one_product_in_product_selection(self):
for rec in self:
Expand Down
32 changes: 29 additions & 3 deletions stock_inventory/models/stock_quant.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from odoo import _, fields, models
from odoo import _, api, fields, models


class StockQuant(models.Model):
Expand All @@ -15,7 +15,10 @@ def _apply_inventory(self):
.search([("state", "=", "in_progress")])
.filtered(
lambda x: rec.location_id in x.location_ids
or rec.location_id in x.location_ids.child_ids
or (
rec.location_id in x.location_ids.child_internal_location_ids
and not x.exclude_sublocation
)
)
)
moves = record_moves.search(
Expand All @@ -36,9 +39,32 @@ def _apply_inventory(self):
raise ValueError(_("No move lines have been created"))
move = moves[len(moves) - 1]
adjustment.stock_move_ids |= move
move.inventory_adjustment_id = adjustment
reference = move.reference
if adjustment.name and move.reference:
reference = adjustment.name + ": " + move.reference
elif adjustment.name:
reference = adjustment.name
move.write(
{
"inventory_adjustment_id": adjustment.id,
"reference": reference,
}
)
rec.to_do = False
if self.env.company.stock_inventory_auto_complete:
adjustment.action_auto_state_to_done()
return res

def _get_inventory_fields_write(self):
return super()._get_inventory_fields_write() + ["to_do"]

@api.model
def create(self, vals):
res = super().create(vals)
if self.env.context.get(
"active_model", False
) == "stock.inventory" and self.env.context.get("active_id", False):
self.env["stock.inventory"].browse(
self.env.context.get("active_id")
).refresh_stock_quant_ids()
return res
11 changes: 11 additions & 0 deletions stock_inventory/security/security.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="0">
<record model="ir.rule" id="stock_inventory_comp_rule">
<field name="name">Stock Inventory multi-company</field>
<field name="model_id" ref="model_stock_inventory" />
<field name="global" eval="True" />
<field
name="domain_force"
>['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
</record>
</odoo>
1 change: 0 additions & 1 deletion stock_inventory/static/description/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
Expand Down
Loading
Loading