diff --git a/modules/purchase_trade/__init__.py b/modules/purchase_trade/__init__.py
index 0bfb6d0..ee2ed4e 100755
--- a/modules/purchase_trade/__init__.py
+++ b/modules/purchase_trade/__init__.py
@@ -72,6 +72,8 @@ def register():
ctrm_reporting.CTRMPnlExplainContext,
ctrm_reporting.CTRMPnlDimension,
ctrm_reporting.CTRMPnlDimensionContext,
+ ctrm_reporting.CTRMCreditRisk,
+ ctrm_reporting.CTRMCreditRiskContext,
configuration.Configuration,
pricing.ImportPricesStart,
pricing.ImportPricesResult,
diff --git a/modules/purchase_trade/ctrm_reporting.py b/modules/purchase_trade/ctrm_reporting.py
index 83a868a..bb70a84 100644
--- a/modules/purchase_trade/ctrm_reporting.py
+++ b/modules/purchase_trade/ctrm_reporting.py
@@ -777,3 +777,122 @@ class CTRMPnlDimension(ModelSQL, ModelView):
grouped.amount.as_('amount'),
grouped.mtm.as_('mtm'),
grouped.mtm_delta.as_('mtm_delta'))
+
+
+class CTRMCreditRiskContext(ModelView):
+ "CTRM Credit Risk Context"
+ __name__ = 'ctrm.reporting.risk.credit.context'
+
+ party = fields.Many2One('party.party', "Party")
+ currency = fields.Many2One('currency.currency', "Credit Currency")
+ risk_level = fields.Selection([
+ (None, ''),
+ ('low', 'Low'),
+ ('medium', 'Medium'),
+ ('high', 'High'),
+ ], "Risk Level")
+ over_limit_only = fields.Boolean("Over Limit Only")
+
+
+class CTRMCreditRisk(ModelSQL, ModelView):
+ "CTRM Credit Risk"
+ __name__ = 'ctrm.reporting.risk.credit'
+
+ party = fields.Many2One('party.party', "Party")
+ credit_limit = fields.Numeric("Credit Limit", digits=(16, 2))
+ credit_currency = fields.Many2One(
+ 'currency.currency', "Credit Currency")
+ open_exposure = fields.Numeric("Open Exposure", digits=(16, 2))
+ internal_limit = fields.Numeric("Internal Limit", digits=(16, 2))
+ insurance_limit = fields.Numeric("Insurance Limit", digits=(16, 2))
+ available_credit = fields.Numeric("Available Credit", digits=(16, 2))
+ utilization = fields.Numeric("Utilization %", digits=(16, 2))
+ risk_level = fields.Selection([
+ (None, ''),
+ ('low', 'Low'),
+ ('medium', 'Medium'),
+ ('high', 'High'),
+ ], "Risk Level")
+ status = fields.Char("Status")
+
+ @classmethod
+ def table_query(cls):
+ Party = Pool().get('party.party')
+ MoveLine = Pool().get('account.move.line')
+ InternalLimit = Pool().get('party.internal.limit')
+ InsuranceLimit = Pool().get('party.insurance.limit')
+
+ party = Party.__table__()
+ move_line = MoveLine.__table__()
+ internal_limit = InternalLimit.__table__()
+ insurance_limit = InsuranceLimit.__table__()
+
+ exposure_query = move_line.select(
+ move_line.party.as_('party'),
+ Sum(Coalesce(move_line.debit, 0)
+ - Coalesce(move_line.credit, 0)).as_('open_exposure'),
+ where=((move_line.party != Null)
+ & (move_line.reconciliation == Null)),
+ group_by=[move_line.party])
+ internal_query = internal_limit.select(
+ internal_limit.party.as_('party'),
+ Sum(Coalesce(internal_limit.amount, 0)).as_('internal_limit'),
+ group_by=[internal_limit.party])
+ insurance_query = insurance_limit.select(
+ insurance_limit.party.as_('party'),
+ Sum(Coalesce(insurance_limit.amount, 0)).as_('insurance_limit'),
+ group_by=[insurance_limit.party])
+
+ exposure = Coalesce(exposure_query.open_exposure, 0)
+ limit = Coalesce(party.credit_limit, 0)
+ utilization = Case(
+ (limit != 0, (exposure / limit) * 100),
+ else_=0)
+ available_credit = limit - exposure
+ status = Case(
+ (exposure > limit, 'Over limit'),
+ (exposure > limit * 0.8, 'Warning'),
+ else_='OK')
+
+ context = Transaction().context
+ where = (
+ (party.credit_limit != Null)
+ | (exposure_query.open_exposure != Null)
+ | (internal_query.internal_limit != Null)
+ | (insurance_query.insurance_limit != Null))
+ if context.get('party'):
+ where &= party.id == context['party']
+ if context.get('currency'):
+ where &= party.credit_currency == context['currency']
+ if context.get('risk_level'):
+ where &= party.risk_level == context['risk_level']
+ if context.get('over_limit_only'):
+ where &= exposure > limit
+
+ return (
+ party
+ .join(exposure_query, 'LEFT',
+ condition=exposure_query.party == party.id)
+ .join(internal_query, 'LEFT',
+ condition=internal_query.party == party.id)
+ .join(insurance_query, 'LEFT',
+ condition=insurance_query.party == party.id)
+ .select(
+ Literal(0).as_('create_uid'),
+ CurrentTimestamp().as_('create_date'),
+ Literal(None).as_('write_uid'),
+ Literal(None).as_('write_date'),
+ party.id.as_('id'),
+ party.id.as_('party'),
+ party.credit_limit.as_('credit_limit'),
+ party.credit_currency.as_('credit_currency'),
+ exposure.as_('open_exposure'),
+ Coalesce(internal_query.internal_limit, 0).as_(
+ 'internal_limit'),
+ Coalesce(insurance_query.insurance_limit, 0).as_(
+ 'insurance_limit'),
+ available_credit.as_('available_credit'),
+ utilization.as_('utilization'),
+ party.risk_level.as_('risk_level'),
+ status.as_('status'),
+ where=where))
diff --git a/modules/purchase_trade/ctrm_reporting.xml b/modules/purchase_trade/ctrm_reporting.xml
index 870a898..c9ac973 100644
--- a/modules/purchase_trade/ctrm_reporting.xml
+++ b/modules/purchase_trade/ctrm_reporting.xml
@@ -140,6 +140,26 @@
+
+ ctrm.reporting.risk.credit.context
+ form
+ ctrm_credit_risk_context_form
+
+
+ ctrm.reporting.risk.credit
+ tree
+ ctrm_credit_risk_list
+
+
+ Credit Risk
+ ctrm.reporting.risk.credit
+ ctrm.reporting.risk.credit.context
+
+
+
+
+
+