Merge branch 'testing' of DigiFi/FirstCore into master

This commit is contained in:
2025-04-24 12:53:52 +00:00
committed by Gogs
20 changed files with 787 additions and 274 deletions
+36 -9
View File
@@ -1,7 +1,8 @@
from flask import Blueprint, request, jsonify, send_from_directory from flask import Blueprint, request, jsonify, send_from_directory
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from app.api.services import RepaymentService from app.api.services.repayment_service import RepaymentService
from app.api.services.loan_charge_service import LoanChargeService
from app.api.services.loan_service import LoanService from app.api.services.loan_service import LoanService
from app.api.services.transaction_service import TransactionService from app.api.services.transaction_service import TransactionService
from app.api.services.auth_service import AuthService from app.api.services.auth_service import AuthService
@@ -138,11 +139,37 @@ def get_transactions():
response = TransactionService.process_request(filters) response = TransactionService.process_request(filters)
return response return response
# Repayment Endpoint @api.route('/repayments', methods=['GET'])
@api.route("/Repayment", methods=["POST"]) # @token_required
# @jwt_required() def get_all_repayments():
def repayment(): # Extract query parameters for filtering
data = request.get_json() filters = {
# logger.info(f"Repayment request received: {data}") 'loan_id': request.args.get('loan_id'),
response = RepaymentService.process_request(data) 'customer_id': request.args.get('customer_id'),
return response 'product_id': request.args.get('product_id'),
'start_date': request.args.get('start_date'),
'end_date': request.args.get('end_date'),
'page': request.args.get('page', 1),
'limit': request.args.get('limit', 20)
}
# logger.info(f"Get repayments request received with filters: {filters}")
response = RepaymentService.get_all_repayments(filters)
return jsonify(response)
@api.route('/loan-charges', methods=['GET'])
# @token_required
def get_all_loan_charges():
# Extract query parameters for filtering
filters = {
'loan_id': request.args.get('loan_id'),
'code': request.args.get('code'),
'start_date': request.args.get('start_date'),
'end_date': request.args.get('end_date'),
'due_before': request.args.get('due_before'),
'due_after': request.args.get('due_after'),
'page': request.args.get('page', 1),
'limit': request.args.get('limit', 20)
}
# logger.info(f"Get loan charges request received with filters: {filters}")
response = LoanChargeService.get_all_loan_charges(filters)
return jsonify(response)
-12
View File
@@ -1,12 +0,0 @@
from marshmallow import Schema, fields
# Repayment Schema
class RepaymentSchema(Schema):
type = fields.Str(required=False)
msisdn = fields.Str(required=False) #optional
debtId = fields.Str(required=True)
productId = fields.Str(required=True)
transactionId = fields.Str(required=True)
accountId = fields.Str(required=True)
customerId = fields.Str(required=True)
channel = fields.Str(required=True)
+1
View File
@@ -6,3 +6,4 @@ from app.api.services.loan_service import LoanService
from app.api.services.auth_service import AuthService from app.api.services.auth_service import AuthService
from app.api.services.dashboard_service import DashboardService from app.api.services.dashboard_service import DashboardService
from app.api.services.repayment_service import RepaymentService from app.api.services.repayment_service import RepaymentService
from app.api.services.loan_charge_service import LoanChargeService
+1 -1
View File
@@ -2,7 +2,7 @@ from flask import request, jsonify
from app.api.services.base_service import BaseService from app.api.services.base_service import BaseService
from marshmallow import ValidationError from marshmallow import ValidationError
from app.utils.logger import logger from app.utils.logger import logger
from app.api.schemas.customer_consent import CustomerConsentSchema from app.api.schemas.customer_consent_schema import CustomerConsentSchema
from app.api.services.base_service import BaseService from app.api.services.base_service import BaseService
from app.api.enums import TransactionType from app.api.enums import TransactionType
from app.extensions import db from app.extensions import db
+107
View File
@@ -0,0 +1,107 @@
import logging
from datetime import datetime
from flask import jsonify
from app.models.loan_charge import LoanCharge
# Configure logging
logger = logging.getLogger(__name__)
class LoanChargeService:
"""
Service class for handling loan charge-related operations.
"""
@staticmethod
def get_all_loan_charges(filters=None):
"""
Get all loan charges with optional filtering.
Args:
filters (dict, optional): Filters for the loan charges query.
Returns:
dict: A standardized response with loan charges data.
"""
try:
if filters is None:
filters = {}
# Extract filters
loan_id = filters.get('loan_id')
code = filters.get('code')
start_date = filters.get('start_date')
end_date = filters.get('end_date')
due_before = filters.get('due_before')
due_after = filters.get('due_after')
# Extract pagination parameters
page = int(filters.get('page', 1))
limit = int(filters.get('limit', 20))
# Ensure page and limit are valid
if page < 1:
page = 1
if limit < 1 or limit > 100:
limit = 20
# Convert string dates to datetime objects if provided
if start_date and isinstance(start_date, str):
start_date = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
if end_date and isinstance(end_date, str):
end_date = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
if due_before and isinstance(due_before, str):
due_before = datetime.fromisoformat(due_before.replace('Z', '+00:00'))
if due_after and isinstance(due_after, str):
due_after = datetime.fromisoformat(due_after.replace('Z', '+00:00'))
# Get loan charges with optional filters and pagination
loan_charges, total_count = LoanCharge.get_all_loan_charges(
loan_id=loan_id,
code=code,
start_date=start_date,
end_date=end_date,
due_before=due_before,
due_after=due_after,
page=page,
limit=limit
)
# Convert loan charges to dictionary format
loan_charges_data = []
for charge in loan_charges:
loan_charges_data.append({
'loan_id': charge.loan_id,
'code': charge.code,
'amount': charge.amount,
'percent': charge.percent,
'description': charge.description,
'due': charge.due,
'transaction_id': charge.transaction_id,
'due_date': charge.due_date.isoformat() if charge.due_date else None,
'created_at': charge.created_at.isoformat() if charge.created_at else None,
'updated_at': charge.updated_at.isoformat() if charge.updated_at else None
})
# Calculate total pages
total_pages = (total_count + limit - 1) // limit
response_data = {
'loan_charges': loan_charges_data,
'count': len(loan_charges_data),
'pagination': {
'total_count': total_count,
'total_pages': total_pages,
'current_page': page,
'limit': limit,
'has_next': page < total_pages,
'has_prev': page > 1
}
}
return response_data
except Exception as e:
logger.error(f"An error occurred: {str(e)}", exc_info=True)
return jsonify({
"message": "Internal Server Error"
}), 500
+1 -1
View File
@@ -3,7 +3,7 @@ from marshmallow import ValidationError
from app.api.enums.loan_status import LoanStatus from app.api.enums.loan_status import LoanStatus
from app.models import Customer from app.models import Customer
from app.utils.logger import logger from app.utils.logger import logger
from app.api.schemas.loan_status import LoanStatusSchema from app.api.schemas.loan_status_schema import LoanStatusSchema
from app.api.services.base_service import BaseService from app.api.services.base_service import BaseService
from app.api.enums import TransactionType from app.api.enums import TransactionType
from app.extensions import db from app.extensions import db
+73 -91
View File
@@ -1,115 +1,97 @@
from flask import request, jsonify import logging
from marshmallow import ValidationError from datetime import datetime
from app.api.enums.loan_status import LoanStatus from flask import jsonify
from app.models.customer import Customer
from app.models.loan import Loan
from app.models.repayment import Repayment from app.models.repayment import Repayment
from app.utils.logger import logger
from app.api.schemas.repayment import RepaymentSchema
from app.api.services.base_service import BaseService
from app.api.enums import TransactionType
from threading import Thread
from app.extensions import db
class RepaymentService(BaseService): # Configure logging
TRANSACTION_TYPE = TransactionType.REPAYMENT logger = logging.getLogger(__name__)
class RepaymentService:
"""
Service class for handling repayment-related operations.
"""
@staticmethod @staticmethod
def process_request(data): def get_all_repayments(filters=None):
""" """
Process the Repayment request. Get all repayments with optional filtering.
Args: Args:
data (dict): The request data. filters (dict, optional): Filters for the repayments query.
Returns: Returns:
dict: A standardized response. dict: A standardized response with repayments data.
""" """
try: try:
with db.session.begin(): if filters is None:
validated_data = RepaymentService.validate_data(data, RepaymentSchema()) filters = {}
customer_id = validated_data.get('customerId')
request_id = validated_data.get('requestId')
loan_id = validated_data.get('debtId')
product_id = validated_data.get('productId')
account_id = validated_data.get('accountId')
customer = Customer.get_customer(customer_id)
if(RepaymentService.validate_account_ownership(account_id = account_id, customer_id = customer_id)):
# Save the repayment details # Extract filters
repayment = Repayment.create_repayment( loan_id = filters.get('loan_id')
customer_id = customer_id, customer_id = filters.get('customer_id')
loan_id = loan_id, product_id = filters.get('product_id')
product_id = product_id start_date = filters.get('start_date')
end_date = filters.get('end_date')
) # Extract pagination parameters
page = int(filters.get('page', 1))
limit = int(filters.get('limit', 20))
if not repayment: # Ensure page and limit are valid
logger.error(f"Failed to save repayment details") if page < 1:
return jsonify({ page = 1
"message": "Failed to save repayment details." if limit < 1 or limit > 100:
}), 400 limit = 20
#Update Loan status
Loan.update_status(loan_id = loan_id, status = LoanStatus.REPAID)
transaction = RepaymentService.log_transaction(validated_data = validated_data)
if not transaction: # Convert string dates to datetime objects if provided
logger.error(f"Failed to log transaction") if start_date and isinstance(start_date, str):
return jsonify({ start_date = datetime.fromisoformat(start_date.replace('Z', '+00:00'))
"message": "Failed to log transaction." if end_date and isinstance(end_date, str):
}), 400 end_date = datetime.fromisoformat(end_date.replace('Z', '+00:00'))
else:
return jsonify({
"message": "Invalid Customer or Account"
}), 400
# Get repayments with optional filters and pagination
repayments, total_count = Repayment.get_all_repayments(
loan_id=loan_id,
customer_id=customer_id,
product_id=product_id,
start_date=start_date,
end_date=end_date,
page=page,
limit=limit
)
# Simulated processing logic # Convert repayments to dictionary format
response_data = { repayments_data = []
"customerId": customer_id, for repayment in repayments:
"productId": product_id, repayments_data.append({
"debtId": loan_id, 'loan_id': repayment.loan_id,
"resultCode": "00", 'customer_id': repayment.customer_id,
"resultDescription": "Successful" 'product_id': repayment.product_id,
'transaction_id': repayment.transaction_id,
'created_at': repayment.created_at.isoformat() if repayment.created_at else None,
'updated_at': repayment.updated_at.isoformat() if repayment.updated_at else None
})
# Calculate total pages
total_pages = (total_count + limit - 1) // limit
response_data = {
'repayments': repayments_data,
'count': len(repayments_data),
'pagination': {
'total_count': total_count,
'total_pages': total_pages,
'current_page': page,
'limit': limit,
'has_next': page < total_pages,
'has_prev': page > 1
} }
}
# return ResponseHelper.success( return response_data
# data=response_data,
# message="Repayment processed successfully"
# )
# Call Kafka in a background thread
thread = Thread(target=RepaymentService.async_send_to_kafka, args=(response_data, request_id, "LOAN_REPAYMENT"))
thread.start()
db.session.commit()
return response_data
except ValidationError as err:
logger.error(f"Validation Error: {getattr(err, 'messages', str(err))}")
db.session.rollback()
return jsonify({
"message": "Validation exception"
}) , 422
except ValueError as err:
logger.error(f"{getattr(err, 'messages', str(err))}")
db.session.rollback()
return jsonify({
"message": str(err)
}) , 400
except Exception as e: except Exception as e:
logger.error(f"An error occurred: {str(e)}", exc_info=True) logger.error(f"An error occurred: {str(e)}", exc_info=True)
db.session.rollback()
return jsonify({ return jsonify({
"message": "Internal Server Error" "message": "Internal Server Error"
}) , 500 }), 500
+3 -1
View File
@@ -3,5 +3,7 @@ from .account import Account
from .loan import Loan from .loan import Loan
from .transaction import Transaction from .transaction import Transaction
from .user import User from .user import User
from .repayment import Repayment
from .loan_charge import LoanCharge
__all__ = ['Customer', 'Account', 'Loan', 'Transaction', User] __all__ = ['Customer', 'Account', 'Loan', 'Transaction', 'User', 'Repayment', 'LoanCharge']
+90
View File
@@ -0,0 +1,90 @@
from datetime import datetime, timezone
from app.extensions import db
from sqlalchemy.exc import IntegrityError
class LoanCharge(db.Model):
__tablename__ = 'loan_charges'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
loan_id = db.Column(db.Integer, nullable=False)
code = db.Column(db.String(50), nullable=False)
amount = db.Column(db.Float, default=0.00)
percent = db.Column(db.Float)
description = db.Column(db.String(255))
due = db.Column(db.Integer)
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))
transaction_id = db.Column(db.String(50))
due_date = db.Column(db.DateTime)
@classmethod
def get_all_loan_charges(cls, loan_id=None, code=None, start_date=None, end_date=None,
due_before=None, due_after=None, page=1, limit=20):
"""
Get all loan charges with optional filtering
Args:
loan_id (int, optional): Filter by loan ID
code (str, optional): Filter by charge code
start_date (datetime, optional): Filter by start date (created_at)
end_date (datetime, optional): Filter by end date (created_at)
due_before (datetime, optional): Filter charges due before this date
due_after (datetime, optional): Filter charges due after this date
page (int, optional): Page number for pagination
limit (int, optional): Number of items per page
Returns:
tuple: (list of LoanCharge objects, total count)
"""
query = cls.query
# Apply filters if provided
if loan_id:
query = query.filter(cls.loan_id == loan_id)
if code:
query = query.filter(cls.code == code)
if start_date:
query = query.filter(cls.created_at >= start_date)
if end_date:
query = query.filter(cls.created_at <= end_date)
if due_before:
query = query.filter(cls.due_date <= due_before)
if due_after:
query = query.filter(cls.due_date >= due_after)
# Order by created_at descending (newest first)
query = query.order_by(cls.created_at.desc())
# Get total count before pagination
total_count = query.count()
# Apply pagination
offset = (page - 1) * limit
query = query.limit(limit).offset(offset)
return query.all(), total_count
def to_dict(self):
"""
Convert the LoanCharge object to a dictionary format for JSON serialization.
"""
return {
'loan_id': self.loan_id,
'code': self.code,
'amount': self.amount,
'percent': self.percent,
'description': self.description,
'due': self.due,
'transaction_id': self.transaction_id,
'due_date': self.due_date.isoformat() if self.due_date else None,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
def __repr__(self):
return f'<LoanCharge {self.id}>'
+58 -31
View File
@@ -1,51 +1,78 @@
from datetime import datetime, timezone from datetime import datetime, timezone
from app.api.enums.loan_status import LoanStatus
from app.extensions import db from app.extensions import db
from app.models.customer import Customer
from app.models.loan import Loan
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
class Repayment(db.Model): class Repayment(db.Model):
__tablename__ = 'repayments' __tablename__ = 'repayments'
id = db.Column( id = db.Column(db.Integer, primary_key=True, autoincrement=True)
db.Integer,
primary_key=True,
autoincrement=True,
)
loan_id = db.Column(db.String(50), nullable=False) loan_id = db.Column(db.String(50), nullable=False)
customer_id = db.Column(db.String(50), nullable=False) customer_id = db.Column(db.String(50), nullable=False)
product_id = db.Column(db.String(20), nullable=True) product_id = db.Column(db.String(50), nullable=False)
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))
transaction_id = db.Column(db.String(50))
@classmethod @classmethod
def create_repayment(cls, customer_id, loan_id, product_id): def get_all_repayments(cls, loan_id=None, customer_id=None, product_id=None,
start_date=None, end_date=None, page=1, limit=20):
"""
Get all repayments with optional filtering
# Check customer exists Args:
if not Customer.is_valid_customer(customer_id): loan_id (str, optional): Filter by loan ID
raise ValueError("Invalid customer") customer_id (str, optional): Filter by customer ID
product_id (str, optional): Filter by product ID
start_date (datetime, optional): Filter by start date (created_at)
end_date (datetime, optional): Filter by end date (created_at)
page (int, optional): Page number for pagination
limit (int, optional): Number of items per page
# Check loan exists Returns:
loan = Loan.get_customer_loan(loan_id = loan_id, customer_id = customer_id) tuple: (list of Repayment objects, total count)
"""
# Check that the loan is active query = cls.query
if loan.status != LoanStatus.ACTIVE:
raise ValueError(f"Repayment cannot be processed. Loan status: ({loan.status})")
repayment = cls( # Apply filters if provided
customer_id=customer_id, if loan_id:
loan_id=loan_id, query = query.filter(cls.loan_id == loan_id)
product_id=product_id,
)
try: if customer_id:
db.session.add(repayment) query = query.filter(cls.customer_id == customer_id)
except IntegrityError as err:
raise ValueError(f"Database integrity error: {err}")
return repayment if product_id:
query = query.filter(cls.product_id == product_id)
if start_date:
query = query.filter(cls.created_at >= start_date)
if end_date:
query = query.filter(cls.created_at <= end_date)
# Order by created_at descending (newest first)
query = query.order_by(cls.created_at.desc())
# Get total count before pagination
total_count = query.count()
# Apply pagination
offset = (page - 1) * limit
query = query.limit(limit).offset(offset)
return query.all(), total_count
def to_dict(self):
"""
Convert the Repayment object to a dictionary format for JSON serialization.
"""
return {
'loan_id': self.loan_id,
'customer_id': self.customer_id,
'product_id': self.product_id,
'transaction_id': self.transaction_id,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
def __repr__(self): def __repr__(self):
return f'<Repayment {self.id}>' return f'<Repayment {self.id}>'
+19 -8
View File
@@ -65,8 +65,16 @@
} }
}, },
{ {
"name": "Repayment", "name": "Repayments",
"description": "Repayment Request.", "description": "Get all repayments with optional filtering.",
"externalDocs": {
"description": "Find out more",
"url": "https://www.simbrellang.net"
}
},
{
"name": "Loan Charges",
"description": "Get all loan charges with optional filtering.",
"externalDocs": { "externalDocs": {
"description": "Find out more", "description": "Find out more",
"url": "https://www.simbrellang.net" "url": "https://www.simbrellang.net"
@@ -89,8 +97,11 @@
"/transactions": { "/transactions": {
"$ref": "../swagger/paths/Transactions.json" "$ref": "../swagger/paths/Transactions.json"
}, },
"/Repayment": { "/repayments": {
"$ref": "../swagger/paths/Repayment.json" "$ref": "../swagger/paths/Repayments.json"
},
"/loan-charges": {
"$ref": "../swagger/paths/LoanCharges.json"
} }
}, },
"components": { "components": {
@@ -119,11 +130,11 @@
"TransactionsResponse": { "TransactionsResponse": {
"$ref": "../swagger/schemas/TransactionsResponse.json" "$ref": "../swagger/schemas/TransactionsResponse.json"
}, },
"RepaymentRequest": { "RepaymentsResponse": {
"$ref": "../swagger/schemas/RepaymentRequest.json" "$ref": "../swagger/schemas/RepaymentsResponse.json"
}, },
"RepaymentResponse": { "LoanChargesResponse": {
"$ref": "../swagger/schemas/RepaymentResponse.json" "$ref": "../swagger/schemas/LoanChargesResponse.json"
} }
}, },
"securitySchemes": { "securitySchemes": {
+117
View File
@@ -0,0 +1,117 @@
{
"get": {
"tags": ["Loan Charges"],
"summary": "Get all loan charges with optional filtering",
"description": "Retrieve loan charges with various filter options including loan ID, charge code, etc.",
"operationId": "getLoanCharges",
"parameters": [
{
"name": "loan_id",
"in": "query",
"description": "Filter by loan ID",
"required": false,
"schema": {
"type": "integer"
},
"example": 7463
},
{
"name": "code",
"in": "query",
"description": "Filter by charge code",
"required": false,
"schema": {
"type": "string"
},
"example": "INTEREST"
},
{
"name": "start_date",
"in": "query",
"description": "Filter by start date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-01-01T00:00:00Z"
},
{
"name": "end_date",
"in": "query",
"description": "Filter by end date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-12-31T23:59:59Z"
},
{
"name": "due_before",
"in": "query",
"description": "Filter charges due before this date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-12-31T23:59:59Z"
},
{
"name": "due_after",
"in": "query",
"description": "Filter charges due after this date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-01-01T00:00:00Z"
},
{
"name": "page",
"in": "query",
"description": "Page number for pagination",
"required": false,
"schema": {
"type": "integer",
"default": 1,
"minimum": 1
},
"example": 1
},
{
"name": "limit",
"in": "query",
"description": "Number of items per page (max 100)",
"required": false,
"schema": {
"type": "integer",
"default": 20,
"minimum": 1,
"maximum": 100
},
"example": 20
}
],
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "../schemas/LoanChargesResponse.json"
}
}
}
},
"400": {
"description": "Invalid request"
},
"500": {
"description": "Internal server error"
}
}
}
}
-56
View File
@@ -1,56 +0,0 @@
{
"post": {
"tags": [
"Repayment"
],
"summary": "Repayment Request",
"description": "Repayment Request",
"operationId": "Repayment",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../schemas/RepaymentRequest.json"
}
},
"application/xml": {
"schema": {
"$ref": "../schemas/RepaymentRequest.json"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "../schemas/RepaymentRequest.json"
}
}
}
},
"responses": {
"200": {
"description": "Repayment Successful",
"content": {
"application/json": {
"schema": {
"$ref": "../schemas/RepaymentResponse.json"
}
},
"application/xml": {
"schema": {
"$ref": "../schemas/RepaymentResponse.json"
}
}
}
},
"400": {
"description": "Invalid request"
},
"422": {
"description": "Validation exception"
},
"500": {
"description": "Internal server error"
}
}
}
}
+105
View File
@@ -0,0 +1,105 @@
{
"get": {
"tags": ["Repayments"],
"summary": "Get all repayments with optional filtering",
"description": "Retrieve repayments with various filter options including loan ID, customer ID, product ID, etc.",
"operationId": "getRepayments",
"parameters": [
{
"name": "loan_id",
"in": "query",
"description": "Filter by loan ID",
"required": false,
"schema": {
"type": "string"
},
"example": "10"
},
{
"name": "customer_id",
"in": "query",
"description": "Filter by customer ID",
"required": false,
"schema": {
"type": "string"
},
"example": "CID0000025585"
},
{
"name": "product_id",
"in": "query",
"description": "Filter by product ID",
"required": false,
"schema": {
"type": "string"
},
"example": "101"
},
{
"name": "start_date",
"in": "query",
"description": "Filter by start date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-01-01T00:00:00Z"
},
{
"name": "end_date",
"in": "query",
"description": "Filter by end date (ISO format)",
"required": false,
"schema": {
"type": "string",
"format": "date-time"
},
"example": "2023-12-31T23:59:59Z"
},
{
"name": "page",
"in": "query",
"description": "Page number for pagination",
"required": false,
"schema": {
"type": "integer",
"default": 1,
"minimum": 1
},
"example": 1
},
{
"name": "limit",
"in": "query",
"description": "Number of items per page (max 100)",
"required": false,
"schema": {
"type": "integer",
"default": 20,
"minimum": 1,
"maximum": 100
},
"example": 20
}
],
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "../schemas/RepaymentsResponse.json"
}
}
}
},
"400": {
"description": "Invalid request"
},
"500": {
"description": "Internal server error"
}
}
}
}
@@ -0,0 +1,100 @@
{
"type": "object",
"properties": {
"loan_charges": {
"type": "array",
"items": {
"type": "object",
"properties": {
"loan_id": {
"type": "integer",
"example": 7463
},
"code": {
"type": "string",
"example": "INTEREST"
},
"amount": {
"type": "number",
"format": "float",
"example": 0.00
},
"percent": {
"type": "number",
"format": "float",
"example": 1.1
},
"description": {
"type": "string",
"example": "This is fee 9000"
},
"due": {
"type": "integer",
"example": 0
},
"transaction_ {
"type": "integer",
"example": 0
},
"transaction_id": {
"type": "string",
"example": "TRX123456",
"nullable": true
},
"due_date": {
"type": "string",
"format": "date-time",
"example": "2025-04-16T20:03:31.998445Z",
"nullable": true
},
"created_at": {
"type": "string",
"format": "date-time",
"example": "2025-04-16T20:03:31.998085Z"
},
"updated_at": {
"type": "string",
"format": "date-time",
"example": "2025-04-16T20:03:31.998445Z"
}
}
}
},
"count": {
"type": "integer",
"example": 1
},
"pagination": {
"type": "object",
"properties": {
"total_count": {
"type": "integer",
"example": 100
},
"total_pages": {
"type": "integer",
"example": 5
},
"current_page": {
"type": "integer",
"example": 1
},
"limit": {
"type": "integer",
"example": 20
},
"has_next": {
"type": "boolean",
"example": true
},
"has_prev": {
"type": "boolean",
"example": false
}
}
}
},
"xml": {
"name": "LoanChargesResponse"
}
}
-36
View File
@@ -1,36 +0,0 @@
{
"type": "object",
"properties": {
"msisdn": {
"type": "string",
"example": "3451342"
},
"debtId": {
"type": "string",
"example": "10"
},
"productId": {
"type": "string",
"example": "101"
},
"transactionId": {
"type": "string",
"example": "20171209232115"
},
"customerId": {
"type": "string",
"example": "CID0000025585"
},
"channel": {
"type": "string",
"example": "USSD"
},
"accountId": {
"type": "string",
"example": "ACN8263457"
}
},
"xml": {
"name": "RepaymentRequest"
}
}
@@ -1,28 +0,0 @@
{
"type": "object",
"properties": {
"customerId": {
"type": "string",
"example": "CN621868"
},
"productId": {
"type": "string",
"example": "101"
},
"debtId": {
"type": "string",
"example": "273194670"
},
"resultCode": {
"type": "string",
"example": "00"
},
"resultDescription": {
"type": "string",
"example": "Successful"
}
},
"xml": {
"name": "RepaymentResponse"
}
}
@@ -0,0 +1,76 @@
{
"type": "object",
"properties": {
"repayments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"loan_id": {
"type": "string",
"example": "10"
},
"customer_id": {
"type": "string",
"example": "CID0000025585"
},
"product_id": {
"type": "string",
"example": "101"
},
"transaction_id": {
"type": "string",
"example": "TRX123456",
"nullable": true
},
"created_at": {
"type": "string",
"format": "date-time",
"example": "2025-04-10T16:45:47.879552Z"
},
"updated_at": {
"type": "string",
"format": "date-time",
"example": "2025-04-10T16:45:47.879642Z"
}
}
}
},
"count": {
"type": "integer",
"example": 1
},
"pagination": {
"type": "object",
"properties": {
"total_count": {
"type": "integer",
"example": 100
},
"total_pages": {
"type": "integer",
"example": 5
},
"current_page": {
"type": "integer",
"example": 1
},
"limit": {
"type": "integer",
"example": 20
},
"has_next": {
"type": "boolean",
"example": true
},
"has_prev": {
"type": "boolean",
"example": false
}
}
}
},
"xml": {
"name": "RepaymentsResponse"
}
}