Change on Facility definition
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
# 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, Workflow, fields
|
||||
from trytond.model import ModelSQL, ModelView, fields
|
||||
from trytond.model import sequence_ordered
|
||||
from trytond.pool import Pool
|
||||
from trytond.pyson import Eval, If, Bool
|
||||
from trytond.pyson import Eval, Bool
|
||||
from trytond.exceptions import UserError
|
||||
|
||||
__all__ = [
|
||||
'FacilityStatus',
|
||||
'Facility',
|
||||
'FacilityCurrency',
|
||||
'FacilityCovenant',
|
||||
'FacilityLimit',
|
||||
'FacilityLimitHaircut',
|
||||
@@ -43,20 +45,30 @@ ATTRIBUTE_TYPES = [
|
||||
('receivable_category', 'Receivable Category'),
|
||||
]
|
||||
|
||||
FACILITY_STATES = [
|
||||
('draft', 'Draft'),
|
||||
('active', 'Active'),
|
||||
('blocked', 'Blocked'),
|
||||
('cancelled', 'Cancelled'),
|
||||
('closed', 'Closed'),
|
||||
]
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 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(Workflow, ModelSQL, ModelView):
|
||||
class Facility(ModelSQL, ModelView):
|
||||
'TF Facility'
|
||||
__name__ = 'trade_finance.facility'
|
||||
_rec_name = 'name'
|
||||
@@ -67,8 +79,8 @@ class Facility(Workflow, ModelSQL, ModelView):
|
||||
help='Bank or fund providing this facility')
|
||||
description = fields.Text('Description')
|
||||
|
||||
status = fields.Selection(FACILITY_STATES, 'Status', required=True,
|
||||
readonly=True)
|
||||
status = fields.Many2One('trade_finance.facility_status', 'Status',
|
||||
ondelete='RESTRICT')
|
||||
commitment_status = fields.Selection([
|
||||
('uncommitted', 'Uncommitted'),
|
||||
('committed', 'Committed'),
|
||||
@@ -94,6 +106,8 @@ class Facility(Workflow, ModelSQL, ModelView):
|
||||
|
||||
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')
|
||||
@@ -101,10 +115,6 @@ class Facility(Workflow, ModelSQL, ModelView):
|
||||
'facility', 'Facility Constraints',
|
||||
domain=[('limit', '=', None)])
|
||||
|
||||
@staticmethod
|
||||
def default_status():
|
||||
return 'draft'
|
||||
|
||||
@staticmethod
|
||||
def default_commitment_status():
|
||||
return 'uncommitted'
|
||||
@@ -113,48 +123,23 @@ class Facility(Workflow, ModelSQL, ModelView):
|
||||
def default_is_tpa():
|
||||
return False
|
||||
|
||||
# Workflow transitions
|
||||
_transitions = {
|
||||
('draft', 'active'),
|
||||
('active', 'blocked'),
|
||||
('blocked', 'active'),
|
||||
('active', 'cancelled'),
|
||||
('active', 'closed'),
|
||||
('blocked', 'cancelled'),
|
||||
('blocked', 'closed'),
|
||||
}
|
||||
_buttons = {
|
||||
'activate': {'invisible': Eval('status') != 'draft'},
|
||||
'block': {'invisible': Eval('status') != 'active'},
|
||||
'unblock': {'invisible': Eval('status') != 'blocked'},
|
||||
'cancel': {'invisible': ~Eval('status').in_(['active', 'blocked'])},
|
||||
'close': {'invisible': ~Eval('status').in_(['active', 'blocked'])},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@Workflow.transition('active')
|
||||
def activate(cls, facilities):
|
||||
pass
|
||||
# ---------------------------------------------------------------------------
|
||||
# Facility Accepted Currency
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@classmethod
|
||||
@Workflow.transition('blocked')
|
||||
def block(cls, facilities):
|
||||
pass
|
||||
class FacilityCurrency(ModelSQL, ModelView):
|
||||
'Facility Currency'
|
||||
__name__ = 'trade_finance.facility_currency'
|
||||
|
||||
@classmethod
|
||||
@Workflow.transition('active')
|
||||
def unblock(cls, facilities):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@Workflow.transition('cancelled')
|
||||
def cancel(cls, facilities):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@Workflow.transition('closed')
|
||||
def close(cls, facilities):
|
||||
pass
|
||||
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')
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -204,6 +189,7 @@ class FacilityLimit(ModelSQL, ModelView):
|
||||
'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)
|
||||
@@ -213,13 +199,6 @@ class FacilityLimit(ModelSQL, ModelView):
|
||||
|
||||
_order = [('sequence', 'ASC'), ('id', 'ASC')]
|
||||
|
||||
is_global = fields.Function(
|
||||
fields.Boolean('Global Limit',
|
||||
states={'invisible': Bool(Eval('parent'))},
|
||||
depends=['parent']),
|
||||
'get_is_global')
|
||||
display_name = fields.Function(fields.Char('Name'), 'get_display_name')
|
||||
|
||||
haircuts = fields.One2Many('trade_finance.facility_limit_haircut', 'limit',
|
||||
'Haircuts')
|
||||
currencies = fields.One2Many('trade_finance.facility_limit_currency',
|
||||
@@ -247,25 +226,27 @@ class FacilityLimit(ModelSQL, ModelView):
|
||||
values['facility'] = parent.facility.id
|
||||
return super().create(vlist)
|
||||
|
||||
def get_is_global(self, name):
|
||||
return self.parent is None
|
||||
|
||||
def get_display_name(self, name):
|
||||
level = 0
|
||||
current = self
|
||||
while current.parent:
|
||||
level += 1
|
||||
current = current.parent
|
||||
prefix = '— ' * level
|
||||
return prefix + (self.name or '')
|
||||
|
||||
@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(
|
||||
@@ -491,9 +472,7 @@ class FacilityConstraint(ModelSQL, ModelView):
|
||||
cap = fields.Many2One('trade_finance.facility_cap', 'Cap',
|
||||
ondelete='CASCADE')
|
||||
|
||||
constraint_type = fields.Selection([
|
||||
('inclusion', 'Inclusion'),
|
||||
('exclusion', 'Exclusion'),
|
||||
], 'Type', required=True)
|
||||
attribute = fields.Selection(ATTRIBUTE_TYPES, 'Attribute', required=True)
|
||||
value = fields.Char('Value', required=True)
|
||||
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')
|
||||
|
||||
Reference in New Issue
Block a user