diff --git a/blocnote/apps/financialInputs/forms.py b/blocnote/apps/financialInputs/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..bca424db326cd75f84eee2d40b93206690ad012c --- /dev/null +++ b/blocnote/apps/financialInputs/forms.py @@ -0,0 +1,21 @@ +"""Forms to render and validate for financial-inputs endpoint.""" +from django.forms import ModelForm +from blocnote.apps.financialInputs.models import FinancingOverview + + +class ProformaInputsForm(ModelForm): + """Define the form to input/display proforma information.""" + + class Meta: + """Choose the model and the relevant fields for the form.""" + + model = FinancingOverview + fields = [ + 'fund', + 'pro_forma_start_date', + 'pro_forma_duration', + 'analysis_date', + 'anticipated_construction_start_date', + 'anticipated_commissioning_date', + 'anticipated_construction_period', + ] diff --git a/blocnote/apps/financialInputs/views.py b/blocnote/apps/financialInputs/old_views.py similarity index 92% rename from blocnote/apps/financialInputs/views.py rename to blocnote/apps/financialInputs/old_views.py index 86193167b84cf64fd104c91ff32a7c5cd5b6284e..c2a86ae9b4c89b96919e5e2bafec6427ff9c9607 100644 --- a/blocnote/apps/financialInputs/views.py +++ b/blocnote/apps/financialInputs/old_views.py @@ -145,95 +145,6 @@ class Index(View): return render(request, 'financialInputs/index.html', context=context) -class BlocNoteHeader(View): - """This class handles header creation and update.""" - - model = FinancingOverview - - def handle_form(self, put, building_id): - """Handle form submit. - - Take the header form passed by the frontend and make an entry in the database. - - Args: - put: This is the data sent in the PUT request by frontend. - building_id: id of the building. - """ - self.model.objects.filter(building_id=building_id).delete() - self.model.objects.create( - building_id=building_id, - fund_id=put['fund'], - pro_forma_start_date=put['pro_forma_start_date'], - pro_forma_duration=put['pro_forma_duration'], - analysis_date=put['analysis_date'], - anticipated_construction_start_date=put['anticipated_construction_start_date'], - anticipated_commissioning_date=put['anticipated_commissioning_date'], - anticipated_construction_period=put['anticipated_construction_period'], - ) - - def put(self, request, building_id): - """PUT route for header. - - Handle the PUT request for the header form. Store all the data received into the database. If storing was - successfull, send success message else send a failure message. - - Args: - request: HTTP PUT request. - building_id: id of the building. - - Returns: - JsonResponse: If success, returns a message saying successful else return message saying it failed. - """ - put = json.loads(request.body.decode()) - result = {} - try: - self.handle_form(put, building_id) - except Exception as err: - return JsonResponse( - { - 'error': 'Sorry, something went wrong. Please try again.', - }, status=400) - return JsonResponse(result) - - def get(self, request, building_id): - """HTTP GET request. - - Fetch the financing overview data from the database and send to the frontend. Send the list of lenders - regardless of whether a financing overview entry has been made or not. - - Args: - request: HTTP GET request. - building_id: id of the building. - - Returns: - JsonResponse: Result with the financing overview data. - """ - financing_overview_objs = self.model.objects.filter(building_id=building_id) - - # Fetch all the funds. - funds_obj = Fund.objects.all() - funds = [] - for fund_obj in funds_obj: - funds.append((fund_obj.id, fund_obj.Name)) - result = {} - - # Check if the financing overview entry exists in the database. - if financing_overview_objs: - financing_overview_obj = financing_overview_objs[0] - result = { - 'present': True, - 'fund': financing_overview_obj.fund.id, - 'pro_forma_start_date': financing_overview_obj.pro_forma_start_date, - 'pro_forma_duration': financing_overview_obj.pro_forma_duration, - 'analysis_date': financing_overview_obj.analysis_date, - 'anticipated_construction_start_date': financing_overview_obj.anticipated_construction_start_date, - 'anticipated_commissioning_date': financing_overview_obj.anticipated_commissioning_date, - 'anticipated_construction_period': financing_overview_obj.anticipated_construction_period, - } - result['funds'] = funds - return JsonResponse({'result': result}) - - class BillsTable(View): """Create the Energy Bills tables. diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js index d2d9204e6fed0e86f03ecff56532d4b7437edf00..c679f63f565bd025f39d56d915e6b73df1e40458 100644 --- a/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/app.js @@ -8,7 +8,6 @@ const todaysDate = { for (var utility_index in utilities) { loadInitialBillsTable(utilities[utility_index]); } -getBillProjectionDatesForm(); loadBillsOverview(); getIncomeStatementTable(); getCustomerPreferenceTable(); @@ -16,182 +15,6 @@ getLiabilitiesTable(); getCashBalanceForm(); getLoanOptionsTable(); -/** - * proformaForm will watch the form and if it changes, it will remove the response message is it was displayed. - */ -var proformaForm = document.querySelector('#pro-forma-form'); -proformaForm.onchange = function() { - var resMsg = document.querySelector('#pro-forma-form-save-msg'); - resMsg.innerHTML = ``; -} - -/** - * The following 2 functions display a warning message saying if fund is changed, it will affect the loan options. - * This message is displayed when the mouse is over the fund select box. - */ -var fund = document.querySelector('#id_fund'); -/** - * This is to watch if fund value is changed. If it did, it would affect loan options. - */ -var didFundChange = false; -fund.onmouseenter = function() { - var errorDiv = document.querySelector('#show-error'); - errorDiv.innerHTML = `Changing fund will affect loan options`; -} - -fund.onmouseleave = function() { - var errorDiv = document.querySelector('#show-error'); - errorDiv.innerHTML = ""; -} - -fund.onchange = function() { - didFundChange = true; -} - -/** - * HTTP GET request to fetch the proforma form information from the database if preent. It would receive the list of - * funds along with. - */ -function getBillProjectionDatesForm() { - request(`finance-overview`, { - method: 'GET', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json' - }, - }).then(res => { - fundDropBox = document.querySelector('#id_fund-select'); - var funds = res.payload.result.funds; - for (var fundIndex = 0; fundIndex < funds.length; fundIndex++) { - fundDropBox.innerHTML += ` - - `; - if (res.payload.result.present) { - document.querySelector('#pro-forma-date-input').value = res.payload.result.pro_forma_start_date; - document.querySelector('#pro-forma-duration-input').value = res.payload.result.pro_forma_duration; - document.querySelector('#analysis-date-input').value = res.payload.result.analysis_date; - document.querySelector('#id_fund-select').value = res.payload.result.fund; - document.querySelector('#anticipated-construction-start-date-input').value = res.payload.result.anticipated_construction_start_date; - document.querySelector('#anticipated-commissioning-date-input').value = res.payload.result.anticipated_commissioning_date; - document.querySelector('#anticipated-construction-period-input').value = res.payload.result.anticipated_construction_period; - } - } - }); -} - -/** - * Handle submition of the header form. Validate commissioning date is greater - * than construction start date. Create result dictionary containing the form - * data and convert into JSON to send to the backend. Upon success, load the - * Bills overview table as the Pro Forma year changes the start year of the - * Bills overview table. - */ -function billProjectionDatesForm(form) { - var proFormaStartDate = document.querySelector('#pro-forma-date-input').value; - var proFormaDuration = document.querySelector('#pro-forma-duration-input').value; - var analysisDate = document.querySelector('#analysis-date-input').value; - var fund = document.querySelector('#id_fund-select').value; - var anticipatedConstructionStartDate = document.querySelector('#anticipated-construction-start-date-input').value; - var anticipatedCommissioningDate = document.querySelector('#anticipated-commissioning-date-input').value; - var anticipatedConstructionPeriod = document.querySelector('#anticipated-construction-period-input').value; - startYear = anticipatedConstructionStartDate.split('-') - endDate = anticipatedCommissioningDate.split('-'); - var anticipatedConstructionStartYear = startYear[0]; - var anticipatedConstructionStartMonth = startYear[1]; - var anticipatedConstructionStartDay = startYear[2]; - var anticipatedCommissioningStartYear = endDate[0]; - var anticipatedCommissioningStartMonth = endDate[1]; - var anticipatedCommissioningStartDay = endDate[2]; - var anticipatedConstructionStartDateDict = { - 'day': anticipatedConstructionStartDay, - 'month': anticipatedConstructionStartMonth, - 'year': anticipatedConstructionStartYear, - } - var anticipatedCommissioningDateDict = { - 'day': anticipatedCommissioningStartDay, - 'month': anticipatedCommissioningStartMonth, - 'year': anticipatedCommissioningStartYear, - } - var proFormaStartYear = Number(proFormaStartDate.split('-')[0]); - var isProFormaDateCorrect = true; - - /** - * Usually proforma start year is 3 years before current year. If the entered date is less than 3, confirm with - * user if that value is correct. - */ - if (todaysDate.year - proFormaStartYear < 3) { - isProFormaDateCorrect = confirm('ARE YOU SURE THE PROFORMA DATE IS CORRECT?'); - } - if (isProFormaDateCorrect) { - // Ensure that commissioning date is after start date. - var validDate = validateDate(anticipatedConstructionStartDateDict, anticipatedCommissioningDateDict); - if (!validDate) { - document.querySelector('#pro-forma-form-save-msg').innerHTML = ` - - Anticipated Commissioning date has to be after Anticipated Construction start date - - `; - } - else { - result = { - 'pro_forma_start_date': proFormaStartDate, - 'pro_forma_duration': proFormaDuration, - 'analysis_date': analysisDate, - 'fund': fund, - 'anticipated_construction_start_date': anticipatedConstructionStartDate, - 'anticipated_commissioning_date': anticipatedCommissioningDate, - 'anticipated_construction_period': anticipatedConstructionPeriod, - } - request('finance-overview/', { - 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) { - // Display response message. Add that loan options changed if fund was changed. Reset didFundChange to false. - responseMessage = 'Saved'; - if (didFundChange) { - responseMessage += ' Loan Options table is reloaded.'; - } - var resMsg = document.querySelector('#pro-forma-form-save-msg'); - resMsg.innerHTML = `${responseMessage}`; - /** - * delete-loan-options request to the backend to delete loan options for this building id if previosly stored. - * This ensures that the loan options table is up to date with the latest fund. Clear the loan options table - * and reload with new fund's default loan options. - */ - if (didFundChange) { - didFundChange = false; - request(`loan-options/?loans=delete-loan-options`, { - method: 'GET', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json' - }, - }).then(res => { - var tableBody = document.querySelector('#loan-options-table tbody'); - tableBody.innerHTML = ``; - }); - } - getLoanOptionsTable(); - } - else { - // Display error message. - var resMsg = document.querySelector('#pro-forma-form-save-msg'); - res.err.responseBody.then((error) => { - resMsg.innerHTML = `${error.error}`; - }) - } - }); - } - } - return false; -} - /** Validate that commissioning date is after the construction start date. */ function validateDate(startDate, endDate) { var startDateYear = startDate.year; diff --git a/blocnote/apps/financialInputs/static/financialInputs/scripts/proformaInputs.js b/blocnote/apps/financialInputs/static/financialInputs/scripts/proformaInputs.js new file mode 100644 index 0000000000000000000000000000000000000000..dbdc284c87df042b39c4ef369c1cd34a2759c942 --- /dev/null +++ b/blocnote/apps/financialInputs/static/financialInputs/scripts/proformaInputs.js @@ -0,0 +1,162 @@ +'use strict'; + +const ERROR_TAGS = [ + 'pro_forma_start_date-error', + 'pro_forma_duration-error', + 'analysis_date-error', + 'fund-error', + 'anticipated_construction_start_date-error', + 'anticipated_commissioning_date-error', + 'anticipated_construction_period-error', +]; +/** + * proformaForm will watch the form and if it changes, it will remove the response message is it + * was displayed. + */ +const proformaForm = document.querySelector('#pro-forma-form'); +proformaForm.onchange = function resetErrors() { + document.querySelector('#pro-forma-form-save-msg').innerHTML = ''; + ERROR_TAGS.forEach((tag) => { + document.querySelector(`#${tag}`).innerHTML = ''; + }); +}; + +/** + * The following 2 functions display a warning message saying if fund is changed, it will affect + * the loan options. This message is displayed when the mouse is over the fund select box. + */ +const fund = document.querySelector('#id_fund'); +/** + * This is to watch if fund value is changed. If it did, it would affect loan options. + */ +let didFundChange = false; +fund.onmouseenter = function showFundWarning() { + document.querySelector('#show-error').innerHTML = ` + Changing fund will affect loan options + `; +}; + +fund.onmouseleave = function clearFundWarning() { + document.querySelector('#show-error').innerHTML = ''; +}; + +fund.onchange = function checkIfFundChanged() { + didFundChange = true; +}; + +/** + * HTTP GET request to fetch the proforma form information from the database if preent. It would + * receive the list of funds along with. + */ +function getBillProjectionDatesForm() { + request('finance-overview', { + method: 'GET', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + }).then((res) => { + if (!res.err) { + const fundDropBox = document.querySelector('#fund_id'); + const funds = res.payload.instance.funds; + // Load fund dropdown box with the list of funds received. + funds.forEach((thisFund) => { + fundDropBox.innerHTML += ` + + `; + }); + // Load all the values if previously filled. + if (res.payload.instance.financing_overview_data) { + const financingOverviewData = res.payload.instance.financing_overview_data; + Object.keys(financingOverviewData).forEach((key) => { + const data = financingOverviewData[key]; + proformaForm.querySelector(`#${key}`).value = data; + }); + } + } else { + // Display error message if error on load. + document.querySelector('#pro-forma-form-save-msg').innerHTML = ` + Sorry, there was an error loading. + `; + } + }); +} + +getBillProjectionDatesForm(); + +/** + * Handle submition of the Proforma form. Validate commissioning date is greater than construction + * start date. Create result dictionary containing the form data and convert into JSON to send to + * the backend. Upon success, load the loan options table with the saved fund if fund changed or + * saving this form for the first time. + * + * @param {any} form : Proforma inputs form filled. + * @returns : False so that page does not reload. + */ +function billProjectionDatesForm(form) { + const formData = new FormData(form); + const result = {}; + formData.forEach((value, field) => { + result[field] = value; + }); + const constructionStartDate = new Date(result.anticipated_construction_start_date); + const constructionCommissioningDate = new Date(result.anticipated_commissioning_date); + // Make sure construction commision date is afer construction start date. + if (constructionStartDate > constructionCommissioningDate) { + document.querySelector('#pro-forma-form-save-msg').innerHTML = ` + + Anticipated Commissioning date has to be after Anticipated Construction start date + + `; + return false; + } + request('finance-overview/', { + method: 'PUT', + credentials: 'same-origin', + body: JSON.stringify(result), + headers: new Headers({ + 'Content-Type': 'application/json', + 'X-CSRFToken': Cookies.get('csrftoken'), + }), + }).then((res) => { + const displayMessage = document.querySelector('#pro-forma-form-save-msg'); + if (!res.err) { + // Display response message. Add that loan options changed if fund was changed. Reset + // didFundChange to false. + let responseMessage = 'Saved. '; + if (didFundChange) { + responseMessage += 'Loan Options table is reloaded.'; + } + displayMessage.innerHTML = `${responseMessage}`; + /** + * delete-loan-options request to the backend to delete loan options for this building id if + * previosly stored. This ensures that the loan options table is up to date with the latest + * fund. Clear the loan options table and reload with new fund's default loan options. + */ + if (didFundChange) { + didFundChange = false; + request('loan-options/?loans=delete-loan-options', { + method: 'GET', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + }, + }).then(() => { + const tableBody = document.querySelector('#loan-options-table tbody'); + tableBody.innerHTML = ''; + }); + getLoanOptionsTable(); + } + } else { + res.err.responseBody.then((error) => { + // Display error message. + Object.keys(error).forEach((key) => { + document.querySelector(`#${key}-error`).innerHTML = ` + ${error[key][0]} + `; + }); + }); + } + }); + return false; +} diff --git a/blocnote/apps/financialInputs/templates/financialInputs/headerForm.html b/blocnote/apps/financialInputs/templates/financialInputs/headerForm.html deleted file mode 100644 index 51a687a6529c72c918100ee0eeea59f961041121..0000000000000000000000000000000000000000 --- a/blocnote/apps/financialInputs/templates/financialInputs/headerForm.html +++ /dev/null @@ -1,51 +0,0 @@ -
diff --git a/blocnote/apps/financialInputs/templates/financialInputs/index.html b/blocnote/apps/financialInputs/templates/financialInputs/index.html index a1e800b4c78eb12dcbee7a6eb97927e32836cdc0..064a91370da324dd5c9e636745eb7de9fa07962e 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/index.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/index.html @@ -55,4 +55,5 @@ + {% endblock %} diff --git a/blocnote/apps/financialInputs/templates/financialInputs/proFormaForm.html b/blocnote/apps/financialInputs/templates/financialInputs/proFormaForm.html index f3d71224eddeb63249bef928b267fc2269805acb..ccc94e43bb4ed034beca7c103900256c5d36bf6c 100644 --- a/blocnote/apps/financialInputs/templates/financialInputs/proFormaForm.html +++ b/blocnote/apps/financialInputs/templates/financialInputs/proFormaForm.html @@ -4,11 +4,15 @@