Lot virtual packing update

This commit is contained in:
2026-07-16 18:53:55 +02:00
parent a0bd6f0e28
commit 4c85f80412
6 changed files with 131 additions and 17 deletions

View File

@@ -1127,6 +1127,15 @@ class CoffeeLineMixin:
'Package weight', digits=(16, 5))
coffee_packing_weight_unit = fields.Many2One(
'product.uom', 'Package weight unit')
coffee_packing_unit = fields.Many2One(
'product.uom', 'Package unit',
domain=[
('category', '=', Eval('coffee_packing_category')),
],
depends=['coffee_packing_category'])
coffee_packing_category = fields.Function(
fields.Many2One('product.uom.category', 'Packing Category'),
'on_change_with_coffee_packing_category')
coffee_packing_description = fields.Char('Packing description')
coffee_market_reference = fields.Many2One(
'price.price', 'Market reference')
@@ -1142,6 +1151,13 @@ class CoffeeLineMixin:
def on_change_with_active_coffee_compatibility(self, name=None):
return self.get_active_coffee_compatibility(name)
@fields.depends()
def on_change_with_coffee_packing_category(self, name=None):
UnitCategory = Pool().get('product.uom.category')
packing = UnitCategory.search([('name', '=', 'Packing')], limit=1)
if packing:
return packing[0]
def _coffee_attributes_name(self):
values = []
for field_name, selection in self._coffee_attribute_name_fields:

View File

@@ -2882,6 +2882,27 @@ class Line(metaclass=PoolMeta):
def _should_regenerate_valuation(cls, values):
return bool(cls._valuation_regeneration_fields() & set(values))
@classmethod
def _sync_virtual_lot_packing(cls, line):
Lot = Pool().get('lot.lot')
if not getattr(line, 'lots', None):
line = cls(line.id)
virtual_lots = [
lot for lot in (line.lots or [])
if getattr(lot, 'lot_type', None) == 'virtual']
if not virtual_lots:
return
lot = virtual_lots[0]
packing_count = getattr(line, 'coffee_packing_count', None)
packing_unit = getattr(line, 'coffee_packing_unit', None)
lot_unit_id = getattr(lot.lot_unit, 'id', lot.lot_unit)
packing_unit_id = getattr(packing_unit, 'id', packing_unit)
if lot.lot_qt == packing_count and lot_unit_id == packing_unit_id:
return
lot.lot_qt = packing_count
lot.lot_unit = packing_unit
Lot.save([lot])
@classmethod
def write(cls, *args):
actions = iter(args)
@@ -3104,17 +3125,18 @@ class Line(metaclass=PoolMeta):
if Decimal(str(line.quantity_theorical or 0)) > 0:
Lot.save([lot])
#check if fees need to be updated
if line.fees:
for fee in line.fees:
fl_check = FeeLots.search([('fee','=',fee.id),('lot','=',lot.id),('line','=',line.id)])
if not fl_check:
fl = FeeLots()
if line.fees:
for fee in line.fees:
fl_check = FeeLots.search([('fee','=',fee.id),('lot','=',lot.id),('line','=',line.id)])
if not fl_check:
fl = FeeLots()
fl.fee = fee.id
fl.lot = lot.id
fl.line = line.id
FeeLots.save([fl])
if line.fee_:
fl.line = line.id
FeeLots.save([fl])
cls._sync_virtual_lot_packing(line)
if line.fee_:
if not line.fee_.purchase:
Fee = Pool().get('fee.fee')
f = Fee(line.fee_)

View File

@@ -3002,6 +3002,27 @@ class SaleLine(metaclass=PoolMeta):
default.setdefault('quantity_theorical', None)
default.setdefault('price_pricing', None)
return super().copy(lines, default=default)
@classmethod
def _sync_virtual_lot_packing(cls, line):
Lot = Pool().get('lot.lot')
if not getattr(line, 'lots', None):
line = cls(line.id)
virtual_lots = [
lot for lot in (line.lots or [])
if getattr(lot, 'lot_type', None) == 'virtual']
if not virtual_lots:
return
lot = virtual_lots[0]
packing_count = getattr(line, 'coffee_packing_count', None)
packing_unit = getattr(line, 'coffee_packing_unit', None)
lot_unit_id = getattr(lot.lot_unit, 'id', lot.lot_unit)
packing_unit_id = getattr(packing_unit, 'id', packing_unit)
if lot.lot_qt == packing_count and lot_unit_id == packing_unit_id:
return
lot.lot_qt = packing_count
lot.lot_unit = packing_unit
Lot.save([lot])
@classmethod
def validate(cls, salelines):
@@ -3061,6 +3082,7 @@ class SaleLine(metaclass=PoolMeta):
fl.lot = lot.id
fl.sale_line = line.id
FeeLots.save([fl])
cls._sync_virtual_lot_packing(line)
#generate valuation for purchase and sale
LotQt = Pool().get('lot.qt')

View File

@@ -210,6 +210,62 @@ class PurchaseTradeTestCase(ModuleTestCase):
self.assertEqual(
line.get_coffee_quality_status(), 'approved')
def test_purchase_line_syncs_packing_to_virtual_lot(self):
'purchase line packing updates the existing virtual lot'
package_unit = Mock(id=60)
lot = Mock(lot_type='virtual', lot_qt=None, lot_unit=None)
line = Mock(
lots=[lot],
coffee_packing_count=440,
coffee_packing_unit=package_unit)
lot_model = Mock()
pool = Mock()
pool.get.return_value = lot_model
with patch.object(purchase_module, 'Pool', return_value=pool):
purchase_module.Line._sync_virtual_lot_packing(line)
self.assertEqual(lot.lot_qt, 440)
self.assertEqual(lot.lot_unit, package_unit)
lot_model.save.assert_called_once_with([lot])
def test_sale_line_syncs_packing_to_virtual_lot(self):
'sale line packing updates the existing virtual lot'
package_unit = Mock(id=90)
old_unit = Mock(id=60)
lot = Mock(lot_type='virtual', lot_qt=440, lot_unit=old_unit)
line = Mock(
lots=[lot],
coffee_packing_count=300,
coffee_packing_unit=package_unit)
lot_model = Mock()
pool = Mock()
pool.get.return_value = lot_model
with patch.object(sale_module, 'Pool', return_value=pool):
sale_module.SaleLine._sync_virtual_lot_packing(line)
self.assertEqual(lot.lot_qt, 300)
self.assertEqual(lot.lot_unit, package_unit)
lot_model.save.assert_called_once_with([lot])
def test_purchase_line_does_not_resave_aligned_virtual_lot_packing(self):
'aligned purchase packing does not resave the virtual lot'
package_unit = Mock(id=60)
lot = Mock(lot_type='virtual', lot_qt=440, lot_unit=Mock(id=60))
line = Mock(
lots=[lot],
coffee_packing_count=440,
coffee_packing_unit=package_unit)
lot_model = Mock()
pool = Mock()
pool.get.return_value = lot_model
with patch.object(purchase_module, 'Pool', return_value=pool):
purchase_module.Line._sync_virtual_lot_packing(line)
lot_model.save.assert_not_called()
def test_itsa_book_year_suffix_uses_april_fiscal_start(self):
'ITSA book year changes on April 1st'
self.assertEqual(

View File

@@ -45,12 +45,11 @@ this repository contains the full copyright notices and license terms. -->
col_widths="min-content,1fr,min-content,1fr">
<label name="coffee_packing_count"/>
<field name="coffee_packing_count" width="120" xexpand="0" xfill="0"/>
<label name="coffee_packing_unit_weight"/>
<field name="coffee_packing_unit_weight" width="120" xexpand="0" xfill="0"/>
<label name="coffee_packing_weight_unit"/>
<field name="coffee_packing_weight_unit"/>
<label name="coffee_packing_unit"/>
<field name="coffee_packing_unit"/>
<label name="coffee_packing_description"/>
<field name="coffee_packing_description"/>
<field name="coffee_packing_category" invisible="1" colspan="4"/>
</group>
<group id="tolerances" string="Tolerances &amp; Inheritance"
colspan="1" col="4" panel="card" icon="tryton-switch"

View File

@@ -42,12 +42,11 @@ this repository contains the full copyright notices and license terms. -->
col_widths="min-content,1fr,min-content,1fr">
<label name="coffee_packing_count"/>
<field name="coffee_packing_count" width="120" xexpand="0" xfill="0"/>
<label name="coffee_packing_unit_weight"/>
<field name="coffee_packing_unit_weight" width="120" xexpand="0" xfill="0"/>
<label name="coffee_packing_weight_unit"/>
<field name="coffee_packing_weight_unit"/>
<label name="coffee_packing_unit"/>
<field name="coffee_packing_unit"/>
<label name="coffee_packing_description"/>
<field name="coffee_packing_description"/>
<field name="coffee_packing_category" invisible="1" colspan="4"/>
</group>
<group id="tolerances" string="Tolerances &amp; Inheritance"
colspan="1" col="4" panel="card" icon="tryton-switch"