diff --git a/modules/purchase_trade/ctrm_reporting.py b/modules/purchase_trade/ctrm_reporting.py
index 12f2f1c..3e2f060 100644
--- a/modules/purchase_trade/ctrm_reporting.py
+++ b/modules/purchase_trade/ctrm_reporting.py
@@ -21,7 +21,10 @@ PHYSICAL_VALUATION_TYPES = [
'market',
]
DERIVATIVE_VALUATION_TYPES = ['derivative']
-ALL_VALUATION_TYPES = PHYSICAL_VALUATION_TYPES + DERIVATIVE_VALUATION_TYPES
+MTM_VALUATION_TYPES = ['mtm']
+ALL_VALUATION_TYPES = (
+ PHYSICAL_VALUATION_TYPES + DERIVATIVE_VALUATION_TYPES
+ + MTM_VALUATION_TYPES)
class CTRMValuationContextMixin:
@@ -249,7 +252,8 @@ class CTRMNetPosition(ModelSQL, ModelView):
context = Transaction().context
where = val.type.in_(
- PHYSICAL_VALUATION_TYPES + DERIVATIVE_VALUATION_TYPES)
+ PHYSICAL_VALUATION_TYPES + DERIVATIVE_VALUATION_TYPES
+ + MTM_VALUATION_TYPES)
if context.get('date'):
where &= val.date == context['date']
if context.get('product'):
@@ -268,14 +272,17 @@ class CTRMNetPosition(ModelSQL, ModelView):
where &= val.state == context['state']
is_derivative = val.type.in_(DERIVATIVE_VALUATION_TYPES)
+ is_mtm = val.type.in_(MTM_VALUATION_TYPES)
physical_quantity = Case(
- (is_derivative, 0), else_=Coalesce(val.quantity, 0))
+ (is_derivative | is_mtm, 0), else_=Coalesce(val.quantity, 0))
derivative_quantity = Case(
(is_derivative, Coalesce(val.quantity, 0)), else_=0)
physical_amount = Case(
- (is_derivative, 0), else_=Coalesce(val.amount, 0))
+ (is_derivative | is_mtm, 0), else_=Coalesce(val.amount, 0))
derivative_amount = Case(
(is_derivative, Coalesce(val.amount, 0)), else_=0)
+ net_quantity = Case(
+ (is_mtm, 0), else_=Coalesce(val.quantity, 0))
group_by = [
val.date,
@@ -302,7 +309,7 @@ class CTRMNetPosition(ModelSQL, ModelView):
val.strategy.as_('strategy'),
Sum(physical_quantity).as_('physical_quantity'),
Sum(derivative_quantity).as_('derivative_quantity'),
- Sum(Coalesce(val.quantity, 0)).as_('net_quantity'),
+ Sum(net_quantity).as_('net_quantity'),
Sum(physical_amount).as_('physical_amount'),
Sum(derivative_amount).as_('derivative_amount'),
Sum(Coalesce(val.amount, 0)).as_('net_amount'),
@@ -396,7 +403,8 @@ class CTRMRealizedPnl(ModelSQL, ModelView):
context = Transaction().context
realization = context.get(
'realization', 'purchase_and_sale_invoiced')
- where = val.type.in_(ALL_VALUATION_TYPES)
+ where = val.type.in_(
+ PHYSICAL_VALUATION_TYPES + DERIVATIVE_VALUATION_TYPES)
if context.get('date'):
where &= val.date == context['date']
if context.get('product'):
@@ -487,6 +495,7 @@ class CTRMMtmPnl(ModelSQL, ModelView):
('sale fee', 'Sale fee'),
('shipment fee', 'Shipment fee'),
('market', 'Market'),
+ ('mtm', 'Mtm'),
('derivative', 'Derivative'),
], "Type")
reference = fields.Char("Reference")
@@ -610,8 +619,10 @@ class CTRMPnlExplain(ModelSQL, ModelView):
'shipment fee',
])
is_derivative = val.type.in_(DERIVATIVE_VALUATION_TYPES)
+ is_mtm = val.type.in_(MTM_VALUATION_TYPES)
physical_pnl = Case(
- (is_fee | is_derivative, 0), else_=Coalesce(val.amount, 0))
+ (is_fee | is_derivative | is_mtm, 0),
+ else_=Coalesce(val.amount, 0))
fee_pnl = Case((is_fee, Coalesce(val.amount, 0)), else_=0)
derivative_pnl = Case(
(is_derivative, Coalesce(val.amount, 0)), else_=0)
@@ -737,7 +748,9 @@ class CTRMPnlDimension(ModelSQL, ModelView):
val.unit.as_('unit'),
val.state.as_('state'),
val.strategy.as_('strategy'),
- Sum(Coalesce(val.quantity, 0)).as_('quantity'),
+ Sum(Case(
+ (val.type.in_(MTM_VALUATION_TYPES), 0),
+ else_=Coalesce(val.quantity, 0))).as_('quantity'),
Sum(Coalesce(val.amount, 0)).as_('amount'),
Sum(Coalesce(val.mtm, 0)).as_('mtm'),
Sum(Coalesce(val.mtm, 0) - Coalesce(val.amount, 0)).as_(
@@ -1613,7 +1626,8 @@ class CTRMAccruals(ModelSQL, ModelView):
else_='physical')
context = Transaction().context
where = (
- val.type.in_(ALL_VALUATION_TYPES)
+ val.type.in_(
+ PHYSICAL_VALUATION_TYPES + DERIVATIVE_VALUATION_TYPES)
& ((val.lot == Null)
| ((lot.invoice_line == Null)
& (lot.sale_invoice_line == Null))))
diff --git a/modules/purchase_trade/lot.py b/modules/purchase_trade/lot.py
index 550df35..05af680 100755
--- a/modules/purchase_trade/lot.py
+++ b/modules/purchase_trade/lot.py
@@ -1022,11 +1022,11 @@ class Lot(metaclass=PoolMeta):
return True
return False
- def createMove(self,r,qt,sh):
- Move = Pool().get('stock.move')
- nm = Move()
- nm.from_location = r.lot_p.line.purchase.from_location
- nm.to_location = r.lot_p.line.purchase.to_location
+ def createMove(self,r,qt,sh):
+ Move = Pool().get('stock.move')
+ nm = Move()
+ nm.from_location = r.lot_p.line.purchase.from_location
+ nm.to_location = r.lot_p.line.purchase.to_location
nm.product = r.lot_p.lot_product
nm.unit = r.lot_unit
nm.quantity = qt
@@ -1034,11 +1034,32 @@ class Lot(metaclass=PoolMeta):
nm.shipment = sh
nm.currency = r.lot_p.line.currency
nm.unit_price = r.lot_p.line.unit_price
- Move.save([nm])
- return nm.id
-
- @classmethod
- def create(cls, vlist):
+ Move.save([nm])
+ return nm.id
+
+ def create_shipment_move(self, shipment_origin):
+ Move = Pool().get('stock.move')
+ line = self.line
+ if not line or not getattr(line, 'purchase', None):
+ return
+ unit = self.lot_unit or self.lot_unit_line or line.unit
+ quantity = self.get_current_quantity_converted(0, unit)
+ move = Move()
+ move.from_location = line.purchase.from_location
+ move.to_location = line.purchase.to_location
+ move.product = self.lot_product
+ move.unit = unit
+ move.quantity = quantity
+ move.origin = line
+ move.shipment = shipment_origin
+ move.currency = line.currency
+ move.unit_price = line.unit_price
+ move.lot = self
+ Move.save([move])
+ return move
+
+ @classmethod
+ def create(cls, vlist):
vlist = [x.copy() for x in vlist]
L = Pool().get('lot.lot')
Uom = Pool().get('product.uom')
@@ -3095,10 +3116,16 @@ class LotShipping(Wizard):
elif self.ship.shipment == 'int':
l.lot_shipment_internal = self.ship.shipment_internal
logger.info("IN_SHIPPING2:%s",l.move)
+ move = None
if not l.move:
- continue
+ move = l.create_shipment_move(shipment_origin)
+ if not move:
+ raise UserError(
+ "Cannot create shipment move for lot %s."
+ % l)
logger.info("IN_SHIPPING3:%s",r)
- move = Move(l.move)
+ if not move:
+ move = Move(l.move)
move.shipment = shipment_origin
Move.save([move])
linked_transit_move = move.get_linked_transit_move()
diff --git a/modules/purchase_trade/tests/test_module.py b/modules/purchase_trade/tests/test_module.py
index 7c2c951..c20d53c 100644
--- a/modules/purchase_trade/tests/test_module.py
+++ b/modules/purchase_trade/tests/test_module.py
@@ -440,6 +440,60 @@ class PurchaseTradeTestCase(ModuleTestCase):
with self.assertRaises(UserError):
wizard.transition_shipping()
+ def test_lot_shipping_physical_without_move_creates_shipment_move(self):
+ 'linking a physical lot to shipment creates the missing stock move'
+ wizard = lot_module.LotShipping()
+ shipment = Mock(id=40)
+ wizard.ship = Mock(
+ shipment='in',
+ shipment_in=shipment,
+ shipment_out=None,
+ shipment_internal=None,
+ create_new_shipment=False,
+ quantity=None,
+ )
+ record = Mock(
+ id=2014,
+ r_lot_type='physic',
+ )
+ wizard.records = [record]
+ move = Mock()
+ move.get_linked_transit_move.return_value = None
+ lot = Mock(
+ id=2014,
+ move=None,
+ line=Mock(),
+ sale_line=None,
+ )
+ lot.get_current_quantity_converted.return_value = Decimal('22')
+ lot.getVlot_p.return_value = Mock()
+ lot.getVlot_s.return_value = None
+ lot.create_shipment_move.return_value = move
+ Lot = Mock()
+ Lot.return_value = lot
+ Lot.skip_quantity_consistency.return_value.__enter__ = Mock()
+ Lot.skip_quantity_consistency.return_value.__exit__ = Mock()
+ LotQt = Mock()
+ Move = Mock()
+
+ pool = Mock()
+ pool.get.side_effect = [Lot, LotQt, Move]
+ with patch('trytond.modules.purchase_trade.lot.Pool',
+ return_value=pool):
+ state = wizard.transition_shipping()
+
+ self.assertEqual(state, 'end')
+ self.assertEqual(lot.lot_shipment_in, shipment)
+ lot.create_shipment_move.assert_called_once_with(
+ 'stock.shipment.in,40')
+ self.assertEqual(move.shipment, 'stock.shipment.in,40')
+ Move.save.assert_called_once_with([move])
+ lot.updateVirtualPart.assert_called_once_with(
+ Decimal('-22'), 'stock.shipment.in,40', None)
+ Lot.save.assert_called()
+ Lot.assert_lines_quantity_consistency.assert_called_once_with([
+ lot.line])
+
def test_lot_shipping_scheduled_lotqt_keeps_planned_locations(self):
'scheduled lot.qt keeps the planned from and to locations'
wizard = lot_module.LotShipping()
@@ -556,8 +610,8 @@ class PurchaseTradeTestCase(ModuleTestCase):
Decimal('-349167.53'))
@with_transaction()
- def test_strategy_mtm_lines_split_by_curve_with_previous_price(self):
- 'strategy MTM creates one valuation line per curve with previous price'
+ def test_strategy_mtm_lines_are_separate_from_realized_price(self):
+ 'strategy MTM creates separate curve lines and preserves realized line'
Valuation = Pool().get('valuation.valuation')
valuation_date = datetime.date(2026, 6, 5)
previous_date = datetime.date(2026, 6, 4)
@@ -583,6 +637,7 @@ class PurchaseTradeTestCase(ModuleTestCase):
price_index=curve_b,
ratio=Decimal('40')),
])
+ line.mtm = [strategy]
values = {
'type': 'pur. priced',
'price': Decimal('10'),
@@ -596,22 +651,28 @@ class PurchaseTradeTestCase(ModuleTestCase):
with patch('trytond.modules.purchase_trade.valuation.Pool') as PoolMock:
PoolMock.return_value.get.return_value = price_value
- Valuation._append_strategy_mtm_lines(
- target, values, strategy, line)
+ Valuation._append_pnl_values(target, values, line)
- self.assertEqual(len(target), 2)
- self.assertEqual(target[0]['mtm_curve'], curve_a.id)
- self.assertEqual(target[0]['amount'], Decimal('-60.00'))
- self.assertEqual(target[0]['mtm_price'], Decimal('60.0000'))
- self.assertEqual(target[0]['mtm_price_prev'], Decimal('54.0000'))
- self.assertEqual(target[0]['amount_prev'], Decimal('-540.00'))
- self.assertEqual(target[0]['mtm'], Decimal('-600.00'))
- self.assertEqual(target[1]['mtm_curve'], curve_b.id)
- self.assertEqual(target[1]['amount'], Decimal('-40.00'))
- self.assertEqual(target[1]['mtm_price'], Decimal('20.0000'))
- self.assertEqual(target[1]['mtm_price_prev'], Decimal('18.0000'))
- self.assertEqual(target[1]['amount_prev'], Decimal('-180.00'))
- self.assertEqual(target[1]['mtm'], Decimal('-200.00'))
+ self.assertEqual(len(target), 3)
+ self.assertEqual(target[0], values)
+ self.assertEqual(target[1]['type'], 'mtm')
+ self.assertEqual(target[1]['mtm_curve'], curve_a.id)
+ self.assertEqual(target[1]['price'], None)
+ self.assertEqual(target[1]['amount'], Decimal('0'))
+ self.assertEqual(target[1]['quantity'], Decimal('10'))
+ self.assertEqual(target[1]['mtm_price'], Decimal('100.0000'))
+ self.assertEqual(target[1]['mtm_price_prev'], Decimal('90.0000'))
+ self.assertEqual(target[1]['amount_prev'], Decimal('-540.00'))
+ self.assertEqual(target[1]['mtm'], Decimal('-600.00'))
+ self.assertEqual(target[2]['type'], 'mtm')
+ self.assertEqual(target[2]['mtm_curve'], curve_b.id)
+ self.assertEqual(target[2]['price'], None)
+ self.assertEqual(target[2]['amount'], Decimal('0'))
+ self.assertEqual(target[2]['quantity'], Decimal('10'))
+ self.assertEqual(target[2]['mtm_price'], Decimal('50.0000'))
+ self.assertEqual(target[2]['mtm_price_prev'], Decimal('45.0000'))
+ self.assertEqual(target[2]['amount_prev'], Decimal('-180.00'))
+ self.assertEqual(target[2]['mtm'], Decimal('-200.00'))
def test_purchase_pnl_uses_partial_lotqt_match_quantity(self):
'open matched purchase and sale pnl use the matched lot.qt quantity'
@@ -1095,6 +1156,7 @@ class PurchaseTradeTestCase(ModuleTestCase):
Valuation._get_generate_types('derivatives'),
{'derivative'})
self.assertIn('pur. priced', Valuation._get_generate_types('goods'))
+ self.assertIn('mtm', Valuation._get_generate_types('goods'))
def test_filter_values_by_types_keeps_matching_entries_only(self):
'type filtering keeps only the requested valuation entries'
diff --git a/modules/purchase_trade/valuation.py b/modules/purchase_trade/valuation.py
index cd28954..d41df89 100644
--- a/modules/purchase_trade/valuation.py
+++ b/modules/purchase_trade/valuation.py
@@ -32,6 +32,7 @@ VALTYPE = [
('sale fee', 'Sale fee'),
('shipment fee', 'Shipment fee'),
('market', 'Market'),
+ ('mtm', 'Mtm'),
('derivative', 'Derivative'),
]
@@ -75,7 +76,7 @@ class ValuationBase(ModelSQL):
'fees': {'line fee', 'pur. fee', 'sale fee', 'shipment fee'},
'goods': {
'priced', 'pur. priced', 'pur. efp',
- 'sale priced', 'sale efp', 'market',
+ 'sale priced', 'sale efp', 'market', 'mtm',
},
'derivatives': {'derivative'},
}
@@ -573,11 +574,6 @@ class ValuationBase(ModelSQL):
scenario.valuation_date,
line.unit,
strategy.currency)
- if component.ratio:
- ratio = Decimal(component.ratio) / Decimal(100)
- value *= ratio
- if previous is not None:
- previous *= ratio
return round(value, 4), (
round(previous, 4) if previous is not None else None)
@@ -587,6 +583,34 @@ class ValuationBase(ModelSQL):
return value
return round(Decimal(value) * share, digits)
+ @classmethod
+ def _mtm_sign(cls, values):
+ amount = values.get('amount')
+ if amount and amount < 0:
+ return Decimal(-1)
+ if not amount and values.get('type') in {'pur. priced', 'pur. efp'}:
+ return Decimal(-1)
+ return Decimal(1)
+
+ @classmethod
+ def _mtm_line_values(cls, values, strategy, mtm_price, mtm_amount,
+ mtm_curve=None, mtm_price_prev=None, mtm_amount_prev=None):
+ line_values = dict(values)
+ line_values.update({
+ 'type': 'mtm',
+ 'reference': 'MTM/%s' % (values.get('reference') or ''),
+ 'price': None,
+ 'amount': Decimal(0),
+ 'base_amount': Decimal(0),
+ 'mtm_price': mtm_price,
+ 'mtm_price_prev': mtm_price_prev,
+ 'mtm_curve': mtm_curve,
+ 'amount_prev': mtm_amount_prev,
+ 'mtm': mtm_amount,
+ 'strategy': strategy,
+ })
+ return line_values
+
@classmethod
def _append_strategy_mtm_lines(cls, target, values, strategy, line):
components = list(strategy.components or [])
@@ -595,76 +619,53 @@ class ValuationBase(ModelSQL):
if component.price_source_type == 'curve'
and component.price_index]
if not curve_components:
- line_values = dict(values)
- line_values['mtm_price'] = cls._get_strategy_mtm_price(
- strategy, line)
- line_values['amount_prev'] = None
- line_values['mtm'] = cls._signed_strategy_mtm(
- values, strategy, line)
- line_values['strategy'] = strategy
- target.append(line_values)
+ target.append(cls._mtm_line_values(
+ values,
+ strategy,
+ cls._get_strategy_mtm_price(strategy, line),
+ cls._signed_strategy_mtm(values, strategy, line)))
return
curve_component_ids = {id(component) for component in curve_components}
- total_weight = sum(
- cls._component_weight(component)
- for component in components) or Decimal(100)
total_mtm_price = cls._get_strategy_mtm_price(strategy, line)
curve_mtm_price = Decimal(0)
- signed_factor = Decimal(1)
- amount = values.get('amount')
- if amount and amount < 0:
- signed_factor = Decimal(-1)
- elif not amount and values.get('type') in {'pur. priced', 'pur. efp'}:
- signed_factor = Decimal(-1)
+ signed_factor = cls._mtm_sign(values)
for component in curve_components:
- share = cls._component_weight(component) / total_weight
+ ratio = cls._component_weight(component) / Decimal(100)
mtm_price, mtm_price_prev = cls._curve_component_price(
component, line, strategy)
- curve_mtm_price += mtm_price
- line_values = dict(values)
- line_values['price'] = cls._split_value(
- values.get('price'), share, digits=4)
- line_values['amount'] = cls._split_value(
- values.get('amount'), share)
- line_values['base_amount'] = cls._split_value(
- values.get('base_amount'), share)
- line_values['mtm_price'] = mtm_price
- line_values['mtm_price_prev'] = mtm_price_prev
- line_values['mtm_curve'] = component.price_index.id
- line_values['amount_prev'] = (
+ weighted_mtm_price = mtm_price * ratio
+ curve_mtm_price += weighted_mtm_price
+ mtm_amount_prev = (
round(
- mtm_price_prev * Decimal(values['quantity'])
+ mtm_price_prev * ratio * Decimal(values['quantity'])
* signed_factor, 2)
if mtm_price_prev is not None else None)
- line_values['mtm'] = round(
- mtm_price * Decimal(values['quantity']) * signed_factor, 2)
- line_values['strategy'] = strategy
- target.append(line_values)
+ target.append(cls._mtm_line_values(
+ values,
+ strategy,
+ mtm_price,
+ round(
+ mtm_price * ratio * Decimal(values['quantity'])
+ * signed_factor, 2),
+ mtm_curve=component.price_index.id,
+ mtm_price_prev=mtm_price_prev,
+ mtm_amount_prev=mtm_amount_prev))
residual_weight = sum(
cls._component_weight(component)
for component in components
if id(component) not in curve_component_ids)
if residual_weight:
- share = residual_weight / total_weight
mtm_price = round(total_mtm_price - curve_mtm_price, 4)
- line_values = dict(values)
- line_values['price'] = cls._split_value(
- values.get('price'), share, digits=4)
- line_values['amount'] = cls._split_value(
- values.get('amount'), share)
- line_values['base_amount'] = cls._split_value(
- values.get('base_amount'), share)
- line_values['mtm_price'] = mtm_price
- line_values['mtm_price_prev'] = None
- line_values['mtm_curve'] = None
- line_values['amount_prev'] = None
- line_values['mtm'] = round(
- mtm_price * Decimal(values['quantity']) * signed_factor, 2)
- line_values['strategy'] = strategy
- target.append(line_values)
+ target.append(cls._mtm_line_values(
+ values,
+ strategy,
+ mtm_price,
+ round(
+ mtm_price * Decimal(values['quantity']) * signed_factor,
+ 2)))
@staticmethod
def _get_basis_component_total(record):
@@ -813,6 +814,7 @@ class ValuationBase(ModelSQL):
def _append_pnl_values(cls, price_lines, values, mtm_source):
if (values and getattr(mtm_source, 'mtm', None)
and cls._supports_strategy_mtm(values)):
+ price_lines.append(values)
for strat in mtm_source.mtm:
cls._append_strategy_mtm_lines(
price_lines, values, strat, mtm_source)
@@ -1014,26 +1016,14 @@ class ValuationBase(ModelSQL):
state='unfixed',
pnl_type='sale priced'
)
- if sale_line.mtm and cls._supports_strategy_mtm(values):
- for strat in sale_line.mtm:
- cls._append_strategy_mtm_lines(
- price_lines, values, strat, sale_line)
- else:
- if values:
- price_lines.append(values)
+ cls._append_pnl_values(price_lines, values, sale_line)
continue
for pc in summaries:
values = cls._build_basis_pnl_from_sale_line(
sale_line=sale_line, lot=lot, pc=pc,
extra_price=premium_delta)
- if sale_line.mtm and cls._supports_strategy_mtm(values):
- for strat in sale_line.mtm:
- cls._append_strategy_mtm_lines(
- price_lines, values, strat, sale_line)
- else:
- if values:
- price_lines.append(values)
+ cls._append_pnl_values(price_lines, values, sale_line)
elif sale_line.price_type in ('priced', 'efp'):
price = cls._get_sale_lot_price(sale_line, lot)
@@ -1046,13 +1036,7 @@ class ValuationBase(ModelSQL):
state='fixed' if sale_line.price_type == 'priced' else 'not fixed',
pnl_type=f'sale {sale_line.price_type}'
)
- if sale_line.mtm and cls._supports_strategy_mtm(values):
- for strat in sale_line.mtm:
- cls._append_strategy_mtm_lines(
- price_lines, values, strat, sale_line)
- else:
- if values:
- price_lines.append(values)
+ cls._append_pnl_values(price_lines, values, sale_line)
return price_lines
diff --git a/modules/purchase_trade/view/valuation_list.xml b/modules/purchase_trade/view/valuation_list.xml
index d3baf8f..a1adffa 100644
--- a/modules/purchase_trade/view/valuation_list.xml
+++ b/modules/purchase_trade/view/valuation_list.xml
@@ -6,7 +6,7 @@
+ badge_colors="priced:#2563eb,Price:#2563eb,pur. priced:#1d4ed8,Pur. price:#1d4ed8,pur. efp:#38bdf8,Pur. efp:#38bdf8,sale priced:#16a34a,Sale price:#16a34a,sale efp:#86efac,Sale efp:#86efac,market:#64748b,Market:#64748b,line fee:#d97706,Line fee:#d97706,pur. fee:#f59e0b,Pur. fee:#f59e0b,sale fee:#fb923c,Sale fee:#fb923c,shipment fee:#0f766e,Shipment fee:#0f766e,mtm:#9f1239,Mtm:#9f1239,derivative:#8b5cf6,Derivative:#8b5cf6,*:#94a3b8"/>
diff --git a/modules/purchase_trade/view/valuation_tree_sequence3.xml b/modules/purchase_trade/view/valuation_tree_sequence3.xml
index 3d6c496..c1ddced 100755
--- a/modules/purchase_trade/view/valuation_tree_sequence3.xml
+++ b/modules/purchase_trade/view/valuation_tree_sequence3.xml
@@ -7,7 +7,7 @@ this repository contains the full copyright notices and license terms. -->
+ badge_colors="priced:#2563eb,Price:#2563eb,pur. priced:#1d4ed8,Pur. price:#1d4ed8,pur. efp:#38bdf8,Pur. efp:#38bdf8,sale priced:#16a34a,Sale price:#16a34a,sale efp:#86efac,Sale efp:#86efac,market:#64748b,Market:#64748b,line fee:#d97706,Line fee:#d97706,pur. fee:#f59e0b,Pur. fee:#f59e0b,sale fee:#fb923c,Sale fee:#fb923c,shipment fee:#0f766e,Shipment fee:#0f766e,mtm:#9f1239,Mtm:#9f1239,derivative:#8b5cf6,Derivative:#8b5cf6,*:#94a3b8"/>