@@ -1,3 +1,4 @@
|
||||
* Rename Uom to Unit for production
|
||||
* Reschedule planned date for late productions
|
||||
|
||||
Version 6.8.0 - 2023-05-01
|
||||
|
||||
@@ -93,7 +93,8 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
uom_category = fields.Function(fields.Many2One(
|
||||
'product.uom.category', 'Uom Category'),
|
||||
'on_change_with_uom_category')
|
||||
uom = fields.Many2One('product.uom', 'Uom',
|
||||
unit = fields.Many2One(
|
||||
'product.uom', "Unit",
|
||||
domain=[
|
||||
('category', '=', Eval('uom_category')),
|
||||
],
|
||||
@@ -103,7 +104,7 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
'invisible': ~Eval('product'),
|
||||
})
|
||||
quantity = fields.Float(
|
||||
"Quantity", digits='uom',
|
||||
"Quantity", digits='unit',
|
||||
states={
|
||||
'readonly': ~Eval('state').in_(['request', 'draft']),
|
||||
'required': Bool(Eval('bom')),
|
||||
@@ -260,6 +261,13 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
table_h = cls.__table_handler__(module_name)
|
||||
|
||||
# Migration from 6.8: rename uom to unit
|
||||
if (table_h.column_exist('uom')
|
||||
and not table_h.column_exist('unit')):
|
||||
table_h.column_rename('uom', 'unit')
|
||||
|
||||
super(Production, cls).__register__(module_name)
|
||||
|
||||
table = cls.__table__()
|
||||
@@ -350,13 +358,13 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
|
||||
@fields.depends(
|
||||
'company', 'location', methods=['picking_location', 'output_location'])
|
||||
def _move(self, type, product, uom, quantity):
|
||||
def _move(self, type, product, unit, quantity):
|
||||
pool = Pool()
|
||||
Move = pool.get('stock.move')
|
||||
assert type in {'input', 'output'}
|
||||
move = Move(
|
||||
product=product,
|
||||
uom=uom,
|
||||
uom=unit,
|
||||
quantity=quantity,
|
||||
company=self.company,
|
||||
currency=self.company.currency if self.company else None)
|
||||
@@ -377,16 +385,16 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
return move
|
||||
|
||||
@fields.depends(
|
||||
'bom', 'product', 'uom', 'quantity', 'company', 'inputs', 'outputs',
|
||||
'bom', 'product', 'unit', 'quantity', 'company', 'inputs', 'outputs',
|
||||
methods=['_explode_move_values'])
|
||||
def explode_bom(self):
|
||||
pool = Pool()
|
||||
Uom = pool.get('product.uom')
|
||||
if not (self.bom and self.product and self.uom):
|
||||
if not (self.bom and self.product and self.unit):
|
||||
return
|
||||
|
||||
factor = self.bom.compute_factor(self.product, self.quantity or 0,
|
||||
self.uom)
|
||||
factor = self.bom.compute_factor(
|
||||
self.product, self.quantity or 0, self.unit)
|
||||
inputs = []
|
||||
for input_ in self.bom.inputs:
|
||||
quantity = input_.compute_quantity(factor)
|
||||
@@ -414,15 +422,15 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
self.location = self.warehouse.production_location
|
||||
|
||||
@fields.depends(
|
||||
'product', 'uom', methods=['explode_bom', 'set_planned_start_date'])
|
||||
'product', 'unit', methods=['explode_bom', 'set_planned_start_date'])
|
||||
def on_change_product(self):
|
||||
if self.product:
|
||||
category = self.product.default_uom.category
|
||||
if not self.uom or self.uom.category != category:
|
||||
self.uom = self.product.default_uom
|
||||
if not self.unit or self.unit.category != category:
|
||||
self.unit = self.product.default_uom
|
||||
else:
|
||||
self.bom = None
|
||||
self.uom = None
|
||||
self.unit = None
|
||||
self.explode_bom()
|
||||
self.set_planned_start_date()
|
||||
|
||||
@@ -437,7 +445,7 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
self.set_planned_start_date()
|
||||
|
||||
@fields.depends(methods=['explode_bom'])
|
||||
def on_change_uom(self):
|
||||
def on_change_unit(self):
|
||||
self.explode_bom()
|
||||
|
||||
@fields.depends(methods=['explode_bom'])
|
||||
@@ -486,7 +494,7 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
if not production.bom:
|
||||
if production.product:
|
||||
move = production._move(
|
||||
'output', production.product, production.uom,
|
||||
'output', production.product, production.unit,
|
||||
production.quantity)
|
||||
if move:
|
||||
move.unit_price = Decimal(0)
|
||||
@@ -495,7 +503,7 @@ class Production(ShipmentAssignMixin, Workflow, ModelSQL, ModelView):
|
||||
continue
|
||||
|
||||
factor = production.bom.compute_factor(
|
||||
production.product, production.quantity, production.uom)
|
||||
production.product, production.quantity, production.unit)
|
||||
for input_ in production.bom.inputs:
|
||||
quantity = input_.compute_quantity(factor)
|
||||
product = input_.product
|
||||
|
||||
@@ -150,7 +150,7 @@ Test reset bom button::
|
||||
>>> production.click(
|
||||
... 'reset_bom',
|
||||
... change=[
|
||||
... 'bom', 'product', 'uom', 'quantity',
|
||||
... 'bom', 'product', 'unit', 'quantity',
|
||||
... 'inputs', 'outputs', 'company', 'warehouse', 'location'])
|
||||
>>> sorted([i.quantity for i in production.inputs]) == [10, 300]
|
||||
True
|
||||
|
||||
@@ -16,8 +16,8 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="bom"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="uom"/>
|
||||
<field name="uom"/>
|
||||
<label name="unit"/>
|
||||
<field name="unit"/>
|
||||
<notebook>
|
||||
<page string="Lines" id="lines">
|
||||
<field name="inputs" colspan="2" view_ids="stock.move_view_list_shipment"/>
|
||||
|
||||
@@ -6,7 +6,7 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="number" expand="1"/>
|
||||
<field name="reference" expand="1" optional="1"/>
|
||||
<field name="product" expand="2"/>
|
||||
<field name="quantity" symbol="uom"/>
|
||||
<field name="quantity" symbol="unit"/>
|
||||
<field name="planned_start_date" optional="0"/>
|
||||
<field name="planned_date" optional="0"/>
|
||||
<field name="state"/>
|
||||
|
||||
@@ -212,7 +212,7 @@ class Production(metaclass=PoolMeta):
|
||||
line.product_supplier = self.routing.supplier_service_supplier
|
||||
line.unit = self.routing.supplier_service.purchase_uom
|
||||
factor = self.bom.compute_factor(
|
||||
self.product, self.quantity or 0, self.uom)
|
||||
self.product, self.quantity or 0, self.unit)
|
||||
line.quantity = line.unit.round(
|
||||
factor * self.routing.supplier_quantity)
|
||||
line.purchase = purchase
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!-- This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/field[@name='uom']" position="after">
|
||||
<xpath expr="/form/field[@name='unit']" position="after">
|
||||
<label name="routing"/>
|
||||
<field name="routing"/>
|
||||
</xpath>
|
||||
|
||||
@@ -25,7 +25,7 @@ class Production(metaclass=PoolMeta):
|
||||
def split_wizard(cls, productions):
|
||||
pass
|
||||
|
||||
def split(self, quantity, uom, count=None):
|
||||
def split(self, quantity, unit, count=None):
|
||||
"""
|
||||
Split production into productions of quantity.
|
||||
If count is not defined, the production will be split until remainder
|
||||
@@ -36,11 +36,11 @@ class Production(metaclass=PoolMeta):
|
||||
Uom = pool.get('product.uom')
|
||||
|
||||
productions = [self]
|
||||
remainder = Uom.compute_qty(self.uom, self.quantity, uom)
|
||||
remainder = Uom.compute_qty(self.unit, self.quantity, unit)
|
||||
if remainder <= quantity:
|
||||
return productions
|
||||
self.quantity = quantity
|
||||
self.uom = uom
|
||||
self.unit = unit
|
||||
self.save()
|
||||
remainder -= quantity
|
||||
if count:
|
||||
@@ -49,19 +49,19 @@ class Production(metaclass=PoolMeta):
|
||||
and (count or count is None)):
|
||||
productions.extend(self.copy([self], {
|
||||
'quantity': quantity,
|
||||
'uom': uom.id,
|
||||
'unit': unit.id,
|
||||
'inputs': None,
|
||||
'outputs': None,
|
||||
}))
|
||||
remainder -= quantity
|
||||
remainder = uom.round(remainder)
|
||||
remainder = unit.round(remainder)
|
||||
if count:
|
||||
count -= 1
|
||||
assert remainder >= 0
|
||||
if remainder:
|
||||
productions.extend(self.copy([self], {
|
||||
'quantity': remainder,
|
||||
'uom': uom.id,
|
||||
'unit': unit.id,
|
||||
}))
|
||||
for production in productions:
|
||||
production.explode_bom()
|
||||
@@ -81,13 +81,13 @@ class SplitProduction(Wizard):
|
||||
|
||||
def default_start(self, fields):
|
||||
return {
|
||||
'uom': self.record.uom.id,
|
||||
'unit': self.record.unit.id,
|
||||
'uom_category': self.record.uom.category.id,
|
||||
}
|
||||
|
||||
def transition_split(self):
|
||||
self.record.split(
|
||||
self.start.quantity, self.start.uom, count=self.start.count)
|
||||
self.start.quantity, self.start.unit, count=self.start.count)
|
||||
return 'end'
|
||||
|
||||
|
||||
@@ -95,8 +95,9 @@ class SplitProductionStart(ModelView):
|
||||
'Split Production'
|
||||
__name__ = 'production.split.start'
|
||||
count = fields.Integer('Count', help='The limit number of productions')
|
||||
quantity = fields.Float("Quantity", digits='uom', required=True)
|
||||
uom = fields.Many2One('product.uom', 'Uom', required=True,
|
||||
quantity = fields.Float("Quantity", digits='unit', required=True)
|
||||
unit = fields.Many2One(
|
||||
'product.uom', "Unit", required=True,
|
||||
domain=[
|
||||
('category', '=', Eval('uom_category')),
|
||||
])
|
||||
|
||||
@@ -34,7 +34,7 @@ class ProductionSplitTestCase(ModuleTestCase):
|
||||
]:
|
||||
production = Production()
|
||||
production.quantity = quantity
|
||||
production.uom = unit
|
||||
production.unit = unit
|
||||
|
||||
productions = production.split(
|
||||
quantity_split, unit, count=count)
|
||||
|
||||
@@ -6,6 +6,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="count"/>
|
||||
<label name="quantity"/>
|
||||
<field name="quantity"/>
|
||||
<label name="uom"/>
|
||||
<field name="uom"/>
|
||||
<label name="unit"/>
|
||||
<field name="unit"/>
|
||||
</form>
|
||||
|
||||
@@ -106,7 +106,7 @@ class Line(metaclass=PoolMeta):
|
||||
location=self.warehouse.production_location,
|
||||
product=product,
|
||||
bom=product.boms[0].bom if product.boms else None,
|
||||
uom=self.unit,
|
||||
unit=self.unit,
|
||||
quantity=quantity,
|
||||
state='request',
|
||||
origin=self,
|
||||
|
||||
@@ -163,8 +163,8 @@ class Production(metaclass=PoolMeta):
|
||||
date = today
|
||||
else:
|
||||
date -= datetime.timedelta(1)
|
||||
uom = product.default_uom
|
||||
quantity = uom.ceil(quantity)
|
||||
unit = product.default_uom
|
||||
quantity = unit.ceil(quantity)
|
||||
if order_point:
|
||||
origin = str(order_point)
|
||||
else:
|
||||
@@ -176,7 +176,7 @@ class Production(metaclass=PoolMeta):
|
||||
location=warehouse.production_location,
|
||||
product=product,
|
||||
bom=product.boms[0].bom if product.boms else None,
|
||||
uom=uom,
|
||||
unit=unit,
|
||||
quantity=quantity,
|
||||
state='request',
|
||||
origin=origin,
|
||||
|
||||
Reference in New Issue
Block a user