diff --git a/blocnote/apps/financialInputs/forms.py b/blocnote/apps/financialInputs/forms.py index 65c2e36a1cd17c54ba454e0706bf3398f21e0931..1c89b2dee2466d424c1b7f9a704d9bee93d02eda 100644 --- a/blocnote/apps/financialInputs/forms.py +++ b/blocnote/apps/financialInputs/forms.py @@ -1,6 +1,6 @@ """Forms to render and validate for financial-inputs endpoint.""" from django.forms import ModelForm -from blocnote.apps.financialInputs.models import FinancingOverview, CustomerPreference, CashBalance +from blocnote.apps.financialInputs.models import FinancingOverview, CustomerPreference, Liabilities, CashBalance class ProformaInputsForm(ModelForm): @@ -35,6 +35,13 @@ class CustomerPreferenceForm(ModelForm): ] +class LiabilitiesForm(ModelForm): + """Define the form that validates the liabilities records of the customer.""" + + class Meta: + model = Liabilities + fields = '__all__' + class CashBalanceForm(ModelForm): """Define the form to validate cash balance entries.""" diff --git a/blocnote/apps/financialInputs/old_views.py b/blocnote/apps/financialInputs/old_views.py index 2f568740e684022e5a49f862414079af34535790..d8c232be8350e48c9cf7f34a6e11f67668f16a4b 100644 --- a/blocnote/apps/financialInputs/old_views.py +++ b/blocnote/apps/financialInputs/old_views.py @@ -10,7 +10,7 @@ from bpfin.back_end_call.back_end_inputs import monthly_bill, form_prior_income_ from blocnote.lib.fetch_data import get_building_data from .models import ( - FinancingOverview, Bills, BillsOverview, EstimationAlgorithm, Liabilities, IncomeStatement, + FinancingOverview, Bills, BillsOverview, EstimationAlgorithm, IncomeStatement, LoanOptions, DefaultLoan, Lender, GrowthRate ) @@ -485,63 +485,6 @@ class BillsOverviewView(View): return JsonResponse({}) -class LiabilitiesTable(View): - """Store and load Mortgage and Liability information for a building id.""" - - model = Liabilities - - def get(self, request, building_id): - """HTTP GET request. - - Fetch Mortgage and Liability information from the database and return to the frontend. - - Args: - request: HTTP GET request. - building_id: id of the building. - - Returns: - JsonResponse: List of records. Each record is a dictionary with key as the attribute name and value - as the value. - """ - objs = self.model.objects.filter(building_id=building_id) - result = [] - if objs: - for obj in objs: - record = { - 'lender': obj.lender, - 'input_date': obj.input_date, - 'monthly_service': obj.monthly_service, - 'remaining_term': obj.remaining_term, - } - result.append(record) - return JsonResponse({'instance': result}) - - def put(self, request, building_id): - """HTTP PUT request. - - Receive Mortgage and Liabilities data from frontend and store in the database. Delete any existing records - before storing new data. - - Args: - request: HTTP PUT request. - building_id: id of the building. - - Returns: - JsonResponse: A status saying OK. - """ - put = json.loads(request.body.decode()) - self.model.objects.filter(building_id=building_id).delete() - for record in put: - self.model.objects.create( - building_id=building_id, - input_date=record['input_date'], - lender=record['lender'], - monthly_service=float(record['monthly-service']), - remaining_term=float(record['remaining-term']), - ) - return JsonResponse({}) - - class IncomeStatementTable(View): """Income Statement table. diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/liabilities.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/liabilities.js new file mode 100644 index 0000000000000000000000000000000000000000..e2c7d8930332a04fc7594605a7b4a18bb47ae4ce --- /dev/null +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/liabilities.js @@ -0,0 +1,135 @@ +/** + * Load the Mortgage and Liabilities table. + * This function loads the mortgage and liabilities table with the given inputList. If inputList is empty, it loads a + * row with empty inputs and 1/1/1980 date as default values. + */ +function loadLiabilitiesTable(inputList) { + if (inputList.length === 0) { + addLiabilitiesRow('', '', '', ''); + } else { + inputList.map(function(record, index) { + record = inputList[index]; + let lender = record['lender']; + let service = record['monthly_service']; + let term = record['remaining_term']; + let date = record['input_date']; + addLiabilitiesRow(lender, service, term, date); + }); + } +} + +/**Add a new row to Mortgage and Liabilities table. */ +function addLiabilitiesRow(lender, service, term, date) { + const table = document.querySelector('#liabilities-table tbody'); + const rowCount = table.rows.length; + let row = table.insertRow(rowCount); + const debtCell = row.insertCell(0); + debtCell.innerHTML = `Debt ${rowCount + 1}`; + const lenderCell = row.insertCell(1); + lenderCell.innerHTML = ` + + `; + const monthlyServiceCell = row.insertCell(2); + monthlyServiceCell.innerHTML = ` + + `; + const remainingTermCell = row.insertCell(3); + remainingTermCell.innerHTML = ` + + `; + const dateCell = row.insertCell(4); + dateCell.innerHTML = ` + + `; + const deleteButtonCell = row.insertCell(5); + deleteButtonCell.innerHTML = ` +
+ +
+ `; +} + +/**Delete a row from the Mortgage and Liabilities table. */ +function deleteLiabilitiesRow(rowIndex) { + const result = confirm('ARE YOU SURE YOU WANT TO DELETE THIS ROW?'); + if (result) { + const table = document.querySelector('#liabilities-table'); + table.deleteRow(rowIndex); + const rowCount = table.rows.length; + for (let rowIndex = 1; rowIndex < rowCount; rowIndex++) { + let row = table.rows.item(rowIndex).cells; + row.item(0).innerHTML = `Debt ${rowIndex}`; + } + } + return false; +} + +const liabilitiesForm = document.querySelector('#Liabilities'); +liabilitiesForm.onchange = function() { + document.querySelector('#liabilities-warning-message').innerHTML = ''; +}; + +/**Submit morgage and other liabilities information for a building.*/ +function liabilitiesSubmitForm(form) { + const table = document.querySelector('#liabilities-table'); + const rowCount = table.rows.length; + let result = []; + for (let rowIndex = 1; rowIndex < rowCount; rowIndex++) { + let record = {}; + record['lender'] = table.rows[rowIndex].cells[1].children[0].value; + record['monthly-service'] = table.rows[rowIndex].cells[2].children[0].value; + record['remaining-term'] = table.rows[rowIndex].cells[3].children[0].value; + let date = table.rows[rowIndex].cells[4].children[0].value; + const liabilitiesDate = new Date(date); + const todaysDate = new Date(); + if (liabilitiesDate > todaysDate) { + alert('Input Date cannot be in the future'); + return false; + } + record['input_date'] = date; + result.push(record); + } + request('liabilities/', { + method: 'PUT', + credentials: 'same-origin', + body: JSON.stringify(result), + headers: new Headers({ + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken') + }) + }).then(res => { + if (!res.err) { + document.querySelector('#liabilities-warning-message').innerHTML = ` + Saved! + `; + } else { + res.err.responseBody.then((error) => { + let errorMsg = ''; + Object.keys(error).forEach((key) => { + errorMsg += `${key}: ${error[key]}\n`; + }); + document.querySelector('#liabilities-warning-message').innerHTML = ` + ${errorMsg} + `; + }); + } + }); + return false; +} + +/**Get mortgage and other liabilities information for a building.*/ +function getLiabilitiesTable() { + request('liabilities/', { + method: 'GET', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json' + }, + }).then(res => { + if (!res.err) { + loadLiabilitiesTable(res.payload.instance); + } + }); +} + +getLiabilitiesTable(); diff --git a/blocnote/apps/financialInputs/views/liabilities.py b/blocnote/apps/financialInputs/views/liabilities.py new file mode 100644 index 0000000000000000000000000000000000000000..85c86d0fa5bd097a25ea082b5ac1928e577254a6 --- /dev/null +++ b/blocnote/apps/financialInputs/views/liabilities.py @@ -0,0 +1,68 @@ +"""Views for liabilities.""" +import json + +from django.views import View +from django.http import JsonResponse + +from blocnote.apps.financialInputs.models import Liabilities +from blocnote.apps.financialInputs.forms import LiabilitiesForm + +class LiabilitiesTable(View): + """Store and load Mortgage and Liability information for a building id.""" + + model = Liabilities + + def get(self, request, building_id): + """HTTP GET request. + + Fetch Mortgage and Liability information from the database and return to the frontend. + + Args: + request: HTTP GET request. + building_id: id of the building. + + Returns: + JsonResponse: List of records. Each record is a dictionary with key as the attribute name and value + as the value. + """ + objs = self.model.objects.filter(building_id=building_id) + result = [] + if objs: + for obj in objs: + record = { + 'lender': obj.lender, + 'input_date': obj.input_date, + 'monthly_service': obj.monthly_service, + 'remaining_term': obj.remaining_term, + } + result.append(record) + return JsonResponse({'instance': result}) + + def put(self, request, building_id): + """HTTP PUT request. + + Receive Mortgage and Liabilities data from frontend and store in the database. Delete any existing records + before storing new data. + + Args: + request: HTTP PUT request. + building_id: id of the building. + + Returns: + JsonResponse: A status saying OK. + """ + put = json.loads(request.body.decode()) + for record in put: + record['building_id'] = building_id + form = LiabilitiesForm(record) + if form.is_valid(): + self.model.objects.update_or_create( + building_id=building_id, + defaults=record, + ) + else: + error_dict = {} + for field, error in form.errors.items(): + error_dict[field] = error + return JsonResponse(error_dict, status=400) + return JsonResponse({})