import datetime
from fineract.objects.document import Document
from fineract.objects.fineract_object import FineractObject, DataFineractObject
from fineract.objects.group import Group
from fineract.objects.loan import Loan
from fineract.objects.savings import Savings
from fineract.objects.types import Type
from fineract.pagination import PaginatedList
[docs]class Client(DataFineractObject):
"""
This class represents a Client.
"""
def __repr__(self):
return self.get__repr__({'client_id': self.id})
def _init_attributes(self):
self.id = None
self.account_no = None
self.external_id = None
self.status = None
self.active = None
self.activation_date = None
self.first_name = None
self.middle_name = None
self.last_name = None
self.full_name = None
self.mobile_no = None
self.date_of_birth = None
self.office_id = None
self.office_name = None
self.savings_product_id = None
self.type = None
self.timeline = None
self.groups = None
def _use_attributes(self, attributes):
self.id = attributes.get('id', None)
self.account_no = attributes.get('accountNo', None)
self.external_id = attributes.get('externalId', None)
self.status = self._make_fineract_object(ClientStatus, attributes.get('status', None))
self.active = attributes.get('active', None)
self.activation_date = self._make_date_object(attributes.get('activationDate', None))
self.first_name = attributes.get('firstname', None)
self.last_name = attributes.get('lastname', None)
self.middle_name = attributes.get('middlename', None)
self.full_name = attributes.get('displayName', None)
self.mobile_no = attributes.get('mobileNo', None)
self.date_of_birth = self._make_date_object(attributes.get('dateOfBirth', None))
self.office_id = attributes.get('officeId', None)
self.office_name = attributes.get('officeName', None)
self.savings_product_id = attributes.get('savingsAccountId', None)
self.type = self._make_fineract_object(ClientType, attributes.get('clientType', None))
self.timeline = self._make_fineract_object(ClientTimeline, attributes.get('timeline', None))
self.groups = self._make_fineract_objects_list(Group, attributes.get('groups', None))
[docs] def update(self, data):
"""Update the core fields of a client
:param data: dict
"""
_id = getattr(self, 'id', None)
res = self._request_handler.make_request(
'PUT',
'/clients/{}'.format(_id),
json=data
)
assert res.get('clientId', None) == _id
[docs] def delete(self):
"""Deletes a client (Must be in pending state)"""
res = self._request_handler.make_request(
'DELETE',
'/clients/{}'.format(self.id),
)
assert res.get('clientId', None) == self.id
[docs] def activate(self, date=None):
"""Activates a client
:param date: Date of client activation
:return: bool
"""
if date is None:
date = self._get_current_date()
_id = getattr(self, 'id', None)
res = self._request_handler.make_request(
'POST',
'/clients/{}?command=activate'.format(_id),
json={'activationDate': date}
)
return res.get('clientId', None) == _id
[docs] def close(self, closure_reason_id, date=None):
"""Close a client
:param closure_reason_id: Closure reason id
:param date: Date of client close
:return: bool
"""
if date is None:
date = self._get_current_date()
_id = getattr(self, 'id', None)
res = self._request_handler.make_request(
'POST',
'/clients/{}?command=close'.format(_id),
json={
'closureDate': date,
'closureReasonId': closure_reason_id
}
)
return res.get('clientId', None) == _id
[docs] def reject(self, rejection_reason_id, date=None):
"""Reject a client
:param rejection_reason_id: Rejection reason id
:param date: Date of client rejection
:return: bool
"""
if date is None:
date = self._get_current_date()
_id = getattr(self, 'id', None)
res = self._request_handler.make_request(
'POST',
'/clients/{}?command=reject'.format(_id),
json={
'rejectionDate': date,
'rejectionReasonId': rejection_reason_id
}
)
return res.get('clientId', None) == _id
[docs] def withdraw(self, withdrawal_reason_id, date=None):
"""Withdraw a client
:param withdrawal_reason_id: Withdrawal reason id
:param date: Date of client rejection
:return: bool
"""
if date is None:
date = self._get_current_date()
_id = getattr(self, 'id', None)
res = self._request_handler.make_request(
'POST',
'/clients/{}?command=withdraw'.format(_id),
json={
'withdrawalDate': date,
'withdrawalReasonId': withdrawal_reason_id
}
)
return res.get('clientId', None) == _id
[docs] def reactivate(self, date=None):
"""Reactivate a client
:param date: Date of client reactivation
:return: bool
"""
if date is None:
date = self._get_current_date()
_id = getattr(self, 'id', None)
res = self._request_handler.make_request(
'POST',
'/clients/{}?command=reactivate'.format(_id),
json={'reactivationDate': date}
)
return res.get('clientId', None) == _id
[docs] def undo_reject(self, date=None):
"""Undo client rejection
:param date: Date of client rejection undoing
:return: bool
"""
if date is None:
date = self._get_current_date()
_id = getattr(self, 'id', None)
res = self._request_handler.make_request(
'POST',
'/clients/{}?command=UndoRejection'.format(_id),
json={'reopenedDate': date}
)
return res.get('clientId', None) == _id
[docs] def undo_withdrawal(self, date=None):
"""Undo client withdrawal
:param date: Date of client withdrawal undoing
:return: bool
"""
if date is None:
date = self._get_current_date()
_id = getattr(self, 'id', None)
res = self._request_handler.make_request(
'POST',
'/clients/{}?command=UndoWithdrawal'.format(_id),
json={'reopenedDate': date}
)
return res.get('clientId', None) == _id
[docs] def add_document(self, name, description, file, filename):
"""Add a document to client
:param name:
:param description:
:param file:
:param filename:
:rtype: :class:`fineract.objects.document.Document`
"""
return Document.create(self._request_handler, Document.CLIENTS, self.id, name, description, file, filename)
[docs] def documents(self):
"""Get all documents for a client
:rtype: list of :class:`fineract.objects.document.Document`
"""
return Document.get_all(self._request_handler, Document.CLIENTS, self.id)
[docs] def document(self, document_id):
"""Get a document that matches ``document_id``
:param document_id:
:rtype: :class:`fineract.objects.document.Document`
"""
return Document.get(self._request_handler, Document.CLIENTS, self.id, document_id)
[docs] def get_loans(self):
"""Get the loans of a client
:rtype: list of :class:`fineract.objects.loan.Loan`
"""
_id = getattr(self, 'id', None)
if _id:
return PaginatedList(
Loan,
self._request_handler,
'/loans',
dict(sqlSearch='l.client_id={}'.format(self.id))
)
raise AttributeError('id not set')
[docs] def get_outstanding_loans(self):
"""Get the outstanding loans of a client
:rtype: list of :class:`fineract.objects.loan.Loan`
"""
return [loan for loan in self.get_loans() if loan.status.active]
[docs] def get_loans_in_arrears(self, active=True, all_loans=False):
"""Get loans in arrears
:param active: boolean flag to choose between closed/open loans
:param all_loans: flag to choose between returning all loans in arrears or specific loans based on active
parameter
:rtype: list of :class:`fineract.objects.loan.Loan`
"""
now = datetime.datetime.now().date()
if all_loans:
return [loan for loan in self.get_loans() if
(loan.in_arrears or (now > loan.timeline.expected_maturity_date.date())) or
(loan.status.closed and loan.timeline.closed_on_date > loan.timeline.expected_maturity_date)]
if active:
return [loan for loan in self.get_loans() if
(loan.in_arrears or (now > loan.timeline.expected_maturity_date.date())) and loan.status.active]
else:
return [loan for loan in self.get_loans() if loan.status.closed and loan.timeline.closed_on_date >
loan.timeline.expected_maturity_date]
[docs] def set_image(self, file, filename, content_type='image/png'):
"""Set the image for a client. Pass `None` to delete an image.
:param file:
"""
if file is None:
data = self.request_handler.make_request(
'DELETE',
'/clients/{}/images'.format(self.id),
)
return data['resourceId'] == self.id
else:
file_descr = (filename, file, content_type)
params = {
'file': file_descr
}
data = self.request_handler.make_request(
'POST',
'/clients/{}/images'.format(self.id),
files=params,
content_type='multipart/form-data'
)
return data['resourceId'] == self.id
[docs] def download_image(self):
"""Download a document
:return: file content
"""
return self._request_handler.make_request(
'GET',
'/clients/{}/images'.format(self.id),
accept='application/octet-stream',
is_file=True
)
[docs] @classmethod
def get_client_by_phone_no(cls, request_handler, phone_no):
"""Get a client by phone no
:param request_handler:
:param phone_no:
:rtype: :class:`fineract.objects.client.Client`
"""
params = {
'sqlSearch': 'c.mobile_no={}'.format(phone_no)
}
data = request_handler.make_request(
'GET',
'/clients',
params=params
)
if data and data['pageItems']:
return cls(request_handler, data['pageItems'][0], False)
return None
[docs] @classmethod
def create(cls, request_handler, firstname, lastname, office_id, active=True, activation_date=None, mobile_no=None,
external_id=None, group_id=None, staff_id=None, savings_product_id=None, gender_id=None,
client_type_id=None, client_classification_id=None, account_no=None, middlename=None,
submittedon_date=None):
"""Create a client and return a Client object
:param account_no:
:param client_classification_id:
:param external_id:
:param group_id:
:param staff_id:
:param savings_product_id:
:param gender_id:
:param client_type_id:
:param mobile_no:
:param request_handler:
:param firstname:
:param lastname:
:param middlename:
:param office_id:
:param active:
:param activation_date:
:rtype: :class:`fineract.objects.client.Client`
"""
data = {
'firstname': firstname,
'lastname': lastname,
'officeId': office_id,
'active': active,
'submittedOnDate': submittedon_date or cls._get_current_date()
}
if middlename:
data['middlename'] = middlename
if active:
data['activationDate'] = activation_date or cls._get_current_date()
if group_id:
data['groupId'] = group_id
if external_id:
data['externalId'] = external_id
if account_no:
data['accountNo'] = account_no
if staff_id:
data['staffId'] = staff_id
if mobile_no:
data['mobileNo'] = mobile_no
if savings_product_id:
data['savingsProductId'] = savings_product_id
if gender_id:
data['genderId'] = gender_id
if client_type_id:
data['clientTypeId'] = client_type_id
if client_classification_id:
data['clientClassifcationId'] = client_classification_id
res = request_handler.make_request(
'POST',
'/clients',
json=data
)
client_id = res['clientId']
return cls(request_handler,
request_handler.make_request(
'GET',
'/clients/{}'.format(client_id)
), False)
[docs] def is_activated(self):
"""
Check if client has been activated
:return: bool
"""
return self.active == True
def get_accounts(self):
data = self.request_handler.make_request(
'GET',
'/clients/{}/accounts'.format(self.id)
)
if data:
if 'loanAccounts' in data:
data['loanAccounts'] = [Loan(self.request_handler, account, False) for account in data['loanAccounts']]
if 'savingsAccounts' in data:
data['savingsAccounts'] = [Savings(self.request_handler, account, False) for account in
data['savingsAccounts']]
self.accounts = data
return data
class ClientStatus(Type):
"""
This class represents a Client status.
"""
pass
class ClientType(FineractObject):
def _init_attributes(self):
self.id = None
self.name = None
self.active = None
self.mandatory = None
def _use_attributes(self, attributes):
self.id = attributes.get('id', None)
self.name = attributes.get('name', None)
self.active = attributes.get('active', None)
self.mandatory = attributes.get('mandatory', None)
class ClientTimeline(FineractObject):
"""
This class represents the timeline of a Client
"""
def _init_attributes(self):
self.submitted_on_date = None
self.submitted_by = None
self.activated_on = None
self.activated_by = None
def _use_attributes(self, attributes):
self.submitted_on_date = self._make_date_object(attributes.get('submittedOnDate', None))
self.submitted_by = attributes.get('submittedByUsername', None)
self.activated_on_date = self._make_date_object(attributes.get('activatedOnDate', None))
self.activated_by = attributes.get('activatedByUsername', None)