From 94e60fef6c722262217fadba9c332d53542ea33e Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Sat, 25 Jul 2026 22:47:41 +0200 Subject: [PATCH] Purchase form --- modules/purchase_trade/purchase.py | 115 +++++++++++++++++- .../view/purchase_line_form.xml | 15 ++- 2 files changed, 123 insertions(+), 7 deletions(-) diff --git a/modules/purchase_trade/purchase.py b/modules/purchase_trade/purchase.py index 9978f1f..5380a31 100755 --- a/modules/purchase_trade/purchase.py +++ b/modules/purchase_trade/purchase.py @@ -966,6 +966,101 @@ class Purchase(metaclass=PoolMeta): except (TypeError, ValueError): return 0.0 + @staticmethod + def _summary_decimal(value): + if value in (None, ''): + return Decimal('0') + try: + return Decimal(str(value)) + except Exception: + return Decimal('0') + + @classmethod + def _summary_percent(cls, part, total): + total = cls._summary_decimal(total) + if not total: + return 0.0 + part = cls._summary_decimal(part) + percent = (part / total) * Decimal('100') + percent = max(Decimal('0'), min(Decimal('100'), percent)) + return float(round(percent, 2)) + + @classmethod + def _line_priced_percent(cls, line): + price_type = getattr(line, 'price_type', None) + if price_type in {'cash', 'priced', 'efp'}: + return 100.0 + progress = getattr(line, 'progress', None) + if progress is None and hasattr(line, 'get_progress'): + progress = line.get_progress('progress') + return cls._summary_percent(progress or 0, 1) + + @classmethod + def _line_hedged_percent(cls, line, quantity): + hedged_quantity = Decimal('0') + for derivative in (getattr(line, 'derivatives', None) or []): + der_quantity = getattr(derivative, 'quantity', None) + if der_quantity is None and hasattr(derivative, 'get_qt'): + der_quantity = derivative.get_qt('quantity') + hedged_quantity += abs(cls._summary_decimal(der_quantity)) + return cls._summary_percent(hedged_quantity, quantity) + + @classmethod + def _line_invoice_quantity_percent(cls, line, quantity, states=None): + invoiced_quantity = Decimal('0') + Uom = Pool().get('product.uom') + for invoice_line in (getattr(line, 'invoice_lines', None) or []): + invoice = getattr(invoice_line, 'invoice', None) + invoice_state = getattr(invoice, 'state', None) + if (not invoice or invoice_state == 'cancelled' + or (states and invoice_state not in states) + or getattr(invoice_line, 'type', 'line') != 'line'): + continue + invoice_quantity = cls._summary_decimal( + getattr(invoice_line, 'quantity', None)) + invoice_unit = getattr(invoice_line, 'unit', None) or getattr( + line, 'unit', None) + line_unit = getattr(line, 'unit', None) + if invoice_unit and line_unit and invoice_unit != line_unit: + invoice_quantity = cls._summary_decimal(Uom.compute_qty( + invoice_unit, float(invoice_quantity), line_unit)) + invoiced_quantity += abs(invoice_quantity) + return cls._summary_percent(invoiced_quantity, quantity) + + @classmethod + def _line_period_name(cls, line): + period = getattr(line, 'del_period', None) + return getattr(period, 'rec_name', None) or getattr(period, 'name', '') or '' + + @classmethod + def _line_amount_value(cls, line): + amount = getattr(line, 'amount', None) + if amount is None and hasattr(line, 'on_change_with_amount'): + amount = line.on_change_with_amount() + return cls._summary_decimal(amount) + + @classmethod + def _line_pnl_values(cls, purchase, line): + line_id = getattr(line, 'id', None) + pnl = Decimal('0') + mtm = Decimal('0') + records = ( + getattr(purchase, 'pnl', None) + if getattr(purchase, 'group_pnl', False) + else getattr(purchase, 'pnl_', None)) + for record in (records or []): + record_line = getattr(record, 'r_line', None) or getattr( + record, 'line', None) + if line_id and getattr(record_line, 'id', None) != line_id: + continue + pnl += cls._summary_decimal( + getattr(record, 'r_pnl', None) + if hasattr(record, 'r_pnl') else getattr(record, 'pnl', None)) + mtm += cls._summary_decimal( + getattr(record, 'r_mtm', None) + if hasattr(record, 'r_mtm') else getattr(record, 'mtm', None)) + return pnl, mtm + def get_execution_summary_viewer(self, name=None): data = self._get_purchase_execution_summary_data() data = dict(data, type='execution-summary') @@ -1158,6 +1253,8 @@ class Purchase(metaclass=PoolMeta): ] unit = getattr(getattr(line, 'unit', None), 'rec_name', '') or '' product = getattr(getattr(line, 'product', None), 'rec_name', '') or '' + amount = self._line_amount_value(line) + line_pnl, line_mtm = self._line_pnl_values(self, line) route = (shipped_routes[0] if shipped_routes else '%s > %s' % ( getattr(getattr(self, 'from_location', None), 'rec_name', None) or '-', @@ -1169,8 +1266,17 @@ class Purchase(metaclass=PoolMeta): 'product': product, 'physical': shipped, 'quantity': quantity, + 'period': self._line_period_name(line), 'matched': matched, 'shipped': shipped, + 'priced': self._line_priced_percent(line), + 'hedged': self._line_hedged_percent(line, quantity), + 'invoiced': self._line_invoice_quantity_percent(line, quantity), + 'paid': self._line_invoice_quantity_percent( + line, quantity, states={'paid'}), + 'pnl': self._json_decimal(line_pnl), + 'amount': self._json_decimal(amount), + 'mtm': self._json_decimal(line_mtm), 'open': max(quantity - shipped, 0.0), 'unit': unit, 'lots': lots_count, @@ -1185,6 +1291,9 @@ class Purchase(metaclass=PoolMeta): totals['shipped'] += shipped totals['open'] += max(quantity - shipped, 0.0) totals['lines'] += 1 + totals['amount'] += float(amount) + totals['pnl'] += float(line_pnl) + totals['mtm'] += float(line_mtm) return { 'unit': rows[0]['unit'] if rows else '', @@ -1295,11 +1404,15 @@ class Purchase(metaclass=PoolMeta): return activity def get_purchase_summary_data(self, name=None): + execution = self._get_purchase_execution_summary_data() data = { "currency": getattr(getattr(self, 'currency', None), 'code', None) or '', "map": self._get_purchase_map_summary_data(), - "execution": self._get_purchase_execution_summary_data(), + "execution": execution, + "amount": { + "value": execution.get('totals', {}).get('amount', 0), + }, "activity": self._get_purchase_activity_summary_data(), "approval": { "status": "need_approval", diff --git a/modules/purchase_trade/view/purchase_line_form.xml b/modules/purchase_trade/view/purchase_line_form.xml index 92d6e46..a9250fe 100755 --- a/modules/purchase_trade/view/purchase_line_form.xml +++ b/modules/purchase_trade/view/purchase_line_form.xml @@ -148,6 +148,12 @@ this repository contains the full copyright notices and license terms. --> view_ids="purchase_trade.fee_view_tree_sequence,purchase_trade.fee_view_form"/>