[IMP] stock_demand_estimate_matrix: black, isort

This commit is contained in:
ps-tubtim
2020-03-23 14:51:53 +07:00
parent 33d1c840b6
commit 89976063c2
8 changed files with 298 additions and 324 deletions

View File

@@ -3,15 +3,11 @@
{
"name": "Stock Demand Estimate Matrix",
"summary": "Allows to create demand estimates.",
"version": "12.0.2.0.0",
"version": "13.0.1.0.0",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/stock-logistics-warehouse",
"category": "Warehouse Management",
"depends": [
"stock_demand_estimate",
"web_widget_x2many_2d_matrix",
"date_range",
],
"depends": ["stock_demand_estimate", "web_widget_x2many_2d_matrix", "date_range"],
"data": [
"views/stock_demand_estimate_view.xml",
"views/date_range.xml",

View File

@@ -8,16 +8,11 @@ class DateRange(models.Model):
_inherit = "date.range"
days = fields.Integer(
string="Days between dates",
compute="_compute_days",
readonly=True,
string="Days between dates", compute="_compute_days", readonly=True,
)
@api.multi
@api.depends("date_start", "date_end")
def _compute_days(self):
for rec in self.filtered(lambda x: x.date_start and x.date_end):
rec.days = abs((
rec.date_end -
rec.date_start
).days) + 1
rec.days = abs((rec.date_end - rec.date_start).days) + 1

View File

@@ -5,12 +5,10 @@ from odoo import api, fields, models
class StockDemandEstimate(models.Model):
_inherit = 'stock.demand.estimate'
_inherit = "stock.demand.estimate"
date_range_id = fields.Many2one(
comodel_name="date.range",
string="Estimating Period",
ondelete='restrict'
comodel_name="date.range", string="Estimating Period", ondelete="restrict"
)
@api.multi
@@ -19,8 +17,7 @@ class StockDemandEstimate(models.Model):
)
def _compute_dates(self):
date_range_records = self.filtered(lambda r: r.date_range_id)
res = super(
StockDemandEstimate, self - date_range_records)._compute_dates()
res = super(StockDemandEstimate, self - date_range_records)._compute_dates()
for rec in date_range_records:
rec.date_from = rec.date_range_id.date_start
rec.date_to = rec.date_range_id.date_end
@@ -32,9 +29,8 @@ class StockDemandEstimate(models.Model):
date_range_records = self.filtered(lambda r: r.date_range_id)
res = super(StockDemandEstimate, self - date_range_records).name_get()
for rec in date_range_records:
name = "%s - %s - %s" % (
rec.date_range_id.name, rec.product_id.name,
rec.location_id.name,
name = "{} - {} - {}".format(
rec.date_range_id.name, rec.product_id.name, rec.location_id.name,
)
res.append((rec.id, name))
return res

View File

@@ -2,6 +2,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from dateutil.rrule import MONTHLY
from odoo import fields
from odoo.exceptions import ValidationError
from odoo.tests.common import SavepointCase
@@ -21,56 +22,48 @@ class TestStockDemandEstimate(SavepointCase):
cls.company = cls.env.ref("base.main_company")
# Create users:
cls.manager = cls._create_user(
"user_1",
[cls.g_stock_manager],
cls.company,
).id
cls.user = cls._create_user(
"user_2",
[cls.g_stock_user],
cls.company,
).id
cls.drt_monthly = cls.env["date.range.type"].create({
"name": "Month",
"allow_overlap": False,
})
cls.manager = cls._create_user("user_1", [cls.g_stock_manager], cls.company,).id
cls.user = cls._create_user("user_2", [cls.g_stock_user], cls.company,).id
cls.drt_monthly = cls.env["date.range.type"].create(
{"name": "Month", "allow_overlap": False}
)
generator = cls.env["date.range.generator"]
generator = generator.create({
"date_start": "1943-01-01",
"name_prefix": "1943-",
"type_id": cls.drt_monthly.id,
"duration_count": 1,
"unit_of_time": MONTHLY,
"count": 12,
})
generator = generator.create(
{
"date_start": "1943-01-01",
"name_prefix": "1943-",
"type_id": cls.drt_monthly.id,
"duration_count": 1,
"unit_of_time": MONTHLY,
"count": 12,
}
)
generator.action_apply()
# Create a product:
cls.product_1 = cls.product_model.create({
"name": "Test Product 1",
"type": "product",
"default_code": "PROD1",
})
cls.product_1 = cls.product_model.create(
{"name": "Test Product 1", "type": "product", "default_code": "PROD1"}
)
# Create a location:
cls.location = cls.stock_location_model.create({
"name": "Place",
"usage": "production",
})
cls.location = cls.stock_location_model.create(
{"name": "Place", "usage": "production"}
)
@classmethod
def _create_user(cls, login, groups, company):
group_ids = [group.id for group in groups]
user = cls.res_users_model.create({
"name": login,
"login": login,
"password": "demo",
"email": "example@yourcompany.com",
"company_id": company.id,
"company_ids": [(4, company.id)],
"groups_id": [(6, 0, group_ids)],
})
user = cls.res_users_model.create(
{
"name": login,
"login": login,
"password": "demo",
"email": "example@yourcompany.com",
"company_id": company.id,
"company_ids": [(4, company.id)],
"groups_id": [(6, 0, group_ids)],
}
)
return user
def test_01_demand_estimate_wizard(self):
@@ -79,20 +72,20 @@ class TestStockDemandEstimate(SavepointCase):
for sheet in sheets:
sheet.unlink()
wiz = self.env["stock.demand.estimate.wizard"]
wiz = wiz.create({
"date_start": "1943-01-01",
"date_end": "1943-12-31",
"location_id": self.location.id,
"date_range_type_id": self.drt_monthly.id,
"product_ids": [(6, 0, [self.product_1.id])],
})
wiz = wiz.create(
{
"date_start": "1943-01-01",
"date_end": "1943-12-31",
"location_id": self.location.id,
"date_range_type_id": self.drt_monthly.id,
"product_ids": [(6, 0, [self.product_1.id])],
}
)
wiz.create_sheet()
sheets = self.env["stock.demand.estimate.sheet"].search([])
for sheet in sheets:
self.assertEqual(
len(sheet.line_ids),
12,
"There should be 12 lines.",
len(sheet.line_ids), 12, "There should be 12 lines.",
)
self.assertEqual(
fields.Date.to_string(sheet.date_start),
@@ -105,9 +98,7 @@ class TestStockDemandEstimate(SavepointCase):
"The date end should be 1943-12-31",
)
self.assertEqual(
sheet.location_id.id,
self.location.id,
"Wrong location",
sheet.location_id.id, self.location.id, "Wrong location",
)
for line in sheet.line_ids:
line.product_uom_qty = 1
@@ -129,9 +120,7 @@ class TestStockDemandEstimate(SavepointCase):
[("date_range_id", "in", ranges.ids)]
)
self.assertEqual(
len(estimates),
12,
"There should be 12 estimate records.",
len(estimates), 12, "There should be 12 estimate records.",
)
for estimate in estimates:
self.assertEqual(
@@ -149,44 +138,49 @@ class TestStockDemandEstimate(SavepointCase):
for sheet in sheets:
sheet.unlink()
wiz = self.env["stock.demand.estimate.wizard"]
wiz = wiz.create({
"date_start": "1943-01-01",
"date_end": "1943-12-31",
"location_id": self.location.id,
"date_range_type_id": self.drt_monthly.id,
"product_ids": [(6, 0, [self.product_1.id])],
})
wiz = wiz.create(
{
"date_start": "1943-01-01",
"date_end": "1943-12-31",
"location_id": self.location.id,
"date_range_type_id": self.drt_monthly.id,
"product_ids": [(6, 0, [self.product_1.id])],
}
)
wiz.create_sheet()
sheets = self.env["stock.demand.estimate.sheet"].search([])
for sheet in sheets:
for line in sheet.line_ids:
self.assertEqual(
line.product_uom_qty,
1,
"The quantity should be 1",
line.product_uom_qty, 1, "The quantity should be 1",
)
def test_02_invalid_dates(self):
wiz = self.env["stock.demand.estimate.wizard"]
with self.assertRaises(ValidationError):
wiz.create({
"date_start": "1943-12-31",
"date_end": "1943-01-01",
"location_id": self.location.id,
"date_range_type_id": self.drt_monthly.id,
"product_ids": [(6, 0, [self.product_1.id])],
})
wiz.create(
{
"date_start": "1943-12-31",
"date_end": "1943-01-01",
"location_id": self.location.id,
"date_range_type_id": self.drt_monthly.id,
"product_ids": [(6, 0, [self.product_1.id])],
}
)
def test_03_computed_fields(self):
range = self.env["date.range"].search(
[("type_id", "=", self.drt_monthly.id)], limit=1)
estimate = self.estimate_model.create({
"product_id": self.product_1.id,
"location_id": self.location.id,
"date_range_id": range.id,
"product_uom_qty": 100.0,
})
expected_date_from = range.date_start
expected_date_to = range.date_end
date_range = self.env["date.range"].search(
[("type_id", "=", self.drt_monthly.id)], limit=1
)
estimate = self.estimate_model.create(
{
"product_id": self.product_1.id,
"location_id": self.location.id,
"date_range_id": range.id,
"product_uom_qty": 100.0,
}
)
expected_date_from = date_range.date_start
expected_date_to = date_range.date_end
self.assertEqual(estimate.date_from, expected_date_from)
self.assertEqual(estimate.date_to, expected_date_to)

View File

@@ -1,10 +1,10 @@
<?xml version="1.0"?>
<?xml version="1.0" ?>
<odoo>
<menuitem id="date_range_menu"
string="Date Ranges"
parent="stock.menu_stock_config_settings"
action="date_range.date_range_action"
sequence="99"/>
<menuitem
id="date_range_menu"
string="Date Ranges"
parent="stock.menu_stock_config_settings"
action="date_range.date_range_action"
sequence="99"
/>
</odoo>

View File

@@ -1,13 +1,15 @@
<?xml version="1.0"?>
<?xml version="1.0" ?>
<odoo>
<record id="stock_demand_estimate_view_tree" model="ir.ui.view">
<field name="name">stock.demand.estimate.tree</field>
<field name="model">stock.demand.estimate</field>
<field name="inherit_id" ref="stock_demand_estimate.stock_demand_estimate_view_tree"/>
<field
name="inherit_id"
ref="stock_demand_estimate.stock_demand_estimate_view_tree"
/>
<field name="arch" type="xml">
<field name="date_from" position="before">
<field name="date_range_id"/>
<field name="date_range_id" />
</field>
<tree position="attributes">
<attribute name="editable">top</attribute>
@@ -15,32 +17,36 @@
</tree>
</field>
</record>
<record id="stock_demand_estimate_view_pivot" model="ir.ui.view">
<field name="name">stock.demand.estimate.pivot</field>
<field name="model">stock.demand.estimate</field>
<field name="inherit_id" ref="stock_demand_estimate.stock_demand_estimate_view_pivot"/>
<field
name="inherit_id"
ref="stock_demand_estimate.stock_demand_estimate_view_pivot"
/>
<field name="arch" type="xml">
<field name="product_id" position="after">
<field name="date_range_id" type="col"/>
<field name="date_range_id" type="col" />
</field>
</field>
</record>
<record id="stock_demand_estimate_view_search" model="ir.ui.view">
<field name="name">stock.demand.estimate.search</field>
<field name="model">stock.demand.estimate</field>
<field name="inherit_id" ref="stock_demand_estimate.stock_demand_estimate_view_search"/>
<field
name="inherit_id"
ref="stock_demand_estimate.stock_demand_estimate_view_search"
/>
<field name="arch" type="xml">
<field name="location_id" position="after">
<field name="date_range_id"/>
<field name="date_range_id" />
</field>
</field>
</record>
<record id="stock_demand_estimate.stock_demand_estimate_action"
model="ir.actions.act_window">
<record
id="stock_demand_estimate.stock_demand_estimate_action"
model="ir.actions.act_window"
>
<field name="view_mode">tree,pivot</field>
</record>
</odoo>

View File

@@ -1,64 +1,56 @@
# Copyright 2019 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models, _
from odoo.osv import expression
from odoo.addons import decimal_precision as dp
from odoo import _, api, fields, models
from odoo.exceptions import UserError, ValidationError
from odoo.osv import expression
from odoo.addons import decimal_precision as dp
class StockDemandEstimateSheet(models.TransientModel):
_name = 'stock.demand.estimate.sheet'
_description = 'Stock Demand Estimate Sheet'
_name = "stock.demand.estimate.sheet"
_description = "Stock Demand Estimate Sheet"
date_start = fields.Date(
string="Date From",
readonly=True,
)
date_end = fields.Date(
string="Date to",
readonly=True,
)
date_start = fields.Date(string="Date From", readonly=True,)
date_end = fields.Date(string="Date to", readonly=True,)
date_range_type_id = fields.Many2one(
string='Date Range Type',
comodel_name='date.range.type',
readonly=True,
string="Date Range Type", comodel_name="date.range.type", readonly=True,
)
location_id = fields.Many2one(
comodel_name="stock.location",
string="Location",
readonly=True,
comodel_name="stock.location", string="Location", readonly=True,
)
line_ids = fields.Many2many(
string="Estimates",
comodel_name='stock.demand.estimate.sheet.line',
relation='stock_demand_estimate_line_rel',
)
product_ids = fields.Many2many(
string="Products",
comodel_name="product.product",
comodel_name="stock.demand.estimate.sheet.line",
relation="stock_demand_estimate_line_rel",
)
product_ids = fields.Many2many(string="Products", comodel_name="product.product",)
@api.onchange('date_start', 'date_end', 'date_range_type_id',)
@api.onchange(
"date_start", "date_end", "date_range_type_id",
)
def _onchange_dates(self):
for sheet in self:
if not all([sheet.date_start, sheet.date_end,
sheet.date_range_type_id]):
if not all([sheet.date_start, sheet.date_end, sheet.date_range_type_id]):
return
ranges = sheet._get_ranges()
if not ranges:
raise UserError(_('There is no ranges created.'))
estimates = self.env['stock.demand.estimate'].search([
('product_id', 'in', sheet.product_ids.ids),
('date_range_id', 'in', ranges.ids),
('location_id', '=', sheet.location_id.id),
])
raise UserError(_("There is no ranges created."))
estimates = self.env["stock.demand.estimate"].search(
[
("product_id", "in", sheet.product_ids.ids),
("date_range_id", "in", ranges.ids),
("location_id", "=", sheet.location_id.id),
]
)
lines = []
for product in sheet.product_ids:
for _range in ranges:
estimate = estimates.filtered(
lambda x: (x.date_range_id == _range and
x.product_id == product)
lambda x: (
x.date_range_id == _range and x.product_id == product
)
)
if estimate:
uom_id = fields.first(estimate).product_uom.id
@@ -68,61 +60,70 @@ class StockDemandEstimateSheet(models.TransientModel):
uom_id = product.uom_id.id
uom_qty = 0.0
estimate_id = None
lines.append((0, 0, sheet._get_default_estimate_line(
_range,
product,
uom_id,
uom_qty,
estimate_id=estimate_id,
)))
lines.append(
(
0,
0,
sheet._get_default_estimate_line(
_range,
product,
uom_id,
uom_qty,
estimate_id=estimate_id,
),
)
)
sheet.line_ids = lines
def _get_ranges(self):
domain_1 = [
'&',
('type_id', '=', self.date_range_type_id.id), '|', '&',
('date_start', '>=', self.date_start),
('date_start', '<=', self.date_end),
'&',
('date_end', '>=', self.date_start),
('date_end', '<=', self.date_end),
"&",
("type_id", "=", self.date_range_type_id.id),
"|",
"&",
("date_start", ">=", self.date_start),
("date_start", "<=", self.date_end),
"&",
("date_end", ">=", self.date_start),
("date_end", "<=", self.date_end),
]
domain_2 = [
'&',
('type_id', '=', self.date_range_type_id.id),
'&',
('date_start', '<=', self.date_start),
('date_end', '>=', self.date_start),
"&",
("type_id", "=", self.date_range_type_id.id),
"&",
("date_start", "<=", self.date_start),
("date_end", ">=", self.date_start),
]
domain = expression.OR([domain_1, domain_2])
ranges = self.env['date.range'].search(domain)
ranges = self.env["date.range"].search(domain)
return ranges
def _get_default_estimate_line(self, _range, product,
uom_id, uom_qty, estimate_id=None):
name_y = '{} - {}'.format(product.name, product.uom_id.name)
def _get_default_estimate_line(
self, _range, product, uom_id, uom_qty, estimate_id=None
):
name_y = "{} - {}".format(product.name, product.uom_id.name)
if product.default_code:
name_y += '[{}] {}'.format(product.default_code, name_y)
name_y += "[{}] {}".format(product.default_code, name_y)
values = {
'value_x': _range.name,
'value_y': name_y,
'date_range_id': _range.id,
'product_id': product.id,
'product_uom': uom_id,
'product_uom_qty': uom_qty,
'location_id': self.location_id.id,
'estimate_id': estimate_id,
"value_x": _range.name,
"value_y": name_y,
"date_range_id": _range.id,
"product_id": product.id,
"product_uom": uom_id,
"product_uom_qty": uom_qty,
"location_id": self.location_id.id,
"estimate_id": estimate_id,
}
return values
@api.model
def _prepare_estimate_data(self, line):
return {
'date_range_id': line.date_range_id.id,
'product_id': line.product_id.id,
'location_id': line.location_id.id,
'product_uom_qty': line.product_uom_qty,
'product_uom': line.product_id.uom_id.id,
"date_range_id": line.date_range_id.id,
"product_id": line.product_id.id,
"location_id": line.location_id.id,
"product_uom_qty": line.product_uom_qty,
"product_uom": line.product_id.uom_id.id,
}
@api.multi
@@ -134,130 +135,107 @@ class StockDemandEstimateSheet(models.TransientModel):
res.append(line.estimate_id.id)
else:
data = self._prepare_estimate_data(line)
estimate = self.env['stock.demand.estimate'].create(data)
estimate = self.env["stock.demand.estimate"].create(data)
res.append(estimate.id)
res = {
'domain': [('id', 'in', res)],
'name': _('Stock Demand Estimates'),
'src_model': 'stock.demand.estimate.wizard',
'view_type': 'form',
'view_mode': 'tree',
'res_model': 'stock.demand.estimate',
'type': 'ir.actions.act_window'
"domain": [("id", "in", res)],
"name": _("Stock Demand Estimates"),
"src_model": "stock.demand.estimate.wizard",
"view_type": "form",
"view_mode": "tree",
"res_model": "stock.demand.estimate",
"type": "ir.actions.act_window",
}
return res
class StockDemandEstimateSheetLine(models.TransientModel):
_name = 'stock.demand.estimate.sheet.line'
_description = 'Stock Demand Estimate Sheet Line'
_name = "stock.demand.estimate.sheet.line"
_description = "Stock Demand Estimate Sheet Line"
estimate_id = fields.Many2one(
comodel_name='stock.demand.estimate'
)
date_range_id = fields.Many2one(
comodel_name='date.range',
string='Period',
)
estimate_id = fields.Many2one(comodel_name="stock.demand.estimate")
date_range_id = fields.Many2one(comodel_name="date.range", string="Period",)
location_id = fields.Many2one(
comodel_name='stock.location',
string="Stock Location",
)
product_id = fields.Many2one(
comodel_name='product.product',
string='Product',
)
value_x = fields.Char(
string='Period Name',
)
value_y = fields.Char(
string='Product Name',
comodel_name="stock.location", string="Stock Location",
)
product_id = fields.Many2one(comodel_name="product.product", string="Product",)
value_x = fields.Char(string="Period Name",)
value_y = fields.Char(string="Product Name",)
product_uom_qty = fields.Float(
string="Quantity",
digits=dp.get_precision('Product UoM'),
string="Quantity", digits=dp.get_precision("Product UoM"),
)
class DemandEstimateWizard(models.TransientModel):
_name = 'stock.demand.estimate.wizard'
_description = 'Stock Demand Estimate Wizard'
_name = "stock.demand.estimate.wizard"
_description = "Stock Demand Estimate Wizard"
date_start = fields.Date(
string="Date From",
required=True,
)
date_end = fields.Date(
string="Date To",
required=True,
)
date_start = fields.Date(string="Date From", required=True,)
date_end = fields.Date(string="Date To", required=True,)
date_range_type_id = fields.Many2one(
string='Date Range Type',
comodel_name='date.range.type',
required=True,
string="Date Range Type", comodel_name="date.range.type", required=True,
)
location_id = fields.Many2one(
comodel_name="stock.location",
string="Location",
required=True,
)
product_ids = fields.Many2many(
comodel_name="product.product",
string="Products",
comodel_name="stock.location", string="Location", required=True,
)
product_ids = fields.Many2many(comodel_name="product.product", string="Products",)
@api.onchange('date_range_type_id')
@api.onchange("date_range_type_id")
def _onchange_date_range_type_id(self):
if self.date_range_type_id.company_id:
return {
'domain': {'location_id': [
('company_id', '=', self.date_range_type_id.company_id.id)
]}
"domain": {
"location_id": [
("company_id", "=", self.date_range_type_id.company_id.id)
]
}
}
return {}
@api.constrains('date_start', 'date_end')
@api.constrains("date_start", "date_end")
def _check_start_end_dates(self):
self.ensure_one()
if self.date_start > self.date_end:
raise ValidationError(
_('The start date cannot be later than the end date.')
_("The start date cannot be later than the end date.")
)
@api.multi
def _prepare_demand_estimate_sheet(self):
self.ensure_one()
return {
'date_start': self.date_start,
'date_end': self.date_end,
'date_range_type_id': self.date_range_type_id.id,
'location_id': self.location_id.id,
"date_start": self.date_start,
"date_end": self.date_end,
"date_range_type_id": self.date_range_type_id.id,
"location_id": self.location_id.id,
}
@api.multi
def create_sheet(self):
self.ensure_one()
if not self.product_ids:
raise UserError(_('You must select at least one product.'))
raise UserError(_("You must select at least one product."))
# 2d matrix widget need real records to work
sheet = self.env['stock.demand.estimate.sheet'].create({
'date_start': self.date_start,
'date_end': self.date_end,
'date_range_type_id': self.date_range_type_id.id,
'location_id': self.location_id.id,
'product_ids': [(6, 0, self.product_ids.ids)],
})
sheet = self.env["stock.demand.estimate.sheet"].create(
{
"date_start": self.date_start,
"date_end": self.date_end,
"date_range_type_id": self.date_range_type_id.id,
"location_id": self.location_id.id,
"product_ids": [(6, 0, self.product_ids.ids)],
}
)
sheet._onchange_dates()
res = {
'name': _('Estimate Sheet'),
'src_model': 'stock.demand.estimate.wizard',
'view_type': 'form',
'view_mode': 'form',
'target': 'new',
'res_model': 'stock.demand.estimate.sheet',
'res_id': sheet.id,
'type': 'ir.actions.act_window',
"name": _("Estimate Sheet"),
"src_model": "stock.demand.estimate.wizard",
"view_type": "form",
"view_mode": "form",
"target": "new",
"res_model": "stock.demand.estimate.sheet",
"res_id": sheet.id,
"type": "ir.actions.act_window",
}
return res

View File

@@ -1,93 +1,102 @@
<?xml version="1.0"?>
<?xml version="1.0" ?>
<odoo>
<record id="stock_demand_estimate_sheet_view_form"
model="ir.ui.view">
<record id="stock_demand_estimate_sheet_view_form" model="ir.ui.view">
<field name="name">stock.demand.estimate.sheet.form</field>
<field name="model">stock.demand.estimate.sheet</field>
<field name="arch" type="xml">
<form string="Stock Demand Estimate Sheet">
<group name="dates">
<label for="date_start" string="Period"/>
<div><field name="date_start" class="oe_inline"/> to <field name="date_end" class="oe_inline"/></div>
<label for="date_start" string="Period" />
<div><field name="date_start" class="oe_inline" /> to <field
name="date_end"
class="oe_inline"
/></div>
</group>
<group>
<group name="attributes" colspan="2">
<field name="date_range_type_id"/>
<field name="location_id"/>
<field name="date_range_type_id" />
<field name="location_id" />
</group>
</group>
<div/>
<group name="estimated_quantity"
string="Estimated quantity">
<field name="line_ids" nolabel="1"
widget="x2many_2d_matrix"
field_x_axis="value_x"
field_y_axis="value_y"
field_value="product_uom_qty"
x_axis_clickable="0"
y_axis_clickable="0">
<div />
<group name="estimated_quantity" string="Estimated quantity">
<field
name="line_ids"
nolabel="1"
widget="x2many_2d_matrix"
field_x_axis="value_x"
field_y_axis="value_y"
field_value="product_uom_qty"
x_axis_clickable="0"
y_axis_clickable="0"
>
<tree>
<field name="value_x"/>
<field name="value_y"/>
<field name="product_uom_qty"/>
<field name="value_x" />
<field name="value_y" />
<field name="product_uom_qty" />
</tree>
</field>
</group>
<footer>
<button name="button_validate"
<button
name="button_validate"
type="object"
string="Validate"
class="oe_highlight"/>
<button class="oe_link"
special="cancel"
string="Cancel"/>
class="oe_highlight"
/>
<button class="oe_link" special="cancel" string="Cancel" />
</footer>
</form>
</field>
</field>
</record>
<record id="demand_estimate_wizard_view_form"
model="ir.ui.view">
<record id="demand_estimate_wizard_view_form" model="ir.ui.view">
<field name="name">stock.demand.estimate.wizard.form</field>
<field name="model">stock.demand.estimate.wizard</field>
<field name="arch" type="xml">
<form string="Stock Demand Estimate Wizard">
<sheet>
<group name="dates">
<label for="date_start" string="Period"/>
<div><field name="date_start" class="oe_inline"/> to <field name="date_end" class="oe_inline"/></div>
<label for="date_start" string="Period" />
<div><field name="date_start" class="oe_inline" /> to <field
name="date_end"
class="oe_inline"
/></div>
</group>
<group>
<group name="attributes">
<field name="date_range_type_id"/>
<field name="location_id"/>
<field name="date_range_type_id" />
<field name="location_id" />
</group>
</group>
<div/>
<div />
<group name="products" string="Products">
<field name="product_ids" nolabel="1"/>
<field name="product_ids" nolabel="1" />
</group>
<footer>
<button name="create_sheet" string="Prepare"
type="object" class="oe_highlight" /> or
<button
name="create_sheet"
string="Prepare"
type="object"
class="oe_highlight"
/> or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</sheet>
</form>
</field>
</field>
</record>
<act_window name="Create Stock Demand Estimates"
<act_window
name="Create Stock Demand Estimates"
res_model="stock.demand.estimate.wizard"
src_model="stock.demand.estimate.sheet"
view_mode="form"
target="new"
key2="client_action_multi"
id="stock_demand_estimate_wizard_action"/>
<menuitem id="stock_demand_estimate_wizard_menu"
parent="stock_demand_estimate.stock_demand_planning_menu"
action="stock_demand_estimate_wizard_action"/>
id="stock_demand_estimate_wizard_action"
/>
<menuitem
id="stock_demand_estimate_wizard_menu"
parent="stock_demand_estimate.stock_demand_planning_menu"
action="stock_demand_estimate_wizard_action"
/>
</odoo>