[IMP] stock: create stock move lines from quants
In a flow where the reservation is used (internal transfers, deliveries, ...) adding a new stock move line is now made from the quantities available in stock. The 'add a line' button in the show detail wizard trigger the quant list view to directly pick the wanted lot or location where the stock is available. Only the quantity done is needed to be updated before the validation. This commit remove the 'quant reserve wizard' as the behaviour is an extension of it. Task: 3256447 Part-of: odoo/odoo#117513
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ReserveStockQuantLine(models.TransientModel):
|
||||
_inherit = 'stock.quant.reserve.line'
|
||||
|
||||
expiration_date = fields.Datetime(related='lot_id.expiration_date')
|
||||
|
||||
|
||||
class ReserveStockQuant(models.TransientModel):
|
||||
_inherit = 'stock.quant.reserve'
|
||||
|
||||
show_expiry = fields.Boolean(related='move_id.product_id.use_expiration_date')
|
||||
@@ -1,15 +0,0 @@
|
||||
<odoo>
|
||||
<record id="stock_quant_reserve_inherit" model="ir.ui.view">
|
||||
<field name="name">stock.quant.reserve.inherit</field>
|
||||
<field name="model">stock.quant.reserve</field>
|
||||
<field name="inherit_id" ref="stock.view_stock_quant_reserve_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="move_id" position="after">
|
||||
<field name="show_expiry" invisible="1"/>
|
||||
</field>
|
||||
<field name="lot_id" position='after'>
|
||||
<field name="expiration_date" groups="stock.group_production_lot" attrs="{'column_invisible': [('parent.show_expiry', '=', False)]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -768,7 +768,7 @@ Please change the quantity done or the rounding precision of your unit of measur
|
||||
|
||||
if self.product_id.tracking == "serial" and self.state == "assigned":
|
||||
self.next_serial = self.env['stock.lot']._get_next_serial(self.company_id, self.product_id)
|
||||
|
||||
quant_mode = self.picking_type_id.code != 'incoming'
|
||||
return {
|
||||
'name': _('Detailed Operations'),
|
||||
'type': 'ir.actions.act_window',
|
||||
@@ -780,13 +780,13 @@ Please change the quantity done or the rounding precision of your unit of measur
|
||||
'res_id': self.id,
|
||||
'context': dict(
|
||||
self.env.context,
|
||||
show_owner=self.picking_type_id.code != 'incoming',
|
||||
show_lots_m2o=self.has_tracking != 'none' and (self.picking_type_id.use_existing_lots or self.state == 'done' or self.origin_returned_move_id.id), # able to create lots, whatever the value of ` use_create_lots`.
|
||||
show_owner=not quant_mode,
|
||||
show_quant=quant_mode,
|
||||
show_lots_m2o=not quant_mode and self.has_tracking != 'none' and (self.picking_type_id.use_existing_lots or self.state == 'done' or self.origin_returned_move_id.id), # able to create lots, whatever the value of ` use_create_lots`.
|
||||
show_lots_text=self.has_tracking != 'none' and self.picking_type_id.use_create_lots and not self.picking_type_id.use_existing_lots and self.state != 'done' and not self.origin_returned_move_id.id,
|
||||
show_source_location=self.picking_type_id.code != 'incoming',
|
||||
show_destination_location=self.picking_type_id.code != 'outgoing',
|
||||
show_package=not self.location_id.usage == 'supplier',
|
||||
show_reserved_quantity=self.state != 'done' and self.picking_type_id.code != 'incoming'
|
||||
show_destination_location=not quant_mode,
|
||||
show_package=not quant_mode,
|
||||
show_reserved_quantity=self.state != 'done' and quant_mode
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,8 @@ class StockMoveLine(models.Model):
|
||||
compute='_compute_reserved_qty', inverse='_set_reserved_qty', store=True)
|
||||
reserved_uom_qty = fields.Float(
|
||||
'Reserved', default=0.0, digits='Product Unit of Measure', required=True, copy=False)
|
||||
qty_done = fields.Float('Done', default=0.0, digits='Product Unit of Measure', copy=False)
|
||||
qty_done = fields.Float('Done', default=0.0, digits='Product Unit of Measure', copy=False,
|
||||
compute='_compute_qty_done', store=True, readonly=False)
|
||||
package_id = fields.Many2one(
|
||||
'stock.quant.package', 'Source Package', ondelete='restrict',
|
||||
check_company=True,
|
||||
@@ -82,6 +83,7 @@ class StockMoveLine(models.Model):
|
||||
tracking = fields.Selection(related='product_id.tracking', readonly=True)
|
||||
origin = fields.Char(related='move_id.origin', string='Source')
|
||||
description_picking = fields.Text(string="Description picking")
|
||||
quant_id = fields.Many2one('stock.quant', "Pick From", store=False) # Dummy field for the detailed operation view
|
||||
|
||||
@api.depends('product_uom_id.category_id', 'product_id.uom_id.category_id', 'move_id.product_uom', 'product_id.uom_id')
|
||||
def _compute_product_uom_id(self):
|
||||
@@ -131,6 +133,12 @@ class StockMoveLine(models.Model):
|
||||
detect errors. """
|
||||
raise UserError(_('The requested operation cannot be processed because of a programming error setting the `reserved_qty` field instead of the `reserved_uom_qty`.'))
|
||||
|
||||
@api.depends('quant_id')
|
||||
def _compute_qty_done(self):
|
||||
for record in self:
|
||||
if not record.qty_done:
|
||||
record.qty_done = min(record.quant_id.quantity, record.move_id.product_qty)
|
||||
|
||||
@api.constrains('lot_id', 'product_id')
|
||||
def _check_lot_product(self):
|
||||
for line in self:
|
||||
@@ -288,6 +296,8 @@ class StockMoveLine(models.Model):
|
||||
vals['company_id'] = self.env['stock.move'].browse(vals['move_id']).company_id.id
|
||||
elif vals.get('picking_id'):
|
||||
vals['company_id'] = self.env['stock.picking'].browse(vals['picking_id']).company_id.id
|
||||
if vals.get('quant_id'):
|
||||
vals.update(self._copy_quant_info(vals))
|
||||
|
||||
mls = super().create(vals_list)
|
||||
|
||||
@@ -379,6 +389,8 @@ class StockMoveLine(models.Model):
|
||||
('owner_id', 'res.partner'),
|
||||
('product_uom_id', 'uom.uom')
|
||||
]
|
||||
if vals.get('quant_id'):
|
||||
vals.update(self._copy_quant_info(vals))
|
||||
updates = {}
|
||||
for key, model in triggers:
|
||||
if key in vals:
|
||||
@@ -872,6 +884,25 @@ class StockMoveLine(models.Model):
|
||||
'partner_id': self.picking_id.partner_id.id,
|
||||
}
|
||||
|
||||
def _copy_quant_info(self, vals):
|
||||
quant = self.env['stock.quant'].browse(vals.get('quant_id', 0))
|
||||
if self:
|
||||
uom = self.product_uom_id
|
||||
elif vals.get('product_uom_id'):
|
||||
uom = self.env['uom.uom'].browse(vals.get('product_uom_id'))
|
||||
else:
|
||||
uom = quant.product_uom_id
|
||||
line_data = {
|
||||
'product_id': quant.product_id.id,
|
||||
'lot_id': quant.lot_id.id,
|
||||
'package_id': quant.package_id.id,
|
||||
'location_id': quant.location_id.id,
|
||||
'owner_id': quant.owner_id.id,
|
||||
}
|
||||
if not (self.qty_done or vals.get('qty_done')):
|
||||
line_data['qty_done'] = uom._compute_quantity(quant.available_quantity, quant.product_uom_id, rounding_method='HALF_UP')
|
||||
return line_data
|
||||
|
||||
def action_open_reference(self):
|
||||
self.ensure_one()
|
||||
if self.move_id:
|
||||
@@ -937,14 +968,3 @@ class StockMoveLine(models.Model):
|
||||
'message': _("The inventory adjustments have been reverted."),
|
||||
}
|
||||
}
|
||||
|
||||
def action_open_reserve_stock(self):
|
||||
move_id = self.env['stock.move'].browse(self.env.context.get('default_move_id'))
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'stock.quant.reserve',
|
||||
'view_mode': 'form',
|
||||
'context': {'default_move_id': move_id.id},
|
||||
'target': 'new',
|
||||
'name': _('Reserve stock: %(product)s', product=move_id.product_id.name),
|
||||
}
|
||||
|
||||
@@ -523,6 +523,20 @@ class StockQuant(models.Model):
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
def name_get(self):
|
||||
"""name that will be displayed in the detailed operation"""
|
||||
name_parts = []
|
||||
for record in self:
|
||||
name = []
|
||||
if self.env.user.has_group('stock.group_stock_multi_locations'):
|
||||
name.append(record.location_id.display_name)
|
||||
if self.env.user.has_group('stock.group_production_lot') and record.lot_id:
|
||||
name.append(record.lot_id.name)
|
||||
name_parts.append(name)
|
||||
if name_parts:
|
||||
return [(quant.id, ' - '.join(name)) for quant, name in zip(self, name_parts)]
|
||||
return []
|
||||
|
||||
@api.constrains('product_id')
|
||||
def check_product_id(self):
|
||||
if any(elem.product_id.type != 'product' for elem in self):
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field";
|
||||
import { sprintf } from "@web/core/utils/strings";
|
||||
import { useSelectCreate } from "@web/views/fields/relational_utils";
|
||||
export class SMLX2ManyField extends X2ManyField {
|
||||
setup() {
|
||||
super.setup();
|
||||
const selectCreate = useSelectCreate({
|
||||
resModel: "stock.quant",
|
||||
activeActions: this.activeActions,
|
||||
onSelected: (resIds) => this.selectRecord(resIds),
|
||||
onCreateEdit: ({ context }) => this._openRecord({ context }),
|
||||
});
|
||||
|
||||
this.selectCreate = (params) => {
|
||||
const p = Object.assign({}, params);
|
||||
return selectCreate(p);
|
||||
};
|
||||
}
|
||||
|
||||
async onAdd({ context, editable } = {}) {
|
||||
context = {};
|
||||
const { string } = this.props;
|
||||
const title = sprintf(this.env._t("Add: %s"), string);
|
||||
const domain = [
|
||||
["product_id", "=", this.props.record.data.product_id[0]],
|
||||
["location_id", "child_of", this.props.context.default_location_id],
|
||||
];
|
||||
return this.selectCreate({ domain, context, title });
|
||||
}
|
||||
|
||||
selectRecord(res_ids) {
|
||||
const params = {
|
||||
context: { default_quant_id: res_ids[0] },
|
||||
};
|
||||
this.addInLine(params);
|
||||
}
|
||||
}
|
||||
|
||||
export const smlX2ManyField = {
|
||||
...x2ManyField,
|
||||
component: SMLX2ManyField,
|
||||
};
|
||||
|
||||
registry.category("fields").add("sml_x2_many", smlX2ManyField);
|
||||
@@ -0,0 +1,53 @@
|
||||
/** @odoo-module */
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { Many2OneField, many2OneField } from "@web/views/fields/many2one/many2one_field";
|
||||
|
||||
const { onWillStart} = owl;
|
||||
|
||||
export class StockPickFrom extends Many2OneField {
|
||||
setup() {
|
||||
super.setup();
|
||||
this.user = useService('user');
|
||||
onWillStart(async () => {
|
||||
const testedGroup = [['location', 'stock.group_stock_multi_locations'], ['lot', 'stock.group_production_lot']];
|
||||
const userGroups = await Promise.all(
|
||||
testedGroup.map((group) => this.user.hasGroup(group[1]))
|
||||
);
|
||||
this.enabledGroups = {};
|
||||
for (const [index, group] of testedGroup.entries()) {
|
||||
this.enabledGroups[group[0]] = userGroups[index];
|
||||
}
|
||||
});
|
||||
}
|
||||
get displayName() {
|
||||
return super.displayName || this._quant_display_name();
|
||||
}
|
||||
|
||||
get value() {
|
||||
return super.value || [0, this._quant_display_name()];
|
||||
}
|
||||
|
||||
_quant_display_name() {
|
||||
let name_parts = [];
|
||||
if (this.props.record.data.id) {
|
||||
// if location group is activated
|
||||
if (this.enabledGroups?.location) {
|
||||
name_parts.push(this.props.record.data.location_id?.[1])
|
||||
}
|
||||
if (this.enabledGroups?.lot) {
|
||||
name_parts.push(this.props.record.data.lot_id?.[1] || this.props.record.data.lot_name)
|
||||
}
|
||||
return name_parts.join(" - ");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export const stockPickFrom = {
|
||||
...many2OneField,
|
||||
component: StockPickFrom,
|
||||
};
|
||||
|
||||
registry.category("fields").add("pick_from", stockPickFrom);
|
||||
@@ -157,6 +157,7 @@
|
||||
<field name="from_immediate_transfer" invisible="1"/>
|
||||
<field name="product_uom_category_id" invisible="1"/>
|
||||
<field name="display_clear_serial" invisible="1"/>
|
||||
<field name="picking_code" invisible="1"/>
|
||||
<group>
|
||||
<group>
|
||||
<field name="product_id" readonly="1"/>
|
||||
@@ -191,8 +192,11 @@
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<field name="move_line_ids" attrs="{'readonly': ['|', ('state', '=', 'cancel'), '&', ('state', '=', 'done'), ('is_locked', '=', True)]}" context="{'tree_view_ref': 'stock.view_stock_move_line_operation_tree', 'default_product_uom_id': product_uom, 'default_picking_id': picking_id, 'default_move_id': id, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_company_id': company_id}"/>
|
||||
<footer class="oe_edit_only" attrs="{'invisible': ['|', ('state', '=', 'cancel'), '&', ('state', '=', 'done'), ('is_locked', '=', True)]}">
|
||||
<field name="move_line_ids"
|
||||
attrs="{'readonly': ['|', ('state', '=', 'cancel'), '&', ('state', '=', 'done'), ('is_locked', '=', True)]}"
|
||||
context="{'tree_view_ref': 'stock.view_stock_move_line_operation_tree', 'default_product_uom_id': product_uom, 'default_picking_id': picking_id, 'default_move_id': id, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id, 'default_company_id': company_id}"
|
||||
widget="sml_x2_many"/>
|
||||
<footer class="d-flex justify-content-between" attrs="{'invisible': ['|', ('state', '=', 'cancel'), '&', ('state', '=', 'done'), ('is_locked', '=', True)]}">
|
||||
<button string="Confirm" special="save" data-hotkey="v" class="oe_highlight"/>
|
||||
<button string="Discard" special="cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
@@ -222,10 +226,6 @@
|
||||
<field name="priority">1000</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom" decoration-muted="state == 'done' and is_locked == True" decoration-success="reserved_uom_qty==qty_done" decoration-danger="qty_done > reserved_uom_qty and state != 'done' and picking_code != 'incoming'">
|
||||
<control>
|
||||
<create name="add_move_line" string="Add a line"/>
|
||||
<button name="action_open_reserve_stock" string="Reserve stock" type="object" class="btn-link" />
|
||||
</control>
|
||||
<field name="company_id" invisible="1" force_save="1"/>
|
||||
<field name="picking_id" invisible="1" force_save="1"/>
|
||||
<field name="move_id" invisible="1" force_save="1" />
|
||||
@@ -235,8 +235,16 @@
|
||||
<field name="location_id" invisible="1"/>
|
||||
<field name="location_dest_id" invisible="1"/>
|
||||
<field name="package_id" invisible="1"/>
|
||||
<field name="location_id" options="{'no_create': True}" attrs="{'readonly': ['&', ('package_level_id', '!=', False), ('parent.picking_type_entire_packs', '=', True)]}" invisible="not context.get('show_source_location')" domain="[('id', 'child_of', parent.location_id), '|', ('company_id', '=', False), ('company_id', '=', company_id), ('usage', '!=', 'view')]" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="location_dest_id" attrs="{'readonly': ['&', ('package_level_id', '!=', False), ('parent.picking_type_entire_packs', '=', True)]}" invisible="not context.get('show_destination_location')" domain="[('id', 'child_of', parent.location_dest_id), '|', ('company_id', '=', False), ('company_id', '=', company_id), ('usage', '!=', 'view')]" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="quant_id"
|
||||
domain="[('product_id', '=', product_id), ('location_id', 'child_of', parent.location_id)]"
|
||||
context="{'default_location_id': location_id, 'default_product_id': product_id, 'search_view_ref': 'stock.quant_search_view', 'tree_view_ref': 'stock.view_stock_quant_tree', 'form_view_ref': 'stock.view_stock_quant_form', 'readonly_form': True}"
|
||||
widget="pick_from"
|
||||
invisible="not context.get('show_quant')"/>
|
||||
<field name="location_dest_id"
|
||||
attrs="{'readonly': ['&', ('package_level_id', '!=', False), ('parent.picking_type_entire_packs', '=', True)]}"
|
||||
invisible="not context.get('show_destination_location')"
|
||||
domain="[('id', 'child_of', parent.location_dest_id), '|', ('company_id', '=', False), ('company_id', '=', company_id), ('usage', '!=', 'view')]"
|
||||
groups="stock.group_stock_multi_locations"/>
|
||||
<field name="lot_id" groups="stock.group_production_lot"
|
||||
attrs="{'readonly': ['&', ('package_level_id', '!=', False), ('parent.picking_type_entire_packs', '=', True)]}"
|
||||
invisible="not context.get('show_lots_m2o')"
|
||||
|
||||
@@ -357,6 +357,36 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_stock_quant_form" model="ir.ui.view">
|
||||
<field name="name">view.stock.quant.form</field>
|
||||
<field name="priority">100</field>
|
||||
<field name="model">stock.quant</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Create Stock">
|
||||
<div class="alert alert-warning" role="alert" invisible="context.get('readonly_form')">
|
||||
You are going to pick products that are not referenced
|
||||
in this location. That leads to a negative stock.
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="location_id" invisible="1"/>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="product_id" force_save="1" invisible="1"/>
|
||||
<field name="lot_id" domain="[('product_id', '=', product_id)]"
|
||||
groups="stock.group_production_lot" readonly="context.get('readonly_form')"/>
|
||||
<field name="location_id" groups="stock.group_stock_multi_locations" readonly="context.get('readonly_form')"/>
|
||||
<field name="package_id" groups="stock.group_tracking_lot" invisible="context.get('readonly_form')"/>
|
||||
<field name="owner_id" groups="stock.group_tracking_owner" invisible="context.get('readonly_form')"/>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="Confirm" class="btn btn-danger" special="save"/>
|
||||
<button string="Discard" class="btn btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Inventory Adjustments + Update Quantity (via product) view -->
|
||||
<record model="ir.ui.view" id="view_stock_quant_tree_inventory_editable">
|
||||
<field name="name">stock.quant.inventory.tree.editable</field>
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
from odoo import _, api, Command, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class ReserveStockQuantLine(models.TransientModel):
|
||||
_name = 'stock.quant.reserve.line'
|
||||
_description = 'Choose quantity to reserve from each stock quant'
|
||||
|
||||
quant_id = fields.Many2one('stock.quant', required=True, readonly=True)
|
||||
location_id = fields.Many2one(related='quant_id.location_id')
|
||||
product_id = fields.Many2one(related='quant_id.product_id')
|
||||
product_uom_id = fields.Many2one(related='product_id.uom_id')
|
||||
lot_id = fields.Many2one(related='quant_id.lot_id')
|
||||
package_id = fields.Many2one(related='quant_id.package_id')
|
||||
available_quantity = fields.Float(related='quant_id.available_quantity', string='Available')
|
||||
qty_to_reserve = fields.Float('To reserve')
|
||||
|
||||
reserve_id = fields.Many2one('stock.quant.reserve')
|
||||
|
||||
|
||||
class ReserveStockQuant(models.TransientModel):
|
||||
_name = 'stock.quant.reserve'
|
||||
_description = 'Manually reserve stock'
|
||||
|
||||
move_id = fields.Many2one('stock.move')
|
||||
product_tracking = fields.Selection(related='move_id.has_tracking')
|
||||
product_uom_id = fields.Many2one(related='move_id.product_id.uom_id', readonly=True)
|
||||
demand_qty = fields.Float(compute='_compute_demand_qty', store=True)
|
||||
qty_to_reserve = fields.Float('Quantity to reserve', compute='_compute_qty_to_reserve')
|
||||
quant_line_ids = fields.One2many('stock.quant.reserve.line', 'reserve_id', compute='_compute_quant_line_ids',
|
||||
readonly=False, store=True)
|
||||
|
||||
@api.depends('quant_line_ids.qty_to_reserve', 'demand_qty')
|
||||
def _compute_qty_to_reserve(self):
|
||||
for wiz in self:
|
||||
wiz.qty_to_reserve = max(wiz.demand_qty - sum(wiz.quant_line_ids.mapped('qty_to_reserve')), 0)
|
||||
|
||||
@api.depends('move_id')
|
||||
def _compute_demand_qty(self):
|
||||
for wiz in self:
|
||||
wiz.demand_qty = wiz.move_id.product_qty - sum(wiz.move_id.move_line_ids.mapped('reserved_qty'))
|
||||
|
||||
@api.depends('move_id')
|
||||
def _compute_quant_line_ids(self):
|
||||
for wiz in self:
|
||||
move_id = wiz.move_id
|
||||
if not move_id:
|
||||
wiz.quant_line_ids = False
|
||||
continue
|
||||
if wiz.quant_line_ids:
|
||||
continue
|
||||
quant_line_cmds = [Command.clear()]
|
||||
quant_ids = self.env['stock.quant'].search([('product_id', '=', move_id.product_id.id),
|
||||
('location_id', 'child_of', move_id.location_id.id)])
|
||||
quant_line_cmds += [Command.create({'quant_id': quant.id}) for quant in quant_ids.filtered(lambda q: q.available_quantity > 0)]
|
||||
wiz.quant_line_ids = quant_line_cmds
|
||||
|
||||
def reserve_stock(self):
|
||||
move_line_vals = []
|
||||
for wiz in self:
|
||||
for line in wiz.quant_line_ids.filtered(lambda l: l.qty_to_reserve > 0):
|
||||
if line.qty_to_reserve > line.available_quantity:
|
||||
raise UserError(_('Cannot reserve more quantity than available!'))
|
||||
line.quant_id.reserved_quantity += line.qty_to_reserve
|
||||
move_line_vals.append(wiz.move_id._prepare_move_line_vals(quantity=line.qty_to_reserve, reserved_quant=line.quant_id))
|
||||
StockMoveLine = self.env['stock.move.line'].with_context(bypass_reservation_update=True)
|
||||
StockMoveLine.create(move_line_vals)
|
||||
self.move_id._recompute_state()
|
||||
@@ -1,43 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_stock_quant_reserve_form" model="ir.ui.view">
|
||||
<field name="name">Reserve stock</field>
|
||||
<field name="model">stock.quant.reserve</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<field name="move_id" invisible="1"/>
|
||||
<field name="product_tracking" invisible="1"/>
|
||||
<group>
|
||||
<group>
|
||||
<label for="qty_to_reserve"/>
|
||||
<div class="o_row">
|
||||
<span><field name="qty_to_reserve" nolabel="1"/></span>
|
||||
<span><field name="product_uom_id" nolabel="1" options="{'no_open': True}"/></span>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<div class="oe_grey">
|
||||
<p>Choose from the following available stock and enter the quantity to reserve.</p>
|
||||
</div>
|
||||
<field name="quant_line_ids" force_save="1">
|
||||
<tree editable="top" create="0">
|
||||
<field name="quant_id" invisible="1" force_save="1"/>
|
||||
<field name="product_id"/>
|
||||
<field name="location_id" groups="stock.group_stock_multi_locations"/>
|
||||
<field name="lot_id" groups="stock.group_production_lot" attrs="{'column_invisible': [('parent.product_tracking','=','none')]}"/>
|
||||
<field name="package_id" groups="stock.group_tracking_lot"/>
|
||||
<field name="available_quantity"/>
|
||||
<field name="product_uom_id" groups="uom.group_uom"/>
|
||||
<field name="qty_to_reserve"/>
|
||||
<widget name="stock_quant_reserve"/>
|
||||
</tree>
|
||||
</field>
|
||||
<footer>
|
||||
<button name="reserve_stock" class="btn btn-primary" type="object" string="Confirm" data-hotkey="v"/>
|
||||
<button special="cancel" string="Cancel" data-hotkey="z"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user