sale_order_add_bom: add kit wizard now available on pickings
This commit is contained in:
@@ -11,6 +11,8 @@
|
|||||||
'description': """
|
'description': """
|
||||||
This module adds a wizard *Add Kit* on the form view of a quotation that allows the user to select a 'kit' BOM: Odoo will automatically add the components of the kit as sale order lines.
|
This module adds a wizard *Add Kit* on the form view of a quotation that allows the user to select a 'kit' BOM: Odoo will automatically add the components of the kit as sale order lines.
|
||||||
|
|
||||||
|
The wizard *Add Kit* is also available on a draft picking.
|
||||||
|
|
||||||
This module has been written by Alexis de Lattre from Akretion
|
This module has been written by Alexis de Lattre from Akretion
|
||||||
<alexis.delattre@akretion.com>.
|
<alexis.delattre@akretion.com>.
|
||||||
""",
|
""",
|
||||||
@@ -19,7 +21,8 @@ This module has been written by Alexis de Lattre from Akretion
|
|||||||
'depends': ['sale', 'mrp'],
|
'depends': ['sale', 'mrp'],
|
||||||
'data': [
|
'data': [
|
||||||
'wizard/sale_add_phantom_bom_view.xml',
|
'wizard/sale_add_phantom_bom_view.xml',
|
||||||
'sale_view.xml',
|
'views/sale_order.xml',
|
||||||
|
'views/stock_picking.xml',
|
||||||
],
|
],
|
||||||
'installable': True,
|
'installable': True,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
<!--
|
||||||
© 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
|
Copyright 2016-2021 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).
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
-->
|
-->
|
||||||
|
|
||||||
24
sale_order_add_bom/views/stock_picking.xml
Normal file
24
sale_order_add_bom/views/stock_picking.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2021 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>
|
||||||
|
<data>
|
||||||
|
|
||||||
|
<record id="view_picking_form" model="ir.ui.view">
|
||||||
|
<field name="name">add.bom.stock.picking.form</field>
|
||||||
|
<field name="model">stock.picking</field>
|
||||||
|
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<button name="action_confirm" position="after">
|
||||||
|
<button name="%(sale_add_phantom_bom_action)d" type="action"
|
||||||
|
string="Add Kit" states="draft" groups="stock.group_stock_user"/>
|
||||||
|
</button>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
@@ -12,19 +12,26 @@ class SaleAddPhantomBom(models.TransientModel):
|
|||||||
_description = 'Add Kit to Quotation'
|
_description = 'Add Kit to Quotation'
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _default_sale_id(self):
|
def default_get(self, fields_list):
|
||||||
assert self._context.get('active_model') == 'sale.order'
|
res = super(SaleAddPhantomBom, self).default_get(fields_list)
|
||||||
return self.env['sale.order'].browse(self._context['active_id'])
|
if self._context.get('active_model') == 'sale.order':
|
||||||
|
res['sale_id'] = self._context['active_id']
|
||||||
|
elif self._context.get('active_model') == 'stock.picking':
|
||||||
|
res['picking_id'] = self._context['active_id']
|
||||||
|
else:
|
||||||
|
raise UserError(_(
|
||||||
|
"The wizard can only be started from a sale order or a picking."))
|
||||||
|
return res
|
||||||
|
|
||||||
bom_id = fields.Many2one(
|
bom_id = fields.Many2one(
|
||||||
'mrp.bom', 'Kit', required=True,
|
'mrp.bom', 'Kit', required=True,
|
||||||
domain=[('type', '=', 'phantom'), ('sale_ok', '=', True)])
|
domain=[('type', '=', 'phantom'), ('sale_ok', '=', True)])
|
||||||
qty = fields.Integer(
|
qty = fields.Integer(
|
||||||
string='Number of Kits to Add', default=1, required=True)
|
string='Number of Kits to Add', default=1, required=True)
|
||||||
# I can 't put the sale_id fields required=True because
|
|
||||||
# it may block the deletion of a sale order
|
|
||||||
sale_id = fields.Many2one(
|
sale_id = fields.Many2one(
|
||||||
'sale.order', string='Quotation', default=_default_sale_id)
|
'sale.order', string='Quotation')
|
||||||
|
picking_id = fields.Many2one(
|
||||||
|
'stock.picking', string='Picking')
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _prepare_sale_order_line(self, bom_line, sale_order, wizard_qty):
|
def _prepare_sale_order_line(self, bom_line, sale_order, wizard_qty):
|
||||||
@@ -36,12 +43,30 @@ class SaleAddPhantomBom(models.TransientModel):
|
|||||||
'product_uom_qty': qty_in_product_uom * wizard_qty,
|
'product_uom_qty': qty_in_product_uom * wizard_qty,
|
||||||
'order_id': sale_order.id,
|
'order_id': sale_order.id,
|
||||||
}
|
}
|
||||||
|
# on sale.order.line, company_id is a related field
|
||||||
|
return vals
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _prepare_stock_move(self, bom_line, picking, wizard_qty):
|
||||||
|
product = bom_line.product_id
|
||||||
|
qty_in_product_uom = bom_line.product_uom_id._compute_quantity(
|
||||||
|
bom_line.product_qty, product.uom_id)
|
||||||
|
vals = {
|
||||||
|
'product_id': product.id,
|
||||||
|
'product_uom_qty': qty_in_product_uom * wizard_qty,
|
||||||
|
'product_uom': product.uom_id.id,
|
||||||
|
'picking_id': picking.id,
|
||||||
|
'company_id': picking.company_id.id,
|
||||||
|
'location_id': picking.location_id.id,
|
||||||
|
'location_dest_id': picking.location_dest_id.id,
|
||||||
|
'name': product.partner_ref,
|
||||||
|
}
|
||||||
return vals
|
return vals
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def add(self):
|
def add(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
assert self.sale_id, 'No related sale_id'
|
assert self.sale_id or self.picking_id, 'No related sale_id or picking_id'
|
||||||
if self.qty < 1:
|
if self.qty < 1:
|
||||||
raise UserError(_(
|
raise UserError(_(
|
||||||
"The number of kits to add must be 1 or superior"))
|
"The number of kits to add must be 1 or superior"))
|
||||||
@@ -50,8 +75,8 @@ class SaleAddPhantomBom(models.TransientModel):
|
|||||||
raise UserError(_("The selected kit is empty !"))
|
raise UserError(_("The selected kit is empty !"))
|
||||||
prec = self.env['decimal.precision'].precision_get(
|
prec = self.env['decimal.precision'].precision_get(
|
||||||
'Product Unit of Measure')
|
'Product Unit of Measure')
|
||||||
today = fields.Date.context_today(self)
|
|
||||||
solo = self.env['sale.order.line']
|
solo = self.env['sale.order.line']
|
||||||
|
smo = self.env['stock.move']
|
||||||
for line in self.bom_id.bom_line_ids:
|
for line in self.bom_id.bom_line_ids:
|
||||||
if float_is_zero(line.product_qty, precision_digits=prec):
|
if float_is_zero(line.product_qty, precision_digits=prec):
|
||||||
continue
|
continue
|
||||||
@@ -59,6 +84,10 @@ class SaleAddPhantomBom(models.TransientModel):
|
|||||||
# of sale order line in the 'sale' module
|
# of sale order line in the 'sale' module
|
||||||
# TODO: if needed, we could increment existing order lines
|
# TODO: if needed, we could increment existing order lines
|
||||||
# with the same product instead of always creating new lines
|
# with the same product instead of always creating new lines
|
||||||
vals = self._prepare_sale_order_line(line, self.sale_id, self.qty)
|
if self.sale_id:
|
||||||
solo.create(vals)
|
vals = self._prepare_sale_order_line(line, self.sale_id, self.qty)
|
||||||
|
solo.create(vals)
|
||||||
|
elif self.picking_id:
|
||||||
|
vals = self._prepare_stock_move(line, self.picking_id, self.qty)
|
||||||
|
smo.create(vals)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
</group>
|
</group>
|
||||||
<footer>
|
<footer>
|
||||||
<button name="add" type="object"
|
<button name="add" type="object"
|
||||||
class="oe_highlight" string="Add to Quotation"/>
|
class="oe_highlight" string="Add"/>
|
||||||
<button special="cancel" string="Cancel" class="oe_link"/>
|
<button special="cancel" string="Cancel" class="oe_link"/>
|
||||||
</footer>
|
</footer>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user