Source code for fineract.objects.loan

from datetime import datetime

from fineract.objects.currency import Currency
from fineract.objects.fineract_object import DataFineractObject, FineractObject
from fineract.objects.types import LoanType, TermPeriodFrequencyType, Type


[docs]class Loan(DataFineractObject): """ This class represents a Loan. """ def __repr__(self): return self.get__repr__({'loan_id': self.id}) def _init_attributes(self): self.id = None self.account_no = None self.status = None self.external_id = None self.client_id = None self.loan_product_id = None self.loan_purpose_id = None self.loan_purpose_name = None self.loan_type = None self.currency = None self.principal = None self.term_frequency = None self.term_frequency_type = None self.timeline = None self.transactions = None self.summary = None self.multi_disburse_loan = None self.can_define_installment_amount = None self.can_disburse = None self.can_use_for_topup = None self.is_topup = None self.in_arrears = None self.is_npa = None def _use_attributes(self, attributes): self.id = attributes.get('id', None) self.account_no = attributes.get('accountNo', None) self.status = self._make_fineract_object(LoanStatus, attributes.get('status', None)) self.external_id = attributes.get('externalId', None) self.client_id = attributes.get('clientId', None) self.loan_product_id = attributes.get('loanProductId', None) self.loan_purpose_id = attributes.get('loanPurposeId', None) self.loan_purpose_name = attributes.get('loanPurposeName', None) self.loan_type = self._make_fineract_object(LoanType, attributes.get('loanType', None)) self.currency = self._make_fineract_object(Currency, attributes.get('currency', None)) self.principal = attributes.get('principal', None) self.term_frequency = attributes.get('termFrequency', None) self.term_frequency_type = self._make_fineract_object(TermPeriodFrequencyType, attributes.get('termPeriodFrequencyType', None)) self.timeline = self._make_fineract_object(LoanTimeline, attributes.get('timeline', None)) self.transactions = self._make_fineract_objects_list(LoanTransaction, attributes.get('transactions', None)) self.summary = self._make_fineract_object(LoanSummary, attributes.get('summary', None)) self.multi_disburse_loan = attributes.get('multiDisburseLoan', None) self.can_define_installment_amount = attributes.get('canDefineInstallmentAmount', None) self.can_disburse = attributes.get('canDisburse', None) self.can_use_for_topup = attributes.get('canUseForTopup', None) self.is_topup = attributes.get('isTopup', None) self.in_arrears = attributes.get('inArrears', None) self.is_npa = attributes.get('isNPA', None)
[docs] def days_in_arrears(self) -> int: """Return the number of days in arrears :return: int """ in_arrears = -1 if self.in_arrears: delta = datetime.today() - self.timeline.expected_maturity_date in_arrears = delta.days return in_arrears
[docs] def is_closed(self) -> bool: """Check whether a loan is closed or not :return: bool """ return self.status is not None and self.status.closed
[docs] @staticmethod def template(request_handler, client_id=None, template_type='individual', product_id=None) -> bool: """Get a loan template :param template_type: :param product_id: :param client_id: :param request_handler: :return: dict """ if client_id and product_id: return request_handler.make_request( 'GET', '/loans/template?templateType={}&clientId={}&productId={}'.format(template_type, client_id, product_id) ) return request_handler.make_request( 'GET', '/loans/template?templateType={}&clientId={}'.format(template_type, client_id) )
[docs] @classmethod def apply(cls, request_handler, client_id, product_id, principal, expected_disbursement_date=datetime.now(), submitted_date=datetime.now(), **kwargs) \ -> 'Loan': """Submit a new loan application :param submitted_date: :param request_handler: :param client_id: :param product_id: :param principal: :param expected_disbursement_date: :return: :class:`fineract.objects.loan.Loan` """ template = cls.template(request_handler, client_id, product_id=product_id) payload = { 'clientId': client_id, 'productId': product_id, 'principal': principal, 'loanTermFrequency': template['termFrequency'], 'loanTermFrequencyType': template['termPeriodFrequencyType']['id'], 'loanType': 'individual', 'numberOfRepayments': template['numberOfRepayments'], 'repaymentEvery': template['repaymentEvery'], 'repaymentFrequencyType': template['repaymentFrequencyType']['id'], 'interestRatePerPeriod': template['interestRatePerPeriod'], 'amortizationType': template['amortizationType']['id'], 'interestType': template['interestType']['id'], 'interestCalculationPeriodType': template['interestCalculationPeriodType']['id'], 'transactionProcessingStrategyId': template['transactionProcessingStrategyId'], 'expectedDisbursementDate': expected_disbursement_date, 'submittedOnDate': submitted_date, } payload.update(kwargs) res = request_handler.make_request( 'POST', '/loans', json=payload ) loan_id = res['loanId'] return cls(request_handler, request_handler.make_request( 'GET', '/loans/{}'.format(loan_id) ), False)
[docs] def delete(self) -> bool: """Delete a loan application. Only loans with status of "Submitted and awaiting approval" :return: bool """ res = self.request_handler.make_request( 'DELETE', '/loans/{}'.format(self.id) ) return res['loanId'] == self.id
def reject(self, note, rejected_on_date=datetime.now()): res = self.request_handler.make_request( 'POST', '/loans/{}?command=reject'.format(self.id), json={'note': note, 'rejectedOnDate': rejected_on_date} ) return res['loanId'] == self.id
[docs] def approve(self, approved_on_date=datetime.now(), approved_loan_amount=None, expected_disbursement_date=None) \ -> bool: """Approve a loan application :param approved_on_date: :param approved_loan_amount: :param expected_disbursement_date: :return: bool """ payload = { 'approvedOnDate': approved_on_date } if approved_loan_amount: payload['approvedLoanAmount'] = approved_loan_amount if expected_disbursement_date: payload['expectedDisbursementDate'] = expected_disbursement_date res = self.request_handler.make_request( 'POST', '/loans/{}?command=approve'.format(self.id), json=payload ) return res['loanId'] == self.id
def undo_approval(self, note) -> bool: res = self.request_handler.make_request( 'POST', '/loans/{}?command=undoApproval'.format(self.id), json={'note': note} ) return res['loanId'] == self.id
[docs] def disburse(self, actual_disbursement_date=datetime.now(), transaction_amount=None, fixed_emi_amount=None) -> bool: """Disburse an approved loan application :param actual_disbursement_date: :param transaction_amount: :param fixed_emi_amount: :return: bool """ payload = { 'actualDisbursementDate': actual_disbursement_date } if transaction_amount: payload['transactionAmount'] = transaction_amount if fixed_emi_amount: payload['fixedEmiAmount'] = fixed_emi_amount res = self.request_handler.make_request( 'POST', '/loans/{}?command=disburse'.format(self.id), json=payload ) return res['loanId'] == self.id
# def disburse_to_savings(self, actual_disbursement_date=datetime.now(), transaction_amount=None, fixed_emi_amount=None): # """Disburse an approved loan application to savings # # :param actual_disbursement_date: # :param transaction_amount: # :param fixed_emi_amount: # :return: bool # """ # payload = { # 'actualDisbursementDate': actual_disbursement_date # } # # if transaction_amount: # payload['transactionAmount'] = transaction_amount # # if fixed_emi_amount: # payload['fixedEmiAmount'] = fixed_emi_amount # # res = self.request_handler.make_request( # 'POST', # '/loans/{}?command=disburseToSavings'.format(self.id), # json=payload # ) # return res['loanId'] == self.id def undo_disbursal(self, note) -> bool: res = self.request_handler.make_request( 'POST', '/loans/{}?command=undoDisbursal'.format(self.id), json={'note': note} ) return res['loanId'] == self.id
[docs] def make_repayment(self, amount, transaction_date, **kwargs): """Make a repayment on a loan :param amount: :param transaction_date: :param kwargs: Extra args see https://demo.mifos.io/api-docs/apiLive.htm#loans_transactions :return: bool """ payload = { 'transactionDate': transaction_date, 'transactionAmount': amount } payload.update(kwargs) # print(payload) res = self.request_handler.make_request( 'POST', '/loans/{}/transactions?command=repayment'.format(self.id), json=payload ) return res['loanId'] == self.id
def apply_charge(self, charge_id, amount, due_date=datetime.now()): res = self.request_handler.make_request( 'POST', '/loans/{}/charges'.format(self.id), json={'chargeId': charge_id, 'amount': amount, 'dueDate': due_date} ) return res['loanId'] == self.id
class LoanStatus(FineractObject): def _init_attributes(self): self.id = None self.code = None self.value = None self.pending_approval = None self.waiting_for_disbursal = None self.active = None self.closed_obligations_met = None self.close_written_off = None self.written_off = None self.closed_rescheduled = None self.closed = None self.overpaid = None def _use_attributes(self, attributes): self.id = attributes.get('id', None) self.code = attributes.get('code', None) self.value = attributes.get('value', None) self.pending_approval = attributes.get('pendingApproval', None) self.waiting_for_disbursal = attributes.get('waitingForDisbursal', None) self.active = attributes.get('active', None) self.closed_obligations_met = attributes.get('closedObligationsMet', None) self.close_written_off = attributes.get('closedWrittenOff', None) self.closed_rescheduled = attributes.get('closedRescheduled', None) self.closed = attributes.get('closed', None) self.overpaid = attributes.get('overpaid', None) class LoanTimeline(FineractObject): """ This class represent the timeline of a Loan """ def _init_attributes(self): self.submitted_on = None self.submitted_by = None self.approved_on = None self.approved_by = None self.expected_disbursement_date = None self.actual_disbursement_date = None self.disbursed_by = None self.closed_on_date = None self.expected_maturity_date = None def _use_attributes(self, attributes): self.submitted_on = self._make_date_object(attributes.get('submittedOnDate', None)) self.submitted_by = attributes.get('submittedByUsername', None) self.approved_on = self._make_date_object(attributes.get('approvedOnDate', None)) self.approved_by = attributes.get('approvedByUsername', None) self.expected_disbursement_date = self._make_date_object(attributes.get('expectedDisbursementDate', None)) self.actual_disbursement_date = self._make_date_object(attributes.get('actualDisbursementDate', None)) self.disbursed_by = attributes.get('disbursedByUsername', None) self.closed_on_date = self._make_date_object(attributes.get('closedOnDate', None)) self.expected_maturity_date = self._make_date_object(attributes.get('expectedMaturityDate', None)) class LoanSummary(FineractObject): """ This is the summary of a Loan """ def _init_attributes(self): self.principal_disbursed = None self.principal_paid = None self.principal_written_off = None self.principal_outstanding = None self.principal_overdue = None self.interest_charged = None self.interest_paid = None self.interest_waived = None self.interest_written_off = None self.interest_outstanding = None self.interest_overdue = None self.fee_charges_charged = None self.fee_charges_due_at_disbursement = None self.fee_charges_paid = None self.fee_charges_waived = None self.fee_charges_written_off = None self.fee_charges_outstanding = None self.fee_charges_overdue = None self.penalty_charges_charged = None self.penalty_charges_paid = None self.penalty_charges_waived = None self.penalty_charges_written_off = None self.penalty_charges_outstanding = None self.penalty_charges_overdue = None self.total_expected_repayment = None self.total_repayment = None self.total_expected_cost_of_loan = None self.total_cost_of_loan = None self.total_waived = None self.total_written_off = None self.total_overdue = None self.total_outstanding = None self.overdue_since_date = None self.in_arrears = None self.is_npa = None def _use_attributes(self, attributes): self.principal_disbursed = attributes.get('principalDisbursed', None) self.principal_paid = attributes.get('principalPaid', None) self.principal_written_off = attributes.get('principalWrittenOff', None) self.principal_outstanding = attributes.get('principalOutstanding', None) self.principal_overdue = attributes.get('principalOverdue', None) self.interest_charged = attributes.get('interestCharged', None) self.interest_paid = attributes.get('interestPaid', None) self.interest_waived = attributes.get('interestWaived', None) self.interest_written_off = attributes.get('interestWrittenOff', None) self.interest_outstanding = attributes.get('interestOutstanding', None) self.interest_overdue = attributes.get('interestOverdue', None) self.fee_charges_charged = attributes.get('feeChargesCharged', None) self.fee_charges_due_at_disbursement = attributes.get('feeChargesDueAtDisbursementCharged', None) self.fee_charges_paid = attributes.get('feeChargesPaid', None) self.fee_charges_waived = attributes.get('feeChargesWaived', None) self.fee_charges_written_off = attributes.get('feeChargesWrittenOff', None) self.fee_charges_outstanding = attributes.get('feeChargesOutstanding', None) self.fee_charges_overdue = attributes.get('feeChargesOverdue', None) self.penalty_charges_charged = attributes.get('penaltyChargesCharged', None) self.penalty_charges_paid = attributes.get('penaltyChargesPaid', None) self.penalty_charges_waived = attributes.get('penaltyChargesWaived', None) self.penalty_charges_written_off = attributes.get('penaltyChargesWrittenOff', None) self.penalty_charges_outstanding = attributes.get('penaltyChargesOutstanding', None) self.penalty_charges_overdue = attributes.get('penaltyChargesOverdue', None) self.total_expected_repayment = attributes.get('totalExpectedRepayment', None) self.total_repayment = attributes.get('totalRepayment', None) self.total_expected_cost_of_loan = attributes.get('totalExpectedCostOfLoan', None) self.total_cost_of_loan = attributes.get('totalCostOfLoan', None) self.total_waived = attributes.get('totalWaived', None) self.total_written_off = attributes.get('totalWrittenOff', None) self.total_outstanding = attributes.get('totalOutstanding', None) self.total_overdue = attributes.get('totalOverdue', None) self.overdue_since_date = self._make_date_object(attributes.get('overdueSinceDate', None)) self.in_arrears = attributes.get('inArrears', None) self.is_npa = attributes.get('isNPA', None) class LoanRepaymentSchedule(FineractObject): """ This class represents a loan repayment schedule """ def _init_attributes(self): self.currency = None self.loan_term_in_days = None self.total_principal_disbursed = None self.total_principal_expected = None self.total_principal_paid = None self.total_interest_charged = None self.total_fee_charges_charged = None self.total_penalty_charges_charged = None self.total_waived = None self.total_written_off = None self.total_repayment_expected = None self.total_repayment = None self.total_outstanding = None self.periods = None def _use_attributes(self, attributes): self.currency = self._make_fineract_object(Currency, attributes.get('currency', None)) self.loan_term_in_days = attributes.get('loanTermInDays', None) self.total_principal_disbursed = attributes.get('totalPrincipalDisbursed', None) self.total_principal_expected = attributes.get('totalPrincipalExpected', None) self.total_principal_paid = attributes.get('totalPrincipalPaid', None) self.total_interest_charged = attributes.get('totalInterestCharged', None) self.total_fee_charges_charged = attributes.get('totalFeeChargesCharged', None) self.total_penalty_charges_charged = attributes.get('totalPenaltyChargesCharged', None) self.total_waived = attributes.get('totalWaived', None) self.total_written_off = attributes.get('totalWrittenOff', None) self.total_repayment_expected = attributes.get('totalRepaymentExpected', None) self.total_repayment = attributes.get('totalRepayment', None) self.total_outstanding = attributes.get('totalOutstanding', None) self.periods = self._make_fineract_objects_list(LoanRepaymentPeriod, attributes.get('periods', None)) class LoanRepaymentPeriod(FineractObject): def _init_attributes(self): self.period = None self.from_date = None self.due_date = None self.days_in_period = None self.principal_original_due = None self.principal_due = None self.principal_outstanding = None self.principal_disbursed = None self.principal_loan_balance = None self.principal_loan_balance_outstanding = None self.interest_original_due = None self.interest_dues = None self.interest_outstanding = None self.fee_charges_due = None self.fee_charges_outstanding = None self.penalty_charges_due = None self.total_original_due_for_period = None self.total_due_for_period = None self.total_paid_for_period = None self.total_outstanding_for_period = None self.total_overdue = None self.total_actual_cost_of_loan_for_period = None def _use_attributes(self, attributes): self.period = attributes.get('period', None) self.due_date = self._make_date_object(attributes.get('dueDate', None)) self.from_date = self._make_date_object(attributes.get('fromDate', None)) self.days_in_period = attributes.get('daysInPeriod', None) self.principal_original_due = attributes.get('principalOriginalDue', None) self.principal_due = attributes.get('principalDue', None) self.principal_outstanding = attributes.get('principalOutstanding', None) self.principal_disbursed = attributes.get('principalDisbursed', None) self.principal_loan_balance_outstanding = attributes.get('principalLoanBalanceOutstanding', None) self.interest_original_due = attributes.get('interestOriginalDue', None) self.interest_due = attributes.get('interestDue', None) self.interest_outstanding = attributes.get('interestOutstanding', None) self.fee_charges_due = attributes.get('feeChargesDue', None) self.fee_charges_outstanding = attributes.get('feeChargesOutstanding', None) self.penalty_charges_due = attributes.get('penaltyChargesDue', None) self.total_original_due_for_period = attributes.get('totalOriginalDueForPeriod', None) self.total_due_for_period = attributes.get('totalDueForPeriod', None) self.total_paid_for_period = attributes.get('totalPaidForPeriod', None) self.total_outstanding_for_period = attributes.get('totalOutstandingForPeriod', None) self.total_overdue = attributes.get('totalOverdue', None) self.total_actual_cost_of_loan_for_period = attributes.get('totalActualCostOfLoanForPeriod', None) class LoanTransaction(FineractObject): """ This class represents a Loan transaction. """ def _init_attributes(self): self.id = None self.office_id = None self.office_name = None self.type = None self.date = None self.currency = None self.amount = None self.principal_portion = None self.interest_portion = None self.fee_charges_portion = None self.penalty_charges_portion = None self.overpayment_portion = None self.unrecognized_income_portion = None self.outstanding_loan_balance = None self.submitted_on_date = None self.manually_reversed = None def _use_attributes(self, attributes): self.id = attributes.get('id', None) self.office_id = attributes.get('officeId', None) self.office_name = attributes.get('officeName', None) self.type = self._make_fineract_object(LoanTransactionType, attributes.get('type', None)) self.date = self._make_date_object(attributes.get('date', None)) self.currency = self._make_fineract_object(Currency, attributes.get('currency', None)) self.amount = attributes.get('amount', None) self.principal_portion = attributes.get('principalPortion', None) self.interest_portion = attributes.get('interestPortion', None) self.fee_charges_portion = attributes.get('feeChargesPortion', None) self.penalty_charges_portion = attributes.get('penaltyChargesPortion', None) self.overpayment_portion = attributes.get('overpaymentPortion', None) self.unrecognized_income_portion = attributes.get('unrecognizedIncomePortion', None) self.outstanding_loan_balance = attributes.get('outstandingLoanBalance', None) self.submitted_on_date = self._make_date_object(attributes.get('submittedOnDate', None)) self.manually_reversed = attributes.get('manuallyReversed', None) class LoanTransactionType(Type): def _init_attributes(self): super(LoanTransactionType, self)._init_attributes() self.disbursement = None self.repayment_at_disbursement = None self.repayment = None self.contra = None self.waive_interest = None self.waive_charges = None self.accrual = None self.write_off = None self.recovery_payment = None self.initiate_transfer = None self.approve_transfer = None self.withdraw_transfer = None self.reject_transfer = None self.charge_payment = None self.refund = None self.refund_for_active_loans = None def _use_attributes(self, attributes): super(LoanTransactionType, self)._use_attributes(attributes) self.disbursement = attributes.get('disbursement', None) self.repayment_at_disbursement = attributes.get('repaymentAtDisbursement', None) self.repayment = attributes.get('repayment', None) self.contra = attributes.get('contra', None) self.waive_interest = attributes.get('waiveInterest', None) self.waive_charges = attributes.get('waiveCharges', None) self.write_off = attributes.get('writeOff', None) self.recovery_payment = attributes.get('recoveryPayment', None) self.initiate_transfer = attributes.get('initiateTransfer', None) self.approve_transfer = attributes.get('approveTransfer', None) self.withdraw_transfer = attributes.get('withdrawTransfer', None) self.reject_transfer = attributes.get('rejectTransfer', None) self.charge_payment = attributes.get('chargePayment', None) self.refund = attributes.get('refund', None) self.refund_for_active_loans = attributes.get('refundForActiveLoans', None)