Implement revenue/expense editing
This commit is contained in:
parent
5eba7b3308
commit
eddddde263
@ -38,7 +38,7 @@
|
||||
<div class="ui calendar" id="cal_date">
|
||||
<div class="ui input left icon grid">
|
||||
<i class="calendar icon" style="z-index: 999;"></i>
|
||||
<input class="twelve wide column" type="text" name="date" value="{{ revision.date }}">
|
||||
<input class="twelve wide column" type="text" name="date" value="{{ revision.date or '' }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -55,7 +55,8 @@
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui inline grid field">
|
||||
<label class="three wide column">Revenue</label>
|
||||
<textarea class="eleven wide column" rows="4" name="revenue">{{ revision.revenue }}</textarea>
|
||||
<div id="revenue_grid"></div>
|
||||
<input type="hidden" name="revenue" id="revenue_input">
|
||||
</div>
|
||||
<div class="ui accordion">
|
||||
<div class="{% if revision.revenue_comments %}active {% endif %}title">
|
||||
@ -72,7 +73,8 @@
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui inline grid field">
|
||||
<label class="three wide column">Expenses</label>
|
||||
<textarea class="eleven wide column" rows="4" name="expense">{{ revision.expense }}</textarea>
|
||||
<div id="expense_grid"></div>
|
||||
<input type="hidden" name="expense" id="expense_input">
|
||||
</div>
|
||||
<div class="ui accordion">
|
||||
<div class="{% if revision.revenue_comments %}active {% endif %}title">
|
||||
@ -98,6 +100,9 @@
|
||||
{{ super() }}
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/semantic-ui-calendar@0.0.8/dist/calendar.min.css" integrity="sha256-KCHiPtYk/vfF5/6lDXpz5r5FuIYchVdai0fepwGft80=" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jsgrid@1.5.3/dist/jsgrid.min.css" integrity="sha256-a/jNbtm7jpeKiXCShJ8YC+eNL9Abh7CBiYXHgaofUVs=" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jsgrid@1.5.3/dist/jsgrid-theme.min.css" integrity="sha256-0rD7ZUV4NLK6VtGhEim14ZUZGC45Kcikjdcr4N03ddA=" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dragula@3.7.2/dist/dragula.min.css" integrity="sha256-iVhQxXOykHeL03K08zkxBGxDCLCuzRGGiTYf2FL6mLY=" crossorigin="anonymous">
|
||||
|
||||
<style type="text/css">
|
||||
textarea {
|
||||
@ -110,6 +115,8 @@
|
||||
{{ super() }}
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/semantic-ui-calendar@0.0.8/dist/calendar.min.js" integrity="sha256-Pnz4CK94A8tUiYWCfg/Ko25YZrHqOKeMS4JDXVTcVA0=" 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>
|
||||
function leftpad(n) {
|
||||
@ -131,9 +138,165 @@
|
||||
|
||||
$('.ui.form').form({
|
||||
on: 'blur',
|
||||
keyboardShortcuts: false,
|
||||
fields: {
|
||||
name: 'empty',
|
||||
contributors: 'empty'
|
||||
},
|
||||
onSuccess: function(event, fields) {
|
||||
var revenue_data = [];
|
||||
$('#revenue_grid .jsgrid-grid-body tr:not(.totalrow)').each(function(i, el) {
|
||||
var row = $(el).data('JSGridItem');
|
||||
revenue_data.push({
|
||||
'Description': row['Description'],
|
||||
'Unit cost': row['Unit cost'],
|
||||
'Units': row['Units'],
|
||||
'IWT': row['IWT'],
|
||||
});
|
||||
});
|
||||
$('#revenue_input').val(JSON.stringify(revenue_data));
|
||||
|
||||
var expense_data = [];
|
||||
$('#expense_grid .jsgrid-grid-body tr:not(.totalrow)').each(function(i, el) {
|
||||
var row = $(el).data('JSGridItem');
|
||||
expense_data.push({
|
||||
'Description': row['Description'],
|
||||
'Unit cost': row['Unit cost'],
|
||||
'Units': row['Units'],
|
||||
});
|
||||
});
|
||||
$('#expense_input').val(JSON.stringify(expense_data));
|
||||
}
|
||||
});
|
||||
|
||||
// Interferes with jsGrid
|
||||
$('.ui.form').on('keyup keypress', function(e) {
|
||||
var keyCode = e.keyCode || e.which;
|
||||
if (keyCode === 13) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
function recalcRevTotal(args) {
|
||||
//console.log(args);
|
||||
var total = 0;
|
||||
var totalIWT = 0;
|
||||
for (var row of args.grid.data) {
|
||||
total += row['Unit cost'] * row['Units'];
|
||||
if (row['Unit cost'] > 0 && row['IWT']) {
|
||||
totalIWT += (row['Unit cost'] - (row['Unit cost'] - 0.8) / 1.019) * row['Units'];
|
||||
}
|
||||
}
|
||||
|
||||
$(args.grid._body).find('.totalrow').remove();
|
||||
|
||||
if (totalIWT > 0) {
|
||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-weight: bold;"></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 cost'] * row['Units'];
|
||||
}
|
||||
|
||||
$(args.grid._body).find('.totalrow').remove();
|
||||
|
||||
var totalrow = $('<tr class="jsgrid-row totalrow" style="font-weight: bold;"></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 }});
|
||||
$('#revenue_grid').jsGrid({
|
||||
width: '100%',
|
||||
height: '20em',
|
||||
inserting: true,
|
||||
editing: true,
|
||||
noDataContent: 'No entries',
|
||||
data: revenue_data,
|
||||
fields: [
|
||||
{ name: 'Description', type: 'text', width: '55%', validate: 'required' },
|
||||
{ name: 'Unit cost', 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 cost'] * item['Units']).toFixed(2); } },
|
||||
{ type: 'control', width: '10%' },
|
||||
],
|
||||
onItemUpdated: recalcRevTotal,
|
||||
onRefreshed: recalcRevTotal,
|
||||
});
|
||||
|
||||
var expense_data = JSON.parse({{ import('json').dumps(import('json').dumps(revision.expense))|safe }});
|
||||
$('#expense_grid').jsGrid({
|
||||
width: '100%',
|
||||
height: '20em',
|
||||
inserting: true,
|
||||
editing: true,
|
||||
noDataContent: 'No entries',
|
||||
data: expense_data,
|
||||
fields: [
|
||||
{ name: 'Description', type: 'text', width: '55%', validate: 'required' },
|
||||
{ name: 'Unit cost', 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 cost'] * item['Units']).toFixed(2); } },
|
||||
{ type: 'control', width: '10%' },
|
||||
],
|
||||
onItemUpdated: recalcExpTotal,
|
||||
onRefreshed: recalcExpTotal,
|
||||
});
|
||||
|
||||
dragula([document.querySelector('#revenue_grid tbody')], {
|
||||
accepts: function (el, target, source, sibling) {
|
||||
return sibling !== null && !sibling.classList.contains('totalrow');
|
||||
},
|
||||
invalid: function (el, handle) {
|
||||
return el.classList.contains('totalrow');
|
||||
}
|
||||
});
|
||||
dragula([document.querySelector('#expense_grid tbody')], {
|
||||
accepts: function (el, target, source, sibling) {
|
||||
return sibling !== null && !sibling.classList.contains('totalrow');
|
||||
},
|
||||
invalid: function (el, handle) {
|
||||
return el.classList.contains('totalrow');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -24,7 +24,10 @@
|
||||
<h1>{{ revision.name }}</h1>
|
||||
|
||||
{% if is_latest %}
|
||||
<a class="ui mini labeled icon button" href="{{ url('budget_edit', kwargs={'id': revision.budget.id}) }}"><i class="edit icon"></i> Edit</a>
|
||||
<div>
|
||||
<span class="ui header">Status: {{ import('sstreasury.models').BudgetState(revision.state).description }}</span>
|
||||
<a class="ui mini labeled right floated icon button" href="{{ url('budget_edit', kwargs={'id': revision.budget.id}) }}"><i class="edit icon"></i> Edit</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="ui warning message">
|
||||
<p>You are viewing an older version of this budget. To make any changes, <a href="{{ url('budget_view', kwargs={'id': revision.budget.id}) }}">click here</a> to return to the current version.</p>
|
||||
|
Loading…
Reference in New Issue
Block a user