Purchase & Sale
This commit is contained in:
@@ -325,6 +325,8 @@ class Sale(metaclass=PoolMeta):
|
||||
viewer = fields.Function(fields.Text(""),'get_viewer')
|
||||
execution_summary_viewer = fields.Function(
|
||||
fields.Text(""), 'get_execution_summary_viewer')
|
||||
sale_summary_data = fields.Function(
|
||||
fields.Text("Sale Summary"), 'get_sale_summary_data')
|
||||
doc_template = fields.Many2One('doc.template',"Template")
|
||||
required_documents = fields.Many2Many(
|
||||
'contract.document.type', 'sale', 'doc_type', 'Required Documents')
|
||||
@@ -1530,15 +1532,242 @@ class Sale(metaclass=PoolMeta):
|
||||
except (TypeError, ValueError):
|
||||
return 0.0
|
||||
|
||||
def get_execution_summary_viewer(self, name=None):
|
||||
@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))
|
||||
|
||||
@staticmethod
|
||||
def _summary_number(value):
|
||||
if value is None:
|
||||
return None
|
||||
try:
|
||||
return float(value)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _summary_link(model, records):
|
||||
unique = []
|
||||
seen = set()
|
||||
for record in records:
|
||||
record_id = getattr(record, 'id', None)
|
||||
if not record_id:
|
||||
continue
|
||||
key = (model, record_id)
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
unique.append(record)
|
||||
link = {
|
||||
"model": model,
|
||||
"count": len(unique),
|
||||
}
|
||||
if len(unique) == 1:
|
||||
link["res_id"] = unique[0].id
|
||||
return link
|
||||
|
||||
@staticmethod
|
||||
def _summary_mixed_link(records, default_model=None):
|
||||
unique = []
|
||||
seen = set()
|
||||
for model, record in records:
|
||||
record_id = getattr(record, 'id', None)
|
||||
if not model or not record_id:
|
||||
continue
|
||||
key = (model, record_id)
|
||||
if key in seen:
|
||||
continue
|
||||
seen.add(key)
|
||||
unique.append((model, record))
|
||||
link = {"count": len(unique)}
|
||||
if default_model:
|
||||
link["model"] = default_model
|
||||
if len(unique) == 1:
|
||||
model, record = unique[0]
|
||||
link.update({
|
||||
"model": model,
|
||||
"res_id": record.id,
|
||||
})
|
||||
elif unique:
|
||||
link["model"] = unique[0][0]
|
||||
return link
|
||||
|
||||
@staticmethod
|
||||
def _summary_shipment_record(record):
|
||||
if getattr(record, 'lot_shipment_out', None):
|
||||
return ('stock.shipment.out', record.lot_shipment_out)
|
||||
if getattr(record, 'lot_shipment_in', None):
|
||||
return ('stock.shipment.in', record.lot_shipment_in)
|
||||
if getattr(record, 'lot_shipment_internal', None):
|
||||
return ('stock.shipment.internal', record.lot_shipment_internal)
|
||||
return (None, None)
|
||||
|
||||
@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, sale, line):
|
||||
line_id = getattr(line, 'id', None)
|
||||
pnl = Decimal('0')
|
||||
mtm = Decimal('0')
|
||||
previous_pnl = Decimal('0')
|
||||
has_previous = False
|
||||
records = (
|
||||
getattr(sale, 'pnl', None)
|
||||
if getattr(sale, 'group_pnl', False)
|
||||
else getattr(sale, 'pnl_', None))
|
||||
for record in (records or []):
|
||||
record_line = getattr(record, 'r_sale_line', None) or getattr(
|
||||
record, 'sale_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))
|
||||
amount_prev = (
|
||||
getattr(record, 'r_amount_prev', None)
|
||||
if hasattr(record, 'r_amount_prev')
|
||||
else getattr(record, 'amount_prev', None))
|
||||
if amount_prev in (None, ''):
|
||||
continue
|
||||
base_amount = (
|
||||
getattr(record, 'r_base_amount', None)
|
||||
if hasattr(record, 'r_base_amount')
|
||||
else getattr(record, 'base_amount', None))
|
||||
previous_pnl += (
|
||||
cls._summary_decimal(base_amount)
|
||||
- cls._summary_decimal(amount_prev))
|
||||
has_previous = True
|
||||
return pnl, mtm, previous_pnl if has_previous else None
|
||||
|
||||
@classmethod
|
||||
def _pnl_change_percent(cls, current, previous):
|
||||
if previous in (None, ''):
|
||||
return None
|
||||
previous = cls._summary_decimal(previous)
|
||||
if not previous:
|
||||
return None
|
||||
current = cls._summary_decimal(current)
|
||||
return float(round(((current - previous) / abs(previous)) * 100, 2))
|
||||
|
||||
def _sale_pnl_change_percent(self):
|
||||
changes = []
|
||||
for line in (self.lines or []):
|
||||
if getattr(line, 'type', 'line') != 'line':
|
||||
continue
|
||||
line_pnl, _line_mtm, previous_pnl = self._line_pnl_values(
|
||||
self, line)
|
||||
change = self._pnl_change_percent(line_pnl, previous_pnl)
|
||||
if change is not None:
|
||||
changes.append(Decimal(str(change)))
|
||||
if not changes:
|
||||
return None
|
||||
return float(round(sum(changes) / Decimal(len(changes)), 2))
|
||||
|
||||
def _get_sale_map_summary_data(self):
|
||||
dep_name = ''
|
||||
arr_name = ''
|
||||
departure = None
|
||||
arrival = None
|
||||
if self.from_location:
|
||||
dep_name = self.from_location.name or self.from_location.rec_name
|
||||
departure = {
|
||||
"name": dep_name,
|
||||
"lat": self._summary_number(self.from_location.lat),
|
||||
"lon": self._summary_number(self.from_location.lon),
|
||||
}
|
||||
if self.to_location:
|
||||
arr_name = self.to_location.name or self.to_location.rec_name
|
||||
arrival = {
|
||||
"name": arr_name,
|
||||
"lat": self._summary_number(self.to_location.lat),
|
||||
"lon": self._summary_number(self.to_location.lon),
|
||||
}
|
||||
return {
|
||||
"from": dep_name,
|
||||
"to": arr_name,
|
||||
"route": '%s > %s' % (dep_name or '-', arr_name or '-'),
|
||||
"departures": [departure] if departure else [],
|
||||
"arrivals": [arrival] if arrival else [],
|
||||
}
|
||||
|
||||
def _get_sale_execution_summary_data(self):
|
||||
LotQt = Pool().get('lot.qt')
|
||||
rows = []
|
||||
totals = defaultdict(float)
|
||||
|
||||
for line in (self.lines or []):
|
||||
if getattr(line, 'type', 'line') != 'line':
|
||||
continue
|
||||
|
||||
for index, line in enumerate((
|
||||
l for l in (self.lines or [])
|
||||
if getattr(l, 'type', 'line') == 'line'), 1):
|
||||
quantity = self._execution_summary_quantity(
|
||||
getattr(line, 'quantity_theorical', None)
|
||||
or getattr(line, 'quantity', None))
|
||||
@@ -1546,6 +1775,8 @@ class Sale(metaclass=PoolMeta):
|
||||
shipped = 0.0
|
||||
lots_count = 0
|
||||
shipped_routes = []
|
||||
purchases = []
|
||||
shipments = []
|
||||
line_lots = list(getattr(line, 'lots', None) or [])
|
||||
lot_ids = [lot.id for lot in line_lots if getattr(lot, 'id', None)]
|
||||
lot_qts = LotQt.search([('lot_s', 'in', lot_ids)]) if lot_ids else []
|
||||
@@ -1557,11 +1788,13 @@ class Sale(metaclass=PoolMeta):
|
||||
or getattr(lqt, 'lot_qt', None))
|
||||
if getattr(lqt, 'lot_p', None):
|
||||
matched += lot_quantity
|
||||
shipment = (
|
||||
getattr(lqt, 'lot_shipment_in', None)
|
||||
or getattr(lqt, 'lot_shipment_internal', None)
|
||||
or getattr(lqt, 'lot_shipment_out', None))
|
||||
purchase_line = getattr(lqt.lot_p, 'line', None)
|
||||
purchase = getattr(purchase_line, 'purchase', None)
|
||||
if purchase:
|
||||
purchases.append(purchase)
|
||||
shipment_model, shipment = self._summary_shipment_record(lqt)
|
||||
if shipment:
|
||||
shipments.append((shipment_model, shipment))
|
||||
shipped += lot_quantity
|
||||
route_from = getattr(
|
||||
getattr(shipment, 'from_location', None),
|
||||
@@ -1584,11 +1817,15 @@ class Sale(metaclass=PoolMeta):
|
||||
getattr(lot, 'lot_qt', None))
|
||||
if getattr(lot, 'lot_p', None) or getattr(lot, 'line', None):
|
||||
matched += lot_quantity
|
||||
shipment = (
|
||||
getattr(lot, 'lot_shipment_in', None)
|
||||
or getattr(lot, 'lot_shipment_internal', None)
|
||||
or getattr(lot, 'lot_shipment_out', None))
|
||||
purchase_line = getattr(lot, 'line', None)
|
||||
if getattr(lot, 'lot_p', None):
|
||||
purchase_line = getattr(lot.lot_p, 'line', None)
|
||||
purchase = getattr(purchase_line, 'purchase', None)
|
||||
if purchase:
|
||||
purchases.append(purchase)
|
||||
shipment_model, shipment = self._summary_shipment_record(lot)
|
||||
if shipment:
|
||||
shipments.append((shipment_model, shipment))
|
||||
shipped += lot_quantity
|
||||
route_from = getattr(
|
||||
getattr(shipment, 'from_location', None),
|
||||
@@ -1608,6 +1845,17 @@ class Sale(metaclass=PoolMeta):
|
||||
|
||||
unit = getattr(getattr(line, 'unit', None), 'rec_name', '') or ''
|
||||
product = getattr(getattr(line, 'product', None), 'rec_name', '') or ''
|
||||
invoices = [
|
||||
invoice_line.invoice
|
||||
for invoice_line in (getattr(line, 'invoice_lines', None) or [])
|
||||
if (getattr(invoice_line, 'invoice', None)
|
||||
and getattr(invoice_line.invoice, 'state', None) != 'cancelled')
|
||||
]
|
||||
amount = self._line_amount_value(line)
|
||||
line_pnl, line_mtm, previous_pnl = self._line_pnl_values(
|
||||
self, line)
|
||||
line_pnl_change = self._pnl_change_percent(
|
||||
line_pnl, previous_pnl)
|
||||
route = (shipped_routes[0] if shipped_routes else '%s > %s' % (
|
||||
getattr(getattr(self, 'from_location', None), 'rec_name', None)
|
||||
or '-',
|
||||
@@ -1615,29 +1863,147 @@ class Sale(metaclass=PoolMeta):
|
||||
or '-'))
|
||||
|
||||
rows.append({
|
||||
'index': index,
|
||||
'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),
|
||||
'pnl_change_d1': line_pnl_change,
|
||||
'amount': self._json_decimal(amount),
|
||||
'mtm': self._json_decimal(line_mtm),
|
||||
'open': max(quantity - shipped, 0.0),
|
||||
'unit': unit,
|
||||
'lots': lots_count,
|
||||
'route': route,
|
||||
'sales': self._summary_link('sale.sale', [self]),
|
||||
'purchases': self._summary_link('purchase.purchase', purchases),
|
||||
'shipments': self._summary_mixed_link(
|
||||
shipments, default_model='stock.shipment.out'),
|
||||
'invoices': self._summary_link('account.invoice', invoices),
|
||||
})
|
||||
totals['quantity'] += quantity
|
||||
totals['matched'] += matched
|
||||
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)
|
||||
|
||||
data = {
|
||||
'type': 'execution-summary',
|
||||
return {
|
||||
'unit': rows[0]['unit'] if rows else '',
|
||||
'totals': dict(totals),
|
||||
'rows': rows[:4],
|
||||
'rows': rows[:3],
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _json_decimal(value):
|
||||
if value in (None, ''):
|
||||
return None
|
||||
return float(value)
|
||||
|
||||
def _get_sale_pnl_summary_data(self):
|
||||
currency = getattr(getattr(self, 'currency', None), 'code', None) or ''
|
||||
pnl_value = Decimal('0')
|
||||
mtm_value = Decimal('0')
|
||||
records = (
|
||||
getattr(self, 'pnl', None)
|
||||
if getattr(self, 'group_pnl', False)
|
||||
else getattr(self, 'pnl_', None))
|
||||
for line in (records or []):
|
||||
pnl_value += self._summary_decimal(
|
||||
getattr(line, 'r_pnl', None)
|
||||
if hasattr(line, 'r_pnl') else getattr(line, 'pnl', None))
|
||||
mtm_value += self._summary_decimal(
|
||||
getattr(line, 'r_mtm', None)
|
||||
if hasattr(line, 'r_mtm') else getattr(line, 'mtm', None))
|
||||
data = {
|
||||
"pnl": {
|
||||
"value": self._json_decimal(pnl_value),
|
||||
"currency": currency,
|
||||
"change_d1": self._sale_pnl_change_percent(),
|
||||
},
|
||||
"mtm": {
|
||||
"value": self._json_decimal(mtm_value),
|
||||
"currency": currency,
|
||||
"change": None,
|
||||
},
|
||||
}
|
||||
return data
|
||||
|
||||
def _get_sale_activity_summary_data(self):
|
||||
if not getattr(self, 'id', None):
|
||||
return []
|
||||
Log = Pool().get('ir.model.log')
|
||||
logs = Log.search([
|
||||
('resource', '=', 'sale.sale,%s' % self.id),
|
||||
('event', '!=', 'create'),
|
||||
], limit=8)
|
||||
event_types = {
|
||||
'write': 'info',
|
||||
'transition': 'approved',
|
||||
'button': 'success',
|
||||
'wizard': 'info',
|
||||
'delete': 'warning',
|
||||
}
|
||||
activity = []
|
||||
for log in logs:
|
||||
if len(activity) >= 4:
|
||||
break
|
||||
date = getattr(log, 'create_date', None)
|
||||
user = getattr(getattr(log, 'user', None), 'rec_name', None) or ''
|
||||
text = getattr(log, 'change_summary', None)
|
||||
text = text or getattr(log, 'action', None) or getattr(
|
||||
log, 'target', None)
|
||||
if not text:
|
||||
text = getattr(log, 'event_string', None) or log.event
|
||||
if ' create' in text or 'create,' in text or text.startswith('Create'):
|
||||
continue
|
||||
activity.append({
|
||||
"type": event_types.get(log.event, 'info'),
|
||||
"text": text,
|
||||
"date": date.strftime('%d/%m/%Y') if date else '',
|
||||
"user": user,
|
||||
})
|
||||
return activity
|
||||
|
||||
def get_execution_summary_viewer(self, name=None):
|
||||
data = self._get_sale_execution_summary_data()
|
||||
data = dict(data, type='execution-summary')
|
||||
return "d3:" + json.dumps(data)
|
||||
|
||||
def get_sale_summary_data(self, name=None):
|
||||
execution = self._get_sale_execution_summary_data()
|
||||
data = {
|
||||
"currency": getattr(getattr(self, 'currency', None), 'code', None)
|
||||
or '',
|
||||
"map": self._get_sale_map_summary_data(),
|
||||
"execution": execution,
|
||||
"amount": {
|
||||
"value": execution.get('totals', {}).get('amount', 0),
|
||||
},
|
||||
"activity": self._get_sale_activity_summary_data(),
|
||||
"approval": {
|
||||
"status": "need_approval",
|
||||
},
|
||||
}
|
||||
data.update(self._get_sale_pnl_summary_data())
|
||||
return json.dumps(data)
|
||||
|
||||
@fields.depends(
|
||||
'currency', 'from_location', 'to_location', 'lines', 'pnl', 'pnl_',
|
||||
'group_pnl')
|
||||
def on_change_with_sale_summary_data(self, name=None):
|
||||
return self.get_sale_summary_data()
|
||||
|
||||
def getLots(self):
|
||||
if self.lines:
|
||||
if self.lines.lots:
|
||||
|
||||
@@ -103,26 +103,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="lines" colspan="4" mode="form,tree"
|
||||
line_chips="quantity,del_period,product"
|
||||
line_chips_label="{quantity}mt {del_period} {product}"/>
|
||||
<notebook colspan="6">
|
||||
<page string="State" id="state">
|
||||
<group col="2" colspan="2" id="states" yfill="1">
|
||||
<label name="invoice_state"/>
|
||||
<field name="invoice_state"/>
|
||||
<label name="shipment_state"/>
|
||||
<field name="shipment_state"/>
|
||||
<label name="state"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
<group col="2" colspan="2" id="amount" yfill="1">
|
||||
<label name="untaxed_amount" xalign="1.0" xexpand="1"/>
|
||||
<field name="untaxed_amount" xalign="1.0" xexpand="0"/>
|
||||
<label name="tax_amount" xalign="1.0" xexpand="1"/>
|
||||
<field name="tax_amount" xalign="1.0" xexpand="0"/>
|
||||
<label name="total_amount" xalign="1.0" xexpand="1"/>
|
||||
<field name="total_amount" xalign="1.0" xexpand="0"/>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='purchase']" position="after">
|
||||
|
||||
@@ -3,10 +3,21 @@
|
||||
this repository contains the full copyright notices and license terms. -->
|
||||
<data>
|
||||
<xpath expr="/form/group[@id='hd'][2]" position="replace"/>
|
||||
<xpath expr="/form/group[@id='buttons']" position="replace"/>
|
||||
<xpath expr="/form/field[@name='party_lang']" position="before">
|
||||
<field name="allow_modification_after_validation" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/group[@id='hd'][1]" position="replace">
|
||||
<group id="sale_header_visuals" colspan="6" col="4"
|
||||
string="Sale summary" icon="tryton-dashboard"
|
||||
panel="card" xalign="0" yalign="0"
|
||||
col_widths="1fr,1fr,1fr,1fr">
|
||||
<field name="sale_summary_data"
|
||||
widget="purchase_summary"
|
||||
colspan="4"
|
||||
height="168"/>
|
||||
</group>
|
||||
|
||||
<group id="sale_header_summary" colspan="6" col="10"
|
||||
panel="summary" xalign="0" yalign="0"
|
||||
col_widths="min-content,2fr,min-content,130px,min-content,1.2fr,min-content,130px,min-content,1fr">
|
||||
@@ -18,100 +29,82 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="reference"/>
|
||||
<label name="sale_date"/>
|
||||
<field name="sale_date"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
<label name="trader"/>
|
||||
<field name="trader"/>
|
||||
<label name="operator"/>
|
||||
<field name="operator"/>
|
||||
<label name="our_reference"/>
|
||||
<field name="our_reference"/>
|
||||
<label name="broker"/>
|
||||
<field name="broker"/>
|
||||
<group id="sale_header_buttons" col="-1" colspan="4"
|
||||
xalign="1" yalign="0">
|
||||
<button name="cancel" icon="tryton-cancel"/>
|
||||
<button name="modify_header" icon="tryton-launch"/>
|
||||
<button name="draft"/>
|
||||
<button name="quote" icon="tryton-forward"/>
|
||||
<button name="handle_invoice_exception" icon="tryton-forward"/>
|
||||
<button name="handle_shipment_exception" icon="tryton-forward"/>
|
||||
<button name="confirm" icon="tryton-forward"/>
|
||||
<button name="process" invisible="1"/>
|
||||
<button name="manual_invoice" icon="tryton-forward"/>
|
||||
<button name="manual_shipment" icon="tryton-forward"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group id="sale_header_left" colspan="4" col="1"
|
||||
xalign="0" yalign="0" col_widths="1fr">
|
||||
<group id="sale_commercial_terms" string="Commercial Terms"
|
||||
colspan="1" col="1" panel="card" icon="tryton-public"
|
||||
<field name="tolerance_min" invisible="1"/>
|
||||
<field name="tolerance_max" invisible="1"/>
|
||||
</xpath>
|
||||
|
||||
<xpath expr="/form/notebook/page[@id='sale']" position="replace">
|
||||
<page string="Header" id="sale">
|
||||
<group id="sale_main_terms" string="Main terms"
|
||||
colspan="6" col="1" panel="card" icon="tryton-public"
|
||||
xalign="0" yalign="0"
|
||||
col_widths="1fr">
|
||||
<group id="sale_commercial_terms_top"
|
||||
colspan="1" col="6" xalign="0" yalign="0"
|
||||
col_widths="min-content,1fr,min-content,1fr,min-content,130px">
|
||||
<label name="payment_term"/>
|
||||
<field name="payment_term"/>
|
||||
<label name="wb"/>
|
||||
<field name="wb"/>
|
||||
<label name="lc_date"/>
|
||||
<field name="lc_date"/>
|
||||
</group>
|
||||
<group id="sale_commercial_terms_incoterm"
|
||||
colspan="1" col="4" xalign="0" yalign="0"
|
||||
col_widths="min-content,1fr,min-content,1fr">
|
||||
<group id="sale_main_terms_route"
|
||||
colspan="1" col="8" xalign="0" yalign="0"
|
||||
col_widths="min-content,1fr,min-content,1fr,min-content,1fr,min-content,1fr">
|
||||
<label name="incoterm"/>
|
||||
<field name="incoterm"/>
|
||||
<label name="incoterm_location"/>
|
||||
<field name="incoterm_location"/>
|
||||
<label name="from_location"/>
|
||||
<field name="from_location"/>
|
||||
<label name="to_location"/>
|
||||
<field name="to_location"/>
|
||||
</group>
|
||||
<group id="sale_commercial_terms_origin"
|
||||
colspan="1" col="4" xalign="0" yalign="0"
|
||||
col_widths="min-content,1fr,min-content,1fr">
|
||||
<label name="product_origin"/>
|
||||
<field name="product_origin"/>
|
||||
<group id="sale_main_terms_payment"
|
||||
colspan="1" col="8" xalign="0" yalign="0"
|
||||
col_widths="min-content,1fr,min-content,1fr,min-content,1fr,min-content,1fr">
|
||||
<label name="payment_term"/>
|
||||
<field name="payment_term"/>
|
||||
<label name="currency"/>
|
||||
<field name="currency"/>
|
||||
<label name="lc_date"/>
|
||||
<field name="lc_date"/>
|
||||
<label name="association"/>
|
||||
<field name="association"/>
|
||||
</group>
|
||||
<group id="sale_main_terms_tolerance"
|
||||
colspan="1" col="8" xalign="0" yalign="0"
|
||||
col_widths="min-content,1.4fr,min-content,90px,min-content,90px,min-content,2fr">
|
||||
<label name="tolerance_option"/>
|
||||
<field name="tolerance_option"/>
|
||||
<label name="certif"/>
|
||||
<field name="certif"/>
|
||||
<label name="tol_min"/>
|
||||
<field name="tol_min"/>
|
||||
<label name="tol_max"/>
|
||||
<field name="tol_max"/>
|
||||
<label name="tolerance_used"/>
|
||||
<field name="tolerance_used" widget="tolerance_gauge"
|
||||
min_field="tolerance_min" max_field="tolerance_max"
|
||||
width="280" xexpand="1" xfill="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<group id="sale_execution" string="People & Execution"
|
||||
colspan="1" col="4" panel="card" icon="tryton-link"
|
||||
xalign="0" yalign="0"
|
||||
col_widths="min-content,1fr,min-content,1fr">
|
||||
<label name="broker"/>
|
||||
<field name="broker"/>
|
||||
<label name="operator"/>
|
||||
<field name="operator"/>
|
||||
<label name="trader"/>
|
||||
<field name="trader"/>
|
||||
<label name="our_reference"/>
|
||||
<field name="our_reference"/>
|
||||
<label name="from_location"/>
|
||||
<field name="from_location"/>
|
||||
<label name="to_location"/>
|
||||
<field name="to_location"/>
|
||||
<label name="association"/>
|
||||
<field name="association"/>
|
||||
<label name="crop"/>
|
||||
<field name="crop"/>
|
||||
</group>
|
||||
|
||||
<group id="sale_tolerance" string="Tolerance"
|
||||
colspan="1" col="6" panel="card" icon="tryton-switch"
|
||||
xalign="0" yalign="0"
|
||||
col_widths="min-content,1fr,min-content,1fr,min-content,200px">
|
||||
<label name="tol_min"/>
|
||||
<field name="tol_min"/>
|
||||
<label name="tol_max"/>
|
||||
<field name="tol_max"/>
|
||||
<label name="tolerance_used"/>
|
||||
<field name="tolerance_used" widget="tolerance_gauge"
|
||||
min_field="tolerance_min" max_field="tolerance_max"
|
||||
width="190" xexpand="0" xfill="0"/>
|
||||
</group>
|
||||
<field name="tolerance_min" invisible="1"/>
|
||||
<field name="tolerance_max" invisible="1"/>
|
||||
</group>
|
||||
|
||||
<group id="sale_header_right" colspan="2" col="1"
|
||||
xalign="0" yalign="0" col_widths="1fr">
|
||||
<group id="sale_map" string="Map" colspan="1" col="1"
|
||||
panel="card" icon="tryton-public" xalign="0" yalign="0"
|
||||
col_widths="1fr">
|
||||
<field name="viewer" widget="html_viewer" height="220"/>
|
||||
</group>
|
||||
<group id="sale_recap" string="Execution summary"
|
||||
colspan="1" col="1" panel="sidebar" icon="tryton-note"
|
||||
xalign="0" yalign="0"
|
||||
col_widths="1fr">
|
||||
<field name="execution_summary_viewer" widget="html_viewer"
|
||||
height="142"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="lines" colspan="4" mode="form,tree"
|
||||
line_chips="quantity,del_period,product"
|
||||
line_chips_label="{quantity}mt {del_period} {product}"/>
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='sale']" position="after">
|
||||
<page string="Contract Clauses" col="4" id="contract_clauses">
|
||||
@@ -153,24 +146,6 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<field name="pnl" mode="tree" view_ids="purchase_trade.valuation_view_tree_sequence4" height="450" colspan="4"/>
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='sale']/notebook/page[@id='line']/field[@name='payment_term']" position="after">
|
||||
<label name="from_location"/>
|
||||
<field name="from_location"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='sale']/notebook/page[@id='line']/field[@name='warehouse']" position="replace">
|
||||
<field name="to_location"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='sale']/notebook/page[@id='line']/label[@name='warehouse']" position="replace">
|
||||
<label name="to_location"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='sale']/notebook/page[@id='line']/label[@name='currency']" position="before">
|
||||
<label name="incoterm"/>
|
||||
<field name="incoterm"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='sale']/notebook/page[@id='line']/label[@name='currency']" position="before">
|
||||
<label name="incoterm_location"/>
|
||||
<field name="incoterm_location"/>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@id='other']/label[@name='company']" position="before">
|
||||
<field name="bank_accounts" colspan="4" invisible="1"/>
|
||||
<label name="bank_account"/>
|
||||
|
||||
Reference in New Issue
Block a user