Merge branch 'charges_model' of DigiFi/digifi-BankToProductCore into master

This commit is contained in:
2025-04-17 15:13:48 +00:00
committed by Gogs
7 changed files with 805 additions and 57 deletions
+5 -51
View File
@@ -8,7 +8,7 @@ from app.models.loan_charge import LoanCharge
from app.utils.logger import logger from app.utils.logger import logger
from app.api.schemas.provide_loan import ProvideLoanSchema from app.api.schemas.provide_loan import ProvideLoanSchema
from threading import Thread from threading import Thread
from app.models import Loan, Offer from app.models import Loan, Offer, Charge
from app.api.enums import LoanStatus from app.api.enums import LoanStatus
from app.extensions import db from app.extensions import db
@@ -81,62 +81,16 @@ class ProvideLoanService(BaseService):
"message": "Failed to save loan details." "message": "Failed to save loan details."
}), 400 }), 400
charges = Charge.get_offer_charges(offer.id)
charges = [ logger.error(f"{charges}")
{"code": "INTEREST", "percent": 1.1, "due": 0, "description": "This is fee 9000"},
{"code": "MGTFEE", "percent": 1.5, "due": 0, "description": "This is fee 90002"},
{"code": "INSURANCE", "percent": 1.5, "due": 30, "description": "This is fee 90003"},
{"code": "VAT", "percent": 1.5, "due": 60, "description": "This is fee 90004"},
]
loan_id = loan.id loan_id = loan.id
loan_charges = LoanCharge.create_charges_for_loan(loan_id = loan_id, transaction_id = transaction_id, referenced_amount = 800, charges = charges) loan_charges = LoanCharge.create_charges_for_loan(loan_id = loan_id, transaction_id = transaction_id, referenced_amount = 800, charges = charges)
# logger.error(f"********* We need to develop the fee array here")
# loan_def = {
# "offers": [
# {
# "offerId": "SAL90",
# "productId": "2030",
# "minAmount": 5000,
# "maxAmount": 100000,
# "tenor": 30
# },
# {
# "offerId": "SAL30",
# "productId": "2090",
# "minAmount": 3000,
# "maxAmount": 500000,
# "tenor": 90
# }
# ],
# "loan_fee": {
# "SAL30": [
# {"code": "INTEREST", "percent": 1.1, "due": 0, "description": "This is fee 000"},
# {"code": "MGTFEE", "percent": 2.5, "due": 0, "description": "This is fee 001"},
# {"code": "INSURANCE", "percent": 3.5, "due": 0, "description": "This is fee 001"},
# {"code": "VAT", "percent": 1.0, "due": 0, "description": "This is fee 001"},
# ],
# "SAL90": [
# {"code": "INTEREST", "percent": 1.1, "due": 0, "description": "This is fee 9000"},
# {"code": "MGTFEE", "percent": 1.5, "due": 0, "description": "This is fee 90002"},
# {"code": "INSURANCE", "percent": 1.5, "due": 30, "description": "This is fee 90003"},
# {"code": "VAT", "percent": 1.5, "due": 60, "description": "This is fee 90004"},
# ]
# }
# }
# Log Transaction
# transaction = ProvideLoanService.log_transaction(validated_data = validated_data)
#
# if not transaction:
# logger.error(f"Failed to log transaction")
# return jsonify({
# "message": "Failed to log transaction."
# }), 400
else: else:
return jsonify({ return jsonify({
+2 -1
View File
@@ -5,6 +5,7 @@ from .transaction import Transaction
from .repayment import Repayment from .repayment import Repayment
from .loan_charge import LoanCharge from .loan_charge import LoanCharge
from .offer import Offer from .offer import Offer
from .charge import Charge
__all__ = ['Customer', 'Account', 'Loan', 'Transaction', 'Repayment', 'LoanCharge', 'Offer'] __all__ = ['Customer', 'Account', 'Loan', 'Transaction', 'Repayment', 'LoanCharge', 'Offer', 'Charge']
+95
View File
@@ -0,0 +1,95 @@
from datetime import datetime, timezone, timedelta
from app.extensions import db
from sqlalchemy.orm import relationship
class Charge(db.Model):
__tablename__ = 'charges'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
offer_id = db.Column(db.String(50), nullable=False)
code = db.Column(db.String(50), nullable=False)
percent = db.Column(db.Float, default=0.0)
description = db.Column(db.Text, nullable=True)
due = db.Column(db.Integer, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc))
updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
offer = relationship(
"Offer",
primaryjoin="Charge.offer_id == Offer.id",
foreign_keys=[offer_id],
back_populates="charges",
)
@classmethod
def add_charges(cls, offer_id, charges):
"""
Add charges to an offer.
Args:
offer_id (int): ID of the offer to associate charges with.
charges (list): A list of dictionaries with keys:
code (str), amount (float), percent (float), description (str), due (int)
"""
if not charges or not isinstance(charges, list):
raise ValueError("Charges must be a non-empty list of dictionaries")
if offer_id is None:
raise ValueError("offer_id cannot be None")
offer_charges = []
for charge in charges:
code = charge.get("code")
percent = charge.get("percent", 0.0)
description = charge.get("description", "")
due_days = charge.get("due", 0)
existing = cls.query.filter_by(offer_id=offer_id, code=code).first()
if existing:
continue
charge_obj = cls(
offer_id = offer_id,
code = code,
percent = percent,
description = description,
due = due_days
)
db.session.add(charge_obj)
offer_charges.append(charge_obj)
return offer_charges
@classmethod
def get_offer_charges(cls, offer_id):
"""
Get all charges for a particular offer as a dictionary
Args:
offer_id (str): The offer ID.
"""
if not offer_id:
raise ValueError("offer_id not found")
charges = cls.query.filter_by(offer_id=offer_id).all()
return charges
def to_dict(self):
return {
'id': self.id,
'offerId': self.offer_id,
'code': self.code,
'percent': self.percent,
'description': self.description,
'due': self.due
}
def __repr__(self):
return f"<Charge {self.id} - Offer {self.offer_id} - {self.code}>"
+5 -5
View File
@@ -45,9 +45,9 @@ class LoanCharge(db.Model):
now = datetime.now(timezone.utc) now = datetime.now(timezone.utc)
for charge in charges: for charge in charges:
due_days = charge.get("due", 0) due_days = getattr(charge, "due", 0)
amount = charge.get("amount", 0.0) amount = getattr(charge, "amount", 0.0)
percent = charge.get("percent", 0.0) percent = getattr(charge, "percent", 0.0)
if amount == 0.0: if amount == 0.0:
amount = (percent / 100.0) * referenced_amount amount = (percent / 100.0) * referenced_amount
@@ -55,10 +55,10 @@ class LoanCharge(db.Model):
charge_obj = cls( charge_obj = cls(
loan_id = loan_id, loan_id = loan_id,
transaction_id = transaction_id, transaction_id = transaction_id,
code = charge.get("code"), code = getattr(charge, "code"),
amount = amount, amount = amount,
percent = percent, percent = percent,
description = charge.get("description", ""), description = getattr(charge, "description", ""),
due = due_days, due = due_days,
due_date = now + timedelta(days=due_days) due_date = now + timedelta(days=due_days)
) )
+9
View File
@@ -1,5 +1,7 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from app.extensions import db from app.extensions import db
from app.models.charge import Charge
from sqlalchemy.orm import relationship
class Offer(db.Model): class Offer(db.Model):
__tablename__ = 'offers' __tablename__ = 'offers'
@@ -12,6 +14,13 @@ class Offer(db.Model):
created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc)) created_at = db.Column(db.DateTime, default=datetime.now(timezone.utc))
updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc)) updated_at = db.Column(db.DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
charges = relationship(
"Charge",
primaryjoin="Offer.id == Charge.offer_id",
foreign_keys="Charge.offer_id",
back_populates="offer",
)
@classmethod @classmethod
def get_all_offers(cls): def get_all_offers(cls):
""" """
@@ -0,0 +1,38 @@
"""Migration on Thu Apr 17 14:15:36 UTC 2025
Revision ID: de9ad96ba34e
Revises: ec8d97f9b584
Create Date: 2025-04-17 14:16:16.537466
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'de9ad96ba34e'
down_revision = 'ec8d97f9b584'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('charges',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('offer_id', sa.String(length=50), nullable=False),
sa.Column('code', sa.String(length=50), nullable=False),
sa.Column('percent', sa.Float(), nullable=True),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('due', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('charges')
# ### end Alembic commands ###
+651
View File
@@ -0,0 +1,651 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Simbrella FirstAdvance API Test">
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables">
<collectionProp name="Arguments.arguments">
<elementProp name="baseUrl" elementType="Argument">
<stringProp name="Argument.name">baseUrl</stringProp>
<stringProp name="Argument.value">http://localhost:4500</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="username" elementType="Argument">
<stringProp name="Argument.name">username</stringProp>
<stringProp name="Argument.value">user</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
<elementProp name="password" elementType="Argument">
<stringProp name="Argument.name">password</stringProp>
<stringProp name="Argument.value">password</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
</TestPlan>
<hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Authorizaton Thread Group">
<intProp name="ThreadGroup.num_threads">1</intProp>
<intProp name="ThreadGroup.ramp_time">1</intProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
<stringProp name="ThreadGroup.on_sample_error">stopthread</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller">
<stringProp name="LoopController.loops">1</stringProp>
<boolProp name="LoopController.continue_forever">false</boolProp>
</elementProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="1. Authorize" enabled="true">
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">4500</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.path">/Authorize</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{&#xd;
&quot;username&quot;:&quot;${username}&quot;,&#xd;
&quot;password&quot;:&quot;${password}&quot;&#xd;
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="JSON Extractor - Auth Token" enabled="true">
<stringProp name="JSONPostProcessor.referenceNames">access_token</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.data.access_token</stringProp>
<stringProp name="JSONPostProcessor.match_numbers">1</stringProp>
<stringProp name="JSONPostProcessor.defaultValues">NOT_FOUND</stringProp>
<stringProp name="JSONPostProcessor.scope">variable</stringProp>
<stringProp name="Scope.variable"></stringProp>
<stringProp name="Sample.scope">all</stringProp>
</JSONPostProcessor>
<hashTree/>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="JSON Extractor - Auth Refresh Token" enabled="true">
<stringProp name="JSONPostProcessor.referenceNames">refresh_token</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.data.refresh_token</stringProp>
<stringProp name="JSONPostProcessor.match_numbers">1</stringProp>
<stringProp name="JSONPostProcessor.defaultValues">NOT_FOUND</stringProp>
<stringProp name="JSONPostProcessor.scope">variable</stringProp>
<stringProp name="Scope.variable"></stringProp>
<stringProp name="Sample.scope">all</stringProp>
</JSONPostProcessor>
<hashTree/>
<JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="JSR223 PostProcessor" enabled="true">
<stringProp name="cacheKey">true</stringProp>
<stringProp name="filename"></stringProp>
<stringProp name="parameters"></stringProp>
<stringProp name="script">props.put(&quot;GLOBAL_ACCESS_TOKEN&quot;, vars.get(&quot;access_token&quot;));
props.put(&quot;GLOBAL_REFRESH_TOKEN&quot;, vars.get(&quot;refresh_token&quot;));</stringProp>
<stringProp name="scriptLanguage">groovy</stringProp>
</JSR223PostProcessor>
<hashTree/>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings"/>
<collectionProp name="Asserter.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
<stringProp name="Assertion.custom_message"></stringProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
<DebugSampler guiclass="TestBeanGUI" testclass="DebugSampler" testname="Debug Sampler" enabled="true">
<boolProp name="displayJMeterProperties">false</boolProp>
<boolProp name="displayJMeterVariables">true</boolProp>
<boolProp name="displaySystemProperties">false</boolProp>
</DebugSampler>
<hashTree/>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="API Test Thread Group">
<intProp name="ThreadGroup.num_threads">1</intProp>
<intProp name="ThreadGroup.ramp_time">1</intProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller">
<stringProp name="LoopController.loops">1</stringProp>
<boolProp name="LoopController.continue_forever">false</boolProp>
</elementProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="2. Authorize Refresh" enabled="true">
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">4500</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.path">/AuthorizeRefresh</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{&#xd;
&#xd;
}&#xd;
</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Authorization</stringProp>
<stringProp name="Header.value">Bearer ${__P(GLOBAL_REFRESH_TOKEN)}</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings"/>
<collectionProp name="Asserter.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
<stringProp name="Assertion.custom_message"></stringProp>
</ResponseAssertion>
<hashTree/>
<JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="JSR223 PreProcessor" enabled="true">
<stringProp name="scriptLanguage">groovy</stringProp>
<stringProp name="parameters"></stringProp>
<stringProp name="filename"></stringProp>
<stringProp name="cacheKey">true</stringProp>
<stringProp name="script">// Generate random IDs and store them in JMeter variables
def transactionId = &quot;TR&quot; + org.apache.commons.lang3.RandomStringUtils.randomAlphanumeric(12)
def customerId = &quot;CN&quot; + org.apache.commons.lang3.RandomUtils.nextInt(1000000, 9999999)
def accountId = &quot;ACN&quot; + org.apache.commons.lang3.RandomUtils.nextInt(1000000, 9999999)
def msisdn = &quot;809&quot; + org.apache.commons.lang3.RandomUtils.nextInt(1000000, 9999999)
// Generate requestId: current timestamp + 6-digit random number
def timestamp = new Date().format(&quot;yyyyMMddHHmmssSSS&quot;) // e.g., 20250414161243123
def randomSuffix = org.apache.commons.lang3.RandomStringUtils.randomNumeric(6)
def requestId = timestamp + randomSuffix
vars.put(&quot;transactionId&quot;, transactionId)
vars.put(&quot;customerId&quot;, customerId.toString())
vars.put(&quot;accountId&quot;, accountId.toString())
vars.put(&quot;msisdn&quot;, msisdn.toString())
vars.put(&quot;requestId&quot;, requestId)
</stringProp>
</JSR223PreProcessor>
<hashTree/>
</hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="3. Eligibility Check">
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">4500</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.path">/EligibilityCheck</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{&#xd;
&quot;transactionId&quot;:&quot;${transactionId}&quot;,&#xd;
&quot;countryCode&quot;:&quot;NGR&quot;,&#xd;
&quot;customerId&quot;:&quot;${customerId}&quot;,&#xd;
&quot;msisdn&quot;:&quot;${msisdn}&quot;,&#xd;
&quot;channel&quot;:&quot;100&quot;,&#xd;
&quot;accountId&quot;:&quot;${accountId}&quot;&#xd;
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Authorization</stringProp>
<stringProp name="Header.value">Bearer ${__P(GLOBAL_ACCESS_TOKEN)}</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="JSON Extractor - Product ID">
<stringProp name="JSONPostProcessor.referenceNames">productId</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.eligibleOffers[0].productId</stringProp>
<stringProp name="JSONPostProcessor.match_numbers">0</stringProp>
<stringProp name="JSONPostProcessor.defaultValues">NOT_FOUND</stringProp>
<stringProp name="JSONPostProcessor.scope">variable</stringProp>
</JSONPostProcessor>
<hashTree/>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings"/>
<collectionProp name="Asserter.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
<stringProp name="Assertion.custom_message"></stringProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="4. Select Offer">
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">4500</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.path">/SelectOffer</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{&#xd;
&quot;requestId&quot;: &quot;${requestId}&quot;,&#xd;
&quot;transactionId&quot;: &quot;${transactionId}&quot;,&#xd;
&quot;customerId&quot;:&quot;${customerId}&quot;,&#xd;
&quot;msisdn&quot;: &quot;${msisdn}&quot;,&#xd;
&quot;requestedAmount&quot;: ${__Random(500000,1000000,)}.00,&#xd;
&quot;accountId&quot;:&quot;${accountId}&quot;,&#xd;
&quot;productId&quot;: &quot;${productId}&quot;,&#xd;
&quot;channel&quot;: &quot;100&quot;&#xd;
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Authorization</stringProp>
<stringProp name="Header.value">Bearer ${__P(GLOBAL_ACCESS_TOKEN)}</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="JSON Extractor - Offer ID">
<stringProp name="JSONPostProcessor.referenceNames">offerId</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.loan[0].offerId</stringProp>
<stringProp name="JSONPostProcessor.match_numbers">0</stringProp>
<stringProp name="JSONPostProcessor.scope">variable</stringProp>
<stringProp name="JSONPostProcessor.defaultValues">NOT_FOUND</stringProp>
<stringProp name="Sample.scope">all</stringProp>
</JSONPostProcessor>
<hashTree/>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="JSON Extractor - Requested Amount" enabled="true">
<stringProp name="JSONPostProcessor.referenceNames">amount</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.loan[0].amount</stringProp>
<stringProp name="JSONPostProcessor.match_numbers">0</stringProp>
<stringProp name="JSONPostProcessor.defaultValues">800</stringProp>
<stringProp name="JSONPostProcessor.scope">variable</stringProp>
<stringProp name="Sample.scope">all</stringProp>
</JSONPostProcessor>
<hashTree/>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings"/>
<collectionProp name="Asserter.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
<stringProp name="Assertion.custom_message"></stringProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="5. Provide Loan">
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">4500</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.path">/ProvideLoan</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{&#xd;
&quot;requestId&quot;:&quot;${requestId}&quot;,&#xd;
&quot;transactionId&quot;:&quot;${transactionId}&quot;,&#xd;
&quot;customerId&quot;:&quot;${customerId}&quot;,&#xd;
&quot;accountId&quot;:&quot;${accountId}&quot;,&#xd;
&quot;msisdn&quot;:&quot;${msisdn}&quot;,&#xd;
&quot;requestedAmount&quot;:${amount},&#xd;
&quot;collectionType&quot;:1,&#xd;
&quot;offerId&quot;:&quot;${offerId}&quot;,&#xd;
&quot;channel&quot;:&quot;100&quot;&#xd;
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Authorization</stringProp>
<stringProp name="Header.value">Bearer ${__P(GLOBAL_ACCESS_TOKEN)}</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings"/>
<collectionProp name="Asserter.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
<stringProp name="Assertion.custom_message"></stringProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="6. Loan Status" enabled="true">
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">4500</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.path">/LoanStatus</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{&#xd;
&quot;transactionId&quot;:&quot;${transactionId}&quot;,&#xd;
&quot;customerId&quot;:&quot;${customerId}&quot;,&#xd;
&quot;msisdn&quot;:&quot;${msisdn}&quot;,&#xd;
&quot;channel&quot;:&quot;100&quot;,&#xd;
&quot;accountId&quot;:&quot;${accountId}&quot;&#xd;
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Authorization</stringProp>
<stringProp name="Header.value">Bearer ${__P(GLOBAL_ACCESS_TOKEN)}</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<JSONPostProcessor guiclass="JSONPostProcessorGui" testclass="JSONPostProcessor" testname="JSON Extractor - Debt Id" enabled="true">
<stringProp name="JSONPostProcessor.referenceNames">debtId</stringProp>
<stringProp name="JSONPostProcessor.jsonPathExprs">$.loans[0].debtId</stringProp>
<stringProp name="JSONPostProcessor.match_numbers">0</stringProp>
<stringProp name="JSONPostProcessor.defaultValues">800</stringProp>
<stringProp name="JSONPostProcessor.scope">variable</stringProp>
<stringProp name="Sample.scope">all</stringProp>
</JSONPostProcessor>
<hashTree/>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings"/>
<collectionProp name="Asserter.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
<stringProp name="Assertion.custom_message"></stringProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="7. Repayment" enabled="true">
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">4500</stringProp>
<stringProp name="HTTPSampler.protocol">http</stringProp>
<stringProp name="HTTPSampler.path">/Repayment</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{&#xd;
&quot;debtId&quot;:&quot;${debtId}&quot;,&#xd;
&quot;transactionId&quot;:&quot;${transactionId}&quot;,&#xd;
&quot;customerId&quot;:&quot;${customerId}&quot;,&#xd;
&quot;msisdn&quot;:&quot;${msisdn}&quot;,&#xd;
&quot;channel&quot;:&quot;100&quot;,&#xd;
&quot;accountId&quot;:&quot;${accountId}&quot;,&#xd;
&quot;productId&quot;: &quot;${productId}&quot;&#xd;
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Accept</stringProp>
<stringProp name="Header.value">application/json</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Authorization</stringProp>
<stringProp name="Header.value">Bearer ${__P(GLOBAL_ACCESS_TOKEN)}</stringProp>
</elementProp>
</collectionProp>
</HeaderManager>
<hashTree/>
<ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
<collectionProp name="Asserion.test_strings"/>
<collectionProp name="Asserter.test_strings">
<stringProp name="49586">200</stringProp>
</collectionProp>
<stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
<stringProp name="Assertion.custom_message"></stringProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<DebugSampler guiclass="TestBeanGUI" testclass="DebugSampler" testname="Debug Sampler" enabled="true">
<boolProp name="displayJMeterProperties">false</boolProp>
<boolProp name="displayJMeterVariables">true</boolProp>
<boolProp name="displaySystemProperties">false</boolProp>
</DebugSampler>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</jmeterTestPlan>