Compare commits

...

24 Commits

Author SHA1 Message Date
AzureAD\SylvainDUVERNAY
8fb6d681a0 Import Prices 2026-05-06 14:47:57 +02:00
AzureAD\SylvainDUVERNAY
17934fe210 Increase import prices result height 2026-05-06 14:31:48 +02:00
AzureAD\SylvainDUVERNAY
ef54b5d94b Import Prices 2026-05-06 14:21:51 +02:00
AzureAD\SylvainDUVERNAY
3a0ea871c2 ITSA - Import Prices 2026-05-05 16:43:29 +02:00
AzureAD\SylvainDUVERNAY
1c8abc7c1e Trade Finance 2026-04-12 17:22:25 +02:00
AzureAD\SylvainDUVERNAY
37bf6ba23b Trade Finance 2026-04-12 17:15:08 +02:00
AzureAD\SylvainDUVERNAY
2e649aa61b Commit 2026-04-12 17:06:26 +02:00
AzureAD\SylvainDUVERNAY
735a72d23e Trade Finance 2026-04-12 16:58:45 +02:00
AzureAD\SylvainDUVERNAY
ebf9b6c495 Trade Finance 2026-04-12 16:47:59 +02:00
AzureAD\SylvainDUVERNAY
13d26ac41b Trade Finance 2026-04-12 16:36:08 +02:00
AzureAD\SylvainDUVERNAY
b829b11791 Trade Finance - Commit 2026-04-12 14:41:12 +02:00
AzureAD\SylvainDUVERNAY
4a056ef402 Change on Facility definition 2026-04-07 21:53:10 +02:00
AzureAD\SylvainDUVERNAY
da65da79c0 Trade Finance - Facility - Limit ordering 2026-03-31 22:06:58 +02:00
AzureAD\SylvainDUVERNAY
aa6b3fb9ad Trade Finance - TF Facility - Order limits 2026-03-31 21:55:34 +02:00
AzureAD\SylvainDUVERNAY
b1dd118628 Trade Finance - Facility - Improve Limits table layout 2026-03-31 21:39:56 +02:00
AzureAD\SylvainDUVERNAY
f1f9d157cc Trade Finance - Facility - Fix error on Save 2026-03-31 16:50:45 +02:00
AzureAD\SylvainDUVERNAY
283b71fda9 Trade Finance - Sub Limit 2026-03-31 14:14:05 +02:00
AzureAD\SylvainDUVERNAY
f27dd5620e Trade Finance - Adjustment on sub-limit 2026-03-31 14:10:43 +02:00
AzureAD\SylvainDUVERNAY
143f59c62e Trade Finance - Facility Fix 3 2026-03-31 13:37:42 +02:00
AzureAD\SylvainDUVERNAY
be6b6517a5 Trade Finance - Facility Fix 2 2026-03-31 13:34:31 +02:00
AzureAD\SylvainDUVERNAY
d96973310b Trade Finance Facility - Fix 1 2026-03-31 13:31:33 +02:00
AzureAD\SylvainDUVERNAY
39278c4483 Trade Finance - Facility 2026-03-31 13:22:18 +02:00
AzureAD\SylvainDUVERNAY
4534ad86f1 Trade Finance - Fix 2026-03-31 12:24:17 +02:00
AzureAD\SylvainDUVERNAY
10e8e5be9b Trade Finance - Initial Commit 2026-03-31 12:14:51 +02:00
83 changed files with 3875 additions and 38 deletions

View File

@@ -0,0 +1,17 @@
{
"permissions": {
"allow": [
"Bash(cd /c/Users/SylvainDUVERNAY/Documents/Visual Studio Code/Tradon DEV/tradon/modules)",
"Bash(ls -d */)",
"Bash(cd /c/Users/SylvainDUVERNAY/Documents/Visual Studio Code/Tradon DEV/tradon/modules/purchase_trade)",
"Bash(ls -1d */)",
"Bash(for f:*)",
"Bash(do echo:*)",
"Read(//c/Users/SylvainDUVERNAY/Documents/Visual Studio Code/Tradon DEV/tradon/**)",
"Bash(done)",
"Bash(cd /c/Users/SylvainDUVERNAY/Documents/Visual Studio Code/Tradon DEV/tradon/modules/purchase_trade/view)",
"Bash(ls -1 *.xml)",
"Bash(py --version)"
]
}
}

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
deployment/vps-TradonDev_Instructions.md
deployment/vps/46.202.173.47-credentials.md

5
deployment/README.md Normal file
View File

@@ -0,0 +1,5 @@
# Deployment Notes
- Runbook onboarding SSH: `deployment/runbooks/vps-onboarding.md`
- VPS 46.202.173.47 credentials: `deployment/vps/46.202.173.47-credentials.md`
- VPS 46.202.173.47 quickstart: `deployment/vps/46.202.173.47-quickstart.md`

View File

@@ -0,0 +1,57 @@
# Procedure - Ajouter un nouveau VPS (SSH)
Date de reference: 2026-04-07
## 1) Preparation locale (Windows)
1. Creer le dossier SSH local si absent:
`New-Item -ItemType Directory -Force $env:USERPROFILE\.ssh`
2. Generer une cle dediee VPS:
`ssh-keygen -t ed25519 -C "vps-deploy" -f $env:USERPROFILE\.ssh\vps_deploy_key`
3. Lire la cle publique:
`Get-Content $env:USERPROFILE\.ssh\vps_deploy_key.pub`
## 2) Installer la cle sur le VPS
1. Se connecter au VPS avec mot de passe (premiere fois):
`ssh <user>@<ip_vps>`
2. Sur le VPS, preparer le dossier SSH:
`mkdir -p ~/.ssh`
`chmod 700 ~/.ssh`
3. Ajouter la cle publique (une ligne complete):
`echo "ssh-ed25519 ... vps-deploy" >> ~/.ssh/authorized_keys`
`chmod 600 ~/.ssh/authorized_keys`
## 3) Tester la connexion par cle
Depuis Windows:
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@<ip_vps>`
## 4) Test operationnel minimal
Creer un dossier distant:
`ssh -i $env:USERPROFILE\.ssh\vps_deploy_key <user>@<ip_vps> "mkdir -p ~/test_codex_deploy && ls -ld ~/test_codex_deploy"`
## 5) Durcissement recommande
- Desactiver l'authentification par mot de passe apres validation de la cle.
- Utiliser une cle dediee par environnement (dev/staging/prod).
- Documenter user + IP + chemin de cle dans une fiche VPS separee.
## 6) Note importante - Contrainte sandbox Codex
- Si une commande SSH/SCP echoue avec un message proche de:
- `Identity file ... not accessible: Permission denied`
- Cause probable:
- la session est en sandbox et ne peut pas lire la cle locale dans
`C:\Users\<user>\.ssh\...`.
- Action:
- relancer la commande en mode `require_escalated` (hors sandbox) pour
autoriser l'acces a la cle locale.
- Exemple observe le 2026-04-07:
- creation du dossier `/root/test` sur `46.202.173.47` reussie uniquement
apres escalation.

View File

@@ -0,0 +1,39 @@
# Commandes Rapides - VPS 46.202.173.47
Date de reference: 2026-04-07
## Cles SSH par utilisateur
| Utilisateur | Fichier cle locale |
|---|---|
| Laurent Barontini | `$env:USERPROFILE\.ssh\vps_deploy_key` |
| Sylvain Duvernay | `$env:USERPROFILE\.ssh\id_ed25519` |
> Les commandes ci-dessous utilisent `id_ed25519` (Sylvain Duvernay).
## 1) Test SSH
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "echo ok"`
## 2) Creer un dossier test distant
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "mkdir -p ~/test_codex_deploy && ls -ld ~/test_codex_deploy"`
## 3) Lister home distant
`ssh -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47 "ls -la ~"`
## 4) Copier un fichier local vers le VPS
`scp -i $env:USERPROFILE\.ssh\id_ed25519 .\local.txt <user>@46.202.173.47:~/local.txt`
## 5) Recuperer un fichier du VPS
`scp -i $env:USERPROFILE\.ssh\id_ed25519 <user>@46.202.173.47:~/remote.txt .\remote.txt`
## 6) Depannage sandbox (Codex)
- Symptome:
- `Identity file ... not accessible: Permission denied`
- Correctif:
- relancer la commande SSH/SCP en mode escalade (`require_escalated`).

View File

@@ -0,0 +1,3 @@
Serveur: 'VPS-62.72.36.116'
Alias Name: 'VPS DEV'
IP Address:'62.72.36.116'

View File

@@ -55,15 +55,18 @@ def register():
lc.LCMT700,
lc.LCMessage,
lc.CreateLCStart,
global_reporting.GRConfiguration,
module='purchase_trade', type_='model')
global_reporting.GRConfiguration,
pricing.ImportPricesStart,
pricing.ImportPricesResult,
module='purchase_trade', type_='model')
Pool.register(
incoming.ImportSwift,
incoming.PrepareDocuments,
incoming.AnalyzeConditions,
lc.CreateLCWizard,
module='purchase_trade', type_='wizard'
)
incoming.ImportSwift,
incoming.PrepareDocuments,
incoming.AnalyzeConditions,
lc.CreateLCWizard,
pricing.ImportPrices,
module='purchase_trade', type_='wizard'
)
Pool.register(
credit_risk.Party,
credit_risk.CreditRiskRule,

View File

@@ -155,3 +155,21 @@ Pour cette regle, couvrir au minimum:
- augmentation sans `lot.qt` ouvert
- diminution possible
- diminution impossible avec erreur
### BR-PT-004 - Market Price Import
- Goal: import market prices from an Excel file
- Description: after having selected an Excel file with the following columns:
- price_index: name of the market
- price_date: date of the price
- high price: high price
- low price: low price
- open price: open price
- price value: price value
- Expected behavior:
- Check if price_index exists prior to impport related prices
- If price_index does not exist, check if option "Create price index is missing" has been checked in the input screen. If checked then try to create first the price index (table: price_price). If option is not checked, then ignore the price_index and mark it as "skipped" in the output results of the import.
- Check if price_date for the price_index already exists.
- If price_date does not exist, then import the price. If price_date already exists then check if option "Overwrite existing price" is checked in the input screen. If checked, update the price for price_index and price_date. If option is not checked, then ignore the price_date and mark as "skipped" in the output.

View File

@@ -1,9 +1,10 @@
# This file is part of Tryton. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import fields
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval, Id
from trytond.model import (ModelSQL, ModelView)
from trytond.model import fields
from trytond.exceptions import UserError
from trytond.pool import Pool, PoolMeta
from trytond.pyson import Bool, Eval, Id
from trytond.model import (ModelSQL, ModelView)
from trytond.tools import is_full_text, lstrip_wildcard
from trytond.transaction import Transaction, inactive_records
from decimal import getcontext, Decimal, ROUND_HALF_UP
@@ -11,12 +12,17 @@ from sql.aggregate import Count, Max, Min, Sum, Avg, BoolOr
from sql.conditionals import Case
from sql import Column, Literal
from sql.functions import CurrentTimestamp, DateTrunc
from trytond.wizard import Button, StateTransition, StateView, Wizard
from itertools import chain, groupby
from operator import itemgetter
import datetime
import logging
from trytond.modules.purchase_trade.purchase import (TRIGGERS)
from trytond.wizard import Button, StateTransition, StateView, Wizard
from itertools import chain, groupby
from operator import itemgetter
import calendar
import datetime
import logging
import re
import zipfile
from io import BytesIO
from xml.etree import ElementTree
from trytond.modules.purchase_trade.purchase import (TRIGGERS)
logger = logging.getLogger(__name__)
@@ -50,13 +56,423 @@ DAYS = [
('sunday', 'Sunday'),
]
class Estimated(ModelSQL, ModelView):
"Estimated date"
__name__ = 'pricing.estimated'
class Estimated(ModelSQL, ModelView):
"Estimated date"
__name__ = 'pricing.estimated'
trigger = fields.Selection(TRIGGERS,"Trigger")
estimated_date = fields.Date("Estimated date")
fin_int_delta = fields.Integer("Financing interests delta")
estimated_date = fields.Date("Estimated date")
fin_int_delta = fields.Integer("Financing interests delta")
class ImportPricesStart(ModelView):
"Import Prices"
__name__ = 'purchase_trade.import_prices.start'
file_ = fields.Binary('Excel file', required=True, filename='filename')
filename = fields.Char('Filename')
create_missing_price_index = fields.Boolean(
"Create price index if missing")
overwrite_existing_price = fields.Boolean("Overwrite existing price")
class ImportPricesResult(ModelView):
"Import Prices Result"
__name__ = 'purchase_trade.import_prices.result'
message = fields.Text("Results", readonly=True)
class ImportPrices(Wizard):
"Import Prices"
__name__ = 'purchase_trade.import_prices'
start = StateView(
'purchase_trade.import_prices.start',
'purchase_trade.import_prices_start_view_form',
[
Button('Cancel', 'end', 'tryton-cancel'),
Button('Import', 'import_', 'tryton-ok', default=True),
])
import_ = StateTransition()
result = StateView(
'purchase_trade.import_prices.result',
'purchase_trade.import_prices_result_view_form',
[
Button('OK', 'end', 'tryton-ok', default=True),
])
REQUIRED_COLUMNS = {
'priceindex': 'price_index',
'pricedate': 'price_date',
'highprice': 'high_price',
'lowprice': 'low_price',
'openprice': 'open_price',
'pricevalue': 'price_value',
}
def transition_import_(self):
rows = self._read_xlsx(self.start.file_)
stats = self._import_rows(
rows,
create_missing_price_index=(
self.start.create_missing_price_index),
overwrite_existing_price=self.start.overwrite_existing_price)
self._result_message = self._format_result(stats)
return 'result'
def default_result(self, fields):
return {
'message': getattr(
self, '_result_message',
'No import result was produced.'),
}
@classmethod
def _import_rows(
cls, rows, create_missing_price_index=False,
overwrite_existing_price=False):
Price = Pool().get('price.price')
PriceValue = Pool().get('price.price_value')
stats = {
'created_indexes': [],
'imported': [],
'updated': [],
'skipped': [],
'errors': [],
}
for row_number, row in enumerate(rows, start=2):
price_index = (row.get('price_index') or '').strip()
try:
price_date = cls._as_date(row.get('price_date'))
if not price_index:
stats['skipped'].append(
cls._result_line(
row_number, '', None, 'missing price_index'))
continue
if not price_date:
stats['skipped'].append(
cls._result_line(
row_number, price_index, None,
'missing price_date'))
continue
prices = Price.search(
[('price_index', '=', price_index)], limit=1)
if prices:
price = prices[0]
elif create_missing_price_index:
price, = Price.create([
cls._price_index_values(price_index)])
stats['created_indexes'].append(
cls._result_line(
row_number, price_index, None,
'price index created'))
else:
stats['skipped'].append(
cls._result_line(
row_number, price_index, None,
'price_index missing'))
continue
values = cls._price_value_values(price, row, price_date)
existing = PriceValue.search([
('price', '=', price.id),
('price_date', '=', price_date),
], limit=1)
if existing:
if overwrite_existing_price:
PriceValue.write(existing, values)
stats['updated'].append(
cls._result_line(
row_number, price_index, price_date,
cls._price_summary(values)))
else:
stats['skipped'].append(cls._result_line(
row_number, price_index, price_date,
'price_date already exists'))
continue
PriceValue.create([values])
stats['imported'].append(
cls._result_line(
row_number, price_index, price_date,
cls._price_summary(values)))
except Exception as exception:
stats['errors'].append(
cls._result_line(
row_number, price_index, row.get('price_date'),
str(exception)))
continue
return stats
@classmethod
def _price_index_values(cls, price_index):
values = {
'price_index': price_index,
'price_desc': price_index,
'price_curve_type': 'future',
}
references = [
('price_type', 'price.fixtype', [('name', '=', 'Market price')]),
('price_currency', 'currency.currency', [('code', '=', 'USD')]),
('price_calendar', 'price.calendar', [('name', '=', 'Argus EU')]),
('price_unit', 'product.uom', [('name', '=', 'Mt')]),
]
for field, model_name, domain in references:
record = cls._first_record(model_name, domain)
if record:
values[field] = record.id
period = cls._period_from_price_index(price_index)
if period:
values['price_period'] = period.id
return values
@classmethod
def _period_from_price_index(cls, price_index):
match = re.search(r'(?<!\d)(20\d{2})[-_/\. ](0[1-9]|1[0-2])(?!\d)',
price_index)
if not match:
return None
year = int(match.group(1))
month = int(match.group(2))
month_name = cls._period_month_name(year, month)
periods = cls._period_model().search(
[('month_name', '=', month_name)], limit=1)
if periods:
return periods[0]
beg_date = datetime.date(year, month, 1)
end_date = datetime.date(
year, month, calendar.monthrange(year, month)[1])
period, = cls._period_model().create([{
'month_name': month_name,
'description': month_name,
'beg_date': beg_date,
'end_date': end_date,
'is_cotation': True,
}])
return period
@staticmethod
def _period_month_name(year, month):
return '%s%s' % (
calendar.month_abbr[month].upper(), str(year)[-2:])
@staticmethod
def _period_model():
return Pool().get('product.month')
@staticmethod
def _first_record(model_name, domain):
records = Pool().get(model_name).search(domain, limit=1)
return records[0] if records else None
@classmethod
def _price_value_values(cls, price, row, price_date):
return {
'price': price.id,
'price_date': price_date,
'high_price': cls._as_float(row.get('high_price')),
'low_price': cls._as_float(row.get('low_price')),
'open_price': cls._as_float(row.get('open_price')),
'price_value': cls._as_float(row.get('price_value')),
}
@staticmethod
def _result_line(row_number, price_index, price_date, detail):
return {
'row': row_number,
'price_index': price_index,
'price_date': price_date,
'detail': detail,
}
@classmethod
def _format_result(cls, stats):
lines = [
'Import completed.',
f"Created price indexes: {len(stats['created_indexes'])}",
f"Imported prices: {len(stats['imported'])}",
f"Updated prices: {len(stats['updated'])}",
f"Skipped rows: {len(stats['skipped'])}",
f"Errors: {len(stats['errors'])}",
]
sections = [
('Created price indexes', stats['created_indexes']),
('Successfully imported prices', stats['imported']),
('Updated existing prices', stats['updated']),
('Skipped records', stats['skipped']),
('Errors', stats['errors']),
]
for title, items in sections:
lines.append('')
lines.append('%s:' % title)
if not items:
lines.append('- None')
continue
for item in items:
lines.append('- %s' % cls._format_result_line(item))
return '\n'.join(lines)
@staticmethod
def _format_result_line(item):
label = item['price_index'] or '<empty price_index>'
price_date = item['price_date']
if isinstance(price_date, datetime.date):
price_date = price_date.isoformat()
if price_date:
label = '%s / %s' % (label, price_date)
return 'Row %(row)s - ' % item + '%s: %s' % (label, item['detail'])
@staticmethod
def _price_summary(values):
parts = []
for name in ('price_value', 'open_price', 'low_price', 'high_price'):
value = values.get(name)
if value is not None:
parts.append('%s=%s' % (name, value))
return ', '.join(parts) or 'price imported'
@classmethod
def _read_xlsx(cls, data):
try:
with zipfile.ZipFile(BytesIO(data)) as workbook:
shared_strings = cls._read_shared_strings(workbook)
sheet_name = cls._first_sheet_name(workbook)
sheet = ElementTree.fromstring(workbook.read(sheet_name))
except (KeyError, zipfile.BadZipFile, ElementTree.ParseError):
raise UserError("The selected file is not a valid Excel .xlsx file.")
ns = {'s': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}
rows = sheet.findall('.//s:sheetData/s:row', ns)
if not rows:
return []
headers = {}
for cell in rows[0].findall('s:c', ns):
index = cls._cell_column_index(cell.get('r'))
header = cls._cell_value(cell, shared_strings)
normalized = cls._normalize_header(header)
if normalized in cls.REQUIRED_COLUMNS:
headers[index] = cls.REQUIRED_COLUMNS[normalized]
missing = set(cls.REQUIRED_COLUMNS.values()) - set(headers.values())
if missing:
raise UserError(
"Missing columns in Excel file: %s"
% ', '.join(sorted(missing)))
result = []
for sheet_row in rows[1:]:
values = {}
for cell in sheet_row.findall('s:c', ns):
index = cls._cell_column_index(cell.get('r'))
field = headers.get(index)
if field:
values[field] = cls._cell_value(cell, shared_strings)
if any(v not in (None, '') for v in values.values()):
result.append(values)
return result
@staticmethod
def _read_shared_strings(workbook):
try:
content = workbook.read('xl/sharedStrings.xml')
except KeyError:
return []
root = ElementTree.fromstring(content)
ns = {'s': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}
strings = []
for item in root.findall('s:si', ns):
strings.append(''.join(
text.text or '' for text in item.findall('.//s:t', ns)))
return strings
@staticmethod
def _first_sheet_name(workbook):
workbook_xml = ElementTree.fromstring(workbook.read('xl/workbook.xml'))
rels_xml = ElementTree.fromstring(
workbook.read('xl/_rels/workbook.xml.rels'))
wb_ns = {
's': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main',
'r': (
'http://schemas.openxmlformats.org/officeDocument/2006/'
'relationships'),
}
rel_ns = {
'r': (
'http://schemas.openxmlformats.org/package/2006/'
'relationships'),
}
sheet = workbook_xml.find('.//s:sheet', wb_ns)
rel_id = sheet.get(
'{http://schemas.openxmlformats.org/officeDocument/2006/'
'relationships}id')
for rel in rels_xml.findall('r:Relationship', rel_ns):
if rel.get('Id') == rel_id:
target = rel.get('Target')
if not target.startswith('/'):
target = 'xl/' + target
return target.lstrip('/')
raise KeyError('No worksheet found')
@staticmethod
def _cell_column_index(reference):
match = re.match(r'([A-Z]+)', reference or '')
if not match:
return None
index = 0
for char in match.group(1):
index = index * 26 + ord(char) - ord('A') + 1
return index
@classmethod
def _cell_value(cls, cell, shared_strings):
ns = {'s': 'http://schemas.openxmlformats.org/spreadsheetml/2006/main'}
if cell.get('t') == 'inlineStr':
text = cell.find('.//s:t', ns)
return text.text if text is not None else ''
value = cell.find('s:v', ns)
if value is None:
return ''
if cell.get('t') == 's':
return shared_strings[int(value.text)]
return value.text
@staticmethod
def _normalize_header(value):
return re.sub(r'[^a-z0-9]', '', (value or '').strip().lower())
@staticmethod
def _as_float(value):
if value in (None, ''):
return None
return float(str(value).replace(',', '.'))
@staticmethod
def _as_date(value):
if isinstance(value, datetime.date):
return value
if value in (None, ''):
return None
text = str(value).strip()
if re.match(r'^\d+(\.\d+)?$', text):
return (
datetime.date(1899, 12, 30)
+ datetime.timedelta(days=int(float(text))))
for fmt in ('%Y-%m-%d', '%d/%m/%Y', '%m/%d/%Y'):
try:
return datetime.datetime.strptime(text, fmt).date()
except ValueError:
pass
raise UserError("Invalid price_date: %s" % text)
class MtmScenario(ModelSQL, ModelView):
"MtM Scenario"

View File

@@ -20,12 +20,29 @@ this repository contains the full copyright notices and license terms. -->
<field name="priority" eval="20"/>
<field name="name">summary_tree_sequence</field>
</record>
<record model="ir.ui.view" id="estimated_view_tree">
<field name="model">pricing.estimated</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">estimated_tree</field>
</record>
<record model="ir.ui.view" id="estimated_view_tree">
<field name="model">pricing.estimated</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">estimated_tree</field>
</record>
<record model="ir.ui.view" id="import_prices_start_view_form">
<field name="model">purchase_trade.import_prices.start</field>
<field name="type">form</field>
<field name="name">import_prices_start_form</field>
</record>
<record model="ir.ui.view" id="import_prices_result_view_form">
<field name="model">purchase_trade.import_prices.result</field>
<field name="type">form</field>
<field name="name">import_prices_result_form</field>
</record>
<record model="ir.action.wizard" id="act_import_prices">
<field name="name">Import Prices</field>
<field name="wiz_name">purchase_trade.import_prices</field>
<field name="window" eval="True"/>
</record>
<record model="ir.ui.view" id="component_view_tree">
<field name="model">pricing.component</field>
@@ -181,12 +198,17 @@ this repository contains the full copyright notices and license terms. -->
sequence="99"
id="menu_mtm"
icon="tradon-mtm" />
<menuitem
name="Strategy"
action="act_strategy_form"
parent="menu_mtm"
sequence="10"
id="menu_strategy" />
</data>
</tryton>
<menuitem
name="Strategy"
action="act_strategy_form"
parent="menu_mtm"
sequence="10"
id="menu_strategy" />
<menuitem
parent="price.menu_price"
sequence="85"
action="act_import_prices"
id="menu_import_prices"/>
</data>
</tryton>

View File

@@ -2,10 +2,13 @@
# this repository contains the full copyright notices and license terms.
from decimal import Decimal
from datetime import date
from types import SimpleNamespace
from unittest.mock import Mock, patch
from trytond.pool import Pool
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.modules.purchase_trade.pricing import ImportPrices
class PurchaseTradeTestCase(ModuleTestCase):
@@ -70,5 +73,314 @@ class PurchaseTradeTestCase(ModuleTestCase):
strategy.get_mtm(line, Decimal('10')),
Decimal('250.00'))
def test_import_prices_skips_missing_index_without_create_option(self):
'import prices skips rows when the price index is missing'
price_model = _FakePriceModel()
price_value_model = _FakePriceValueModel()
with patch('trytond.modules.purchase_trade.pricing.Pool') as pool:
pool.return_value.get.side_effect = {
'price.price': price_model,
'price.price_value': price_value_model,
}.__getitem__
stats = ImportPrices._import_rows([{
'price_index': 'LME Copper',
'price_date': date(2026, 3, 27),
'high_price': '100',
'low_price': '90',
'open_price': '95',
'price_value': '98',
}])
self.assertEqual(len(stats['imported']), 0)
self.assertEqual(len(stats['skipped']), 1)
self.assertEqual(stats['skipped'][0]['detail'], 'price_index missing')
def test_import_prices_creates_index_and_imports_new_price(self):
'import prices creates missing index when requested'
price_model = _FakePriceModel()
price_value_model = _FakePriceValueModel()
with patch('trytond.modules.purchase_trade.pricing.Pool') as pool:
pool.return_value.get.side_effect = {
'price.price': price_model,
'price.price_value': price_value_model,
'price.fixtype': _FakeRecordModel([
SimpleNamespace(id=10, name='Market price')]),
'currency.currency': _FakeRecordModel([
SimpleNamespace(id=20, code='USD')]),
'price.calendar': _FakeRecordModel([
SimpleNamespace(id=30, name='Argus EU')]),
'product.uom': _FakeRecordModel([
SimpleNamespace(id=40, name='Mt')]),
'product.month': _FakeRecordModel(),
}.__getitem__
stats = ImportPrices._import_rows([{
'price_index': 'LME Copper',
'price_date': date(2026, 3, 27),
'high_price': '100',
'low_price': '90',
'open_price': '95',
'price_value': '98',
}], create_missing_price_index=True)
self.assertEqual(len(stats['created_indexes']), 1)
self.assertEqual(len(stats['imported']), 1)
self.assertEqual(price_model.records[0].price_index, 'LME Copper')
self.assertEqual(price_value_model.records[0].price_value, 98.0)
def test_import_prices_defaults_created_price_index(self):
'import prices defaults newly created price index fields'
price_model = _FakePriceModel()
price_value_model = _FakePriceValueModel()
period_model = _FakeRecordModel()
with patch('trytond.modules.purchase_trade.pricing.Pool') as pool:
pool.return_value.get.side_effect = {
'price.price': price_model,
'price.price_value': price_value_model,
'price.fixtype': _FakeRecordModel([
SimpleNamespace(id=10, name='Market price')]),
'currency.currency': _FakeRecordModel([
SimpleNamespace(id=20, code='USD')]),
'price.calendar': _FakeRecordModel([
SimpleNamespace(id=30, name='Argus EU')]),
'product.uom': _FakeRecordModel([
SimpleNamespace(id=40, name='Mt')]),
'product.month': period_model,
}.__getitem__
ImportPrices._import_rows([{
'price_index': 'Argus Copper 2026-07',
'price_date': date(2026, 3, 27),
'price_value': '98',
}], create_missing_price_index=True)
price = price_model.records[0]
period = period_model.records[0]
self.assertEqual(price.price_type, 10)
self.assertEqual(price.price_currency, 20)
self.assertEqual(price.price_calendar, 30)
self.assertEqual(price.price_curve_type, 'future')
self.assertEqual(price.price_unit, 40)
self.assertEqual(price.price_period, period.id)
self.assertEqual(period.month_name, 'JUL26')
self.assertEqual(period.beg_date, date(2026, 7, 1))
self.assertEqual(period.end_date, date(2026, 7, 31))
self.assertTrue(period.is_cotation)
def test_import_prices_uses_existing_period_from_price_index(self):
'import prices reuses existing period parsed from the price index'
price_model = _FakePriceModel()
price_value_model = _FakePriceValueModel()
existing_period = SimpleNamespace(
id=55, month_name='APR25', description='APR25')
period_model = _FakeRecordModel([existing_period])
with patch('trytond.modules.purchase_trade.pricing.Pool') as pool:
pool.return_value.get.side_effect = {
'price.price': price_model,
'price.price_value': price_value_model,
'price.fixtype': _FakeRecordModel(),
'currency.currency': _FakeRecordModel(),
'price.calendar': _FakeRecordModel(),
'product.uom': _FakeRecordModel(),
'product.month': period_model,
}.__getitem__
ImportPrices._import_rows([{
'price_index': 'Argus Copper 2025-04',
'price_date': date(2026, 3, 27),
'price_value': '98',
}], create_missing_price_index=True)
self.assertEqual(price_model.records[0].price_period, 55)
self.assertEqual(period_model.records, [existing_period])
def test_import_prices_skips_existing_date_without_overwrite(self):
'import prices skips existing price dates unless overwrite is enabled'
price = SimpleNamespace(id=1, price_index='LME Copper')
price_model = _FakePriceModel([price])
price_value_model = _FakePriceValueModel([
SimpleNamespace(
id=1, price=1, price_date=date(2026, 3, 27),
price_value=97.0)])
with patch('trytond.modules.purchase_trade.pricing.Pool') as pool:
pool.return_value.get.side_effect = {
'price.price': price_model,
'price.price_value': price_value_model,
}.__getitem__
stats = ImportPrices._import_rows([{
'price_index': 'LME Copper',
'price_date': date(2026, 3, 27),
'price_value': '98',
}])
self.assertEqual(len(stats['updated']), 0)
self.assertEqual(price_value_model.records[0].price_value, 97.0)
self.assertEqual(
stats['skipped'][0]['detail'], 'price_date already exists')
def test_import_prices_overwrites_existing_date_when_requested(self):
'import prices updates existing price dates when requested'
price = SimpleNamespace(id=1, price_index='LME Copper')
price_model = _FakePriceModel([price])
price_value_model = _FakePriceValueModel([
SimpleNamespace(
id=1, price=1, price_date=date(2026, 3, 27),
price_value=97.0)])
with patch('trytond.modules.purchase_trade.pricing.Pool') as pool:
pool.return_value.get.side_effect = {
'price.price': price_model,
'price.price_value': price_value_model,
}.__getitem__
stats = ImportPrices._import_rows([{
'price_index': 'LME Copper',
'price_date': date(2026, 3, 27),
'price_value': '98',
}], overwrite_existing_price=True)
self.assertEqual(len(stats['updated']), 1)
self.assertEqual(price_value_model.records[0].price_value, 98.0)
def test_import_prices_collects_row_errors(self):
'import prices reports invalid row values without hiding the result'
price = SimpleNamespace(id=1, price_index='LME Copper')
price_model = _FakePriceModel([price])
price_value_model = _FakePriceValueModel()
with patch('trytond.modules.purchase_trade.pricing.Pool') as pool:
pool.return_value.get.side_effect = {
'price.price': price_model,
'price.price_value': price_value_model,
}.__getitem__
stats = ImportPrices._import_rows([{
'price_index': 'LME Copper',
'price_date': date(2026, 3, 27),
'price_value': 'not-a-number',
}])
self.assertEqual(len(stats['errors']), 1)
self.assertIn('not-a-number', stats['errors'][0]['detail'])
def test_import_prices_result_lists_all_outcome_sections(self):
'import prices formats detailed result sections'
message = ImportPrices._format_result({
'created_indexes': [{
'row': 2,
'price_index': 'LME Copper',
'price_date': None,
'detail': 'price index created',
}],
'imported': [{
'row': 2,
'price_index': 'LME Copper',
'price_date': date(2026, 3, 27),
'detail': 'price_value=98.0',
}],
'updated': [],
'skipped': [{
'row': 3,
'price_index': 'LME Copper',
'price_date': date(2026, 3, 27),
'detail': 'price_date already exists',
}],
'errors': [{
'row': 4,
'price_index': 'LME Zinc',
'price_date': 'bad-date',
'detail': 'Invalid price_date: bad-date',
}],
})
self.assertIn('Created price indexes:', message)
self.assertIn('Successfully imported prices:', message)
self.assertIn('Skipped records:', message)
self.assertIn('Errors:', message)
self.assertIn('Row 2 - LME Copper / 2026-03-27', message)
class _FakePriceModel:
def __init__(self, records=None):
self.records = records or []
def search(self, domain, limit=None):
price_index = domain[0][2]
records = [
record for record in self.records
if record.price_index == price_index]
return records[:limit] if limit else records
def create(self, values):
records = []
for value in values:
record = SimpleNamespace(id=len(self.records) + 1, **value)
self.records.append(record)
records.append(record)
return records
class _FakePriceValueModel:
def __init__(self, records=None):
self.records = records or []
def search(self, domain, limit=None):
price = domain[0][2]
price_date = domain[1][2]
records = [
record for record in self.records
if record.price == price and record.price_date == price_date]
return records[:limit] if limit else records
def create(self, values):
records = []
for value in values:
record = SimpleNamespace(id=len(self.records) + 1, **value)
self.records.append(record)
records.append(record)
return records
def write(self, records, values):
for record in records:
for name, value in values.items():
setattr(record, name, value)
class _FakeRecordModel:
def __init__(self, records=None):
self.records = records or []
def search(self, domain, limit=None):
records = self.records
for name, operator, value in domain:
self.assert_operator(operator)
records = [
record for record in records
if getattr(record, name, None) == value]
return records[:limit] if limit else records
def create(self, values):
records = []
for value in values:
record = SimpleNamespace(id=len(self.records) + 1, **value)
self.records.append(record)
records.append(record)
return records
@staticmethod
def assert_operator(operator):
if operator != '=':
raise AssertionError('Unsupported operator: %s' % operator)
del ModuleTestCase

View File

@@ -0,0 +1,3 @@
<form col="4">
<field name="message" colspan="4" height="400"/>
</form>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="file_"/>
<field name="file_"/>
<newline/>
<label name="create_missing_price_index"/>
<field name="create_missing_price_index"/>
<newline/>
<label name="overwrite_existing_price"/>
<field name="overwrite_existing_price"/>
</form>

View File

@@ -0,0 +1,58 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.pool import Pool
from . import (
financing,
market,
valuation,
document,
template_execution,
counterparty,
fx,
operational,
facility,
constraint_type,
)
def register():
Pool.register(
financing.FinancingType,
financing.OperationalStatus,
market.MarketIndex,
market.MarketIndexRate,
market.IndexTerm,
market.InterestFormula,
valuation.ValuationMethod,
valuation.HaircutFormula,
valuation.CollateralType,
document.EvidenceType,
template_execution.TemplateSegment,
template_execution.ExecutionTemplate,
template_execution.ExecutionTemplateLine,
counterparty.ReceivableCategory,
counterparty.PaymentConditionType,
fx.FxFeeder,
operational.BlockingReason,
operational.ChargeType,
facility.FacilityStatus,
facility.Facility,
facility.FacilityCurrency,
facility.FacilityCovenant,
facility.FacilityLimit,
facility.FacilityLimitHaircut,
facility.FacilityLimitCurrency,
facility.FacilityLimitCost,
facility.FacilityLimitCostVariation,
facility.FacilityLimitOpStatus,
facility.FacilityLimitBankAccount,
facility.FacilityCap,
facility.FacilityCapHaircut,
facility.FacilityConstraint,
constraint_type.ConstraintType,
module='trade_finance', type_='model')
Pool.register(
fx.PriceCalendar,
module='price', type_='model')

View File

@@ -0,0 +1,54 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
from trytond.transaction import Transaction
__all__ = ['ConstraintType']
class ConstraintType(ModelSQL, ModelView):
'Constraint Type'
__name__ = 'trade_finance.constraint_type'
_rec_name = 'name'
name = fields.Char('Type', required=True, size=50)
view_name = fields.Selection([], 'View')
value_field = fields.Selection(
'get_column_selection', 'Value Field',
selection_change_with=['view_name'])
label_field = fields.Selection(
'get_column_selection', 'Label Field',
selection_change_with=['view_name'])
@fields.depends('view_name')
def get_column_selection(self):
if not self.view_name:
return [('', '')]
cursor = Transaction().connection.cursor()
cursor.execute(
"SELECT column_name FROM information_schema.columns "
"WHERE table_schema = 'public' AND table_name = %s "
"ORDER BY ordinal_position",
(self.view_name,)
)
return [('', '')] + [(r[0], r[0]) for r in cursor.fetchall()]
@fields.depends('view_name')
def on_change_view_name(self):
self.value_field = None
self.label_field = None
@classmethod
def fields_get(cls, fields_names=None, level=0):
res = super().fields_get(fields_names, level)
if 'view_name' in res:
cursor = Transaction().connection.cursor()
cursor.execute(
"SELECT table_name FROM information_schema.views "
"WHERE table_schema = 'public' AND table_name LIKE 'vw_tf_const_%' "
"ORDER BY table_name"
)
res['view_name']['selection'] = (
[('', '')] + [(r[0], r[0]) for r in cursor.fetchall()])
return res

View File

@@ -0,0 +1,66 @@
<?xml version="1.0"?>
<tryton>
<data>
<!-- ================================================================ -->
<!-- CONSTRAINT TYPE -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="constraint_type_view_tree">
<field name="model">trade_finance.constraint_type</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">constraint_type_tree</field>
</record>
<record model="ir.ui.view" id="constraint_type_view_form">
<field name="model">trade_finance.constraint_type</field>
<field name="type">form</field>
<field name="name">constraint_type_form</field>
</record>
<record model="ir.action.act_window" id="act_constraint_type">
<field name="name">Constraint Types</field>
<field name="res_model">trade_finance.constraint_type</field>
</record>
<record model="ir.action.act_window.view" id="act_constraint_type_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="constraint_type_view_tree"/>
<field name="act_window" ref="act_constraint_type"/>
</record>
<record model="ir.action.act_window.view" id="act_constraint_type_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="constraint_type_view_form"/>
<field name="act_window" ref="act_constraint_type"/>
</record>
<menuitem
name="Constraint Types"
sequence="10"
id="menu_constraint_type"
parent="menu_trade_finance_configuration"
action="act_constraint_type"/>
<record model="ir.model.access" id="access_constraint_type">
<field name="model">trade_finance.constraint_type</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_constraint_type_user">
<field name="model">trade_finance.constraint_type</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_constraint_type_admin">
<field name="model">trade_finance.constraint_type</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,39 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
__all__ = ['ReceivableCategory', 'PaymentConditionType']
class ReceivableCategory(ModelSQL, ModelView):
'Receivable Category'
__name__ = 'trade_finance.receivable_category'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
description = fields.Text('Description')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
class PaymentConditionType(ModelSQL, ModelView):
'Payment Condition Type'
__name__ = 'trade_finance.payment_condition_type'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
remaining_risk_pct = fields.Numeric(
'Remaining Risk (%)', digits=(16, 2),
help='Percentage of residual credit risk retained by the company '
'under this payment condition')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True

View File

@@ -0,0 +1,21 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
__all__ = ['EvidenceType']
class EvidenceType(ModelSQL, ModelView):
'Evidence Type'
__name__ = 'trade_finance.evidence_type'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
description = fields.Text('Description')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True

View File

@@ -0,0 +1,497 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
from trytond.model import sequence_ordered
from trytond.pool import Pool
from trytond.pyson import Eval, Bool
from trytond.exceptions import UserError
__all__ = [
'FacilityStatus',
'Facility',
'FacilityCurrency',
'FacilityCovenant',
'FacilityLimit',
'FacilityLimitHaircut',
'FacilityLimitCurrency',
'FacilityLimitCost',
'FacilityLimitCostVariation',
'FacilityLimitOpStatus',
'FacilityLimitBankAccount',
'FacilityCap',
'FacilityCapHaircut',
'FacilityConstraint',
]
# ---------------------------------------------------------------------------
# Shared Selection lists
# ---------------------------------------------------------------------------
ATTRIBUTE_TYPES = [
('commodity', 'Commodity'),
('article', 'Article'),
('origin', 'Origin'),
('destination', 'Destination'),
('loading_place', 'Loading Place'),
('supplier', 'Supplier'),
('customer', 'Customer'),
('payment_condition_purchase', 'Payment Condition (Purchase)'),
('payment_condition_sale', 'Payment Condition (Sale)'),
('purchase_currency', 'Purchase Currency'),
('warehouse', 'Warehouse'),
('incoterm', 'Incoterm'),
('counterparty_rating', 'Counterparty Rating'),
('receivable_category', 'Receivable Category'),
]
# ---------------------------------------------------------------------------
# Facility Status (configurable reference)
# ---------------------------------------------------------------------------
class FacilityStatus(ModelSQL, ModelView):
'Facility Status'
__name__ = 'trade_finance.facility_status'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
# ---------------------------------------------------------------------------
# Facility Header
# ---------------------------------------------------------------------------
class Facility(ModelSQL, ModelView):
'TF Facility'
__name__ = 'trade_finance.facility'
_rec_name = 'name'
name = fields.Char('Name', required=True)
tfe = fields.Many2One('bank', 'Trade Finance Entity', required=True,
ondelete='RESTRICT',
help='Bank or fund providing this facility')
description = fields.Text('Description')
status = fields.Many2One('trade_finance.facility_status', 'Status',
ondelete='RESTRICT')
commitment_status = fields.Selection([
('uncommitted', 'Uncommitted'),
('committed', 'Committed'),
], 'Commitment Status', required=True)
date_from = fields.Date('Valid From')
date_to = fields.Date('Valid To')
currency = fields.Many2One('currency.currency', 'Facility Currency',
required=True, ondelete='RESTRICT')
fx_feeder = fields.Many2One('trade_finance.fx_feeder', 'FX Rate Feeder',
ondelete='RESTRICT')
fx_haircut_pct = fields.Numeric('FX Haircut (%)', digits=(16, 2))
is_tpa = fields.Boolean('TPA',
help='Third Party Account — broker managed structure')
broker = fields.Many2One('party.party', 'Broker',
states={'invisible': ~Bool(Eval('is_tpa'))},
depends=['is_tpa'], ondelete='RESTRICT')
broker_account = fields.Char('Broker Account',
states={'invisible': ~Bool(Eval('is_tpa'))},
depends=['is_tpa'])
limits = fields.One2Many('trade_finance.facility_limit', 'facility',
'Limits')
currencies = fields.One2Many('trade_finance.facility_currency', 'facility',
'Accepted Currencies')
caps = fields.One2Many('trade_finance.facility_cap', 'facility', 'Caps')
covenants = fields.One2Many('trade_finance.facility_covenant', 'facility',
'Covenants')
constraints = fields.One2Many('trade_finance.facility_constraint',
'facility', 'Facility Constraints',
domain=[('limit', '=', None)])
@classmethod
def default_status(cls):
pool = Pool()
FacilityStatus = pool.get('trade_finance.facility_status')
statuses = FacilityStatus.search([('code', '=', 'DRAFT')], limit=1)
if statuses:
return statuses[0].id
return None
@staticmethod
def default_commitment_status():
return 'uncommitted'
@staticmethod
def default_is_tpa():
return False
# ---------------------------------------------------------------------------
# Facility Accepted Currency
# ---------------------------------------------------------------------------
class FacilityCurrency(ModelSQL, ModelView):
'Facility Currency'
__name__ = 'trade_finance.facility_currency'
facility = fields.Many2One('trade_finance.facility', 'Facility',
required=True, ondelete='CASCADE')
currency = fields.Many2One('currency.currency', 'Currency',
required=True, ondelete='RESTRICT')
fx_haircut_formula = fields.Many2One('trade_finance.haircut_formula',
'FX Haircut Formula', ondelete='RESTRICT')
fx_feeder = fields.Many2One('trade_finance.fx_feeder', 'FX Rate Feeder',
ondelete='RESTRICT')
valuation_method = fields.Many2One('trade_finance.valuation_method',
'Valuation Method', ondelete='RESTRICT')
date_from = fields.Date('Valid From')
date_to = fields.Date('Valid To')
# ---------------------------------------------------------------------------
# Covenant
# ---------------------------------------------------------------------------
class FacilityCovenant(ModelSQL, ModelView):
'Facility Covenant'
__name__ = 'trade_finance.facility_covenant'
_rec_name = 'name'
facility = fields.Many2One('trade_finance.facility', 'Facility',
required=True, ondelete='CASCADE')
name = fields.Char('Covenant', required=True)
ratio_type = fields.Selection([
('leverage_ratio', 'Leverage Ratio'),
('dso', 'DSO'),
('interest_coverage', 'Interest Coverage Ratio'),
('current_ratio', 'Current Ratio'),
('other', 'Other'),
], 'Ratio Type', required=True)
threshold = fields.Numeric('Threshold', digits=(16, 4))
currency = fields.Many2One('currency.currency', 'Currency',
ondelete='RESTRICT')
notes = fields.Text('Notes')
# ---------------------------------------------------------------------------
# Facility Limit (Global Limit + Sub-Limits in one model)
# ---------------------------------------------------------------------------
class FacilityLimit(ModelSQL, ModelView):
'Facility Limit'
__name__ = 'trade_finance.facility_limit'
_rec_name = 'name'
facility = fields.Many2One('trade_finance.facility', 'Facility',
required=True, ondelete='CASCADE',
states={'readonly': Bool(Eval('parent'))},
depends=['parent'])
parent = fields.Many2One('trade_finance.facility_limit', 'Parent Limit',
ondelete='RESTRICT',
domain=[('facility', '=', Eval('facility'))],
depends=['facility'],
help='Leave empty for Global Limit (root node)')
children = fields.One2Many('trade_finance.facility_limit', 'parent',
'Sub-Limits')
name = fields.Char('Name', required=True)
alternative_name = fields.Char('Limit Alternative Name')
financing_type = fields.Many2One('trade_finance.financing_type',
'Financing Type', ondelete='RESTRICT')
amount = fields.Numeric('Amount', digits=(16, 2), required=True)
tenor = fields.Integer('Tenor (days)',
help='Maximum duration of financing from drawdown to repayment')
sequence = fields.Integer('Sequence')
_order = [('sequence', 'ASC'), ('id', 'ASC')]
haircuts = fields.One2Many('trade_finance.facility_limit_haircut', 'limit',
'Haircuts')
currencies = fields.One2Many('trade_finance.facility_limit_currency',
'limit', 'Accepted Currencies')
costs = fields.One2Many('trade_finance.facility_limit_cost', 'limit',
'Costs')
op_statuses = fields.One2Many('trade_finance.facility_limit_op_status',
'limit', 'Expected Operational Statuses')
bank_accounts = fields.One2Many('trade_finance.facility_limit_bank_account',
'limit', 'Bank Accounts')
constraints = fields.One2Many('trade_finance.facility_constraint', 'limit',
'Limit Constraints',
domain=[('facility', '=', None)])
@staticmethod
def default_sequence():
return 10
@classmethod
def create(cls, vlist):
vlist = [v.copy() for v in vlist]
for values in vlist:
if values.get('parent') and not values.get('facility'):
parent = cls(values['parent'])
values['facility'] = parent.facility.id
return super().create(vlist)
@classmethod
def validate(cls, limits):
super().validate(limits)
for limit in limits:
limit.check_single_root()
limit.check_amount_vs_parent()
limit.check_children_amounts()
def check_single_root(self):
if self.parent is None:
roots = self.__class__.search([
('facility', '=', self.facility.id),
('parent', '=', None),
('id', '!=', self.id),
])
if roots:
raise UserError(
f"Facility '{self.facility.name}' already has a Global "
f"Limit ('{roots[0].name}'). Only one root limit is "
f"allowed per facility.")
def check_amount_vs_parent(self):
if self.parent and self.amount > self.parent.amount:
raise UserError(
f"Sub-limit '{self.name}' amount ({self.amount}) "
f"cannot exceed parent limit '{self.parent.name}' "
f"amount ({self.parent.amount}).")
def check_children_amounts(self):
for child in self.children:
if child.amount > self.amount:
raise UserError(
f"Sub-limit '{child.name}' amount ({child.amount}) "
f"cannot exceed parent limit '{self.name}' "
f"amount ({self.amount}).")
# ---------------------------------------------------------------------------
# Limit Haircut
# ---------------------------------------------------------------------------
class FacilityLimitHaircut(ModelSQL, ModelView):
'Facility Limit Haircut'
__name__ = 'trade_finance.facility_limit_haircut'
limit = fields.Many2One('trade_finance.facility_limit', 'Limit',
required=True, ondelete='CASCADE')
attribute = fields.Selection(ATTRIBUTE_TYPES, 'Attribute Type',
required=True)
value = fields.Char('Value',
help='The specific attribute value this haircut applies to '
'(e.g. "Brazil", "Coffee", "USD")')
haircut_pct = fields.Numeric('Haircut (%)', digits=(16, 2), required=True)
date_from = fields.Date('Valid From')
date_to = fields.Date('Valid To')
# ---------------------------------------------------------------------------
# Limit Accepted Currency
# ---------------------------------------------------------------------------
class FacilityLimitCurrency(ModelSQL, ModelView):
'Facility Limit Currency'
__name__ = 'trade_finance.facility_limit_currency'
limit = fields.Many2One('trade_finance.facility_limit', 'Limit',
required=True, ondelete='CASCADE')
currency = fields.Many2One('currency.currency', 'Currency',
required=True, ondelete='RESTRICT')
haircut_pct = fields.Numeric('FX Haircut (%)', digits=(16, 2))
fx_feeder = fields.Many2One('trade_finance.fx_feeder', 'FX Rate Feeder',
ondelete='RESTRICT')
valuation_method = fields.Many2One('trade_finance.valuation_method',
'Valuation Method', ondelete='RESTRICT')
date_from = fields.Date('Valid From')
date_to = fields.Date('Valid To')
# ---------------------------------------------------------------------------
# Limit Cost
# ---------------------------------------------------------------------------
class FacilityLimitCost(ModelSQL, ModelView):
'Facility Limit Cost'
__name__ = 'trade_finance.facility_limit_cost'
limit = fields.Many2One('trade_finance.facility_limit', 'Limit',
required=True, ondelete='CASCADE')
cost_type = fields.Selection([
('interest', 'Interest-Based'),
('flat', 'Flat / Minimal Amount'),
], 'Cost Type', required=True)
# Interest-based fields
spread = fields.Numeric('Spread (%)', digits=(16, 4),
states={'invisible': Eval('cost_type') != 'interest'},
depends=['cost_type'])
index = fields.Many2One('trade_finance.market_index', 'Market Index',
ondelete='RESTRICT',
states={'invisible': Eval('cost_type') != 'interest'},
depends=['cost_type'])
index_term = fields.Many2One('trade_finance.index_term', 'Index Term',
ondelete='RESTRICT',
states={'invisible': Eval('cost_type') != 'interest'},
depends=['cost_type'])
interest_formula = fields.Many2One('trade_finance.interest_formula',
'Interest Formula', ondelete='RESTRICT',
states={'invisible': Eval('cost_type') != 'interest'},
depends=['cost_type'])
# Flat fields
flat_amount = fields.Numeric('Flat Amount', digits=(16, 2),
states={'invisible': Eval('cost_type') != 'flat'},
depends=['cost_type'])
flat_currency = fields.Many2One('currency.currency', 'Currency',
ondelete='RESTRICT',
states={'invisible': Eval('cost_type') != 'flat'},
depends=['cost_type'])
date_from = fields.Date('Valid From')
date_to = fields.Date('Valid To')
variations = fields.One2Many('trade_finance.facility_limit_cost_variation',
'cost', 'Cost Variations')
# ---------------------------------------------------------------------------
# Limit Cost Variation
# ---------------------------------------------------------------------------
class FacilityLimitCostVariation(ModelSQL, ModelView):
'Facility Limit Cost Variation'
__name__ = 'trade_finance.facility_limit_cost_variation'
cost = fields.Many2One('trade_finance.facility_limit_cost', 'Cost',
required=True, ondelete='CASCADE')
attribute = fields.Selection(ATTRIBUTE_TYPES, 'Attribute Type',
required=True)
value = fields.Char('Value')
variation_type = fields.Selection([
('pct', 'Percentage (+/-)'),
('flat', 'Flat Amount (+/-)'),
], 'Variation Type', required=True)
variation_value = fields.Numeric('Variation Value', digits=(16, 4),
required=True)
variation_currency = fields.Many2One('currency.currency', 'Currency',
ondelete='RESTRICT',
states={'invisible': Eval('variation_type') != 'flat'},
depends=['variation_type'])
# ---------------------------------------------------------------------------
# Limit Expected Operational Status
# ---------------------------------------------------------------------------
class FacilityLimitOpStatus(ModelSQL, ModelView):
'Facility Limit Operational Status'
__name__ = 'trade_finance.facility_limit_op_status'
limit = fields.Many2One('trade_finance.facility_limit', 'Limit',
required=True, ondelete='CASCADE')
operational_status = fields.Many2One('trade_finance.operational_status',
'Operational Status', required=True, ondelete='RESTRICT')
evidence_type = fields.Many2One('trade_finance.evidence_type',
'Required Evidence', ondelete='RESTRICT',
help='Evidence required when this operational status is reached')
# ---------------------------------------------------------------------------
# Limit Bank Account
# ---------------------------------------------------------------------------
class FacilityLimitBankAccount(ModelSQL, ModelView):
'Facility Limit Bank Account'
__name__ = 'trade_finance.facility_limit_bank_account'
limit = fields.Many2One('trade_finance.facility_limit', 'Limit',
required=True, ondelete='CASCADE')
bank_account = fields.Many2One('bank.account', 'Bank Account',
required=True, ondelete='RESTRICT')
currency = fields.Many2One('currency.currency', 'Currency',
required=True, ondelete='RESTRICT')
is_default = fields.Boolean('Default')
date_from = fields.Date('Valid From')
date_to = fields.Date('Valid To')
@staticmethod
def default_is_default():
return False
# ---------------------------------------------------------------------------
# Cap
# ---------------------------------------------------------------------------
class FacilityCap(ModelSQL, ModelView):
'Facility Cap'
__name__ = 'trade_finance.facility_cap'
_rec_name = 'name'
facility = fields.Many2One('trade_finance.facility', 'Facility',
required=True, ondelete='CASCADE')
name = fields.Char('Name', required=True)
amount = fields.Numeric('Cap Amount', digits=(16, 2), required=True)
date_from = fields.Date('Valid From')
date_to = fields.Date('Valid To')
haircuts = fields.One2Many('trade_finance.facility_cap_haircut', 'cap',
'Haircuts')
constraints = fields.One2Many('trade_finance.facility_constraint', 'cap',
'Cap Constraints')
# ---------------------------------------------------------------------------
# Cap Haircut
# ---------------------------------------------------------------------------
class FacilityCapHaircut(ModelSQL, ModelView):
'Facility Cap Haircut'
__name__ = 'trade_finance.facility_cap_haircut'
cap = fields.Many2One('trade_finance.facility_cap', 'Cap',
required=True, ondelete='CASCADE')
attribute = fields.Selection(ATTRIBUTE_TYPES, 'Attribute Type',
required=True)
value = fields.Char('Value')
haircut_pct = fields.Numeric('Haircut (%)', digits=(16, 2), required=True)
date_from = fields.Date('Valid From')
date_to = fields.Date('Valid To')
# ---------------------------------------------------------------------------
# Constraint (shared by Facility, Limit, Cap)
# ---------------------------------------------------------------------------
class FacilityConstraint(ModelSQL, ModelView):
'Facility Constraint'
__name__ = 'trade_finance.facility_constraint'
facility = fields.Many2One('trade_finance.facility', 'Facility',
ondelete='CASCADE')
limit = fields.Many2One('trade_finance.facility_limit', 'Limit',
ondelete='CASCADE')
cap = fields.Many2One('trade_finance.facility_cap', 'Cap',
ondelete='CASCADE')
constraint_type = fields.Many2One('trade_finance.constraint_type',
'Constraint Type', required=True, ondelete='RESTRICT')
is_exclusion = fields.Boolean('Exclusion',
help='Checked = Exclusion constraint, unchecked = Inclusion constraint')
date_from = fields.Date('Valid From')
date_to = fields.Date('Valid To')
@staticmethod
def default_is_exclusion():
return False

View File

@@ -0,0 +1,447 @@
<?xml version="1.0"?>
<tryton>
<data>
<!-- ================================================================ -->
<!-- ACCESS GROUP -->
<!-- ================================================================ -->
<record model="res.group" id="group_trade_finance_user">
<field name="name">Trade Finance</field>
</record>
<record model="res.user-res.group"
id="user_admin_group_trade_finance_user">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_trade_finance_user"/>
</record>
<!-- ================================================================ -->
<!-- FACILITY STATUS — Reference Data -->
<!-- ================================================================ -->
<record model="trade_finance.facility_status" id="facility_status_draft">
<field name="code">DRAFT</field>
<field name="name">Draft</field>
<field name="active" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- FACILITY STATUS -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="facility_status_view_tree">
<field name="model">trade_finance.facility_status</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_status_tree</field>
</record>
<record model="ir.ui.view" id="facility_status_view_form">
<field name="model">trade_finance.facility_status</field>
<field name="type">form</field>
<field name="name">facility_status_form</field>
</record>
<record model="ir.action.act_window" id="act_facility_status">
<field name="name">Facility Statuses</field>
<field name="res_model">trade_finance.facility_status</field>
</record>
<record model="ir.action.act_window.view" id="act_facility_status_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="facility_status_view_tree"/>
<field name="act_window" ref="act_facility_status"/>
</record>
<record model="ir.action.act_window.view" id="act_facility_status_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="facility_status_view_form"/>
<field name="act_window" ref="act_facility_status"/>
</record>
<menuitem
name="Facility Statuses"
sequence="5"
id="menu_facility_status"
parent="menu_trade_finance_configuration"
action="act_facility_status"/>
<record model="ir.model.access" id="access_facility_status">
<field name="model">trade_finance.facility_status</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_status_user">
<field name="model">trade_finance.facility_status</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_status_admin">
<field name="model">trade_finance.facility_status</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- FACILITY -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="facility_view_tree">
<field name="model">trade_finance.facility</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_tree</field>
</record>
<record model="ir.ui.view" id="facility_view_form">
<field name="model">trade_finance.facility</field>
<field name="type">form</field>
<field name="name">facility_form</field>
</record>
<record model="ir.action.act_window" id="act_facility">
<field name="name">TF Facilities</field>
<field name="res_model">trade_finance.facility</field>
</record>
<record model="ir.action.act_window.view" id="act_facility_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="facility_view_tree"/>
<field name="act_window" ref="act_facility"/>
</record>
<record model="ir.action.act_window.view" id="act_facility_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="facility_view_form"/>
<field name="act_window" ref="act_facility"/>
</record>
<menuitem
name="Facilities"
sequence="10"
id="menu_facility"
parent="menu_trade_finance"
action="act_facility"/>
<record model="ir.model.access" id="access_facility">
<field name="model">trade_finance.facility</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_user">
<field name="model">trade_finance.facility</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_admin">
<field name="model">trade_finance.facility</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- FACILITY CURRENCY -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="facility_currency_view_tree">
<field name="model">trade_finance.facility_currency</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_currency_tree</field>
</record>
<record model="ir.model.access" id="access_facility_currency">
<field name="model">trade_finance.facility_currency</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_currency_user">
<field name="model">trade_finance.facility_currency</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- FACILITY COVENANT -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="facility_covenant_view_tree">
<field name="model">trade_finance.facility_covenant</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_covenant_tree</field>
</record>
<record model="ir.model.access" id="access_facility_covenant">
<field name="model">trade_finance.facility_covenant</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_covenant_user">
<field name="model">trade_finance.facility_covenant</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- FACILITY LIMIT -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="facility_limit_view_tree">
<field name="model">trade_finance.facility_limit</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_limit_tree</field>
</record>
<record model="ir.ui.view" id="facility_limit_view_form">
<field name="model">trade_finance.facility_limit</field>
<field name="type">form</field>
<field name="name">facility_limit_form</field>
</record>
<record model="ir.model.access" id="access_facility_limit">
<field name="model">trade_finance.facility_limit</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_limit_user">
<field name="model">trade_finance.facility_limit</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- FACILITY LIMIT CHILD TABLES -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="facility_limit_haircut_view_tree">
<field name="model">trade_finance.facility_limit_haircut</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_limit_haircut_tree</field>
</record>
<record model="ir.model.access" id="access_facility_limit_haircut">
<field name="model">trade_finance.facility_limit_haircut</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_limit_haircut_user">
<field name="model">trade_finance.facility_limit_haircut</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.ui.view" id="facility_limit_currency_view_tree">
<field name="model">trade_finance.facility_limit_currency</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_limit_currency_tree</field>
</record>
<record model="ir.model.access" id="access_facility_limit_currency">
<field name="model">trade_finance.facility_limit_currency</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_limit_currency_user">
<field name="model">trade_finance.facility_limit_currency</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.ui.view" id="facility_limit_cost_view_tree">
<field name="model">trade_finance.facility_limit_cost</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_limit_cost_tree</field>
</record>
<record model="ir.model.access" id="access_facility_limit_cost">
<field name="model">trade_finance.facility_limit_cost</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_limit_cost_user">
<field name="model">trade_finance.facility_limit_cost</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.ui.view" id="facility_limit_cost_variation_view_tree">
<field name="model">trade_finance.facility_limit_cost_variation</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_limit_cost_variation_tree</field>
</record>
<record model="ir.model.access" id="access_facility_limit_cost_variation">
<field name="model">trade_finance.facility_limit_cost_variation</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_limit_cost_variation_user">
<field name="model">trade_finance.facility_limit_cost_variation</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.ui.view" id="facility_limit_op_status_view_tree">
<field name="model">trade_finance.facility_limit_op_status</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_limit_op_status_tree</field>
</record>
<record model="ir.model.access" id="access_facility_limit_op_status">
<field name="model">trade_finance.facility_limit_op_status</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_limit_op_status_user">
<field name="model">trade_finance.facility_limit_op_status</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.ui.view" id="facility_limit_bank_account_view_tree">
<field name="model">trade_finance.facility_limit_bank_account</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_limit_bank_account_tree</field>
</record>
<record model="ir.model.access" id="access_facility_limit_bank_account">
<field name="model">trade_finance.facility_limit_bank_account</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_limit_bank_account_user">
<field name="model">trade_finance.facility_limit_bank_account</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- FACILITY CAP -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="facility_cap_view_tree">
<field name="model">trade_finance.facility_cap</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_cap_tree</field>
</record>
<record model="ir.model.access" id="access_facility_cap">
<field name="model">trade_finance.facility_cap</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_cap_user">
<field name="model">trade_finance.facility_cap</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<record model="ir.ui.view" id="facility_cap_haircut_view_tree">
<field name="model">trade_finance.facility_cap_haircut</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_cap_haircut_tree</field>
</record>
<record model="ir.model.access" id="access_facility_cap_haircut">
<field name="model">trade_finance.facility_cap_haircut</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_cap_haircut_user">
<field name="model">trade_finance.facility_cap_haircut</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- FACILITY CONSTRAINT -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="facility_constraint_view_tree">
<field name="model">trade_finance.facility_constraint</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">facility_constraint_tree</field>
</record>
<record model="ir.ui.view" id="facility_constraint_view_form">
<field name="model">trade_finance.facility_constraint</field>
<field name="type">form</field>
<field name="name">facility_constraint_form</field>
</record>
<record model="ir.model.access" id="access_facility_constraint">
<field name="model">trade_finance.facility_constraint</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_facility_constraint_user">
<field name="model">trade_finance.facility_constraint</field>
<field name="group" ref="group_trade_finance_user"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,47 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
__all__ = ['FinancingType', 'OperationalStatus']
class FinancingType(ModelSQL, ModelView):
'Financing Type'
__name__ = 'trade_finance.financing_type'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
sequence = fields.Integer('Sequence')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
@staticmethod
def default_sequence():
return 10
class OperationalStatus(ModelSQL, ModelView):
'Operational Status'
__name__ = 'trade_finance.operational_status'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
financing_type = fields.Many2One(
'trade_finance.financing_type', 'Default Financing Type',
ondelete='RESTRICT')
sequence = fields.Integer('Sequence')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
@staticmethod
def default_sequence():
return 10

View File

@@ -0,0 +1,37 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
from trytond.pool import PoolMeta
__all__ = ['FxFeeder', 'PriceCalendar']
class FxFeeder(ModelSQL, ModelView):
'FX Rate Feeder'
__name__ = 'trade_finance.fx_feeder'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
source_description = fields.Text(
'Source Description',
help='Description of the FX rate source / provider')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
class PriceCalendar(metaclass=PoolMeta):
__name__ = 'price.calendar'
purpose = fields.Selection([
(None, ''),
('banking', 'Banking'),
('market', 'Market'),
('factoring', 'Factoring'),
], 'Purpose',
help='Use of this calendar: Banking days, Market trading days, '
'or Factoring program calendar')

View File

@@ -0,0 +1,64 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
__all__ = ['MarketIndex', 'MarketIndexRate', 'IndexTerm', 'InterestFormula']
class MarketIndex(ModelSQL, ModelView):
'Market Index'
__name__ = 'trade_finance.market_index'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
rates = fields.One2Many(
'trade_finance.market_index_rate', 'index', 'Rates')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
class MarketIndexRate(ModelSQL, ModelView):
'Market Index Rate'
__name__ = 'trade_finance.market_index_rate'
index = fields.Many2One(
'trade_finance.market_index', 'Index',
required=True, ondelete='CASCADE')
rate_date = fields.Date('Date', required=True)
rate = fields.Numeric('Rate (%)', digits=(16, 6), required=True)
class IndexTerm(ModelSQL, ModelView):
'Index Term'
__name__ = 'trade_finance.index_term'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
days = fields.Integer('Days', required=True,
help='Approximate number of days for this term (O/N=1, 1W=7, 1M=30, '
'3M=90, 6M=180, 1Y=365)')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
class InterestFormula(ModelSQL, ModelView):
'Interest Calculation Formula'
__name__ = 'trade_finance.interest_formula'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True

View File

@@ -0,0 +1,35 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
__all__ = ['BlockingReason', 'ChargeType']
class BlockingReason(ModelSQL, ModelView):
'Blocking Reason'
__name__ = 'trade_finance.blocking_reason'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
class ChargeType(ModelSQL, ModelView):
'Charge Type'
__name__ = 'trade_finance.charge_type'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
description = fields.Text('Description')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True

View File

@@ -0,0 +1,947 @@
<?xml version="1.0"?>
<tryton>
<data>
<!-- ================================================================ -->
<!-- ACCESS GROUPS -->
<!-- ================================================================ -->
<record model="res.group" id="group_trade_finance_admin">
<field name="name">Trade Finance Administration</field>
</record>
<record model="res.user-res.group"
id="user_admin_group_trade_finance_admin">
<field name="user" ref="res.user_admin"/>
<field name="group" ref="group_trade_finance_admin"/>
</record>
<!-- ================================================================ -->
<!-- TOP-LEVEL MENU -->
<!-- ================================================================ -->
<menuitem
name="Trade Finance"
sequence="70"
id="menu_trade_finance"/>
<menuitem
name="Configuration"
sequence="99"
id="menu_trade_finance_configuration"
parent="menu_trade_finance"/>
<menuitem
name="Market Data"
sequence="20"
id="menu_trade_finance_market_data"
parent="menu_trade_finance"/>
<!-- ================================================================ -->
<!-- FINANCING TYPE -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="financing_type_view_tree">
<field name="model">trade_finance.financing_type</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">financing_type_tree</field>
</record>
<record model="ir.ui.view" id="financing_type_view_form">
<field name="model">trade_finance.financing_type</field>
<field name="type">form</field>
<field name="name">financing_type_form</field>
</record>
<record model="ir.action.act_window" id="act_financing_type">
<field name="name">Financing Types</field>
<field name="res_model">trade_finance.financing_type</field>
</record>
<record model="ir.action.act_window.view" id="act_financing_type_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="financing_type_view_tree"/>
<field name="act_window" ref="act_financing_type"/>
</record>
<record model="ir.action.act_window.view" id="act_financing_type_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="financing_type_view_form"/>
<field name="act_window" ref="act_financing_type"/>
</record>
<menuitem
name="Financing Types"
sequence="10"
id="menu_financing_type"
parent="menu_trade_finance_configuration"
action="act_financing_type"/>
<record model="ir.model.access" id="access_financing_type">
<field name="model">trade_finance.financing_type</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_financing_type_admin">
<field name="model">trade_finance.financing_type</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- OPERATIONAL STATUS -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="operational_status_view_tree">
<field name="model">trade_finance.operational_status</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">operational_status_tree</field>
</record>
<record model="ir.ui.view" id="operational_status_view_form">
<field name="model">trade_finance.operational_status</field>
<field name="type">form</field>
<field name="name">operational_status_form</field>
</record>
<record model="ir.action.act_window" id="act_operational_status">
<field name="name">Operational Statuses</field>
<field name="res_model">trade_finance.operational_status</field>
</record>
<record model="ir.action.act_window.view" id="act_operational_status_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="operational_status_view_tree"/>
<field name="act_window" ref="act_operational_status"/>
</record>
<record model="ir.action.act_window.view" id="act_operational_status_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="operational_status_view_form"/>
<field name="act_window" ref="act_operational_status"/>
</record>
<menuitem
name="Operational Statuses"
sequence="20"
id="menu_operational_status"
parent="menu_trade_finance_configuration"
action="act_operational_status"/>
<record model="ir.model.access" id="access_operational_status">
<field name="model">trade_finance.operational_status</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_operational_status_admin">
<field name="model">trade_finance.operational_status</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- MARKET INDEX -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="market_index_view_tree">
<field name="model">trade_finance.market_index</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">market_index_tree</field>
</record>
<record model="ir.ui.view" id="market_index_view_form">
<field name="model">trade_finance.market_index</field>
<field name="type">form</field>
<field name="name">market_index_form</field>
</record>
<record model="ir.action.act_window" id="act_market_index">
<field name="name">Market Indexes</field>
<field name="res_model">trade_finance.market_index</field>
</record>
<record model="ir.action.act_window.view" id="act_market_index_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="market_index_view_tree"/>
<field name="act_window" ref="act_market_index"/>
</record>
<record model="ir.action.act_window.view" id="act_market_index_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="market_index_view_form"/>
<field name="act_window" ref="act_market_index"/>
</record>
<menuitem
name="Market Indexes"
sequence="10"
id="menu_market_index"
parent="menu_trade_finance_configuration"
action="act_market_index"/>
<record model="ir.model.access" id="access_market_index">
<field name="model">trade_finance.market_index</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_market_index_admin">
<field name="model">trade_finance.market_index</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- MARKET INDEX RATE -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="market_index_rate_view_tree">
<field name="model">trade_finance.market_index_rate</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">market_index_rate_tree</field>
</record>
<record model="ir.ui.view" id="market_index_rate_view_form">
<field name="model">trade_finance.market_index_rate</field>
<field name="type">form</field>
<field name="name">market_index_rate_form</field>
</record>
<record model="ir.action.act_window" id="act_market_index_rate">
<field name="name">Market Index Rates</field>
<field name="res_model">trade_finance.market_index_rate</field>
</record>
<record model="ir.action.act_window.view" id="act_market_index_rate_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="market_index_rate_view_tree"/>
<field name="act_window" ref="act_market_index_rate"/>
</record>
<record model="ir.action.act_window.view" id="act_market_index_rate_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="market_index_rate_view_form"/>
<field name="act_window" ref="act_market_index_rate"/>
</record>
<menuitem
name="Market Index Rates"
sequence="10"
id="menu_market_index_rate"
parent="menu_trade_finance_market_data"
action="act_market_index_rate"/>
<record model="ir.model.access" id="access_market_index_rate">
<field name="model">trade_finance.market_index_rate</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_market_index_rate_admin">
<field name="model">trade_finance.market_index_rate</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- INDEX TERM -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="index_term_view_tree">
<field name="model">trade_finance.index_term</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">index_term_tree</field>
</record>
<record model="ir.ui.view" id="index_term_view_form">
<field name="model">trade_finance.index_term</field>
<field name="type">form</field>
<field name="name">index_term_form</field>
</record>
<record model="ir.action.act_window" id="act_index_term">
<field name="name">Index Terms</field>
<field name="res_model">trade_finance.index_term</field>
</record>
<record model="ir.action.act_window.view" id="act_index_term_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="index_term_view_tree"/>
<field name="act_window" ref="act_index_term"/>
</record>
<record model="ir.action.act_window.view" id="act_index_term_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="index_term_view_form"/>
<field name="act_window" ref="act_index_term"/>
</record>
<menuitem
name="Index Terms"
sequence="30"
id="menu_index_term"
parent="menu_trade_finance_configuration"
action="act_index_term"/>
<record model="ir.model.access" id="access_index_term">
<field name="model">trade_finance.index_term</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_index_term_admin">
<field name="model">trade_finance.index_term</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- INTEREST FORMULA -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="interest_formula_view_tree">
<field name="model">trade_finance.interest_formula</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">interest_formula_tree</field>
</record>
<record model="ir.ui.view" id="interest_formula_view_form">
<field name="model">trade_finance.interest_formula</field>
<field name="type">form</field>
<field name="name">interest_formula_form</field>
</record>
<record model="ir.action.act_window" id="act_interest_formula">
<field name="name">Interest Formulas</field>
<field name="res_model">trade_finance.interest_formula</field>
</record>
<record model="ir.action.act_window.view" id="act_interest_formula_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="interest_formula_view_tree"/>
<field name="act_window" ref="act_interest_formula"/>
</record>
<record model="ir.action.act_window.view" id="act_interest_formula_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="interest_formula_view_form"/>
<field name="act_window" ref="act_interest_formula"/>
</record>
<menuitem
name="Interest Formulas"
sequence="40"
id="menu_interest_formula"
parent="menu_trade_finance_configuration"
action="act_interest_formula"/>
<record model="ir.model.access" id="access_interest_formula">
<field name="model">trade_finance.interest_formula</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_interest_formula_admin">
<field name="model">trade_finance.interest_formula</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- HAIRCUT FORMULA -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="haircut_formula_view_tree">
<field name="model">trade_finance.haircut_formula</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">haircut_formula_tree</field>
</record>
<record model="ir.ui.view" id="haircut_formula_view_form">
<field name="model">trade_finance.haircut_formula</field>
<field name="type">form</field>
<field name="name">haircut_formula_form</field>
</record>
<record model="ir.action.act_window" id="act_haircut_formula">
<field name="name">Haircut Formulas</field>
<field name="res_model">trade_finance.haircut_formula</field>
</record>
<record model="ir.action.act_window.view" id="act_haircut_formula_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="haircut_formula_view_tree"/>
<field name="act_window" ref="act_haircut_formula"/>
</record>
<record model="ir.action.act_window.view" id="act_haircut_formula_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="haircut_formula_view_form"/>
<field name="act_window" ref="act_haircut_formula"/>
</record>
<menuitem
name="Haircut Formulas"
sequence="50"
id="menu_haircut_formula"
parent="menu_trade_finance_configuration"
action="act_haircut_formula"/>
<record model="ir.model.access" id="access_haircut_formula">
<field name="model">trade_finance.haircut_formula</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_haircut_formula_admin">
<field name="model">trade_finance.haircut_formula</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- VALUATION METHOD -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="valuation_method_view_tree">
<field name="model">trade_finance.valuation_method</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">valuation_method_tree</field>
</record>
<record model="ir.ui.view" id="valuation_method_view_form">
<field name="model">trade_finance.valuation_method</field>
<field name="type">form</field>
<field name="name">valuation_method_form</field>
</record>
<record model="ir.action.act_window" id="act_valuation_method">
<field name="name">Valuation Methods</field>
<field name="res_model">trade_finance.valuation_method</field>
</record>
<record model="ir.action.act_window.view" id="act_valuation_method_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="valuation_method_view_tree"/>
<field name="act_window" ref="act_valuation_method"/>
</record>
<record model="ir.action.act_window.view" id="act_valuation_method_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="valuation_method_view_form"/>
<field name="act_window" ref="act_valuation_method"/>
</record>
<menuitem
name="Valuation Methods"
sequence="60"
id="menu_valuation_method"
parent="menu_trade_finance_configuration"
action="act_valuation_method"/>
<record model="ir.model.access" id="access_valuation_method">
<field name="model">trade_finance.valuation_method</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_valuation_method_admin">
<field name="model">trade_finance.valuation_method</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- COLLATERAL TYPE -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="collateral_type_view_tree">
<field name="model">trade_finance.collateral_type</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">collateral_type_tree</field>
</record>
<record model="ir.ui.view" id="collateral_type_view_form">
<field name="model">trade_finance.collateral_type</field>
<field name="type">form</field>
<field name="name">collateral_type_form</field>
</record>
<record model="ir.action.act_window" id="act_collateral_type">
<field name="name">Collateral Types</field>
<field name="res_model">trade_finance.collateral_type</field>
</record>
<record model="ir.action.act_window.view" id="act_collateral_type_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="collateral_type_view_tree"/>
<field name="act_window" ref="act_collateral_type"/>
</record>
<record model="ir.action.act_window.view" id="act_collateral_type_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="collateral_type_view_form"/>
<field name="act_window" ref="act_collateral_type"/>
</record>
<menuitem
name="Collateral Types"
sequence="70"
id="menu_collateral_type"
parent="menu_trade_finance_configuration"
action="act_collateral_type"/>
<record model="ir.model.access" id="access_collateral_type">
<field name="model">trade_finance.collateral_type</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_collateral_type_admin">
<field name="model">trade_finance.collateral_type</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- EVIDENCE TYPE -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="evidence_type_view_tree">
<field name="model">trade_finance.evidence_type</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">evidence_type_tree</field>
</record>
<record model="ir.ui.view" id="evidence_type_view_form">
<field name="model">trade_finance.evidence_type</field>
<field name="type">form</field>
<field name="name">evidence_type_form</field>
</record>
<record model="ir.action.act_window" id="act_evidence_type">
<field name="name">Evidence Types</field>
<field name="res_model">trade_finance.evidence_type</field>
</record>
<record model="ir.action.act_window.view" id="act_evidence_type_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="evidence_type_view_tree"/>
<field name="act_window" ref="act_evidence_type"/>
</record>
<record model="ir.action.act_window.view" id="act_evidence_type_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="evidence_type_view_form"/>
<field name="act_window" ref="act_evidence_type"/>
</record>
<menuitem
name="Evidence Types"
sequence="80"
id="menu_evidence_type"
parent="menu_trade_finance_configuration"
action="act_evidence_type"/>
<record model="ir.model.access" id="access_evidence_type">
<field name="model">trade_finance.evidence_type</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_evidence_type_admin">
<field name="model">trade_finance.evidence_type</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- TEMPLATE SEGMENT -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="template_segment_view_tree">
<field name="model">trade_finance.template_segment</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">template_segment_tree</field>
</record>
<record model="ir.ui.view" id="template_segment_view_form">
<field name="model">trade_finance.template_segment</field>
<field name="type">form</field>
<field name="name">template_segment_form</field>
</record>
<record model="ir.action.act_window" id="act_template_segment">
<field name="name">Template Segments</field>
<field name="res_model">trade_finance.template_segment</field>
</record>
<record model="ir.action.act_window.view" id="act_template_segment_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="template_segment_view_tree"/>
<field name="act_window" ref="act_template_segment"/>
</record>
<record model="ir.action.act_window.view" id="act_template_segment_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="template_segment_view_form"/>
<field name="act_window" ref="act_template_segment"/>
</record>
<menuitem
name="Template Segments"
sequence="90"
id="menu_template_segment"
parent="menu_trade_finance_configuration"
action="act_template_segment"/>
<record model="ir.model.access" id="access_template_segment">
<field name="model">trade_finance.template_segment</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_template_segment_admin">
<field name="model">trade_finance.template_segment</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- EXECUTION TEMPLATE -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="execution_template_view_tree">
<field name="model">trade_finance.execution_template</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">execution_template_tree</field>
</record>
<record model="ir.ui.view" id="execution_template_view_form">
<field name="model">trade_finance.execution_template</field>
<field name="type">form</field>
<field name="name">execution_template_form</field>
</record>
<record model="ir.action.act_window" id="act_execution_template">
<field name="name">Execution Templates</field>
<field name="res_model">trade_finance.execution_template</field>
</record>
<record model="ir.action.act_window.view" id="act_execution_template_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="execution_template_view_tree"/>
<field name="act_window" ref="act_execution_template"/>
</record>
<record model="ir.action.act_window.view" id="act_execution_template_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="execution_template_view_form"/>
<field name="act_window" ref="act_execution_template"/>
</record>
<menuitem
name="Execution Templates"
sequence="100"
id="menu_execution_template"
parent="menu_trade_finance_configuration"
action="act_execution_template"/>
<record model="ir.model.access" id="access_execution_template">
<field name="model">trade_finance.execution_template</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_execution_template_admin">
<field name="model">trade_finance.execution_template</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ExecutionTemplateLine — no separate menu (child of ExecutionTemplate) -->
<record model="ir.ui.view" id="execution_template_line_view_tree">
<field name="model">trade_finance.execution_template_line</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">execution_template_line_tree</field>
</record>
<record model="ir.model.access" id="access_execution_template_line">
<field name="model">trade_finance.execution_template_line</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_execution_template_line_admin">
<field name="model">trade_finance.execution_template_line</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- RECEIVABLE CATEGORY -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="receivable_category_view_tree">
<field name="model">trade_finance.receivable_category</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">receivable_category_tree</field>
</record>
<record model="ir.ui.view" id="receivable_category_view_form">
<field name="model">trade_finance.receivable_category</field>
<field name="type">form</field>
<field name="name">receivable_category_form</field>
</record>
<record model="ir.action.act_window" id="act_receivable_category">
<field name="name">Receivable Categories</field>
<field name="res_model">trade_finance.receivable_category</field>
</record>
<record model="ir.action.act_window.view" id="act_receivable_category_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="receivable_category_view_tree"/>
<field name="act_window" ref="act_receivable_category"/>
</record>
<record model="ir.action.act_window.view" id="act_receivable_category_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="receivable_category_view_form"/>
<field name="act_window" ref="act_receivable_category"/>
</record>
<menuitem
name="Receivable Categories"
sequence="110"
id="menu_receivable_category"
parent="menu_trade_finance_configuration"
action="act_receivable_category"/>
<record model="ir.model.access" id="access_receivable_category">
<field name="model">trade_finance.receivable_category</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_receivable_category_admin">
<field name="model">trade_finance.receivable_category</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- PAYMENT CONDITION TYPE -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="payment_condition_type_view_tree">
<field name="model">trade_finance.payment_condition_type</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">payment_condition_type_tree</field>
</record>
<record model="ir.ui.view" id="payment_condition_type_view_form">
<field name="model">trade_finance.payment_condition_type</field>
<field name="type">form</field>
<field name="name">payment_condition_type_form</field>
</record>
<record model="ir.action.act_window" id="act_payment_condition_type">
<field name="name">Payment Condition Types</field>
<field name="res_model">trade_finance.payment_condition_type</field>
</record>
<record model="ir.action.act_window.view" id="act_payment_condition_type_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="payment_condition_type_view_tree"/>
<field name="act_window" ref="act_payment_condition_type"/>
</record>
<record model="ir.action.act_window.view" id="act_payment_condition_type_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="payment_condition_type_view_form"/>
<field name="act_window" ref="act_payment_condition_type"/>
</record>
<menuitem
name="Payment Condition Types"
sequence="120"
id="menu_payment_condition_type"
parent="menu_trade_finance_configuration"
action="act_payment_condition_type"/>
<record model="ir.model.access" id="access_payment_condition_type">
<field name="model">trade_finance.payment_condition_type</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_payment_condition_type_admin">
<field name="model">trade_finance.payment_condition_type</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- FX FEEDER -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="fx_feeder_view_tree">
<field name="model">trade_finance.fx_feeder</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">fx_feeder_tree</field>
</record>
<record model="ir.ui.view" id="fx_feeder_view_form">
<field name="model">trade_finance.fx_feeder</field>
<field name="type">form</field>
<field name="name">fx_feeder_form</field>
</record>
<record model="ir.action.act_window" id="act_fx_feeder">
<field name="name">FX Rate Feeders</field>
<field name="res_model">trade_finance.fx_feeder</field>
</record>
<record model="ir.action.act_window.view" id="act_fx_feeder_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="fx_feeder_view_tree"/>
<field name="act_window" ref="act_fx_feeder"/>
</record>
<record model="ir.action.act_window.view" id="act_fx_feeder_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="fx_feeder_view_form"/>
<field name="act_window" ref="act_fx_feeder"/>
</record>
<menuitem
name="FX Rate Feeders"
sequence="130"
id="menu_fx_feeder"
parent="menu_trade_finance_configuration"
action="act_fx_feeder"/>
<record model="ir.model.access" id="access_fx_feeder">
<field name="model">trade_finance.fx_feeder</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_fx_feeder_admin">
<field name="model">trade_finance.fx_feeder</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- BLOCKING REASON -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="blocking_reason_view_tree">
<field name="model">trade_finance.blocking_reason</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">blocking_reason_tree</field>
</record>
<record model="ir.ui.view" id="blocking_reason_view_form">
<field name="model">trade_finance.blocking_reason</field>
<field name="type">form</field>
<field name="name">blocking_reason_form</field>
</record>
<record model="ir.action.act_window" id="act_blocking_reason">
<field name="name">Blocking Reasons</field>
<field name="res_model">trade_finance.blocking_reason</field>
</record>
<record model="ir.action.act_window.view" id="act_blocking_reason_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="blocking_reason_view_tree"/>
<field name="act_window" ref="act_blocking_reason"/>
</record>
<record model="ir.action.act_window.view" id="act_blocking_reason_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="blocking_reason_view_form"/>
<field name="act_window" ref="act_blocking_reason"/>
</record>
<menuitem
name="Blocking Reasons"
sequence="140"
id="menu_blocking_reason"
parent="menu_trade_finance_configuration"
action="act_blocking_reason"/>
<record model="ir.model.access" id="access_blocking_reason">
<field name="model">trade_finance.blocking_reason</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_blocking_reason_admin">
<field name="model">trade_finance.blocking_reason</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
<!-- ================================================================ -->
<!-- CHARGE TYPE -->
<!-- ================================================================ -->
<record model="ir.ui.view" id="charge_type_view_tree">
<field name="model">trade_finance.charge_type</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="name">charge_type_tree</field>
</record>
<record model="ir.ui.view" id="charge_type_view_form">
<field name="model">trade_finance.charge_type</field>
<field name="type">form</field>
<field name="name">charge_type_form</field>
</record>
<record model="ir.action.act_window" id="act_charge_type">
<field name="name">Charge Types</field>
<field name="res_model">trade_finance.charge_type</field>
</record>
<record model="ir.action.act_window.view" id="act_charge_type_view1">
<field name="sequence" eval="10"/>
<field name="view" ref="charge_type_view_tree"/>
<field name="act_window" ref="act_charge_type"/>
</record>
<record model="ir.action.act_window.view" id="act_charge_type_view2">
<field name="sequence" eval="20"/>
<field name="view" ref="charge_type_view_form"/>
<field name="act_window" ref="act_charge_type"/>
</record>
<menuitem
name="Charge Types"
sequence="150"
id="menu_charge_type"
parent="menu_trade_finance_configuration"
action="act_charge_type"/>
<record model="ir.model.access" id="access_charge_type">
<field name="model">trade_finance.charge_type</field>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_delete" eval="False"/>
</record>
<record model="ir.model.access" id="access_charge_type_admin">
<field name="model">trade_finance.charge_type</field>
<field name="group" ref="group_trade_finance_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_delete" eval="True"/>
</record>
</data>
</tryton>

View File

@@ -0,0 +1,61 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields, sequence_ordered
__all__ = ['TemplateSegment', 'ExecutionTemplate', 'ExecutionTemplateLine']
class TemplateSegment(ModelSQL, ModelView):
'Template Segment'
__name__ = 'trade_finance.template_segment'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
from_place = fields.Many2One(
'stock.location', 'From Place',
help='Origin location / port of loading')
to_place = fields.Many2One(
'stock.location', 'To Place',
help='Destination location / port of discharge')
default_duration_days = fields.Integer(
'Default Duration (days)',
help='Default number of days for this segment')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
class ExecutionTemplate(ModelSQL, ModelView):
'Execution Template'
__name__ = 'trade_finance.execution_template'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
description = fields.Text('Description')
lines = fields.One2Many(
'trade_finance.execution_template_line', 'template', 'Segments')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
class ExecutionTemplateLine(sequence_ordered(), ModelSQL, ModelView):
'Execution Template Line'
__name__ = 'trade_finance.execution_template_line'
template = fields.Many2One(
'trade_finance.execution_template', 'Template',
required=True, ondelete='CASCADE')
segment = fields.Many2One(
'trade_finance.template_segment', 'Segment',
required=True, ondelete='RESTRICT')
duration_days = fields.Integer(
'Duration (days)',
help='Overrides the segment default duration for this template')

View File

@@ -0,0 +1,12 @@
[tryton]
version=7.2.7
depends:
ir
res
stock
price
bank
xml:
reference.xml
facility.xml
constraint_type.xml

View File

@@ -0,0 +1,50 @@
# This file is part of Tradon. The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
from trytond.model import ModelSQL, ModelView, fields
__all__ = ['ValuationMethod', 'HaircutFormula', 'CollateralType']
class ValuationMethod(ModelSQL, ModelView):
'Valuation Method'
__name__ = 'trade_finance.valuation_method'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
description = fields.Text('Description')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
class HaircutFormula(ModelSQL, ModelView):
'Haircut Formula'
__name__ = 'trade_finance.haircut_formula'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
description = fields.Text('Description')
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True
class CollateralType(ModelSQL, ModelView):
'Collateral Type'
__name__ = 'trade_finance.collateral_type'
_rec_name = 'name'
code = fields.Char('Code', required=True)
name = fields.Char('Name', required=True)
active = fields.Boolean('Active')
@staticmethod
def default_active():
return True

View File

@@ -0,0 +1,8 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
<label name="description"/>
<field name="description" colspan="4"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,8 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,12 @@
<form col="4">
<group id="main" col="4" colspan="4">
<label name="name"/>
<field name="name" colspan="3"/>
<label name="view_name"/>
<field name="view_name" colspan="3"/>
<label name="value_field"/>
<field name="value_field" colspan="3"/>
<label name="label_field"/>
<field name="label_field" colspan="3"/>
</group>
</form>

View File

@@ -0,0 +1,3 @@
<tree>
<field name="name"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
<label name="description"/>
<field name="description" colspan="4"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,12 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
<label name="description"/>
<field name="description" colspan="4"/>
<newline/>
<field name="lines" colspan="4"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="sequence"/>
<field name="segment"/>
<field name="duration_days"/>
</tree>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,7 @@
<tree>
<field name="attribute"/>
<field name="value"/>
<field name="haircut_pct"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>

View File

@@ -0,0 +1,6 @@
<tree>
<field name="name"/>
<field name="amount"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="constraint_type"/>
<field name="constraint_type" colspan="3"/>
<label name="date_from"/>
<field name="date_from"/>
<label name="date_to"/>
<field name="date_to"/>
<label name="is_exclusion"/>
<field name="is_exclusion"/>
</form>

View File

@@ -0,0 +1,6 @@
<tree>
<field name="constraint_type"/>
<field name="is_exclusion"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>

View File

@@ -0,0 +1,6 @@
<tree>
<field name="name"/>
<field name="ratio_type"/>
<field name="threshold"/>
<field name="currency"/>
</tree>

View File

@@ -0,0 +1,8 @@
<tree>
<field name="currency"/>
<field name="valuation_method"/>
<field name="fx_haircut_formula"/>
<field name="fx_feeder"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>

View File

@@ -0,0 +1,50 @@
<form col="6">
<group id="header" col="6" colspan="6">
<label name="name"/>
<field name="name" colspan="3"/>
<label name="status"/>
<field name="status" widget="selection"/>
<label name="tfe"/>
<field name="tfe" colspan="3"/>
<label name="commitment_status"/>
<field name="commitment_status"/>
<label name="currency"/>
<field name="currency"/>
<label name="fx_feeder"/>
<field name="fx_feeder"/>
<label name="fx_haircut_pct"/>
<field name="fx_haircut_pct"/>
<newline/>
<label name="date_from"/>
<field name="date_from"/>
<label name="date_to"/>
<field name="date_to"/>
<label name="is_tpa"/>
<field name="is_tpa"/>
<label name="broker"/>
<field name="broker"/>
<label name="broker_account"/>
<field name="broker_account" colspan="3"/>
<newline/>
<label name="description"/>
<field name="description" colspan="5"/>
</group>
<notebook colspan="6">
<page string="Limits" id="limits">
<field name="limits" colspan="6"
domain="[('parent', '=', None)]"/>
</page>
<page string="Currencies" id="currencies">
<field name="currencies" colspan="6"/>
</page>
<page string="Constraints" id="constraints">
<field name="constraints" colspan="6"/>
</page>
<page string="Caps" id="caps">
<field name="caps" colspan="6"/>
</page>
<page string="Covenants" id="covenants">
<field name="covenants" colspan="6"/>
</page>
</notebook>
</form>

View File

@@ -0,0 +1,7 @@
<tree>
<field name="bank_account"/>
<field name="currency"/>
<field name="is_default"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>

View File

@@ -0,0 +1,11 @@
<tree>
<field name="cost_type"/>
<field name="spread"/>
<field name="index"/>
<field name="index_term"/>
<field name="interest_formula"/>
<field name="flat_amount"/>
<field name="flat_currency"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>

View File

@@ -0,0 +1,7 @@
<tree>
<field name="attribute"/>
<field name="value"/>
<field name="variation_type"/>
<field name="variation_value"/>
<field name="variation_currency"/>
</tree>

View File

@@ -0,0 +1,8 @@
<tree>
<field name="currency"/>
<field name="haircut_pct"/>
<field name="fx_feeder"/>
<field name="valuation_method"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>

View File

@@ -0,0 +1,44 @@
<form col="4">
<group id="header" col="4" colspan="4">
<label name="name"/>
<field name="name" colspan="3"/>
<label name="alternative_name"/>
<field name="alternative_name" colspan="3"/>
<label name="financing_type"/>
<field name="financing_type" colspan="3"/>
<label name="amount"/>
<field name="amount"/>
<label name="tenor"/>
<field name="tenor"/>
<label name="sequence"/>
<field name="sequence"/>
<newline/>
<label name="parent"/>
<field name="parent" colspan="3"/>
<label name="facility"/>
<field name="facility" colspan="3"/>
</group>
<notebook colspan="4">
<page string="Sub-Limits" id="children">
<field name="children" colspan="4"/>
</page>
<page string="Haircuts" id="haircuts">
<field name="haircuts" colspan="4"/>
</page>
<page string="Currencies" id="currencies">
<field name="currencies" colspan="4"/>
</page>
<page string="Costs" id="costs">
<field name="costs" colspan="4"/>
</page>
<page string="Operational Statuses" id="op_statuses">
<field name="op_statuses" colspan="4"/>
</page>
<page string="Bank Accounts" id="bank_accounts">
<field name="bank_accounts" colspan="4"/>
</page>
<page string="Constraints" id="limit_constraints">
<field name="constraints" colspan="4"/>
</page>
</notebook>
</form>

View File

@@ -0,0 +1,7 @@
<tree>
<field name="attribute"/>
<field name="value"/>
<field name="haircut_pct"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>

View File

@@ -0,0 +1,4 @@
<tree>
<field name="operational_status"/>
<field name="evidence_type"/>
</tree>

View File

@@ -0,0 +1,7 @@
<tree expand="1">
<field name="name"/>
<field name="parent"/>
<field name="financing_type"/>
<field name="amount"/>
<field name="tenor"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<group id="main" col="4" colspan="4">
<label name="code"/>
<field name="code"/>
<label name="active"/>
<field name="active"/>
<label name="name"/>
<field name="name" colspan="3"/>
</group>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,9 @@
<tree>
<field name="name"/>
<field name="tfe"/>
<field name="currency"/>
<field name="status"/>
<field name="commitment_status"/>
<field name="date_from"/>
<field name="date_to"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="sequence"/>
<field name="sequence"/>
<label name="active"/>
<field name="active"/>
</form>

View File

@@ -0,0 +1,6 @@
<tree>
<field name="sequence"/>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
<label name="source_description"/>
<field name="source_description" colspan="4"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
<label name="description"/>
<field name="description" colspan="4"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="days"/>
<field name="days"/>
<label name="active"/>
<field name="active"/>
</form>

View File

@@ -0,0 +1,6 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="days"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,8 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
<newline/>
<field name="rates" colspan="4"/>
</form>

View File

@@ -0,0 +1,8 @@
<form col="4">
<label name="index"/>
<field name="index"/>
<label name="rate_date"/>
<field name="rate_date"/>
<label name="rate"/>
<field name="rate"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="index"/>
<field name="rate_date"/>
<field name="rate"/>
</tree>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,12 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="financing_type"/>
<field name="financing_type"/>
<label name="sequence"/>
<field name="sequence"/>
<label name="active"/>
<field name="active"/>
</form>

View File

@@ -0,0 +1,7 @@
<tree>
<field name="sequence"/>
<field name="code"/>
<field name="name"/>
<field name="financing_type"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="remaining_risk_pct"/>
<field name="remaining_risk_pct"/>
<label name="active"/>
<field name="active"/>
</form>

View File

@@ -0,0 +1,6 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="remaining_risk_pct"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
<label name="description"/>
<field name="description" colspan="4"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,14 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="from_place"/>
<field name="from_place"/>
<label name="to_place"/>
<field name="to_place"/>
<label name="default_duration_days"/>
<field name="default_duration_days"/>
<label name="active"/>
<field name="active"/>
</form>

View File

@@ -0,0 +1,8 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="from_place"/>
<field name="to_place"/>
<field name="default_duration_days"/>
<field name="active"/>
</tree>

View File

@@ -0,0 +1,10 @@
<form col="4">
<label name="code"/>
<field name="code"/>
<label name="name"/>
<field name="name"/>
<label name="active"/>
<field name="active"/>
<label name="description"/>
<field name="description" colspan="4"/>
</form>

View File

@@ -0,0 +1,5 @@
<tree>
<field name="code"/>
<field name="name"/>
<field name="active"/>
</tree>