290 lines
9.9 KiB
HTML
290 lines
9.9 KiB
HTML
{% extends 'sstreasury/base.html' %}
|
|
|
|
{#
|
|
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/>.
|
|
#}
|
|
|
|
{% block title %}{{ revision.name }}{% endblock %}
|
|
|
|
{% block maincontent %}
|
|
<h1>{{ revision.name }}</h1>
|
|
|
|
{% if is_latest %}
|
|
<div>
|
|
<span class="ui header">Status: {{ revision.get_state_display() }}</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>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<table class="ui mydefinition table">
|
|
<tbody>
|
|
<tr>
|
|
<td class="two wide">ID</td>
|
|
<td class="fourteen wide">{{ revision.budget.id }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Name</td>
|
|
<td>{{ revision.name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Due date</td>
|
|
<td>{{ revision.date or '' }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Contributors</td>
|
|
<td>
|
|
<div class="ui list">
|
|
{% for contributor in revision.contributors.all() %}
|
|
<div class="item">
|
|
<i class="user circle icon"></i>
|
|
<div class="content"><a href="mailto:{{ contributor.email }}">
|
|
{% if contributor.first_name %}
|
|
{{ contributor.first_name }} {{ contributor.last_name }}
|
|
{% else %}
|
|
{{ contributor.email }}
|
|
{% endif %}
|
|
</a></div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Comments</td>
|
|
<td>{{ revision.comments }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Revenue</td>
|
|
<td>
|
|
<div id="revenue_grid"></div>
|
|
{% if revision.revenue_comments %}
|
|
<div class="ui accordion">
|
|
<div class="active title">
|
|
<i class="dropdown icon"></i>
|
|
Revenue comments
|
|
</div>
|
|
<div class="active content">
|
|
{{ revision.revenue_comments }}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Expenses</td>
|
|
<td>
|
|
<div id="expense_grid"></div>
|
|
{% if revision.expense_comments %}
|
|
<div class="ui accordion">
|
|
<div class="active title">
|
|
<i class="dropdown icon"></i>
|
|
Expense comments
|
|
</div>
|
|
<div class="active content">
|
|
{{ revision.expense_comments }}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
{% if is_latest %}
|
|
<form class="ui form" action="{{ url('budget_action', kwargs={'id': revision.budget.id}) }}" method="POST">
|
|
<div class="required field">
|
|
<textarea rows="4" name="comment"></textarea>
|
|
</div>
|
|
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
|
|
<input class="ui primary button" type="submit" name="action" value="Comment">
|
|
</form>
|
|
|
|
<div class="ui feed">
|
|
{% for item in history %}
|
|
{% if item.__class__.__name__ == 'BudgetComment' %}
|
|
<div class="event">
|
|
<div class="label">
|
|
<i class="comment alternate outline icon"></i>
|
|
</div>
|
|
<div class="content">
|
|
<div class="summary">
|
|
<i class="user circle icon"></i>
|
|
<a href="mailto:{{ item.author.email }}">{{ item.author.first_name }} {{ item.author.last_name}}</a> commented
|
|
<div class="date">
|
|
{{ localtime(item.time) }}
|
|
</div>
|
|
</div>
|
|
<div class="extra text">
|
|
{{ item.content }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% elif item.__class__.__name__ == 'BudgetRevision' %}
|
|
<div class="event">
|
|
<div class="label">
|
|
<i class="edit icon"></i>
|
|
</div>
|
|
<div class="content">
|
|
<div class="summary">
|
|
<i class="user circle icon"></i>
|
|
<a href="mailto:{{ item.author.email }}">{{ item.author.first_name }} {{ item.author.last_name}}</a> edited the budget <a href="{{ url('budget_view', kwargs={'id': revision.budget.id}) }}?revision={{ item.id }}">(view)</a>
|
|
<div class="date">
|
|
{{ localtime(item.time) }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
{% endblock %}
|
|
|
|
{% block head %}
|
|
{{ super() }}
|
|
|
|
<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">
|
|
|
|
<style>
|
|
/* Fix the CSS */
|
|
|
|
.ui.mydefinition.table > tbody > tr > td:first-child:not(.ignored) {
|
|
background: rgba(0,0,0,.03);
|
|
font-weight: 700;
|
|
color: rgba(0,0,0,.95);
|
|
}
|
|
|
|
.jsgrid-align-right, .jsgrid-align-right input, .jsgrid-align-right select, .jsgrid-align-right textarea {
|
|
text-align: right !important;
|
|
}
|
|
.jsgrid-cell {
|
|
padding: .5em !important;
|
|
}
|
|
.jsgrid-header-row .jsgrid-header-cell {
|
|
text-align: center !important;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block script %}
|
|
{{ super() }}
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/jsgrid@1.5.3/dist/jsgrid.min.js" integrity="sha256-lzjMTpg04xOdI+MJdjBst98bVI6qHToLyVodu3EywFU=" crossorigin="anonymous"></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 }});
|
|
$('#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 }});
|
|
$('#expense_grid').jsGrid({
|
|
width: '100%',
|
|
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>
|
|
{% endblock %}
|