Skip to content

Commit be634aa

Browse files
Merge pull request #113 from theflyingmachine/dev-loan-app
Loan App Enhancements
2 parents 2b3065a + 0a1492d commit be634aa

File tree

3 files changed

+81
-39
lines changed

3 files changed

+81
-39
lines changed

loan/templates/loan/react_loan.html

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -183,29 +183,29 @@
183183
method: 'POST',
184184
body: formData,
185185
};
186-
await fetch('{% url 'add_loan_transaction_api' %}', requestOptions)
186+
await fetch('{% url 'add_transaction_api' %}', requestOptions)
187187
.then(response => response.json())
188188
.then(res => {
189-
if (res.status === 'success') {
190-
const loanToUpdateId = res.saved_transaction.loan_id;
191-
let current_loans = this.state.data;
192-
// Find the index of the loan with a matching ID in the all_loans array
193-
const loanIndexToUpdate = current_loans.all_loans.findIndex((loan) => loan.id === loanToUpdateId);
194-
// create a new object with the updated transaction_entry
195-
const updatedLoan = {
196-
...current_loans.all_loans[loanIndexToUpdate],
197-
transaction_entry: [...current_loans.all_loans[loanIndexToUpdate].transaction_entry, res.saved_transaction],
198-
};
199-
// Create a new object with the updated all_loans array
200-
const updatedCurrentLoans = {
201-
...current_loans,
202-
all_loans: current_loans.all_loans.map((loan, index) => (index === loanIndexToUpdate ? updatedLoan : loan)),
203-
};
204-
this.setState({data: updatedCurrentLoans})
205-
show_toast('Success', `${res.saved_transaction.type} Transaction of ${formatCurrency(res.saved_transaction.transaction_amount)} saved for ${updatedLoan.name}`, 'success');
206-
} else {
207-
show_toast('Error', `${data.error}`, 'danger');
208-
}
189+
if (res.status === 'success') {
190+
const loanToUpdateId = res.saved_transaction.loan_id;
191+
let current_loans = this.state.data;
192+
// Find the index of the loan with a matching ID in the all_loans array
193+
const loanIndexToUpdate = current_loans.all_loans.findIndex((loan) => loan.id === loanToUpdateId);
194+
// create a new object with the updated transaction_entry
195+
const updatedLoan = {
196+
...current_loans.all_loans[loanIndexToUpdate],
197+
transaction_entry: [...current_loans.all_loans[loanIndexToUpdate].transaction_entry, res.saved_transaction],
198+
};
199+
// Create a new object with the updated all_loans array
200+
const updatedCurrentLoans = {
201+
...current_loans,
202+
all_loans: current_loans.all_loans.map((loan, index) => (index === loanIndexToUpdate ? updatedLoan : loan)),
203+
};
204+
this.setState({data: updatedCurrentLoans})
205+
show_toast('Success', `${res.saved_transaction.type} Transaction of ${formatCurrency(res.saved_transaction.transaction_amount)} saved for ${updatedLoan.name}`, 'success');
206+
} else {
207+
show_toast('Error', `${res.error}`, 'danger');
208+
}
209209
}
210210
)
211211
$('#myModalTransaction').modal('hide');
@@ -304,6 +304,7 @@ <h5 className="card-title">{loan.name}</h5>
304304
History
305305
</button>
306306
<button type="button"
307+
disabled={!loan.status}
307308
className={`btn ${total(loan, "REMAINING") > 0 ? 'btn-primary' : 'btn-secondary'} mx-2`}
308309
data-toggle="modal"
309310
data-target="#myModalTransaction"

loan/urls.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
path('', views.loan_dashboard, name='loan_dashboard'),
77

88
# V1 API
9-
path('api/v1/add', views.LoanAPI.add_loan_api, name='add_loan_api'),
10-
path('api/v1/transaction', views.LoanAPI.add_loan_transaction_api,
11-
name='add_loan_transaction_api'),
12-
path('api/v1/list', views.LoanAPI.get_loan_api, name='list_loan_api'),
9+
path('api/v1/list', views.LoanAPI.list_loan, name='list_loan_api'),
10+
path('api/v1/add', views.LoanAPI.add_loan, name='add_loan_api'),
11+
path('api/v1/transaction', views.LoanAPI.add_transaction, name='add_transaction_api'),
12+
path('api/v1/delete/<int:trans_id>', views.LoanAPI.delete_transaction,
13+
name='delete_transaction_api'),
1314
]

loan/views.py

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
from decimal import Decimal
33

44
from django.contrib.auth.decorators import login_required
5-
from django.db.models import Prefetch
5+
from django.db.models import Prefetch, Sum, Q, DecimalField
6+
from django.db.models.functions import Coalesce
67
from django.http import JsonResponse
78
from django.shortcuts import render
89

@@ -31,8 +32,22 @@ def loan_dashboard(request):
3132

3233

3334
class LoanAPI():
35+
3436
@login_required()
35-
def add_loan_api(request):
37+
def list_loan(request):
38+
"""
39+
This function returns JSON data for the loan entry
40+
"""
41+
all_loan = Loan.objects.prefetch_related(
42+
Prefetch('transaction_set',
43+
queryset=Transaction.objects.all().order_by('transaction_date'))
44+
).filter(tenant_id=request.user.id).distinct().order_by('-lending_date')
45+
return JsonResponse({'status': 'success',
46+
'all_loans': LoanSerializer(instance=all_loan, many=True).data,
47+
})
48+
49+
@login_required()
50+
def add_loan(request):
3651
"""
3752
This function records new loan entry
3853
"""
@@ -57,16 +72,38 @@ def add_loan_api(request):
5772
})
5873

5974
@login_required()
60-
def add_loan_transaction_api(request):
75+
def add_transaction(request):
6176
"""
6277
This function records new transaction entry
6378
"""
6479
if request.method == 'POST':
6580
form = TransactionForm(request.POST)
6681
if form.is_valid():
82+
# Fetch loan data with principal sum using annotations
83+
loan_data = Loan.objects.filter(tenant_id=request.user.id,
84+
id=form.data['loan_id']).annotate(
85+
principal_sum=Coalesce(Sum('transaction__transaction_amount',
86+
filter=Q(transaction__type='PRINCIPAL')), 0,
87+
output_field=DecimalField())
88+
).values('amount', 'principal_sum').first()
89+
if not loan_data:
90+
return JsonResponse({'status': 'false', 'error': 'Loan not found.'})
91+
92+
transaction_amount = Decimal(form.data['transaction_amount'])
93+
if form.data['type'] == 'PRINCIPAL':
94+
total_principal = loan_data['principal_sum'] + transaction_amount
95+
if total_principal > loan_data['amount']:
96+
return JsonResponse({
97+
'status': 'false',
98+
'error': f'Cannot accept ₹{transaction_amount} principal transaction. '
99+
f'It will exceed the principal amount of ₹{loan_data["amount"]}',
100+
})
101+
102+
if total_principal == loan_data['amount']:
103+
Loan.objects.filter(id=form.data['loan_id']).update(status=False)
104+
105+
# Save the transaction
67106
saved_transaction = form.save()
68-
# todo; Update Loan Status to 0, when all the added transactions add up to loan_amount
69-
# todo; Validate Principal amount not exceeding loan amount
70107
return JsonResponse({'status': 'success',
71108
'saved_transaction': TransactionSerializer(
72109
instance=saved_transaction,
@@ -77,14 +114,17 @@ def add_loan_transaction_api(request):
77114
})
78115

79116
@login_required()
80-
def get_loan_api(request):
117+
def delete_transaction(request, transaction_id):
81118
"""
82-
This function returns JSON data for the loan entry
119+
This functions used to delete transaction
83120
"""
84-
all_loan = Loan.objects.prefetch_related(
85-
Prefetch('transaction_set',
86-
queryset=Transaction.objects.all().order_by('transaction_date'))
87-
).filter(tenant_id=request.user.id, status=True).distinct().order_by('-lending_date')
88-
return JsonResponse({'status': 'success',
89-
'all_loans': LoanSerializer(instance=all_loan, many=True).data,
90-
})
121+
try:
122+
Transaction.objects.get(id=transaction_id,
123+
loan_id__tenant_id=request.user.id).delete()
124+
return JsonResponse({'status': 'success',
125+
'deleted': transaction_id,
126+
})
127+
except Transaction.DoesNotExist:
128+
return JsonResponse({'status': 'false',
129+
'error': 'Transaction does not exist',
130+
})

0 commit comments

Comments
 (0)