Jauge tolerance
This commit is contained in:
@@ -417,11 +417,15 @@ class Purchase(metaclass=PoolMeta):
|
||||
LotQt = Pool().get('lot.qt')
|
||||
quantity = Decimal(0)
|
||||
unit = getattr(line, 'unit', None)
|
||||
for lot in getattr(line, 'lots', None) or []:
|
||||
if getattr(lot, 'lot_type', None) == 'physic':
|
||||
quantity += Decimal(str(
|
||||
physical_lots = [
|
||||
lot for lot in getattr(line, 'lots', None) or []
|
||||
if getattr(lot, 'lot_type', None) == 'physic']
|
||||
if physical_lots:
|
||||
return sum(
|
||||
Decimal(str(
|
||||
lot.get_current_quantity_converted(unit=unit) or 0))
|
||||
continue
|
||||
for lot in physical_lots)
|
||||
for lot in getattr(line, 'lots', None) or []:
|
||||
if getattr(lot, 'lot_type', None) != 'virtual':
|
||||
continue
|
||||
for lqt in LotQt.search([('lot_p', '=', lot.id)]):
|
||||
@@ -1354,10 +1358,16 @@ class Line(metaclass=PoolMeta):
|
||||
tol_max_qt = fields.Numeric("Tol +",states={
|
||||
'readonly': (Eval('inherit_tol')),
|
||||
})
|
||||
inherit_tol = fields.Boolean("Inherit tolerance")
|
||||
tol_min_v = fields.Function(fields.Numeric("Qt min"),'get_tol_min')
|
||||
tol_max_v = fields.Function(fields.Numeric("Qt max"),'get_tol_max')
|
||||
# certification = fields.Selection([
|
||||
inherit_tol = fields.Boolean("Inherit tolerance")
|
||||
tol_min_v = fields.Function(fields.Numeric("Qt min"),'get_tol_min')
|
||||
tol_max_v = fields.Function(fields.Numeric("Qt max"),'get_tol_max')
|
||||
tolerance_used = fields.Function(
|
||||
fields.Numeric("Tolerance used"), 'get_tolerance_used')
|
||||
tolerance_min = fields.Function(
|
||||
fields.Numeric("Tolerance min"), 'get_tolerance_min')
|
||||
tolerance_max = fields.Function(
|
||||
fields.Numeric("Tolerance max"), 'get_tolerance_max')
|
||||
# certification = fields.Selection([
|
||||
# (None, ''),
|
||||
# ('bci', 'BCI'),
|
||||
# ],"Certification",states={'readonly': (Eval('inherit_cer')),})
|
||||
@@ -1496,14 +1506,74 @@ class Line(metaclass=PoolMeta):
|
||||
if self.tol_min and self.quantity_theorical:
|
||||
return round((1-(self.tol_min/100))*Decimal(self.quantity_theorical),3)
|
||||
|
||||
def get_tol_max(self,name):
|
||||
if self.inherit_tol:
|
||||
if self.purchase.tol_max and self.quantity_theorical:
|
||||
return round((1+(self.purchase.tol_max/100))*Decimal(self.quantity_theorical),3)
|
||||
else:
|
||||
if self.tol_max and self.quantity_theorical:
|
||||
return round((1+(self.tol_max/100))*Decimal(self.quantity_theorical),3)
|
||||
|
||||
def get_tol_max(self,name):
|
||||
if self.inherit_tol:
|
||||
if self.purchase.tol_max and self.quantity_theorical:
|
||||
return round((1+(self.purchase.tol_max/100))*Decimal(self.quantity_theorical),3)
|
||||
else:
|
||||
if self.tol_max and self.quantity_theorical:
|
||||
return round((1+(self.tol_max/100))*Decimal(self.quantity_theorical),3)
|
||||
|
||||
def _line_quantity_in_unit(self, quantity, from_unit, to_unit):
|
||||
if not from_unit or not to_unit or from_unit == to_unit:
|
||||
return Decimal(str(quantity or 0))
|
||||
Uom = Pool().get('product.uom')
|
||||
factor = None
|
||||
rate = None
|
||||
if from_unit.category.id != to_unit.category.id:
|
||||
factor = 1
|
||||
rate = 1
|
||||
return Decimal(str(Uom.compute_qty(
|
||||
from_unit, float(quantity or 0), to_unit, True, factor, rate)))
|
||||
|
||||
def _tolerance_theoretical_quantity(self):
|
||||
return Decimal(str(
|
||||
getattr(self, 'quantity_theorical', None)
|
||||
or getattr(self, 'quantity', None)
|
||||
or 0))
|
||||
|
||||
def _tolerance_actual_quantity(self):
|
||||
LotQt = Pool().get('lot.qt')
|
||||
unit = getattr(self, 'unit', None)
|
||||
physical_lots = [
|
||||
lot for lot in getattr(self, 'lots', None) or []
|
||||
if getattr(lot, 'lot_type', None) == 'physic']
|
||||
if physical_lots:
|
||||
return sum(
|
||||
Decimal(str(
|
||||
lot.get_current_quantity_converted(unit=unit) or 0))
|
||||
for lot in physical_lots)
|
||||
quantity = Decimal(0)
|
||||
for lot in getattr(self, 'lots', None) or []:
|
||||
if getattr(lot, 'lot_type', None) != 'virtual':
|
||||
continue
|
||||
for lqt in LotQt.search([('lot_p', '=', lot.id)]):
|
||||
quantity += self._line_quantity_in_unit(
|
||||
getattr(lqt, 'lot_quantity', 0),
|
||||
getattr(lqt, 'lot_unit', None),
|
||||
unit)
|
||||
return quantity
|
||||
|
||||
def get_tolerance_used(self, name):
|
||||
theoretical = self._tolerance_theoretical_quantity()
|
||||
if not theoretical:
|
||||
return Decimal(0)
|
||||
return round(
|
||||
((self._tolerance_actual_quantity() - theoretical)
|
||||
/ theoretical) * Decimal(100), 5)
|
||||
|
||||
def get_tolerance_min(self, name):
|
||||
purchase = getattr(self, 'purchase', None)
|
||||
if self.inherit_tol and purchase:
|
||||
return -Decimal(str(purchase.tol_min or 0))
|
||||
return -Decimal(str(self.tol_min or 0))
|
||||
|
||||
def get_tolerance_max(self, name):
|
||||
purchase = getattr(self, 'purchase', None)
|
||||
if self.inherit_tol and purchase:
|
||||
return Decimal(str(purchase.tol_max or 0))
|
||||
return Decimal(str(self.tol_max or 0))
|
||||
|
||||
def get_progress(self,name):
|
||||
PS = Pool().get('purchase.pricing.summary')
|
||||
ps = PS.search(['line','=',self.id])
|
||||
|
||||
@@ -381,11 +381,15 @@ class Sale(metaclass=PoolMeta):
|
||||
LotQt = Pool().get('lot.qt')
|
||||
quantity = Decimal(0)
|
||||
unit = getattr(line, 'unit', None)
|
||||
for lot in getattr(line, 'lots', None) or []:
|
||||
if getattr(lot, 'lot_type', None) == 'physic':
|
||||
quantity += abs(Decimal(str(
|
||||
physical_lots = [
|
||||
lot for lot in getattr(line, 'lots', None) or []
|
||||
if getattr(lot, 'lot_type', None) == 'physic']
|
||||
if physical_lots:
|
||||
return sum(
|
||||
abs(Decimal(str(
|
||||
lot.get_current_quantity_converted(unit=unit) or 0)))
|
||||
continue
|
||||
for lot in physical_lots)
|
||||
for lot in getattr(line, 'lots', None) or []:
|
||||
if getattr(lot, 'lot_type', None) != 'virtual':
|
||||
continue
|
||||
for lqt in LotQt.search([('lot_s', '=', lot.id)]):
|
||||
@@ -1339,6 +1343,12 @@ class SaleLine(metaclass=PoolMeta):
|
||||
inherit_tol = fields.Boolean("Inherit tolerance")
|
||||
tol_min_v = fields.Function(fields.Numeric("Qt min"),'get_tol_min')
|
||||
tol_max_v = fields.Function(fields.Numeric("Qt max"),'get_tol_max')
|
||||
tolerance_used = fields.Function(
|
||||
fields.Numeric("Tolerance used"), 'get_tolerance_used')
|
||||
tolerance_min = fields.Function(
|
||||
fields.Numeric("Tolerance min"), 'get_tolerance_min')
|
||||
tolerance_max = fields.Function(
|
||||
fields.Numeric("Tolerance max"), 'get_tolerance_max')
|
||||
certif = fields.Many2One('purchase.certification',"Certification",states={'readonly': (Eval('inherit_cer')),})
|
||||
# certification = fields.Selection([
|
||||
# (None, ''),
|
||||
@@ -1494,6 +1504,66 @@ class SaleLine(metaclass=PoolMeta):
|
||||
if self.tol_max and self.quantity_theorical:
|
||||
return round((1+(self.tol_max/100))*Decimal(self.quantity_theorical),3)
|
||||
|
||||
def _line_quantity_in_unit(self, quantity, from_unit, to_unit):
|
||||
if not from_unit or not to_unit or from_unit == to_unit:
|
||||
return abs(Decimal(str(quantity or 0)))
|
||||
Uom = Pool().get('product.uom')
|
||||
factor = None
|
||||
rate = None
|
||||
if from_unit.category.id != to_unit.category.id:
|
||||
factor = 1
|
||||
rate = 1
|
||||
return abs(Decimal(str(Uom.compute_qty(
|
||||
from_unit, float(quantity or 0), to_unit, True, factor, rate))))
|
||||
|
||||
def _tolerance_theoretical_quantity(self):
|
||||
return abs(Decimal(str(
|
||||
getattr(self, 'quantity_theorical', None)
|
||||
or getattr(self, 'quantity', None)
|
||||
or 0)))
|
||||
|
||||
def _tolerance_actual_quantity(self):
|
||||
LotQt = Pool().get('lot.qt')
|
||||
unit = getattr(self, 'unit', None)
|
||||
physical_lots = [
|
||||
lot for lot in getattr(self, 'lots', None) or []
|
||||
if getattr(lot, 'lot_type', None) == 'physic']
|
||||
if physical_lots:
|
||||
return sum(
|
||||
abs(Decimal(str(
|
||||
lot.get_current_quantity_converted(unit=unit) or 0)))
|
||||
for lot in physical_lots)
|
||||
quantity = Decimal(0)
|
||||
for lot in getattr(self, 'lots', None) or []:
|
||||
if getattr(lot, 'lot_type', None) != 'virtual':
|
||||
continue
|
||||
for lqt in LotQt.search([('lot_s', '=', lot.id)]):
|
||||
quantity += self._line_quantity_in_unit(
|
||||
getattr(lqt, 'lot_quantity', 0),
|
||||
getattr(lqt, 'lot_unit', None),
|
||||
unit)
|
||||
return quantity
|
||||
|
||||
def get_tolerance_used(self, name):
|
||||
theoretical = self._tolerance_theoretical_quantity()
|
||||
if not theoretical:
|
||||
return Decimal(0)
|
||||
return round(
|
||||
((self._tolerance_actual_quantity() - theoretical)
|
||||
/ theoretical) * Decimal(100), 5)
|
||||
|
||||
def get_tolerance_min(self, name):
|
||||
sale = getattr(self, 'sale', None)
|
||||
if self.inherit_tol and sale:
|
||||
return -Decimal(str(sale.tol_min or 0))
|
||||
return -Decimal(str(self.tol_min or 0))
|
||||
|
||||
def get_tolerance_max(self, name):
|
||||
sale = getattr(self, 'sale', None)
|
||||
if self.inherit_tol and sale:
|
||||
return Decimal(str(sale.tol_max or 0))
|
||||
return Decimal(str(self.tol_max or 0))
|
||||
|
||||
def get_progress(self,name):
|
||||
PS = Pool().get('sale.pricing.summary')
|
||||
ps = PS.search(['sale_line','=',self.id])
|
||||
|
||||
@@ -2837,29 +2837,32 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
self.assertEqual(result['lot_s'][0]['lot_qt_max'], Decimal('80.00000'))
|
||||
self.assertEqual(result['lot_s'][0]['lot_cp'], 40)
|
||||
|
||||
def test_purchase_tolerance_used_sums_open_and_physical_quantities(self):
|
||||
'purchase tolerance gauge uses lot.qt plus physical lot quantities'
|
||||
def test_purchase_tolerance_used_is_weighted_line_average(self):
|
||||
'purchase tolerance gauge averages line gauges by theoretical quantity'
|
||||
unit = Mock()
|
||||
virtual = Mock(id=10, lot_type='virtual')
|
||||
physical = Mock(lot_type='physic')
|
||||
physical.get_current_quantity_converted.return_value = Decimal('10')
|
||||
line = Mock(
|
||||
physical.get_current_quantity_converted.return_value = Decimal('105')
|
||||
physical_line = Mock(
|
||||
type='line', unit=unit, quantity_theorical=Decimal('100'),
|
||||
lots=[virtual, physical])
|
||||
lots=[physical])
|
||||
virtual = Mock(id=10, lot_type='virtual')
|
||||
open_line = Mock(
|
||||
type='line', unit=unit, quantity_theorical=Decimal('300'),
|
||||
lots=[virtual])
|
||||
purchase = purchase_module.Purchase()
|
||||
purchase.lines = [line]
|
||||
purchase.lines = [physical_line, open_line]
|
||||
purchase.tol_min = Decimal('5')
|
||||
purchase.tol_max = Decimal('10')
|
||||
lot_qt_model = Mock()
|
||||
lot_qt_model.search.return_value = [
|
||||
Mock(lot_quantity=Decimal('95'), lot_unit=unit)]
|
||||
Mock(lot_quantity=Decimal('270'), lot_unit=unit)]
|
||||
pool = Mock()
|
||||
pool.get.return_value = lot_qt_model
|
||||
|
||||
with patch.object(purchase_module, 'Pool', return_value=pool):
|
||||
self.assertEqual(
|
||||
purchase.get_tolerance_used('tolerance_used'),
|
||||
Decimal('5.00000'))
|
||||
Decimal('-6.25000'))
|
||||
self.assertEqual(
|
||||
purchase.get_tolerance_min('tolerance_min'),
|
||||
Decimal('-5'))
|
||||
@@ -2867,20 +2870,78 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
purchase.get_tolerance_max('tolerance_max'),
|
||||
Decimal('10'))
|
||||
|
||||
def test_sale_tolerance_used_sums_open_and_physical_quantities(self):
|
||||
'sale tolerance gauge uses absolute lot.qt plus physical quantities'
|
||||
def test_sale_tolerance_used_is_weighted_line_average(self):
|
||||
'sale tolerance gauge averages line gauges by theoretical quantity'
|
||||
unit = Mock()
|
||||
virtual = Mock(id=20, lot_type='virtual')
|
||||
physical = Mock(lot_type='physic')
|
||||
physical.get_current_quantity_converted.return_value = Decimal('10')
|
||||
line = Mock(
|
||||
physical.get_current_quantity_converted.return_value = Decimal('105')
|
||||
physical_line = Mock(
|
||||
type='line', unit=unit, quantity_theorical=Decimal('100'),
|
||||
lots=[virtual, physical])
|
||||
lots=[physical])
|
||||
virtual = Mock(id=20, lot_type='virtual')
|
||||
open_line = Mock(
|
||||
type='line', unit=unit, quantity_theorical=Decimal('300'),
|
||||
lots=[virtual])
|
||||
sale = sale_module.Sale()
|
||||
sale.lines = [line]
|
||||
sale.lines = [physical_line, open_line]
|
||||
sale.tol_min = Decimal('5')
|
||||
sale.tol_max = Decimal('10')
|
||||
lot_qt_model = Mock()
|
||||
lot_qt_model.search.return_value = [
|
||||
Mock(lot_quantity=Decimal('-270'), lot_unit=unit)]
|
||||
pool = Mock()
|
||||
pool.get.return_value = lot_qt_model
|
||||
|
||||
with patch.object(sale_module, 'Pool', return_value=pool):
|
||||
self.assertEqual(
|
||||
sale.get_tolerance_used('tolerance_used'),
|
||||
Decimal('-6.25000'))
|
||||
self.assertEqual(
|
||||
sale.get_tolerance_min('tolerance_min'),
|
||||
Decimal('-5'))
|
||||
self.assertEqual(
|
||||
sale.get_tolerance_max('tolerance_max'),
|
||||
Decimal('10'))
|
||||
|
||||
def test_purchase_line_tolerance_uses_physical_lots_when_present(self):
|
||||
'purchase line tolerance ignores open lot.qt when physical lots exist'
|
||||
unit = Mock()
|
||||
virtual = Mock(id=10, lot_type='virtual')
|
||||
physical = Mock(lot_type='physic')
|
||||
physical.get_current_quantity_converted.return_value = Decimal('105')
|
||||
line = purchase_module.Line()
|
||||
line.unit = unit
|
||||
line.quantity_theorical = Decimal('100')
|
||||
line.quantity = Decimal('100')
|
||||
line.lots = [virtual, physical]
|
||||
line.inherit_tol = False
|
||||
line.tol_min = Decimal('5')
|
||||
line.tol_max = Decimal('10')
|
||||
lot_qt_model = Mock()
|
||||
lot_qt_model.search.return_value = [
|
||||
Mock(lot_quantity=Decimal('80'), lot_unit=unit)]
|
||||
pool = Mock()
|
||||
pool.get.return_value = lot_qt_model
|
||||
|
||||
with patch.object(purchase_module, 'Pool', return_value=pool):
|
||||
self.assertEqual(
|
||||
line.get_tolerance_used('tolerance_used'),
|
||||
Decimal('5.00000'))
|
||||
lot_qt_model.search.assert_not_called()
|
||||
|
||||
def test_sale_line_tolerance_uses_open_lot_qt_without_physical_lots(self):
|
||||
'sale line tolerance uses absolute lot.qt when no physical lot exists'
|
||||
unit = Mock()
|
||||
virtual = Mock(id=20, lot_type='virtual')
|
||||
line = sale_module.SaleLine()
|
||||
line.unit = unit
|
||||
line.quantity_theorical = Decimal('100')
|
||||
line.quantity = Decimal('100')
|
||||
line.lots = [virtual]
|
||||
line.inherit_tol = False
|
||||
line.tol_min = Decimal('5')
|
||||
line.tol_max = Decimal('10')
|
||||
lot_qt_model = Mock()
|
||||
lot_qt_model.search.return_value = [
|
||||
Mock(lot_quantity=Decimal('-95'), lot_unit=unit)]
|
||||
pool = Mock()
|
||||
@@ -2888,14 +2949,8 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
|
||||
with patch.object(sale_module, 'Pool', return_value=pool):
|
||||
self.assertEqual(
|
||||
sale.get_tolerance_used('tolerance_used'),
|
||||
Decimal('5.00000'))
|
||||
self.assertEqual(
|
||||
sale.get_tolerance_min('tolerance_min'),
|
||||
Decimal('-5'))
|
||||
self.assertEqual(
|
||||
sale.get_tolerance_max('tolerance_max'),
|
||||
Decimal('10'))
|
||||
line.get_tolerance_used('tolerance_used'),
|
||||
Decimal('-5.00000'))
|
||||
|
||||
def test_lot_context_group_defaults_to_all(self):
|
||||
'lots management opens without grouping by physical lot'
|
||||
|
||||
@@ -32,12 +32,12 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<label name="tol_max"/>
|
||||
<field name="tol_max"/>
|
||||
<newline/>
|
||||
<field name="tolerance_min" invisible="1"/>
|
||||
<field name="tolerance_max" invisible="1"/>
|
||||
<label name="tolerance_used"/>
|
||||
<field name="tolerance_used" widget="tolerance_gauge"
|
||||
min_field="tolerance_min" max_field="tolerance_max"
|
||||
xexpand="1" xfill="1"/>
|
||||
<field name="tolerance_min" invisible="1"/>
|
||||
<field name="tolerance_max" invisible="1"/>
|
||||
<newline/>
|
||||
<label name="from_location"/>
|
||||
<field name="from_location"/>
|
||||
|
||||
@@ -39,13 +39,21 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<label name="tol_min_v"/>
|
||||
<field name="tol_min_v"/>
|
||||
<newline/>
|
||||
<label name="tol_max"/>
|
||||
<field name="tol_max"/>
|
||||
<label name="tol_max_v"/>
|
||||
<field name="tol_max_v"/>
|
||||
<newline/>
|
||||
<label name="tol_min_qt"/>
|
||||
<field name="tol_min_qt"/>
|
||||
<label name="tol_max"/>
|
||||
<field name="tol_max"/>
|
||||
<label name="tol_max_v"/>
|
||||
<field name="tol_max_v"/>
|
||||
<newline/>
|
||||
<label string="" id="tolerance_gauge_spacer" colspan="2"/>
|
||||
<label name="tolerance_used"/>
|
||||
<field name="tolerance_used" widget="tolerance_gauge"
|
||||
min_field="tolerance_min" max_field="tolerance_max"
|
||||
xexpand="1" xfill="1"/>
|
||||
<field name="tolerance_min" invisible="1"/>
|
||||
<field name="tolerance_max" invisible="1"/>
|
||||
<newline/>
|
||||
<label name="tol_min_qt"/>
|
||||
<field name="tol_min_qt"/>
|
||||
<newline/>
|
||||
<label name="tol_max_qt"/>
|
||||
<field name="tol_max_qt"/>
|
||||
|
||||
@@ -32,12 +32,12 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<label name="tol_max"/>
|
||||
<field name="tol_max"/>
|
||||
<newline/>
|
||||
<field name="tolerance_min" invisible="1"/>
|
||||
<field name="tolerance_max" invisible="1"/>
|
||||
<label name="tolerance_used"/>
|
||||
<field name="tolerance_used" widget="tolerance_gauge"
|
||||
min_field="tolerance_min" max_field="tolerance_max"
|
||||
xexpand="1" xfill="1"/>
|
||||
<field name="tolerance_min" invisible="1"/>
|
||||
<field name="tolerance_max" invisible="1"/>
|
||||
<newline/>
|
||||
<label name="from_location"/>
|
||||
<field name="from_location"/>
|
||||
|
||||
@@ -39,13 +39,21 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<label name="tol_min_v"/>
|
||||
<field name="tol_min_v"/>
|
||||
<newline/>
|
||||
<label name="tol_max"/>
|
||||
<field name="tol_max"/>
|
||||
<label name="tol_max_v"/>
|
||||
<field name="tol_max_v"/>
|
||||
<newline/>
|
||||
<label name="tol_min_qt"/>
|
||||
<field name="tol_min_qt"/>
|
||||
<label name="tol_max"/>
|
||||
<field name="tol_max"/>
|
||||
<label name="tol_max_v"/>
|
||||
<field name="tol_max_v"/>
|
||||
<newline/>
|
||||
<label string="" id="tolerance_gauge_spacer" colspan="2"/>
|
||||
<label name="tolerance_used"/>
|
||||
<field name="tolerance_used" widget="tolerance_gauge"
|
||||
min_field="tolerance_min" max_field="tolerance_max"
|
||||
xexpand="1" xfill="1"/>
|
||||
<field name="tolerance_min" invisible="1"/>
|
||||
<field name="tolerance_max" invisible="1"/>
|
||||
<newline/>
|
||||
<label name="tol_min_qt"/>
|
||||
<field name="tol_min_qt"/>
|
||||
<newline/>
|
||||
<label name="tol_max_qt"/>
|
||||
<field name="tol_max_qt"/>
|
||||
|
||||
Reference in New Issue
Block a user