diff --git a/modules/trade_finance/__init__.py b/modules/trade_finance/__init__.py index 92b2408..f8db5d4 100644 --- a/modules/trade_finance/__init__.py +++ b/modules/trade_finance/__init__.py @@ -13,6 +13,7 @@ from . import ( fx, operational, facility, + constraint_type, ) @@ -36,7 +37,9 @@ def register(): fx.FxFeeder, operational.BlockingReason, operational.ChargeType, + facility.FacilityStatus, facility.Facility, + facility.FacilityCurrency, facility.FacilityCovenant, facility.FacilityLimit, facility.FacilityLimitHaircut, @@ -48,6 +51,7 @@ def register(): facility.FacilityCap, facility.FacilityCapHaircut, facility.FacilityConstraint, + constraint_type.ConstraintType, module='trade_finance', type_='model') Pool.register( fx.PriceCalendar, diff --git a/modules/trade_finance/constraint_type.py b/modules/trade_finance/constraint_type.py new file mode 100644 index 0000000..cba3f8e --- /dev/null +++ b/modules/trade_finance/constraint_type.py @@ -0,0 +1,24 @@ +# 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.pyson import Eval + +__all__ = ['ConstraintType'] + + +class ConstraintType(ModelSQL, ModelView): + 'Constraint Type' + __name__ = 'trade_finance.constraint_type' + _rec_name = 'name' + + name = fields.Char('Type', required=True, size=50) + model = fields.Many2One('ir.model', 'Table', ondelete='RESTRICT') + value_field = fields.Many2One('ir.model.field', 'Value Field', + ondelete='RESTRICT', + domain=[('model', '=', Eval('model'))], + depends=['model']) + label_field = fields.Many2One('ir.model.field', 'Label Field', + ondelete='RESTRICT', + domain=[('model', '=', Eval('model'))], + depends=['model']) diff --git a/modules/trade_finance/constraint_type.xml b/modules/trade_finance/constraint_type.xml new file mode 100644 index 0000000..9f5dd2f --- /dev/null +++ b/modules/trade_finance/constraint_type.xml @@ -0,0 +1,66 @@ + + + + + + + + + + trade_finance.constraint_type + tree + + constraint_type_tree + + + trade_finance.constraint_type + form + constraint_type_form + + + Constraint Types + trade_finance.constraint_type + + + + + + + + + + + + + + + trade_finance.constraint_type + + + + + + + trade_finance.constraint_type + + + + + + + + trade_finance.constraint_type + + + + + + + + + diff --git a/modules/trade_finance/facility.py b/modules/trade_finance/facility.py index 55cd8d5..215e28a 100644 --- a/modules/trade_finance/facility.py +++ b/modules/trade_finance/facility.py @@ -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') diff --git a/modules/trade_finance/facility.xml b/modules/trade_finance/facility.xml index 4a4afcd..1382dba 100644 --- a/modules/trade_finance/facility.xml +++ b/modules/trade_finance/facility.xml @@ -15,6 +15,66 @@ + + + + + + trade_finance.facility_status + tree + + facility_status_tree + + + trade_finance.facility_status + form + facility_status_form + + + Facility Statuses + trade_finance.facility_status + + + + + + + + + + + + + + + trade_finance.facility_status + + + + + + + trade_finance.facility_status + + + + + + + + trade_finance.facility_status + + + + + + + @@ -75,6 +135,32 @@ + + + + + + trade_finance.facility_currency + tree + + facility_currency_tree + + + trade_finance.facility_currency + + + + + + + trade_finance.facility_currency + + + + + + + diff --git a/modules/trade_finance/tryton.cfg b/modules/trade_finance/tryton.cfg index 328deab..6848bac 100644 --- a/modules/trade_finance/tryton.cfg +++ b/modules/trade_finance/tryton.cfg @@ -9,3 +9,4 @@ depends: xml: reference.xml facility.xml + constraint_type.xml diff --git a/modules/trade_finance/view/constraint_type_form.xml b/modules/trade_finance/view/constraint_type_form.xml new file mode 100644 index 0000000..73c68c6 --- /dev/null +++ b/modules/trade_finance/view/constraint_type_form.xml @@ -0,0 +1,12 @@ +
+ + +
diff --git a/modules/trade_finance/view/constraint_type_tree.xml b/modules/trade_finance/view/constraint_type_tree.xml new file mode 100644 index 0000000..8fd8540 --- /dev/null +++ b/modules/trade_finance/view/constraint_type_tree.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/modules/trade_finance/view/facility_constraint_tree.xml b/modules/trade_finance/view/facility_constraint_tree.xml index 8d882ba..dd51296 100644 --- a/modules/trade_finance/view/facility_constraint_tree.xml +++ b/modules/trade_finance/view/facility_constraint_tree.xml @@ -1,5 +1,4 @@ - - + diff --git a/modules/trade_finance/view/facility_currency_tree.xml b/modules/trade_finance/view/facility_currency_tree.xml new file mode 100644 index 0000000..859f9f9 --- /dev/null +++ b/modules/trade_finance/view/facility_currency_tree.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/modules/trade_finance/view/facility_form.xml b/modules/trade_finance/view/facility_form.xml index 586cfe7..4bc6377 100644 --- a/modules/trade_finance/view/facility_form.xml +++ b/modules/trade_finance/view/facility_form.xml @@ -30,8 +30,13 @@ + + + - + diff --git a/modules/trade_finance/view/facility_limit_form.xml b/modules/trade_finance/view/facility_limit_form.xml index 569a01a..c49dbfe 100644 --- a/modules/trade_finance/view/facility_limit_form.xml +++ b/modules/trade_finance/view/facility_limit_form.xml @@ -2,6 +2,8 @@