[IMP] stock, sale_stock, mrp, purchase_stock: transfer the custom variant information

Allow to propagate custom information to upper document. It could be
usefull in many buisness that do custom stuff for people
(t-shirt with user defined text, poster with own pictures...)

The customes values are pass throught procurements'values and are
display on MO or purchase order line.

Task: 1970476
This commit is contained in:
Mitali Patel
2020-03-05 14:31:13 +00:00
committed by Simon Lejeune
parent ffc6fe1704
commit 6f247d799a
11 changed files with 89 additions and 6 deletions
+1
View File
@@ -205,6 +205,7 @@ class MrpProduction(models.Model):
procurement_group_id = fields.Many2one(
'procurement.group', 'Procurement Group',
copy=False)
product_description_variants = fields.Char('Custom Description')
orderpoint_id = fields.Many2one('stock.warehouse.orderpoint', 'Orderpoint')
propagate_cancel = fields.Boolean(
'Propagate cancel and split',
+1
View File
@@ -84,6 +84,7 @@ class StockRule(models.Model):
return {
'origin': origin,
'product_id': product_id.id,
'product_description_variants': values.get('product_description_variants'),
'product_qty': product_qty,
'product_uom_id': product_uom.id,
'location_src_id': self.location_src_id.id or self.picking_type_id.default_location_src_id.id or location_id.id,
@@ -32,6 +32,10 @@
<strong>Finished Product:</strong><br/>
<span t-field="o.product_id"/>
</div>
<div class="col-3" t-if="o.product_description_variants">
<strong>Description:</strong><br/>
<span t-field="o.product_description_variants"/>
</div>
<div class="col-3">
<strong>Quantity to Produce:</strong><br/>
<span t-field="o.product_qty"/>
@@ -115,6 +115,7 @@
<field name="id" invisible="1"/>
<field name="product_id" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="product_tmpl_id" invisible="1"/>
<field name="product_description_variants" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<label for="product_qty"/>
<div class="o_row no-gutters d-flex">
<div class="col">
+15 -3
View File
@@ -239,6 +239,7 @@ class PurchaseOrderLine(models.Model):
orderpoint_id = fields.Many2one('stock.warehouse.orderpoint', 'Orderpoint')
move_dest_ids = fields.One2many('stock.move', 'created_purchase_line_id', 'Downstream Moves')
delay_alert = fields.Boolean(string='Delay alert')
product_description_variants = fields.Char('Custom Description')
propagate_date = fields.Boolean(string="Propagate Rescheduling", help='The rescheduling is propagated to the next move.')
propagate_date_minimum_delta = fields.Integer(string='Reschedule if Higher Than', help='The change must be higher than this value to be propagated')
propagate_cancel = fields.Boolean('Propagate cancellation', default=True)
@@ -381,12 +382,14 @@ class PurchaseOrderLine(models.Model):
def _prepare_stock_move_vals(self, picking, price_unit, product_uom_qty, product_uom):
self.ensure_one()
description_picking = self.product_id._get_description(self.order_id.picking_type_id)
if self.product_description_variants:
description_picking += self.product_description_variants
return {
# truncate to 2000 to avoid triggering index limit error
# TODO: remove index in master?
'name': (self.name or '')[:2000],
'product_id': self.product_id.id,
'product_uom': self.product_uom.id,
'date': self.order_id.date_order,
'date_expected': self.date_planned,
'location_id': self.order_id.partner_id.property_stock_supplier.id,
@@ -403,7 +406,7 @@ class PurchaseOrderLine(models.Model):
'origin': self.order_id.name,
'propagate_date': self.propagate_date,
'propagate_date_minimum_delta': self.propagate_date_minimum_delta,
'description_picking': self.product_id._get_description(self.order_id.picking_type_id),
'description_picking': description_picking,
'propagate_cancel': self.propagate_cancel,
'delay_alert': self.delay_alert,
'route_ids': self.order_id.picking_type_id.warehouse_id and [(6, 0, [x.id for x in self.order_id.picking_type_id.warehouse_id.route_ids])] or [],
@@ -414,14 +417,19 @@ class PurchaseOrderLine(models.Model):
@api.model
def _prepare_purchase_order_line_from_procurement(self, product_id, product_qty, product_uom, company_id, values, po):
line_description = product_id._get_description(po.picking_type_id)
if values.get('product_description_variants'):
line_description += values['product_description_variants']
supplier = values.get('supplier')
res = self._prepare_purchase_order_line(product_id, product_qty, product_uom, company_id, supplier, po)
res['name'] = line_description
res['move_dest_ids'] = [(4, x.id) for x in values.get('move_dest_ids', [])]
res['orderpoint_id'] = values.get('orderpoint_id', False) and values.get('orderpoint_id').id
res['propagate_cancel'] = values.get('propagate_cancel')
res['delay_alert'] = values.get('delay_alert')
res['propagate_date'] = values.get('propagate_date')
res['propagate_date_minimum_delta'] = values.get('propagate_date_minimum_delta')
res['product_description_variants'] = values.get('product_description_variants')
return res
def _create_stock_moves(self, picking):
@@ -442,9 +450,13 @@ class PurchaseOrderLine(models.Model):
args can be merged. If it returns an empty record then a new line will
be created.
"""
description_picking = product_id._get_description(self.order_id.picking_type_id) or ''
if values.get('product_description_variants'):
description_picking += values['product_description_variants']
lines = self.filtered(
lambda l: l.propagate_date == values['propagate_date'] and
l.propagate_date_minimum_delta == values['propagate_date_minimum_delta'] and
l.propagate_cancel == values['propagate_cancel'] and
((values['orderpoint_id'] and not values['move_dest_ids']) and l.orderpoint_id == values['orderpoint_id'] or True))
((values['orderpoint_id'] and not values['move_dest_ids']) and l.orderpoint_id == values['orderpoint_id'] or True) and
(values.get('product_description_variants') and l.name == description_picking or True))
return lines and lines[0] or self.env['purchase.order.line']
@@ -159,12 +159,14 @@ class StockRule(models.Model):
# necessary anymore since those values are taken from destination moves.
return procurement.product_id, procurement.product_uom, procurement.values['propagate_date'],\
procurement.values['propagate_date_minimum_delta'], procurement.values['propagate_cancel'],\
procurement.values.get('product_description_variants'),\
(procurement.values.get('orderpoint_id') and not procurement.values.get('move_dest_ids')) and procurement.values['orderpoint_id']
@api.model
def _get_procurements_to_merge_sorted(self, procurement):
return procurement.product_id.id, procurement.product_uom.id, procurement.values['propagate_date'],\
procurement.values['propagate_date_minimum_delta'], procurement.values['propagate_cancel'],\
procurement.values.get('product_description_variants'),\
(procurement.values.get('orderpoint_id') and not procurement.values.get('move_dest_ids')) and procurement.values['orderpoint_id']
@api.model
+6
View File
@@ -35,6 +35,12 @@ class PurchaseTestCommon(TestStockCommon):
'route_ids': [(6, 0, [cls.route_buy, cls.route_mto])],
'seller_ids': [(0, 0, {'name': cls.partner_1.id, 'delay': 5})]})
cls.t_shirt = cls.env['product.product'].create({
'name': 'T-shirt',
'route_ids': [(6, 0, [cls.route_buy, cls.route_mto])],
'seller_ids': [(0, 0, {'name': cls.partner_1.id, 'delay': 5})]
})
# Update product_2 with type, route and Delivery Lead Time
cls.product_2.write({
'type': 'product',
@@ -242,6 +242,57 @@ class TestPurchaseLeadTime(PurchaseTestCommon):
self.assertEqual(po_line[0].product_qty, 10, 'the purchase order line has a wrong quantity')
self.assertEqual(po_line[1].product_qty, 5, 'the purchase order line has a wrong quantity')
def test_merge_po_line_3(self):
"""Chage merging po line if same procurement is done depending on
propagate_date, propagate_date_minimum_delta and custom values.
"""
# Create procurement order of product_1
ProcurementGroup = self.env['procurement.group']
procurement_values = {
'warehouse_id': self.warehouse_1,
'rule_id': self.warehouse_1.buy_pull_id,
'date_planned': fields.Datetime.to_string(fields.datetime.now() + timedelta(days=10)),
'group_id': False,
'route_ids': [],
}
procurement_values['product_description_variants'] = '[{"color": "Red"}]'
procurement_values['description'] = "T-shirt (Red)"
order_1_values = procurement_values
ProcurementGroup.run([self.env['procurement.group'].Procurement(
self.t_shirt, 5, self.uom_unit, self.warehouse_1.lot_stock_id,
self.t_shirt.name, '/', self.env.company, order_1_values)
])
purchase_order = self.env['purchase.order.line'].search([('product_id', '=', self.t_shirt.id)], limit=1).order_id
self.assertEqual(len(purchase_order.order_line), 1, 'wrong number of order line is created')
procurement_values['product_description_variants'] = '[{"color": "Red"}]'
order_2_values = procurement_values
ProcurementGroup.run([self.env['procurement.group'].Procurement(
self.t_shirt, 10, self.uom_unit, self.warehouse_1.lot_stock_id,
self.t_shirt.name, '/', self.env.company, order_2_values)
])
self.env['procurement.group'].run_scheduler()
self.assertEqual(len(purchase_order.order_line), 1, 'line with same custom value should be merged')
self.assertEqual(purchase_order.order_line[0].product_qty, 15, 'line with same custom value should be merged and qty should be update')
procurement_values['product_description_variants'] = '[{"color": "Green"}]'
procurement_values['description'] = "T-shirt (Green)"
order_3_values = procurement_values
ProcurementGroup.run([self.env['procurement.group'].Procurement(
self.t_shirt, 10, self.uom_unit, self.warehouse_1.lot_stock_id,
self.t_shirt.name, '/', self.env.company, order_3_values)
])
self.assertEqual(len(purchase_order.order_line), 2, 'line with different custom value should not be merged')
self.assertEqual(purchase_order.order_line.filtered(lambda x: x.product_qty == 15).name, "T-shirt (Red)", 'wrong description in po lines')
self.assertEqual(purchase_order.order_line.filtered(lambda x: x.product_qty == 10).name, "T-shirt (Green)", 'wrong description in po lines')
purchase_order.button_confirm()
self.assertEqual(purchase_order.picking_ids[0].move_ids_without_package.filtered(lambda x: x.product_uom_qty == 15).description_picking, "T-shirt (Red)", 'wrong description in picking')
self.assertEqual(purchase_order.picking_ids[0].move_ids_without_package.filtered(lambda x: x.product_uom_qty == 10).description_picking, "T-shirt (Green)", 'wrong description in picking')
def test_reordering_days_to_purchase(self):
self.patcher = patch('odoo.addons.stock.models.stock_orderpoint.fields.Date', wraps=fields.Date)
self.mock_date = self.patcher.start()
+1
View File
@@ -445,6 +445,7 @@ class SaleOrderLine(models.Model):
'route_ids': self.route_id,
'warehouse_id': self.order_id.warehouse_id or False,
'partner_id': self.order_id.partner_shipping_id.id,
'product_description_variants': self._get_sale_order_line_multiline_description_variants(),
'company_id': self.order_id.company_id,
})
for line in self.filtered("order_id.commitment_date"):
+3 -2
View File
@@ -691,7 +691,7 @@ class StockMove(models.Model):
'product_id', 'price_unit', 'procure_method', 'location_id', 'location_dest_id',
'product_uom', 'restrict_partner_id', 'scrapped', 'origin_returned_move_id',
'package_level_id', 'propagate_cancel', 'propagate_date', 'propagate_date_minimum_delta',
'delay_alert',
'delay_alert', 'description_picking'
]
@api.model
@@ -701,7 +701,7 @@ class StockMove(models.Model):
move.product_id.id, move.price_unit, move.procure_method, move.location_id, move.location_dest_id,
move.product_uom.id, move.restrict_partner_id.id, move.scrapped, move.origin_returned_move_id.id,
move.package_level_id.id, move.propagate_cancel, move.propagate_date, move.propagate_date_minimum_delta,
move.delay_alert,
move.delay_alert, move.description_picking
]
def _clean_merged(self):
@@ -1040,6 +1040,7 @@ class StockMove(models.Model):
elif self.rule_id.group_propagation_option == 'none':
group_id = False
return {
'product_description_variants': self.description_picking and self.description_picking.replace(self.product_id._get_description(self.picking_type_id), ''),
'date_planned': self.date_expected,
'move_dest_ids': self,
'group_id': group_id,
+4 -1
View File
@@ -285,6 +285,9 @@ class StockRule(models.Model):
date_expected = fields.Datetime.to_string(
fields.Datetime.from_string(values['date_planned']) - relativedelta(days=self.delay or 0)
)
picking_description = product_id._get_description(self.picking_type_id)
if values.get('product_description_variants'):
picking_description += values['product_description_variants']
# it is possible that we've already got some move done, so check for the done qty and create
# a new move with the correct qty
qty_left = product_qty
@@ -315,7 +318,7 @@ class StockRule(models.Model):
'propagate_cancel': self.propagate_cancel,
'propagate_date': self.propagate_date,
'propagate_date_minimum_delta': self.propagate_date_minimum_delta,
'description_picking': product_id._get_description(self.picking_type_id),
'description_picking': picking_description,
'priority': values.get('priority', "1"),
'delay_alert': self.delay_alert,
'orderpoint_id': values.get('orderpoint_id') and values['orderpoint_id'].id,