Merge PR #2169 into 16.0

Signed-off-by LoisRForgeFlow
This commit is contained in:
OCA-git-bot
2024-12-09 10:57:29 +00:00
15 changed files with 552 additions and 42 deletions

View File

@@ -17,12 +17,14 @@
"views/stock_warehouse_view.xml",
"views/stock_inventory_view.xml",
"views/stock_location_view.xml",
"views/stock_move_line_view.xml",
"views/res_config_settings_view.xml",
"data/cycle_count_sequence.xml",
"data/cycle_count_ir_cron.xml",
"reports/stock_location_accuracy_report.xml",
"reports/stock_cycle_count_report.xml",
"security/ir.model.access.csv",
"security/security.xml",
],
"license": "AGPL-3",
"installable": True,

View File

@@ -6,3 +6,5 @@ from . import stock_location
from . import stock_inventory
from . import stock_warehouse
from . import stock_move
from . import stock_move_line
from . import stock_quant

View File

@@ -1,10 +1,13 @@
# Copyright 2017-18 ForgeFlow S.L.
# (http://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
import logging
from odoo import _, api, fields, models
from odoo.exceptions import UserError
_logger = logging.getLogger(__name__)
class StockCycleCount(models.Model):
_name = "stock.cycle.count"
@@ -24,7 +27,7 @@ class StockCycleCount(models.Model):
comodel_name="res.users",
string="Assigned to",
readonly=True,
states={"draft": [("readonly", False)]},
states={"draft": [("readonly", False)], "open": [("readonly", False)]},
tracking=True,
)
date_deadline = fields.Date(
@@ -32,6 +35,21 @@ class StockCycleCount(models.Model):
readonly=True,
states={"draft": [("readonly", False)]},
tracking=True,
compute="_compute_date_deadline",
inverse="_inverse_date_deadline",
store=True,
)
automatic_deadline_date = fields.Date(
string="Automatic Required Date",
readonly=True,
states={"draft": [("readonly", False)]},
tracking=True,
)
manual_deadline_date = fields.Date(
string="Manual Required Date",
readonly=True,
states={"draft": [("readonly", False)]},
tracking=True,
)
cycle_count_rule_id = fields.Many2one(
comodel_name="stock.cycle.count.rule",
@@ -99,15 +117,10 @@ class StockCycleCount(models.Model):
data = rec._prepare_inventory_adjustment()
inv = self.env["stock.inventory"].create(data)
if rec.company_id.auto_start_inventory_from_cycle_count:
inv.prefill_counted_quantity = (
rec.company_id.inventory_adjustment_counted_quantities
)
inv.action_state_to_in_progress()
if inv.prefill_counted_quantity == "zero":
inv.stock_quant_ids.write({"inventory_quantity": 0})
else:
for quant in inv.stock_quant_ids:
quant.write({"inventory_quantity": quant.quantity})
try:
inv.action_state_to_in_progress()
except Exception as e:
_logger.info("Error when beginning an adjustment: %s", str(e))
self.write({"state": "open"})
return True
@@ -124,3 +137,15 @@ class StockCycleCount(models.Model):
action["views"] = [(res and res.id or False, "form")]
action["res_id"] = adjustment_ids and adjustment_ids[0] or False
return action
@api.depends("automatic_deadline_date", "manual_deadline_date")
def _compute_date_deadline(self):
for rec in self:
if rec.manual_deadline_date:
rec.date_deadline = rec.manual_deadline_date
else:
rec.date_deadline = rec.automatic_deadline_date
def _inverse_date_deadline(self):
for rec in self:
rec.manual_deadline_date = rec.date_deadline

View File

@@ -160,6 +160,7 @@ class StockCycleCountRule(models.Model):
"date": fields.Datetime.from_string(date),
"location": location,
"rule_type": self,
"company_id": location.company_id,
}
return cycle_count
@@ -172,7 +173,7 @@ class StockCycleCountRule(models.Model):
.search(
[
("location_ids", "in", [loc.id]),
("state", "in", ["confirm", "done", "draft"]),
("state", "in", ["in_progress", "done", "draft"]),
],
order="date desc",
limit=1,

View File

@@ -29,25 +29,48 @@ class StockInventory(models.Model):
)
inventory_accuracy = fields.Float(
string="Accuracy",
compute="_compute_inventory_accuracy",
digits=(3, 2),
store=True,
group_operator="avg",
default=False,
)
responsible_id = fields.Many2one(
tracking=True,
compute="_compute_responsible_id",
inverse="_inverse_responsible_id",
store=True,
readonly=False,
)
@api.depends("state", "stock_quant_ids")
def _compute_inventory_accuracy(self):
@api.depends("cycle_count_id.responsible_id")
def _compute_responsible_id(self):
for inv in self:
theoretical = sum(inv.stock_quant_ids.mapped(lambda x: abs(x.quantity)))
abs_discrepancy = sum(
inv.stock_quant_ids.mapped(lambda x: abs(x.inventory_diff_quantity))
)
if theoretical:
inv.inventory_accuracy = max(
PERCENT * (theoretical - abs_discrepancy) / theoretical, 0.0
if inv.cycle_count_id:
inv.responsible_id = inv.cycle_count_id.responsible_id
inv.stock_quant_ids.write(
{"user_id": inv.cycle_count_id.responsible_id}
)
if not inv.stock_quant_ids and inv.state == "done":
inv.inventory_accuracy = PERCENT
def _inverse_responsible_id(self):
for inv in self:
if inv.cycle_count_id and inv.responsible_id:
inv.cycle_count_id.responsible_id = inv.responsible_id
def write(self, vals):
result = super().write(vals)
if "responsible_id" in vals:
if not self.env.context.get("no_propagate"):
if (
self.cycle_count_id
and self.cycle_count_id.responsible_id.id != vals["responsible_id"]
):
self.cycle_count_id.with_context(no_propagate=True).write(
{"responsible_id": vals["responsible_id"]}
)
for quant in self.mapped("stock_quant_ids"):
if quant.user_id.id != vals["responsible_id"]:
quant.write({"user_id": vals["responsible_id"]})
return result
def _update_cycle_state(self):
for inv in self:
@@ -61,6 +84,26 @@ class StockInventory(models.Model):
("location_id", "in", self.location_ids.ids),
]
def _calculate_inventory_accuracy(self):
for inv in self:
accuracy = 100
sum_line_accuracy = 0
sum_theoretical_qty = 0
if inv.stock_move_ids:
for line in inv.stock_move_ids:
sum_line_accuracy += line.theoretical_qty * line.line_accuracy
sum_theoretical_qty += line.theoretical_qty
if sum_theoretical_qty != 0:
accuracy = (sum_line_accuracy / sum_theoretical_qty) * 100
else:
accuracy = 0
inv.update(
{
"inventory_accuracy": accuracy,
}
)
return False
def _link_to_planned_cycle_count(self):
self.ensure_one()
domain = self._domain_cycle_count_candidate()
@@ -85,11 +128,13 @@ class StockInventory(models.Model):
def action_state_to_done(self):
res = super().action_state_to_done()
self._calculate_inventory_accuracy()
self._update_cycle_state()
return res
def action_force_done(self):
res = super().action_force_done()
self._calculate_inventory_accuracy()
self._update_cycle_state()
return res
@@ -144,3 +189,15 @@ class StockInventory(models.Model):
message=msg,
)
)
def action_state_to_in_progress(self):
res = super().action_state_to_in_progress()
self.prefill_counted_quantity = (
self.company_id.inventory_adjustment_counted_quantities
)
if self.prefill_counted_quantity == "zero":
self.stock_quant_ids.write({"inventory_quantity": 0})
elif self.prefill_counted_quantity == "counted":
for quant in self.stock_quant_ids:
quant.write({"inventory_quantity": quant.quantity})
return res

View File

@@ -106,7 +106,7 @@ class StockLocation(models.Model):
)
self.env["stock.cycle.count"].create(
{
"date_deadline": date,
"automatic_deadline_date": date,
"location_id": self.id,
"cycle_count_rule_id": rule.id,
"state": "draft",

View File

@@ -0,0 +1,15 @@
# Copyright 2024 ForgeFlow S.L.
# (http://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class StockMoveLine(models.Model):
_inherit = "stock.move.line"
line_accuracy = fields.Float(
string="Accuracy",
store=True,
)
theoretical_qty = fields.Float(string="Theoretical Quantity", store=True)
counted_qty = fields.Float(string="Counted Quantity", store=True)

View File

@@ -0,0 +1,44 @@
# Copyright 2024 ForgeFlow S.L.
# (http://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
from odoo import models
class StockQuant(models.Model):
_inherit = "stock.quant"
def _apply_inventory(self):
accuracy_dict = {}
theoretical_dict = {}
counted_dict = {}
for rec in self:
if rec.discrepancy_percent > 100:
line_accuracy = 0
else:
line_accuracy = 1 - (rec.discrepancy_percent / 100)
accuracy_dict[rec.id] = line_accuracy
theoretical_dict[rec.id] = rec.quantity
counted_dict[rec.id] = rec.inventory_quantity
res = super()._apply_inventory()
for rec in self:
record_moves = self.env["stock.move.line"]
moves = record_moves.search(
[
("product_id", "=", rec.product_id.id),
("lot_id", "=", rec.lot_id.id),
"|",
("location_id", "=", rec.location_id.id),
("location_dest_id", "=", rec.location_id.id),
]
+ ([("company_id", "=", rec.company_id.id)] if rec.company_id else []),
order="create_date asc",
)
move = moves[len(moves) - 1]
move.write(
{
"line_accuracy": accuracy_dict[rec.id],
"theoretical_qty": theoretical_dict[rec.id],
"counted_qty": counted_dict[rec.id],
}
)
return res

View File

@@ -70,10 +70,11 @@ class StockWarehouse(models.Model):
@api.model
def _prepare_cycle_count(self, cycle_count_proposed):
return {
"date_deadline": cycle_count_proposed["date"],
"automatic_deadline_date": cycle_count_proposed["date"],
"location_id": cycle_count_proposed["location"].id,
"cycle_count_rule_id": cycle_count_proposed["rule_type"].id,
"state": "draft",
"company_id": cycle_count_proposed["company_id"].id,
}
def action_compute_cycle_count_rules(self):
@@ -106,12 +107,17 @@ class StockWarehouse(models.Model):
cycle_count_proposed = next(
filter(lambda x: x["date"] == earliest_date, proposed_for_loc)
)
self._handle_existing_cycle_counts(loc, cycle_count_proposed)
existing_cycle_counts = self._handle_existing_cycle_counts(
loc, cycle_count_proposed
)
delta = (
fields.Datetime.from_string(cycle_count_proposed["date"])
- datetime.today()
)
if delta.days < self.cycle_count_planning_horizon:
if (
not existing_cycle_counts
and delta.days < self.cycle_count_planning_horizon
):
cc_vals = self._prepare_cycle_count(cycle_count_proposed)
cc_vals_list.append(cc_vals)
return cc_vals_list
@@ -133,10 +139,11 @@ class StockWarehouse(models.Model):
)
cc_to_update.write(
{
"date_deadline": cycle_count_proposed_date,
"automatic_deadline_date": cycle_count_proposed_date,
"cycle_count_rule_id": cycle_count_proposed["rule_type"].id,
}
)
return existing_cycle_counts
@api.model
def cron_cycle_count(self):

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="0">
<record model="ir.rule" id="stock_cycle_count_comp_rule">
<field name="name">Stock Cycle Count multi-company</field>
<field name="model_id" ref="model_stock_cycle_count" />
<field name="global" eval="True" />
<field
name="domain_force"
>['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
</record>
</odoo>

View File

@@ -8,10 +8,11 @@
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
@@ -274,7 +275,7 @@ pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -300,7 +301,7 @@ span.option {
span.pre {
white-space: pre }
span.problematic {
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
@@ -513,7 +514,9 @@ If you spotted it first, help us to smash it by providing a detailed and welcome
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-14">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>

View File

@@ -58,10 +58,13 @@ class TestStockCycleCount(common.TransactionCase):
]
cls.big_wh.write({"cycle_count_rule_ids": [(6, 0, cls.rule_ids)]})
# Create a location:
# Create locations:
cls.count_loc = cls.stock_location_model.create(
{"name": "Place", "usage": "production"}
)
cls.count_loc_2 = cls.stock_location_model.create(
{"name": "Place 2", "usage": "production"}
)
cls.stock_location_model._parent_store_compute()
# Create a cycle count:
@@ -77,6 +80,9 @@ class TestStockCycleCount(common.TransactionCase):
cls.product1 = cls.product_model.create(
{"name": "Test Product 1", "type": "product", "default_code": "PROD1"}
)
cls.product2 = cls.product_model.create(
{"name": "Test Product 2", "type": "product", "default_code": "PROD2"}
)
@classmethod
def _create_user(cls, login, groups, company):
@@ -153,7 +159,7 @@ class TestStockCycleCount(common.TransactionCase):
"name": "To be cancelled when running cron job.",
"cycle_count_rule_id": self.rule_periodic.id,
"location_id": loc.id,
"date_deadline": date_pre_existing_cc,
"automatic_deadline_date": date_pre_existing_cc,
}
)
self.assertEqual(
@@ -188,14 +194,13 @@ class TestStockCycleCount(common.TransactionCase):
move1._action_assign()
move1.move_line_ids[0].qty_done = 1.0
move1._action_done()
# Remove the pre_existing_count
self.inventory_model.search(
[("cycle_count_id", "=", pre_existing_count.id)], limit=1
).unlink()
pre_existing_count.unlink()
# Execute cron for first time
wh.cron_cycle_count()
self.assertNotEqual(
pre_existing_count.date_deadline,
date_pre_existing_cc,
"Date of pre-existing cycle counts has not been " "updated.",
)
counts = self.cycle_count_model.search([("location_id", "in", locs.ids)])
self.assertTrue(counts, "Cycle counts not planned")
# Zero-confirmations:
count = self.cycle_count_model.search(
[
@@ -334,5 +339,279 @@ class TestStockCycleCount(common.TransactionCase):
with self.assertRaises(ValidationError):
inventory.exclude_sublocation = False
company = self.env["res.company"].create({"name": "Test"})
with self.assertRaises(ValidationError):
inventory.company_id = company
def test_inventory_adjustment_accuracy(self):
date = datetime.today() - timedelta(days=1)
# Create location
loc = self.stock_location_model.create(
{"name": "Test Location", "usage": "internal"}
)
# Create stock quants for specific location
quant1 = self.quant_model.create(
{
"product_id": self.product1.id,
"location_id": loc.id,
"quantity": 10.0,
}
)
quant2 = self.quant_model.create(
{
"product_id": self.product2.id,
"location_id": loc.id,
"quantity": 15.0,
}
)
# Create adjustments for specific location
adjustment = self.inventory_model.create(
{
"name": "Pre-existing inventory",
"location_ids": [(4, loc.id)],
"date": date,
}
)
# Start the adjustment
adjustment.action_state_to_in_progress()
# Check that there are stock quants for the specific location
self.assertTrue(self.env["stock.quant"].search([("location_id", "=", loc.id)]))
# Make the count of the stock
quant1.update(
{
"inventory_quantity": 5,
}
)
quant2.update(
{
"inventory_quantity": 10,
}
)
# Apply the changes
quant1._apply_inventory()
quant2._apply_inventory()
# Check that line_accuracy is calculated properly
sml = self.env["stock.move.line"].search(
[("location_id", "=", loc.id), ("product_id", "=", self.product1.id)]
)
self.assertEqual(sml.line_accuracy, 0.5)
sml = self.env["stock.move.line"].search(
[("location_id", "=", loc.id), ("product_id", "=", self.product2.id)]
)
self.assertEqual(sml.line_accuracy, 0.6667000000000001)
# Set Inventory Adjustment to Done
adjustment.action_state_to_done()
# Check that accuracy is correctly calculated
self.assertEqual(adjustment.inventory_accuracy, 60)
def test_zero_inventory_adjustment_accuracy(self):
date = datetime.today() - timedelta(days=1)
# Create location
loc = self.stock_location_model.create(
{"name": "Test Location", "usage": "internal"}
)
# Create stock quants for specific location
quant1 = self.quant_model.create(
{
"product_id": self.product1.id,
"location_id": loc.id,
"quantity": 15.0,
}
)
quant2 = self.quant_model.create(
{
"product_id": self.product2.id,
"location_id": loc.id,
"quantity": 10.0,
}
)
# Create adjustment for specific location
adjustment = self.inventory_model.create(
{
"name": "Pre-existing inventory qty zero",
"location_ids": [(4, loc.id)],
"date": date,
}
)
# Start the adjustment
adjustment.action_state_to_in_progress()
# Check that there are stock quants for the specific location
self.assertTrue(self.env["stock.quant"].search([("location_id", "=", loc.id)]))
# Make the count of the stock
quant1.update(
{
"inventory_quantity": 0,
}
)
quant2.update(
{
"inventory_quantity": 0,
}
)
# Apply the changes
quant1._apply_inventory()
quant2._apply_inventory()
# Check that line_accuracy is calculated properly
move_1 = adjustment.stock_move_ids.filtered(
lambda c: c.product_id == self.product1
)
move_2 = adjustment.stock_move_ids.filtered(
lambda c: c.product_id == self.product1
)
self.assertEqual(move_1.line_accuracy, 0)
self.assertEqual(move_2.line_accuracy, 0)
# Set Inventory Adjustment to Done
adjustment.action_state_to_done()
# Check that accuracy is correctly calculated
self.assertEqual(adjustment.inventory_accuracy, 0)
# Check discrepancy over 100%
adjustment_2 = self.inventory_model.create(
{
"name": "Adjustment 2",
"location_ids": [(4, loc.id)],
"date": date,
}
)
adjustment_2.action_state_to_in_progress()
quant1.update(
{
"inventory_quantity": 1500,
}
)
quant1._apply_inventory()
# Check that line_accuracy is calculated properly
sml = self.env["stock.move.line"].search(
[("location_id", "=", loc.id), ("product_id", "=", self.product1.id)]
)
# Check that line_accuracy is still 0
self.assertEqual(sml.line_accuracy, 0)
def test_auto_start_inventory_from_cycle_count(self):
# Set the auto_start_inventory_from_cycle_count rule to True
self.company.auto_start_inventory_from_cycle_count = True
# Create Cycle Count 1 cont_loc_2
cycle_count_1 = self.cycle_count_model.create(
{
"name": "Cycle Count 1",
"cycle_count_rule_id": self.rule_periodic.id,
"location_id": self.count_loc_2.id,
"date_deadline": "2026-11-30",
"manual_deadline_date": "2026-11-30",
}
)
cycle_count_1.flush()
# Confirm the Cycle Count
cycle_count_1.action_create_inventory_adjustment()
# Inventory adjustments change their state to in_progress
self.assertEqual(cycle_count_1.stock_adjustment_ids.state, "in_progress")
def test_prefill_counted_quantity(self):
self.company.inventory_adjustment_counted_quantities = "counted"
date = datetime.today() - timedelta(days=1)
# Create locations
loc_1 = self.stock_location_model.create(
{"name": "Test Location 1", "usage": "internal"}
)
loc_2 = self.stock_location_model.create(
{"name": "Test Location 2", "usage": "internal"}
)
# Create stock quants for different locations
quant_1 = self.quant_model.create(
{
"product_id": self.product1.id,
"location_id": loc_1.id,
"quantity": 25,
}
)
quant_2 = self.quant_model.create(
{
"product_id": self.product1.id,
"location_id": loc_2.id,
"quantity": 50,
}
)
# Create adjustments for different locations
adjustment_1 = self.inventory_model.create(
{
"name": "Adjustment Location 1",
"location_ids": [(4, loc_1.id)],
"date": date,
}
)
adjustment_2 = self.inventory_model.create(
{
"name": "Adjustment Location 2",
"location_ids": [(4, loc_2.id)],
"date": date,
}
)
# Start the adjustment 1 with prefill quantity as counted
adjustment_1.action_state_to_in_progress()
# Check that the inventory_quantity is 25
self.assertEqual(quant_1.inventory_quantity, 25)
# Change company prefill option to zero
self.company.inventory_adjustment_counted_quantities = "zero"
# Start the adjustment 2 with prefill quantity as zero
adjustment_2.action_state_to_in_progress()
# Check that the inventory_quantity is 0
self.assertEqual(quant_2.inventory_quantity, 0.0)
def test_responsible_id_propagation_with_inventory_adjustment(self):
additional_user = self._create_user(
"user_3", [self.g_stock_manager], self.company
)
additional_user_2 = self._create_user(
"user_4", [self.g_stock_manager], self.company
)
self.cycle_count_1.responsible_id = self.manager
self.assertEqual(
self.cycle_count_1.responsible_id.id,
self.manager,
"Initial responsible not correctly assigned.",
)
self.quant_model.create(
{
"product_id": self.product1.id,
"location_id": self.count_loc.id,
"quantity": 100,
}
)
self.cycle_count_1.action_create_inventory_adjustment()
inventory = self.cycle_count_1.stock_adjustment_ids[0]
self.assertEqual(
inventory.responsible_id.id,
self.cycle_count_1.responsible_id.id,
"Inventory responsible does not match cycle count responsible.",
)
for quant in inventory.stock_quant_ids:
self.assertEqual(
quant.user_id.id,
inventory.responsible_id.id,
"Quant user does not match inventory responsible.",
)
self.cycle_count_1.responsible_id = additional_user.id
inventory.invalidate_cache()
self.cycle_count_1.stock_adjustment_ids[0].stock_quant_ids.invalidate_cache()
self.assertEqual(
inventory.responsible_id.id,
additional_user.id,
"Inventory responsible not updated after cycle count responsible change.",
)
for quant in inventory.stock_quant_ids:
self.assertEqual(
quant.user_id.id,
additional_user.id,
"Quant user not updated after inventory responsible change.",
)
inventory.responsible_id = additional_user_2
self.assertEqual(
self.cycle_count_1.responsible_id.id,
additional_user_2.id,
"Cycle Count not updated after inventory responsible change.",
)
for quant in inventory.stock_quant_ids:
self.assertEqual(
quant.user_id.id,
additional_user_2.id,
"Quant user not updated after inventory responsible change.",
)

View File

@@ -10,6 +10,8 @@
<tree
decoration-muted="state == 'cancelled'"
decoration-info="state == 'draft'"
multi_edit="1"
default_order="date_deadline asc"
>
<field name="name" />
<field name="location_id" />
@@ -17,6 +19,7 @@
<field name="responsible_id" />
<field name="date_deadline" />
<field name="state" />
<field name="company_id" optional="hide" />
</tree>
</field>
</record>
@@ -124,6 +127,7 @@
domain="[('state','=', 'cancelled')]"
help="Cycle Counts Cancelled"
/>
<separator />
<filter
name="assigned_to_user"
string="Assigned to me"
@@ -150,6 +154,12 @@
domain="[]"
context="{'group_by':'responsible_id'}"
/>
<filter
string="Required Date"
name="date_deadline"
domain="[]"
context="{'group_by': 'date_deadline:day'}"
/>
</group>
</search>
</field>

View File

@@ -8,7 +8,6 @@
<field name="inherit_id" ref="stock_inventory.view_inventory_group_tree" />
<field name="arch" type="xml">
<field name="name" position="after">
<field name="location_ids" />
<field name="cycle_count_id" />
<field name="inventory_accuracy" />
</field>

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2017 ForgeFlow S.L.
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<record id="view_stock_move_line_tree" model="ir.ui.view">
<field name="name">Stock Move Line Tree - cycle count extension</field>
<field name="model">stock.move.line</field>
<field
name="inherit_id"
ref="stock_inventory.view_stock_move_line_inventory_tree"
/>
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="is_inventory" invisible="1" />
<field
name="theoretical_qty"
attrs="{'invisible': [('is_inventory', '=', False)]}"
/>
<field
name="counted_qty"
attrs="{'invisible': [('is_inventory', '=', False)]}"
/>
<field
name="line_accuracy"
attrs="{'invisible': [('is_inventory', '=', False)]}"
widget="percentage"
/>
</field>
</field>
</record>
<record id="view_stock_move_line_form" model="ir.ui.view">
<field name="name">Stock Move Line Form - cycle count extension</field>
<field name="model">stock.move.line</field>
<field name="inherit_id" ref="stock.view_move_line_form" />
<field name="arch" type="xml">
<field name="lot_id" position="after">
<field name="is_inventory" invisible="1" />
<field
name="theoretical_qty"
attrs="{'invisible': [('is_inventory', '=', False)]}"
/>
<field
name="counted_qty"
attrs="{'invisible': [('is_inventory', '=', False)]}"
/>
<field
name="line_accuracy"
attrs="{'invisible': [('is_inventory', '=', False)]}"
class="oe_inline"
widget="percentage"
/>
</field>
</field>
</record>
</odoo>