Make emergency fund optional and consolidate budget JS code
This commit is contained in:
parent
2ca59c0684
commit
2d9ea3b311
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,5 +8,4 @@ settings.py
|
|||||||
**/migrations/**
|
**/migrations/**
|
||||||
!**/migrations/__init__.py
|
!**/migrations/__init__.py
|
||||||
|
|
||||||
static
|
|
||||||
promo_uploads
|
promo_uploads
|
||||||
|
@ -32,3 +32,4 @@ if 'ssmembership' in settings.INSTALLED_APPS:
|
|||||||
|
|
||||||
urlpatterns.append(path('', include('ssmain.urls')))
|
urlpatterns.append(path('', include('ssmain.urls')))
|
||||||
urlpatterns.extend(static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT))
|
urlpatterns.extend(static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT))
|
||||||
|
urlpatterns.extend(static(settings.STATIC_URL, document_root=settings.STATIC_ROOT))
|
||||||
|
@ -55,13 +55,14 @@
|
|||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui inline grid field">
|
<div class="ui inline grid field">
|
||||||
<label class="three wide column">Revenue</label>
|
<label class="three wide column">Revenue</label>
|
||||||
<div id="revenue_grid"></div>
|
<div class="eleven wide column"></div>
|
||||||
<input type="hidden" name="revenue" id="revenue_input">
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="revenue_grid"></div>
|
||||||
|
<input type="hidden" name="revenue" id="revenue_input">
|
||||||
<div class="ui accordion">
|
<div class="ui accordion">
|
||||||
<div class="{% if revision.revenue_comments %}active {% endif %}title">
|
<div class="{% if revision.revenue_comments %}active {% endif %}title">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
Revenue comments
|
Revenue comments (click to show/hide)
|
||||||
</div>
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<div class="ui inline grid field">
|
<div class="ui inline grid field">
|
||||||
@ -73,16 +74,17 @@
|
|||||||
<div class="ui divider"></div>
|
<div class="ui divider"></div>
|
||||||
<div class="ui inline grid field">
|
<div class="ui inline grid field">
|
||||||
<label class="three wide column">Expenses</label>
|
<label class="three wide column">Expenses</label>
|
||||||
<div id="expense_grid"></div>
|
<div class="eleven wide column"><input type="checkbox" name="expense_no_emergency_fund" id="expense_no_emergency_fund"{% if revision.expense_no_emergency_fund %} checked{% endif %}> <label for="expense_no_emergency_fund">No emergency fund required (please add a comment explaining why)</label></div>
|
||||||
<input type="hidden" name="expense" id="expense_input">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ui accordion">
|
<div id="expense_grid"></div>
|
||||||
<div class="{% if revision.expense_comments %}active {% endif %}title">
|
<input type="hidden" name="expense" id="expense_input">
|
||||||
|
<div class="ui accordion" id="expense_comments_accordion">
|
||||||
|
<div class="{% if revision.expense_comments or revision.expense_no_emergency_fund %}active {% endif %}title">
|
||||||
<i class="dropdown icon"></i>
|
<i class="dropdown icon"></i>
|
||||||
Expense comments
|
Expense comments (click to show/hide)
|
||||||
</div>
|
</div>
|
||||||
<div class="{% if revision.expense_comments %}active {% endif %}content">
|
<div class="{% if revision.expense_comments or revision.expense_no_emergency_fund %}active {% endif %}content">
|
||||||
<div class="ui inline grid field">
|
<div class="ui {% if revision.expense_no_emergency_fund %}required {% endif %}inline grid field">
|
||||||
<label class="three wide column">Comments</label>
|
<label class="three wide column">Comments</label>
|
||||||
<textarea class="eleven wide column" rows="2" name="expense_comments">{{ revision.expense_comments }}</textarea>
|
<textarea class="eleven wide column" rows="2" name="expense_comments">{{ revision.expense_comments }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
@ -112,13 +114,14 @@
|
|||||||
<script src="https://cdn.jsdelivr.net/npm/jsgrid@1.5.3/dist/jsgrid.min.js" integrity="sha256-lzjMTpg04xOdI+MJdjBst98bVI6qHToLyVodu3EywFU=" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/jsgrid@1.5.3/dist/jsgrid.min.js" integrity="sha256-lzjMTpg04xOdI+MJdjBst98bVI6qHToLyVodu3EywFU=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dragula@3.7.2/dist/dragula.min.js" integrity="sha256-ug4bHfqHFAj2B5MESRxbLd3R3wdVMQzug2KHZqFEmFI=" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/dragula@3.7.2/dist/dragula.min.js" integrity="sha256-ug4bHfqHFAj2B5MESRxbLd3R3wdVMQzug2KHZqFEmFI=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<script src="{{ static('sstreasury/budget.js') }}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function leftpad(n) {
|
function leftpad(n) {
|
||||||
if (n < 10)
|
if (n < 10)
|
||||||
return '0' + n;
|
return '0' + n;
|
||||||
return '' + n;
|
return '' + n;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#cal_date').calendar({
|
$('#cal_date').calendar({
|
||||||
type: 'date',
|
type: 'date',
|
||||||
formatter: {
|
formatter: {
|
||||||
@ -128,7 +131,16 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('.ui.accordion').accordion();
|
$('#expense_no_emergency_fund').change(function() {
|
||||||
|
if ($('#expense_no_emergency_fund').prop('checked')) {
|
||||||
|
$('#expense_comments_accordion').accordion('open', 0);
|
||||||
|
$('#expense_comments_accordion .field').addClass('required');
|
||||||
|
} else {
|
||||||
|
$('#expense_comments_accordion .field').removeClass('required');
|
||||||
|
}
|
||||||
|
|
||||||
|
recalcExpTotal({'grid': $('#expense_grid').data('JSGrid')});
|
||||||
|
});
|
||||||
|
|
||||||
$('.ui.form').form({
|
$('.ui.form').form({
|
||||||
on: 'blur',
|
on: 'blur',
|
||||||
@ -172,110 +184,10 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function recalcRevTotal(args) {
|
|
||||||
//console.log(args);
|
|
||||||
var total = 0;
|
|
||||||
var totalIWT = 0;
|
|
||||||
for (var row of args.grid.data) {
|
|
||||||
total += row['Unit price'] * row['Units'];
|
|
||||||
if (row['Unit price'] > 0 && row['IWT']) {
|
|
||||||
totalIWT += (row['Unit price'] - (row['Unit price'] - 0.8) / 1.019) * row['Units'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(args.grid._body).find('.totalrow').remove();
|
|
||||||
|
|
||||||
if (totalIWT > 0) {
|
|
||||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-style: italic;"></tr>');
|
|
||||||
totalrow.append($('<td class="jsgrid-cell">Less IWT fees:</td>').prop('colspan', args.grid.fields.length - 2));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('($' + totalIWT.toFixed(2) + ')'));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell"></td>'));
|
|
||||||
$(args.grid._body).find('tr:last').after(totalrow);
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-weight: bold;"></tr>');
|
|
||||||
totalrow.append($('<td class="jsgrid-cell">Total:</td>').prop('colspan', args.grid.fields.length - 2));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('$' + (total - totalIWT).toFixed(2)));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell"></td>'));
|
|
||||||
$(args.grid._body).find('tr:last').after(totalrow);
|
|
||||||
}
|
|
||||||
|
|
||||||
function recalcExpTotal(args) {
|
|
||||||
var total = 0;
|
|
||||||
for (var row of args.grid.data) {
|
|
||||||
total += row['Unit price'] * row['Units'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$(args.grid._body).find('.totalrow').remove();
|
|
||||||
|
|
||||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-style: italic;"></tr>');
|
|
||||||
totalrow.append($('<td class="jsgrid-cell">Plus emergency fund:</td>').prop('colspan', args.grid.fields.length - 2));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('$' + (total * 0.05).toFixed(2)));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell"></td>'));
|
|
||||||
$(args.grid._body).find('tr:last').after(totalrow);
|
|
||||||
|
|
||||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-weight: bold;"></tr>');
|
|
||||||
totalrow.append($('<td class="jsgrid-cell">Total:</td>').prop('colspan', args.grid.fields.length - 2));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('$' + (total * 1.05).toFixed(2)));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell"></td>'));
|
|
||||||
$(args.grid._body).find('tr:last').after(totalrow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow floats
|
|
||||||
function FloatNumberField(config) {
|
|
||||||
jsGrid.NumberField.call(this, config);
|
|
||||||
}
|
|
||||||
FloatNumberField.prototype = new jsGrid.NumberField({
|
|
||||||
filterValue: function() {
|
|
||||||
return parseFloat(this.filterControl.val());
|
|
||||||
},
|
|
||||||
insertValue: function() {
|
|
||||||
return parseFloat(this.insertControl.val());
|
|
||||||
},
|
|
||||||
editValue: function() {
|
|
||||||
return parseFloat(this.editControl.val());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
jsGrid.fields.float = FloatNumberField;
|
|
||||||
|
|
||||||
var revenue_data = JSON.parse({{ import('json').dumps(import('json').dumps(revision.revenue))|safe }});
|
var revenue_data = JSON.parse({{ import('json').dumps(import('json').dumps(revision.revenue))|safe }});
|
||||||
$('#revenue_grid').jsGrid({
|
|
||||||
width: '100%',
|
|
||||||
height: '20em',
|
|
||||||
inserting: true,
|
|
||||||
editing: true,
|
|
||||||
noDataContent: 'No entries. Click the green plus icon at the top right to add a new row.',
|
|
||||||
data: revenue_data,
|
|
||||||
fields: [
|
|
||||||
{ name: 'Description', type: 'text', width: '50%', validate: 'required' },
|
|
||||||
{ name: 'Unit price', type: 'float', width: '12.5%', validate: 'required', itemTemplate: function(value, item) { return '$' + value.toFixed(2); } },
|
|
||||||
{ name: 'Units', type: 'float', width: '12.5%', validate: 'required' },
|
|
||||||
{ name: 'IWT', type: 'checkbox', width: '5%', insertTemplate: function() { var result = jsGrid.fields.checkbox.prototype.insertTemplate.call(this); result.prop('checked', true); return result; } },
|
|
||||||
{ name: 'Total', align: 'right', width: '10%', itemTemplate: function(value, item) { return '$' + (item['Unit price'] * item['Units']).toFixed(2); } },
|
|
||||||
{ type: 'control', width: '10%', modeSwitchButton: false },
|
|
||||||
],
|
|
||||||
onItemUpdated: recalcRevTotal,
|
|
||||||
onRefreshed: recalcRevTotal,
|
|
||||||
});
|
|
||||||
|
|
||||||
var expense_data = JSON.parse({{ import('json').dumps(import('json').dumps(revision.expense))|safe }});
|
var expense_data = JSON.parse({{ import('json').dumps(import('json').dumps(revision.expense))|safe }});
|
||||||
$('#expense_grid').jsGrid({
|
var editing = true;
|
||||||
width: '100%',
|
makeGrid();
|
||||||
height: '20em',
|
|
||||||
inserting: true,
|
|
||||||
editing: true,
|
|
||||||
noDataContent: 'No entries. Click the green plus icon at the top right to add a new row.',
|
|
||||||
data: expense_data,
|
|
||||||
fields: [
|
|
||||||
{ name: 'Description', type: 'text', width: '50%', validate: 'required' },
|
|
||||||
{ name: 'Unit price', type: 'float', width: '12.5%', validate: 'required', itemTemplate: function(value, item) { return '$' + value.toFixed(2); } },
|
|
||||||
{ name: 'Units', type: 'float', width: '12.5%', validate: 'required' },
|
|
||||||
{ name: 'Total', align: 'right', width: '10%', itemTemplate: function(value, item) { return '$' + (item['Unit price'] * item['Units']).toFixed(2); } },
|
|
||||||
{ type: 'control', width: '10%', modeSwitchButton: false },
|
|
||||||
],
|
|
||||||
onItemUpdated: recalcExpTotal,
|
|
||||||
onRefreshed: recalcExpTotal,
|
|
||||||
});
|
|
||||||
|
|
||||||
dragula([document.querySelector('#revenue_grid tbody')], {
|
dragula([document.querySelector('#revenue_grid tbody')], {
|
||||||
accepts: function (el, target, source, sibling) {
|
accepts: function (el, target, source, sibling) {
|
||||||
|
@ -91,6 +91,9 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>Expenses</td>
|
<td>Expenses</td>
|
||||||
<td>
|
<td>
|
||||||
|
{% if revision.expense_no_emergency_fund %}
|
||||||
|
<p><input type="checkbox" id="expense_no_emergency_fund" disabled checked> No emergency fund required (please add a comment explaining why)</p>
|
||||||
|
{% endif %}
|
||||||
<div id="expense_grid"></div>
|
<div id="expense_grid"></div>
|
||||||
{% if revision.expense_comments %}
|
{% if revision.expense_comments %}
|
||||||
<div class="ui accordion">
|
<div class="ui accordion">
|
||||||
@ -190,100 +193,12 @@
|
|||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jsgrid@1.5.3/dist/jsgrid.min.js" integrity="sha256-lzjMTpg04xOdI+MJdjBst98bVI6qHToLyVodu3EywFU=" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/jsgrid@1.5.3/dist/jsgrid.min.js" integrity="sha256-lzjMTpg04xOdI+MJdjBst98bVI6qHToLyVodu3EywFU=" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<script src="{{ static('sstreasury/budget.js') }}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
$('.ui.accordion').accordion();
|
|
||||||
|
|
||||||
function recalcRevTotal(args) {
|
|
||||||
//console.log(args);
|
|
||||||
var total = 0;
|
|
||||||
var totalIWT = 0;
|
|
||||||
for (var row of args.grid.data) {
|
|
||||||
total += row['Unit price'] * row['Units'];
|
|
||||||
if (row['Unit price'] > 0 && row['IWT']) {
|
|
||||||
totalIWT += (row['Unit price'] - (row['Unit price'] - 0.8) / 1.019) * row['Units'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$(args.grid._body).find('.totalrow').remove();
|
|
||||||
|
|
||||||
if (totalIWT > 0) {
|
|
||||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-style: italic;"></tr>');
|
|
||||||
totalrow.append($('<td class="jsgrid-cell">Less IWT fees:</td>').prop('colspan', args.grid.fields.length - 1));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('($' + totalIWT.toFixed(2) + ')'));
|
|
||||||
$(args.grid._body).find('tr:last').after(totalrow);
|
|
||||||
}
|
|
||||||
|
|
||||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-weight: bold;"></tr>');
|
|
||||||
totalrow.append($('<td class="jsgrid-cell">Total:</td>').prop('colspan', args.grid.fields.length - 1));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('$' + (total - totalIWT).toFixed(2)));
|
|
||||||
$(args.grid._body).find('tr:last').after(totalrow);
|
|
||||||
}
|
|
||||||
|
|
||||||
function recalcExpTotal(args) {
|
|
||||||
var total = 0;
|
|
||||||
for (var row of args.grid.data) {
|
|
||||||
total += row['Unit price'] * row['Units'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$(args.grid._body).find('.totalrow').remove();
|
|
||||||
|
|
||||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-style: italic;"></tr>');
|
|
||||||
totalrow.append($('<td class="jsgrid-cell">Plus emergency fund:</td>').prop('colspan', args.grid.fields.length - 1));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('$' + (total * 0.05).toFixed(2)));
|
|
||||||
$(args.grid._body).find('tr:last').after(totalrow);
|
|
||||||
|
|
||||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-weight: bold;"></tr>');
|
|
||||||
totalrow.append($('<td class="jsgrid-cell">Total:</td>').prop('colspan', args.grid.fields.length - 1));
|
|
||||||
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('$' + (total * 1.05).toFixed(2)));
|
|
||||||
$(args.grid._body).find('tr:last').after(totalrow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow floats
|
|
||||||
function FloatNumberField(config) {
|
|
||||||
jsGrid.NumberField.call(this, config);
|
|
||||||
}
|
|
||||||
FloatNumberField.prototype = new jsGrid.NumberField({
|
|
||||||
filterValue: function() {
|
|
||||||
return parseFloat(this.filterControl.val());
|
|
||||||
},
|
|
||||||
insertValue: function() {
|
|
||||||
return parseFloat(this.insertControl.val());
|
|
||||||
},
|
|
||||||
editValue: function() {
|
|
||||||
return parseFloat(this.editControl.val());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
jsGrid.fields.float = FloatNumberField;
|
|
||||||
|
|
||||||
var revenue_data = JSON.parse({{ import('json').dumps(import('json').dumps(revision.revenue))|safe }});
|
var revenue_data = JSON.parse({{ import('json').dumps(import('json').dumps(revision.revenue))|safe }});
|
||||||
$('#revenue_grid').jsGrid({
|
|
||||||
width: '100%',
|
|
||||||
height: 'auto',
|
|
||||||
noDataContent: 'No entries',
|
|
||||||
data: revenue_data,
|
|
||||||
fields: [
|
|
||||||
{ name: 'Description', type: 'text', width: '55%', validate: 'required' },
|
|
||||||
{ name: 'Unit price', type: 'float', width: '10%', validate: 'required', itemTemplate: function(value, item) { return '$' + value.toFixed(2); } },
|
|
||||||
{ name: 'Units', type: 'float', width: '10%', validate: 'required' },
|
|
||||||
{ name: 'IWT', type: 'checkbox', width: '5%' },
|
|
||||||
{ name: 'Total', align: 'right', width: '10%', itemTemplate: function(value, item) { return '$' + (item['Unit price'] * item['Units']).toFixed(2); } },
|
|
||||||
],
|
|
||||||
onRefreshed: recalcRevTotal,
|
|
||||||
});
|
|
||||||
|
|
||||||
var expense_data = JSON.parse({{ import('json').dumps(import('json').dumps(revision.expense))|safe }});
|
var expense_data = JSON.parse({{ import('json').dumps(import('json').dumps(revision.expense))|safe }});
|
||||||
$('#expense_grid').jsGrid({
|
var editing = false;
|
||||||
width: '100%',
|
makeGrid();
|
||||||
height: 'auto',
|
|
||||||
noDataContent: 'No entries',
|
|
||||||
data: expense_data,
|
|
||||||
fields: [
|
|
||||||
{ name: 'Description', type: 'text', width: '55%', validate: 'required' },
|
|
||||||
{ name: 'Unit price', type: 'float', width: '10%', validate: 'required', itemTemplate: function(value, item) { return '$' + value.toFixed(2); } },
|
|
||||||
{ name: 'Units', type: 'float', width: '10%', validate: 'required' },
|
|
||||||
{ name: 'Total', align: 'right', width: '10%', itemTemplate: function(value, item) { return '$' + (item['Unit price'] * item['Units']).toFixed(2); } },
|
|
||||||
],
|
|
||||||
onRefreshed: recalcExpTotal,
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -63,6 +63,7 @@ class BudgetRevision(models.Model):
|
|||||||
revenue_comments = models.TextField()
|
revenue_comments = models.TextField()
|
||||||
|
|
||||||
expense = JSONField(default=[])
|
expense = JSONField(default=[])
|
||||||
|
expense_no_emergency_fund = models.BooleanField()
|
||||||
expense_comments = models.TextField()
|
expense_comments = models.TextField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
144
sstreasury/static/sstreasury/budget.js
Normal file
144
sstreasury/static/sstreasury/budget.js
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/*
|
||||||
|
Society Self-Service
|
||||||
|
Copyright © 2018 Yingtong Li (RunasSudo)
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$('.ui.accordion').accordion();
|
||||||
|
|
||||||
|
function recalcRevTotal(args) {
|
||||||
|
var total = 0;
|
||||||
|
var totalIWT = 0;
|
||||||
|
for (var row of args.grid.data) {
|
||||||
|
total += row['Unit price'] * row['Units'];
|
||||||
|
if (row['Unit price'] > 0 && row['IWT']) {
|
||||||
|
totalIWT += (row['Unit price'] - (row['Unit price'] - 0.8) / 1.019) * row['Units'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(args.grid._body).find('.totalrow').remove();
|
||||||
|
|
||||||
|
if (totalIWT > 0) {
|
||||||
|
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-style: italic;"></tr>');
|
||||||
|
totalrow.append($('<td class="jsgrid-cell">Less IWT fees:</td>').prop('colspan', args.grid.fields.length - (editing ? 2 : 1)));
|
||||||
|
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('($' + totalIWT.toFixed(2) + ')'));
|
||||||
|
if (editing) {
|
||||||
|
totalrow.append($('<td class="jsgrid-cell"></td>'));
|
||||||
|
}
|
||||||
|
$(args.grid._body).find('tr:last').after(totalrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-weight: bold;"></tr>');
|
||||||
|
totalrow.append($('<td class="jsgrid-cell">Total:</td>').prop('colspan', args.grid.fields.length - (editing ? 2 : 1)));
|
||||||
|
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('$' + (total - totalIWT).toFixed(2)));
|
||||||
|
if (editing) {
|
||||||
|
totalrow.append($('<td class="jsgrid-cell"></td>'));
|
||||||
|
}
|
||||||
|
$(args.grid._body).find('tr:last').after(totalrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
function recalcExpTotal(args) {
|
||||||
|
var total = 0;
|
||||||
|
for (var row of args.grid.data) {
|
||||||
|
total += row['Unit price'] * row['Units'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$(args.grid._body).find('.totalrow').remove();
|
||||||
|
|
||||||
|
var emergency_fund_mult = 0.05;
|
||||||
|
if ($('#expense_no_emergency_fund').length > 0 && $('#expense_no_emergency_fund').prop('checked')) {
|
||||||
|
emergency_fund_mult = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-style: italic;"></tr>');
|
||||||
|
totalrow.append($('<td class="jsgrid-cell">Plus emergency fund:</td>').prop('colspan', args.grid.fields.length - (editing ? 2 : 1)));
|
||||||
|
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('$' + (total * emergency_fund_mult).toFixed(2)));
|
||||||
|
if (editing) {
|
||||||
|
totalrow.append($('<td class="jsgrid-cell"></td>'));
|
||||||
|
}
|
||||||
|
$(args.grid._body).find('tr:last').after(totalrow);
|
||||||
|
|
||||||
|
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-weight: bold;"></tr>');
|
||||||
|
totalrow.append($('<td class="jsgrid-cell">Total:</td>').prop('colspan', args.grid.fields.length - (editing ? 2 : 1)));
|
||||||
|
totalrow.append($('<td class="jsgrid-cell jsgrid-align-right"></td>').text('$' + (total * (1 + emergency_fund_mult)).toFixed(2)));
|
||||||
|
if (editing) {
|
||||||
|
totalrow.append($('<td class="jsgrid-cell"></td>'));
|
||||||
|
}
|
||||||
|
$(args.grid._body).find('tr:last').after(totalrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow floats
|
||||||
|
function FloatNumberField(config) {
|
||||||
|
jsGrid.NumberField.call(this, config);
|
||||||
|
}
|
||||||
|
FloatNumberField.prototype = new jsGrid.NumberField({
|
||||||
|
filterValue: function() {
|
||||||
|
return parseFloat(this.filterControl.val());
|
||||||
|
},
|
||||||
|
insertValue: function() {
|
||||||
|
return parseFloat(this.insertControl.val());
|
||||||
|
},
|
||||||
|
editValue: function() {
|
||||||
|
return parseFloat(this.editControl.val());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jsGrid.fields.float = FloatNumberField;
|
||||||
|
|
||||||
|
function makeGrid() {
|
||||||
|
f = [
|
||||||
|
{ name: 'Description', type: 'text', width: '50%', validate: 'required' },
|
||||||
|
{ name: 'Unit price', type: 'float', width: '12.5%', validate: 'required', itemTemplate: function(value, item) { return '$' + value.toFixed(2); } },
|
||||||
|
{ name: 'Units', type: 'float', width: '12.5%', validate: 'required' },
|
||||||
|
{ name: 'IWT', type: 'checkbox', width: '5%', insertTemplate: function() { var result = jsGrid.fields.checkbox.prototype.insertTemplate.call(this); result.prop('checked', true); return result; } },
|
||||||
|
{ name: 'Total', align: 'right', width: '10%', itemTemplate: function(value, item) { return '$' + (item['Unit price'] * item['Units']).toFixed(2); } },
|
||||||
|
];
|
||||||
|
if (editing) {
|
||||||
|
f.push({ type: 'control', width: '10%', modeSwitchButton: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#revenue_grid').jsGrid({
|
||||||
|
width: '100%',
|
||||||
|
height: editing ? '20em' : 'auto',
|
||||||
|
inserting: editing,
|
||||||
|
editing: editing,
|
||||||
|
noDataContent: editing ? 'No entries. Click the green plus icon at the top right to add a new row.' : 'No entries',
|
||||||
|
data: revenue_data,
|
||||||
|
fields: f,
|
||||||
|
onItemUpdated: recalcRevTotal,
|
||||||
|
onRefreshed: recalcRevTotal,
|
||||||
|
});
|
||||||
|
|
||||||
|
f = [
|
||||||
|
{ name: 'Description', type: 'text', width: '50%', validate: 'required' },
|
||||||
|
{ name: 'Unit price', type: 'float', width: '12.5%', validate: 'required', itemTemplate: function(value, item) { return '$' + value.toFixed(2); } },
|
||||||
|
{ name: 'Units', type: 'float', width: '12.5%', validate: 'required' },
|
||||||
|
{ name: 'Total', align: 'right', width: '10%', itemTemplate: function(value, item) { return '$' + (item['Unit price'] * item['Units']).toFixed(2); } },
|
||||||
|
]
|
||||||
|
if (editing) {
|
||||||
|
f.push({ type: 'control', width: '10%', modeSwitchButton: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#expense_grid').jsGrid({
|
||||||
|
width: '100%',
|
||||||
|
height: editing ? '20em' : 'auto',
|
||||||
|
inserting: editing,
|
||||||
|
editing: editing,
|
||||||
|
noDataContent: editing ? 'No entries. Click the green plus icon at the top right to add a new row.' : 'No entries',
|
||||||
|
data: expense_data,
|
||||||
|
fields: f,
|
||||||
|
onItemUpdated: recalcExpTotal,
|
||||||
|
onRefreshed: recalcExpTotal,
|
||||||
|
});
|
||||||
|
}
|
@ -86,6 +86,7 @@ def revision_from_form(budget, revision, form):
|
|||||||
revision.revenue_comments = form['revenue_comments']
|
revision.revenue_comments = form['revenue_comments']
|
||||||
revision.expense = json.loads(form['expense'])
|
revision.expense = json.loads(form['expense'])
|
||||||
revision.expense_comments = form['expense_comments']
|
revision.expense_comments = form['expense_comments']
|
||||||
|
revision.expense_no_emergency_fund = True if form.get('expense_no_emergency_fund', False) else False
|
||||||
|
|
||||||
revision.save()
|
revision.save()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user