Pnl clean up
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
from sql import Table
|
||||
from sql.operators import Exists
|
||||
|
||||
from trytond.model import ModelSingleton, ModelSQL, ModelView, fields
|
||||
from trytond.pool import Pool, PoolMeta
|
||||
from trytond.pyson import Eval
|
||||
@@ -119,6 +122,10 @@ class Configuration(ModelSingleton, ModelSQL, ModelView):
|
||||
'menu_coffee_cupping_session_sale',
|
||||
'menu_coffee_cupping_criterion_sale',
|
||||
)
|
||||
_ORPHAN_ACT_WINDOW_XML_IDS = (
|
||||
'act_itsa_operations_workflow',
|
||||
'act_tradon_processes',
|
||||
)
|
||||
|
||||
pricing_rule = fields.Text("Pricing Rule")
|
||||
active_coffee_compatibility = fields.Boolean(
|
||||
@@ -163,6 +170,27 @@ class Configuration(ModelSingleton, ModelSQL, ModelView):
|
||||
shipment_packing_list_report_label = fields.Char(
|
||||
"Packing List Menu Label")
|
||||
|
||||
@classmethod
|
||||
def __register__(cls, module_name):
|
||||
super().__register__(module_name)
|
||||
|
||||
if module_name == 'purchase_trade':
|
||||
cls._cleanup_orphan_act_window_xml_ids()
|
||||
|
||||
@classmethod
|
||||
def _cleanup_orphan_act_window_xml_ids(cls):
|
||||
cursor = Transaction().connection.cursor()
|
||||
model_data = Table('ir_model_data')
|
||||
act_window = Table('ir_action_act_window')
|
||||
|
||||
cursor.execute(*model_data.delete(
|
||||
where=(model_data.module == 'purchase_trade')
|
||||
& (model_data.model == 'ir.action.act_window')
|
||||
& model_data.fs_id.in_(cls._ORPHAN_ACT_WINDOW_XML_IDS)
|
||||
& ~Exists(act_window.select(
|
||||
act_window.id,
|
||||
where=act_window.id == model_data.db_id))))
|
||||
|
||||
@classmethod
|
||||
def default_active_coffee_compatibility(cls):
|
||||
return False
|
||||
|
||||
@@ -1824,6 +1824,47 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
{'type': 'derivative', 'amount': Decimal('30')},
|
||||
])
|
||||
|
||||
def test_delete_existing_purchase_clears_matched_sale_line_only_values(self):
|
||||
'purchase valuation cleanup removes matched sale-only valuation rows'
|
||||
Valuation = Pool().get('valuation.valuation')
|
||||
today = datetime.date(2026, 7, 1)
|
||||
sale_line = Mock(id=30)
|
||||
sale_lot = Mock(sale_line=sale_line)
|
||||
purchase_lot = Mock(id=20, lot_type='virtual', sale_line=None)
|
||||
line = Mock(
|
||||
id=10,
|
||||
finished=False,
|
||||
lots=[purchase_lot],
|
||||
get_matched_lines=Mock(return_value=[]))
|
||||
valuation_model = Mock()
|
||||
valuation_line_model = Mock()
|
||||
lot_qt_model = Mock()
|
||||
lot_qt_model.search.return_value = [Mock(lot_s=sale_lot)]
|
||||
|
||||
with patch('trytond.modules.purchase_trade.valuation.Pool') as PoolMock:
|
||||
PoolMock.return_value.get.side_effect = lambda name: {
|
||||
'ir.date': Mock(today=Mock(return_value=today)),
|
||||
'valuation.valuation': valuation_model,
|
||||
'valuation.valuation.line': valuation_line_model,
|
||||
'lot.qt': lot_qt_model,
|
||||
}[name]
|
||||
|
||||
Valuation._delete_existing(line)
|
||||
|
||||
valuation_model.search.assert_any_call([
|
||||
('line', '=', line.id),
|
||||
('date', '=', today),
|
||||
])
|
||||
valuation_model.search.assert_any_call([
|
||||
('sale_line', '=', sale_line.id),
|
||||
('line', '=', None),
|
||||
('date', '=', today),
|
||||
])
|
||||
valuation_line_model.search.assert_any_call([
|
||||
('sale_line', '=', sale_line.id),
|
||||
('line', '=', None),
|
||||
])
|
||||
|
||||
def test_generate_keeps_finished_purchase_line_physical_pnl(self):
|
||||
'finished purchase lines still generate physical lot valuation'
|
||||
Valuation = Pool().get('valuation.valuation')
|
||||
@@ -2728,6 +2769,8 @@ class PurchaseTradeTestCase(ModuleTestCase):
|
||||
self.assertEqual(segments, [{
|
||||
'quantity': Decimal('18000'),
|
||||
'share': Decimal('1'),
|
||||
'sale_lot': None,
|
||||
'sale_line': None,
|
||||
'shipment_in': shipment.id,
|
||||
}])
|
||||
|
||||
|
||||
@@ -340,6 +340,7 @@ class ValuationBase(ModelSQL):
|
||||
Date = Pool().get('ir.date')
|
||||
Valuation = Pool().get('valuation.valuation')
|
||||
ValuationLine = Pool().get('valuation.valuation.line')
|
||||
LotQt = Pool().get('lot.qt')
|
||||
|
||||
valuation_domain = [
|
||||
('line', '=', line.id),
|
||||
@@ -359,6 +360,47 @@ class ValuationBase(ModelSQL):
|
||||
if valuation_lines:
|
||||
ValuationLine.delete(valuation_lines)
|
||||
|
||||
for sale_line in cls._matched_sale_lines_from_purchase_line(
|
||||
line, LotQt=LotQt):
|
||||
cls._delete_existing_sale_line_only(
|
||||
sale_line, selected_types=selected_types)
|
||||
|
||||
@classmethod
|
||||
def _matched_sale_lines_from_purchase_line(cls, line, LotQt=None):
|
||||
if LotQt is None:
|
||||
LotQt = Pool().get('lot.qt')
|
||||
|
||||
sale_lines = []
|
||||
seen = set()
|
||||
|
||||
def add_sale_line(sale_line):
|
||||
sale_line_id = cls._record_id(sale_line)
|
||||
if not sale_line or sale_line_id in seen:
|
||||
return
|
||||
sale_lines.append(sale_line)
|
||||
seen.add(sale_line_id)
|
||||
|
||||
getter = getattr(line, 'get_matched_lines', None)
|
||||
matched_lines = getter() if callable(getter) else []
|
||||
for matched_line in matched_lines or []:
|
||||
lot_s = getattr(matched_line, 'lot_s', None)
|
||||
add_sale_line(getattr(lot_s, 'sale_line', None))
|
||||
|
||||
for lot in cls._valuation_lots(line):
|
||||
add_sale_line(getattr(lot, 'sale_line', None))
|
||||
lot_id = getattr(lot, 'id', None)
|
||||
if not lot_id:
|
||||
continue
|
||||
for lqt in LotQt.search([
|
||||
('lot_p', '=', lot_id),
|
||||
('lot_s', '>', 0),
|
||||
('lot_quantity', '>', 0),
|
||||
]):
|
||||
lot_s = getattr(lqt, 'lot_s', None)
|
||||
add_sale_line(getattr(lot_s, 'sale_line', None))
|
||||
|
||||
return sale_lines
|
||||
|
||||
@classmethod
|
||||
def _value_key(cls, value):
|
||||
return (
|
||||
@@ -522,6 +564,34 @@ class ValuationBase(ModelSQL):
|
||||
if valuation_lines:
|
||||
ValuationLine.delete(valuation_lines)
|
||||
|
||||
@classmethod
|
||||
def _delete_existing_sale_line_only(cls, sale_line, selected_types=None):
|
||||
Date = Pool().get('ir.date')
|
||||
Valuation = Pool().get('valuation.valuation')
|
||||
ValuationLine = Pool().get('valuation.valuation.line')
|
||||
|
||||
valuation_domain = [
|
||||
('sale_line', '=', sale_line.id),
|
||||
('line', '=', None),
|
||||
('date', '=', Date.today()),
|
||||
]
|
||||
valuation_line_domain = [
|
||||
('sale_line', '=', sale_line.id),
|
||||
('line', '=', None),
|
||||
]
|
||||
|
||||
if selected_types is not None:
|
||||
valuation_domain.append(('type', 'in', list(selected_types)))
|
||||
valuation_line_domain.append(('type', 'in', list(selected_types)))
|
||||
|
||||
valuations = Valuation.search(valuation_domain)
|
||||
if valuations:
|
||||
Valuation.delete(valuations)
|
||||
|
||||
valuation_lines = ValuationLine.search(valuation_line_domain)
|
||||
if valuation_lines:
|
||||
ValuationLine.delete(valuation_lines)
|
||||
|
||||
@classmethod
|
||||
def _base_pnl(cls, *, line, lot, pnl_type, sale=None, sale_line=None,
|
||||
shipment_in=None):
|
||||
|
||||
Reference in New Issue
Block a user