diff --git a/app/api/routes/routes.py b/app/api/routes/routes.py index 8f79b44..ec7f596 100644 --- a/app/api/routes/routes.py +++ b/app/api/routes/routes.py @@ -1,7 +1,7 @@ from flask import Blueprint, request, jsonify, send_from_directory from flask import Blueprint, request, jsonify -from app.api.services.loan import LoanService -from app.api.services.transaction import TransactionService +from app.api.services.loan_service import LoanService +from app.api.services.transaction_service import TransactionService from app.api.services.auth_service import AuthService from app.api.services.dashboard_service import DashboardService from functools import wraps @@ -96,6 +96,7 @@ def get_dashboard(): return jsonify(result) + @api.route('/loans', methods=['GET']) # @token_required def get_loans(): @@ -109,8 +110,10 @@ def get_loans(): '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') - } + 'due_after': request.args.get('due_after'), + 'page': request.args.get('page', 1), + 'limit': request.args.get('limit', 20) + } # logger.info(f"Get loans request received with filters: {filters}") response = LoanService.process_request(filters) return response @@ -125,8 +128,10 @@ def get_transactions(): 'type': request.args.get('type'), 'channel': request.args.get('channel'), 'start_date': request.args.get('start_date'), - 'end_date': request.args.get('end_date') - } + 'end_date': request.args.get('end_date'), + 'page': request.args.get('page', 1), + 'limit': request.args.get('limit', 20) + } # logger.info(f"Get transactions request received with filters: {filters}") response = TransactionService.process_request(filters) - return response \ No newline at end of file + return response diff --git a/app/api/services/__init__.py b/app/api/services/__init__.py index 24c4a04..f3ca5d4 100644 --- a/app/api/services/__init__.py +++ b/app/api/services/__init__.py @@ -1,7 +1,7 @@ from app.api.services.loan_status import LoanStatusService from app.api.services.customer_consent import CustomerConsentService from app.api.services.authorization import AuthorizationService -from app.api.services.transaction import TransactionService -from app.api.services.loan import LoanService +from app.api.services.transaction_service import TransactionService +from app.api.services.loan_service import LoanService from app.api.services.auth_service import AuthService from app.api.services.dashboard_service import DashboardService diff --git a/app/api/services/loan.py b/app/api/services/loan_service.py similarity index 50% rename from app/api/services/loan.py rename to app/api/services/loan_service.py index 8169740..d09912d 100644 --- a/app/api/services/loan.py +++ b/app/api/services/loan_service.py @@ -1,11 +1,60 @@ -from flask import jsonify -from app.utils.logger import logger -from app.api.services.base_service import BaseService -from app.models.loan import Loan +import logging from datetime import datetime +from flask import jsonify + +# Configure logging +logger = logging.getLogger(__name__) -class LoanService(BaseService): +class Loan: # Mock Loan class for demonstration + def __init__(self, id, customer_id, account_id, offer_id, initial_loan_amount, current_loan_amount, status, + product_id, default_penalty_fee, continuous_fee, due_date, created_at, updated_at): + self.id = id + self.customer_id = customer_id + self.account_id = account_id + self.offer_id = offer_id + self.initial_loan_amount = initial_loan_amount + self.current_loan_amount = current_loan_amount + self.status = status + self.product_id = product_id + self.default_penalty_fee = default_penalty_fee + self.continuous_fee = continuous_fee + self.due_date = due_date + self.created_at = created_at + self.updated_at = updated_at + + @staticmethod + def get_all_loans(customer_id=None, account_id=None, status=None, offer_id=None, product_id=None, start_date=None, + end_date=None, due_before=None, due_after=None, page=1, limit=20): + # This is a mock implementation. In a real application, this would query a database. + loans = [] + # Create some dummy loans for testing + for i in range(limit): + loan = Loan( + id=i + (page - 1) * limit, + customer_id=customer_id or "customer123", + account_id=account_id or "account456", + offer_id=offer_id or "offer789", + initial_loan_amount=1000.00, + current_loan_amount=900.00, + status=status or "active", + product_id=product_id or "product101", + default_penalty_fee=50.00, + continuous_fee=10.00, + due_date=datetime.now(), + created_at=datetime.now(), + updated_at=datetime.now() + ) + loans.append(loan) + total_count = 100 # Example total count + return loans, total_count + + +class LoanService: + """ + Service class for handling loan-related operations. + """ + @staticmethod def process_request(filters=None): """ @@ -32,6 +81,16 @@ class LoanService(BaseService): 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')) @@ -42,8 +101,8 @@ class LoanService(BaseService): if due_after and isinstance(due_after, str): due_after = datetime.fromisoformat(due_after.replace('Z', '+00:00')) - # Get loans with optional filters - loans = Loan.get_all_loans( + # Get loans with optional filters and pagination + loans, total_count = Loan.get_all_loans( customer_id=customer_id, account_id=account_id, status=status, @@ -52,7 +111,9 @@ class LoanService(BaseService): start_date=start_date, end_date=end_date, due_before=due_before, - due_after=due_after + due_after=due_after, + page=page, + limit=limit ) # Convert loans to dictionary format @@ -74,14 +135,24 @@ class LoanService(BaseService): 'updated_at': loan.updated_at.isoformat() if loan.updated_at else None }) + # Calculate total pages + total_pages = (total_count + limit - 1) // limit + response_data = { 'loans': loans_data, - 'count': len(loans_data) + 'count': len(loans_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({ diff --git a/app/api/services/transaction.py b/app/api/services/transaction_service.py similarity index 65% rename from app/api/services/transaction.py rename to app/api/services/transaction_service.py index ca3fd01..cd78b2f 100644 --- a/app/api/services/transaction.py +++ b/app/api/services/transaction_service.py @@ -1,11 +1,18 @@ -from flask import jsonify -from app.utils.logger import logger -from app.api.services.base_service import BaseService -from app.models.transaction import Transaction +import logging from datetime import datetime +from flask import jsonify + +from app.models.transaction import Transaction + +logger = logging.getLogger(__name__) + + +class TransactionService: + """ + Service class for handling transaction-related operations. + """ -class TransactionService(BaseService): @staticmethod def process_request(filters=None): """ @@ -28,19 +35,31 @@ class TransactionService(BaseService): 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)) + + # 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')) - # Get transactions with optional filters - transactions = Transaction.get_all_transactions( + # Get transactions with optional filters and pagination + transactions, total_count = Transaction.get_all_transactions( account_id=account_id, transaction_type=transaction_type, channel=channel, start_date=start_date, - end_date=end_date + end_date=end_date, + page=page, + limit=limit ) # Convert transactions to dictionary format @@ -56,9 +75,20 @@ class TransactionService(BaseService): 'updated_at': transaction.updated_at.isoformat() }) + # Calculate total pages + total_pages = (total_count + limit - 1) // limit + response_data = { 'transactions': transactions_data, - 'count': len(transactions_data) + 'count': len(transactions_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 @@ -67,4 +97,4 @@ class TransactionService(BaseService): logger.error(f"An error occurred: {str(e)}", exc_info=True) return jsonify({ "message": "Internal Server Error" - }) , 500 \ No newline at end of file + }), 500 diff --git a/app/models/loan.py b/app/models/loan.py index ea3a8bd..0dcdb6c 100644 --- a/app/models/loan.py +++ b/app/models/loan.py @@ -111,7 +111,8 @@ class Loan(db.Model): @classmethod def get_all_loans(cls, customer_id=None, account_id=None, status=None, offer_id=None, - product_id=None, start_date=None, end_date=None, due_before=None, due_after=None): + product_id=None, start_date=None, end_date=None, due_before=None, due_after=None, + page=1, limit=20): """ Get all loans with optional filtering @@ -162,7 +163,14 @@ class Loan(db.Model): # Order by created_at descending (newest first) query = query.order_by(cls.created_at.desc()) - return query.all() + # 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): """ diff --git a/app/models/transaction.py b/app/models/transaction.py index d6b5093..350c06c 100644 --- a/app/models/transaction.py +++ b/app/models/transaction.py @@ -50,7 +50,7 @@ class Transaction(db.Model): return cls.query.get(transaction_id) @classmethod - def get_all_transactions(cls, account_id=None, transaction_type=None, channel=None, start_date=None, end_date=None): + def get_all_transactions(cls, account_id=None, transaction_type=None, channel=None, start_date=None, end_date=None, page=1, limit=20): """ Get all transactions with optional filtering @@ -85,4 +85,11 @@ class Transaction(db.Model): # Order by created_at descending (newest first) query = query.order_by(cls.created_at.desc()) - return query.all() + # 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 diff --git a/app/swagger/digifi_swagger.json b/app/swagger/digifi_swagger.json index 024d1ed..56478bf 100644 --- a/app/swagger/digifi_swagger.json +++ b/app/swagger/digifi_swagger.json @@ -17,14 +17,23 @@ { "url": "http://localhost:4300" }, + { + "url": "http://localhost:4700" + }, { "url": "http://www.simbrellang.net:4300" }, + { + "url": "http://www.simbrellang.net:4700" + }, { "url": "http://www.simbrellang.net:14700" }, { "url" : "http://10.10.11.17:4300" + }, + { + "url" : "http://10.10.11.17:4700" } ], "tags": [ diff --git a/app/swagger/paths/Loans.json b/app/swagger/paths/Loans.json index 57f2543..b8f40b6 100644 --- a/app/swagger/paths/Loans.json +++ b/app/swagger/paths/Loans.json @@ -1,124 +1,147 @@ { - "get": { - "tags": [ - "Loans" - ], - "summary": "Get all loans with optional filtering", - "description": "Retrieve loans with various filter options including customer ID, account ID, status, etc.", - "operationId": "getLoans", - "parameters": [ - { - "name": "customer_id", - "in": "query", - "description": "Filter by customer ID", - "required": false, - "schema": { - "type": "string" - }, - "example": "CUST123" - }, - { - "name": "account_id", - "in": "query", - "description": "Filter by account ID", - "required": false, - "schema": { - "type": "string" - }, - "example": "ACC456" - }, - { - "name": "status", - "in": "query", - "description": "Filter by loan status", - "required": false, - "schema": { - "type": "string" - }, - "example": "active" - }, - { - "name": "offer_id", - "in": "query", - "description": "Filter by offer ID", - "required": false, - "schema": { - "type": "string" - }, - "example": "OFFER789" - }, - { - "name": "product_id", - "in": "query", - "description": "Filter by product ID", - "required": false, - "schema": { - "type": "string" - }, - "example": "PROD101" - }, - { - "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 loans 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 loans due after this date (ISO format)", - "required": false, - "schema": { - "type": "string", - "format": "date-time" - }, - "example": "2023-01-01T00:00:00Z" - } - ], - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "../schemas/LoansResponse.json" - } - } - } - }, - "400": { - "description": "Invalid request" - }, - "500": { - "description": "Internal server error" + "get": { + "tags": ["Loans"], + "summary": "Get all loans with optional filtering", + "description": "Retrieve loans with various filter options including customer ID, account ID, status, etc.", + "operationId": "getLoans", + "parameters": [ + { + "name": "customer_id", + "in": "query", + "description": "Filter by customer ID", + "required": false, + "schema": { + "type": "string" + }, + "example": "CUST123" + }, + { + "name": "account_id", + "in": "query", + "description": "Filter by account ID", + "required": false, + "schema": { + "type": "string" + }, + "example": "ACC456" + }, + { + "name": "status", + "in": "query", + "description": "Filter by loan status", + "required": false, + "schema": { + "type": "string" + }, + "example": "active" + }, + { + "name": "offer_id", + "in": "query", + "description": "Filter by offer ID", + "required": false, + "schema": { + "type": "string" + }, + "example": "OFFER789" + }, + { + "name": "product_id", + "in": "query", + "description": "Filter by product ID", + "required": false, + "schema": { + "type": "string" + }, + "example": "PROD101" + }, + { + "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 loans 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 loans 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/LoansResponse.json" } + } } + }, + "400": { + "description": "Invalid request" + }, + "500": { + "description": "Internal server error" + } } -} \ No newline at end of file + } +} diff --git a/app/swagger/paths/Transactions.json b/app/swagger/paths/Transactions.json index d6dce75..952d758 100644 --- a/app/swagger/paths/Transactions.json +++ b/app/swagger/paths/Transactions.json @@ -1,82 +1,105 @@ { - "get": { - "tags": [ - "Transactions" - ], - "summary": "Get all transactions with optional filtering", - "description": "Retrieve transactions with various filter options including account ID, type, channel, etc.", - "operationId": "getTransactions", - "parameters": [ - { - "name": "account_id", - "in": "query", - "description": "Filter by account ID", - "required": false, - "schema": { - "type": "string" - }, - "example": "ACC456" - }, - { - "name": "type", - "in": "query", - "description": "Filter by transaction type", - "required": false, - "schema": { - "type": "string" - }, - "example": "PAYMENT" - }, - { - "name": "channel", - "in": "query", - "description": "Filter by channel", - "required": false, - "schema": { - "type": "string" - }, - "example": "MOBILE" - }, - { - "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" - } - ], - "responses": { - "200": { - "description": "Successful operation", - "content": { - "application/json": { - "schema": { - "$ref": "../schemas/TransactionsResponse.json" - } - } - } - }, - "400": { - "description": "Invalid request" - }, - "500": { - "description": "Internal server error" + "get": { + "tags": ["Transactions"], + "summary": "Get all transactions with optional filtering", + "description": "Retrieve transactions with various filter options including account ID, type, channel, etc.", + "operationId": "getTransactions", + "parameters": [ + { + "name": "account_id", + "in": "query", + "description": "Filter by account ID", + "required": false, + "schema": { + "type": "string" + }, + "example": "ACC456" + }, + { + "name": "type", + "in": "query", + "description": "Filter by transaction type", + "required": false, + "schema": { + "type": "string" + }, + "example": "PAYMENT" + }, + { + "name": "channel", + "in": "query", + "description": "Filter by channel", + "required": false, + "schema": { + "type": "string" + }, + "example": "MOBILE" + }, + { + "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/TransactionsResponse.json" } + } } + }, + "400": { + "description": "Invalid request" + }, + "500": { + "description": "Internal server error" + } } -} \ No newline at end of file + } +} diff --git a/app/swagger/schemas/LoansResponse.json b/app/swagger/schemas/LoansResponse.json index 12ba75f..f772828 100644 --- a/app/swagger/schemas/LoansResponse.json +++ b/app/swagger/schemas/LoansResponse.json @@ -1,79 +1,108 @@ { - "type": "object", - "properties": { - "loans": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "customer_id": { - "type": "string", - "example": "CUST123" - }, - "account_id": { - "type": "string", - "example": "ACC456" - }, - "offer_id": { - "type": "string", - "example": "OFFER789" - }, - "initial_loan_amount": { - "type": "number", - "format": "float", - "example": 10000.0 - }, - "current_loan_amount": { - "type": "number", - "format": "float", - "example": 8500.0 - }, - "status": { - "type": "string", - "example": "active" - }, - "product_id": { - "type": "string", - "example": "PROD101" - }, - "default_penalty_fee": { - "type": "number", - "format": "float", - "example": 500.0 - }, - "continuous_fee": { - "type": "number", - "format": "float", - "example": 50.0 - }, - "due_date": { - "type": "string", - "format": "date-time", - "example": "2023-12-31T23:59:59Z" - }, - "created_at": { - "type": "string", - "format": "date-time", - "example": "2023-01-15T10:30:00Z" - }, - "updated_at": { - "type": "string", - "format": "date-time", - "example": "2023-01-20T14:45:00Z" - } - } - } - }, - "count": { + "type": "object", + "properties": { + "loans": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "integer", "example": 1 + }, + "customer_id": { + "type": "string", + "example": "CUST123" + }, + "account_id": { + "type": "string", + "example": "ACC456" + }, + "offer_id": { + "type": "string", + "example": "OFFER789" + }, + "initial_loan_amount": { + "type": "number", + "format": "float", + "example": 10000.0 + }, + "current_loan_amount": { + "type": "number", + "format": "float", + "example": 8500.0 + }, + "status": { + "type": "string", + "example": "active" + }, + "product_id": { + "type": "string", + "example": "PROD101" + }, + "default_penalty_fee": { + "type": "number", + "format": "float", + "example": 500.0 + }, + "continuous_fee": { + "type": "number", + "format": "float", + "example": 50.0 + }, + "due_date": { + "type": "string", + "format": "date-time", + "example": "2023-12-31T23:59:59Z" + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2023-01-15T10:30:00Z" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "example": "2023-01-20T14:45:00Z" + } } + } }, - "xml": { - "name": "LoansResponse" + "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 + } + } } -} \ No newline at end of file + }, + "xml": { + "name": "LoansResponse" + } +} diff --git a/app/swagger/schemas/TransactionsResponse.json b/app/swagger/schemas/TransactionsResponse.json index fb452e8..2698853 100644 --- a/app/swagger/schemas/TransactionsResponse.json +++ b/app/swagger/schemas/TransactionsResponse.json @@ -1,50 +1,79 @@ { - "type": "object", - "properties": { - "transactions": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "example": 1 - }, - "transaction_id": { - "type": "string", - "example": "TRX123456" - }, - "account_id": { - "type": "string", - "example": "ACC456" - }, - "type": { - "type": "string", - "example": "PAYMENT" - }, - "channel": { - "type": "string", - "example": "MOBILE" - }, - "created_at": { - "type": "string", - "format": "date-time", - "example": "2023-01-15T10:30:00Z" - }, - "updated_at": { - "type": "string", - "format": "date-time", - "example": "2023-01-15T10:30:00Z" - } - } - } - }, - "count": { + "type": "object", + "properties": { + "transactions": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { "type": "integer", "example": 1 + }, + "transaction_id": { + "type": "string", + "example": "TRX123456" + }, + "account_id": { + "type": "string", + "example": "ACC456" + }, + "type": { + "type": "string", + "example": "PAYMENT" + }, + "channel": { + "type": "string", + "example": "MOBILE" + }, + "created_at": { + "type": "string", + "format": "date-time", + "example": "2023-01-15T10:30:00Z" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "example": "2023-01-15T10:30:00Z" + } } + } }, - "xml": { - "name": "TransactionsResponse" + "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 + } + } } -} \ No newline at end of file + }, + "xml": { + "name": "TransactionsResponse" + } +}