mirror of
https://github.com/nottinghamtec/PyRIGS.git
synced 2026-01-17 05:22:16 +00:00
WIP: Basic work on audit
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
<div class="modal fade" id="itemModal" role="dialog" aria-labelledby="itemModal" aria-hidded="true">
|
<div class="modal fade" id="itemModal" role="dialog" aria-labelledby="itemModal" aria-hidden="true">
|
||||||
<div class="modal-dialog modal-lg">
|
<div class="modal-dialog modal-lg">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
@@ -71,4 +71,4 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class AssetForm(forms.ModelForm):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = models.Asset
|
model = models.Asset
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
exclude = ['asset_id_prefix', 'asset_id_number']
|
exclude = ['asset_id_prefix', 'asset_id_number', 'last_audited_at', 'last_audited_by']
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|||||||
26
assets/migrations/0011_auto_20200208_2304.py
Normal file
26
assets/migrations/0011_auto_20200208_2304.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Generated by Django 2.0.13 on 2020-02-08 23:04
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('assets', '0010_auto_20200207_1737'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='last_audited_at',
|
||||||
|
field=models.DateTimeField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='asset',
|
||||||
|
name='last_audited_by',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='audited_by', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -9,7 +9,7 @@ from django.dispatch.dispatcher import receiver
|
|||||||
from reversion import revisions as reversion
|
from reversion import revisions as reversion
|
||||||
from reversion.models import Version
|
from reversion.models import Version
|
||||||
|
|
||||||
from RIGS.models import RevisionMixin
|
from RIGS.models import RevisionMixin, Profile
|
||||||
|
|
||||||
|
|
||||||
class AssetCategory(models.Model):
|
class AssetCategory(models.Model):
|
||||||
@@ -88,8 +88,13 @@ class Asset(models.Model, RevisionMixin):
|
|||||||
purchase_price = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=10)
|
purchase_price = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=10)
|
||||||
salvage_value = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=10)
|
salvage_value = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=10)
|
||||||
comments = models.TextField(blank=True)
|
comments = models.TextField(blank=True)
|
||||||
|
# TODO Remove
|
||||||
next_sched_maint = models.DateField(blank=True, null=True)
|
next_sched_maint = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
|
# Audit
|
||||||
|
last_audited_at = models.DateTimeField(blank=True, null=True)
|
||||||
|
last_audited_by = models.ForeignKey(Profile, on_delete=models.SET_NULL, related_name='audited_by', blank=True, null=True)
|
||||||
|
|
||||||
# Cable assets
|
# Cable assets
|
||||||
is_cable = models.BooleanField(default=False)
|
is_cable = models.BooleanField(default=False)
|
||||||
plug = models.ForeignKey(Connector, on_delete=models.SET_NULL,
|
plug = models.ForeignKey(Connector, on_delete=models.SET_NULL,
|
||||||
|
|||||||
82
assets/templates/asset_audit.html
Normal file
82
assets/templates/asset_audit.html
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
{% extends 'base_assets.html' %}
|
||||||
|
{% load widget_tweaks %}
|
||||||
|
{% block title %}Audit Asset {{ object.asset_id }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>
|
||||||
|
Audit Asset: {{ object.asset_id }}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<form class="form-horizontal" method="post" id="asset_update_form" action="{% url 'asset_audit' pk=object.asset_id%}">
|
||||||
|
{% include 'form_errors.html' %}
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="id" value="{{ object.id|default:0 }}" hidden=true>
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.asset_id.id_for_label }}" class="col-sm-2 control-label">Asset ID</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
{% render_field form.asset_id|add_class:'form-control' value=object.asset_id %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.description.id_for_label }}" class="col-sm-2 control-label">Description</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
{% render_field form.description|add_class:'form-control' value=object.description %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.category.id_for_label }}" class="col-sm-2 control-label">Category</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
{% render_field form.category|add_class:'form-control'%}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.status.id_for_label }}" class="col-sm-2 control-label">Status</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
{% render_field form.status|add_class:'form-control'%}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.serial_number.id_for_label }}" class="col-sm-2 control-label">Serial Number</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
{% render_field form.serial_number|add_class:'form-control' value=object.serial_number %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.date_acquired.id_for_label }}" class="col-sm-2 control-label">Date Acquired</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
{% render_field form.date_acquired|add_class:'form-control' value=object.date_acquired %}
|
||||||
|
</div>
|
||||||
|
<!--- TODO Add buttons for 'today' and 'unknown' (sets to beginning of time)--->
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="{{ form.is_cable.id_for_label }}" class="col-sm-2 control-label">Cable?</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
{% render_field form.is_cable|attr:'onchange=checkIfCableHidden()' %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-10 pull-right">
|
||||||
|
<button type="submit" class="btn btn-success"><i class="glyphicon glyphicon-floppy-disk"></i> Audit</button>
|
||||||
|
<br>
|
||||||
|
<button type="reset" class="btn btn-link" onclick="history.back()">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js%}
|
||||||
|
{% if edit %}
|
||||||
|
<script>
|
||||||
|
function checkIfCableHidden() {
|
||||||
|
if (document.getElementById("id_is_cable").checked) {
|
||||||
|
document.getElementById("cable-table").hidden = false;
|
||||||
|
} else {
|
||||||
|
document.getElementById("cable-table").hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkIfCableHidden();
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
@@ -9,26 +9,7 @@
|
|||||||
<h1 class="text-center">Asset List</h1>
|
<h1 class="text-center">Asset List</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="asset-search-form" method="get" class="form-inline pull-right">
|
{% include 'partials/asset_search.html' %}
|
||||||
<div class="input-group pull-right" style="width: auto;">
|
|
||||||
{% render_field form.query|add_class:'form-control' placeholder='Search by Asset ID/Desc/Serial' style="width: 250px"%}
|
|
||||||
<label for="query" class="sr-only">Asset ID/Description/Serial Number:</label>
|
|
||||||
<span class="input-group-btn"><button type="submit" class="btn btn-default">Search</button></span>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<div style="margin-top: 1em;" class="pull-right">
|
|
||||||
<div id="category-group" class="form-group">
|
|
||||||
<label for="category" class="sr-only">Category</label>
|
|
||||||
{% render_field form.category|attr:'multiple'|add_class:'form-control selectpicker' data-none-selected-text="Categories" data-header="Categories" data-actions-box="true" %}
|
|
||||||
</div>
|
|
||||||
<div id="status-group" class="form-group">
|
|
||||||
<label for="status" class="sr-only">Status</label>
|
|
||||||
{% render_field form.status|attr:'multiple'|add_class:'form-control selectpicker' data-none-selected-text="Statuses" data-header="Statuses" data-actions-box="true" %}
|
|
||||||
</div>
|
|
||||||
<!---TODO: Auto filter whenever an option is selected, instead of using a button -->
|
|
||||||
<button id="filter-submit" type="submit" class="btn btn-default">Filter</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
|||||||
@@ -35,6 +35,11 @@
|
|||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
{% include 'partials/parent_form.html' %}
|
{% include 'partials/parent_form.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
{% if not edit %}
|
||||||
|
<div class="col-md-4">
|
||||||
|
{% include 'partials/audit_details.html' %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
|
|||||||
28
assets/templates/partials/asset_search.html
Normal file
28
assets/templates/partials/asset_search.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{% extends request.is_ajax|yesno:'base_ajax.html,base_assets.html' %}
|
||||||
|
|
||||||
|
{% load widget_tweaks %}
|
||||||
|
|
||||||
|
{% block title %}Asset Search{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<form id="asset-search-form" method="get" class="form-inline pull-right">
|
||||||
|
<div class="input-group pull-right" style="width: auto;">
|
||||||
|
{% render_field form.query|add_class:'form-control' placeholder='Search by Asset ID/Desc/Serial' style="width: 250px"%}
|
||||||
|
<label for="query" class="sr-only">Asset ID/Description/Serial Number:</label>
|
||||||
|
<span class="input-group-btn"><button type="submit" class="btn btn-default">Search</button></span>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div style="margin-top: 1em;" class="pull-right">
|
||||||
|
<div id="category-group" class="form-group">
|
||||||
|
<label for="category" class="sr-only">Category</label>
|
||||||
|
{% render_field form.category|attr:'multiple'|add_class:'form-control selectpicker' data-none-selected-text="Categories" data-header="Categories" data-actions-box="true" %}
|
||||||
|
</div>
|
||||||
|
<div id="status-group" class="form-group">
|
||||||
|
<label for="status" class="sr-only">Status</label>
|
||||||
|
{% render_field form.status|attr:'multiple'|add_class:'form-control selectpicker' data-none-selected-text="Statuses" data-header="Statuses" data-actions-box="true" %}
|
||||||
|
</div>
|
||||||
|
<!---TODO: Auto filter whenever an option is selected, instead of using a button -->
|
||||||
|
<button id="filter-submit" type="submit" class="btn btn-default">Filter</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
8
assets/templates/partials/audit_details.html
Normal file
8
assets/templates/partials/audit_details.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<div class="panel {% if object.last_audited_at is not None %} panel-success {% else %} panel-default {% endif %}">
|
||||||
|
<div class="panel-heading">
|
||||||
|
Audit Details
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<p>Audited at <span class="label label-default">{{ object.last_audited_at|default_if_none:'-' }}</span> by <span class="label label-info">{{ object.last_audited_by|default_if_none:'-' }}</span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -31,6 +31,9 @@ urlpatterns = [
|
|||||||
views.AssetOembed.as_view(),
|
views.AssetOembed.as_view(),
|
||||||
name='asset_oembed'),
|
name='asset_oembed'),
|
||||||
|
|
||||||
|
path('asset/audit/', permission_required_with_403('assets.view_asset')(views.AssetAuditList.as_view()), name='asset_audit_list'),
|
||||||
|
path('asset/id/<str:pk>/audit/', permission_required_with_403('assets.view_asset')(views.AssetAudit.as_view()), name='asset_audit'),
|
||||||
|
|
||||||
path('supplier/list', views.SupplierList.as_view(), name='supplier_list'),
|
path('supplier/list', views.SupplierList.as_view(), name='supplier_list'),
|
||||||
path('supplier/<int:pk>', views.SupplierDetail.as_view(), name='supplier_detail'),
|
path('supplier/<int:pk>', views.SupplierDetail.as_view(), name='supplier_detail'),
|
||||||
path('supplier/create', permission_required_with_403('assets.add_supplier')
|
path('supplier/create', permission_required_with_403('assets.add_supplier')
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from assets import models, forms
|
|||||||
from RIGS import versioning
|
from RIGS import versioning
|
||||||
|
|
||||||
import simplejson
|
import simplejson
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
@@ -173,6 +174,34 @@ class AssetEmbed(AssetDetail):
|
|||||||
template_name = 'asset_embed.html'
|
template_name = 'asset_embed.html'
|
||||||
|
|
||||||
|
|
||||||
|
class AssetAuditList(LoginRequiredMixin, generic.ListView):
|
||||||
|
model = models.Asset
|
||||||
|
template_name = 'asset_list.html'
|
||||||
|
paginate_by = 40
|
||||||
|
ordering = ['-pk']
|
||||||
|
|
||||||
|
|
||||||
|
class AssetAudit(LoginRequiredMixin, AssetIDUrlMixin, generic.UpdateView):
|
||||||
|
template_name = 'asset_audit.html'
|
||||||
|
model = models.Asset
|
||||||
|
form_class = forms.AssetForm
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['edit'] = True
|
||||||
|
context['connectors'] = models.Connector.objects.all()
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
# TODO For some reason this doesn't stick when done in form_valid??
|
||||||
|
asset = self.get_object()
|
||||||
|
asset.last_audited_by = self.request.user
|
||||||
|
asset.last_audited_at = datetime.datetime.now()
|
||||||
|
asset.save()
|
||||||
|
return reverse("asset_audit_list")
|
||||||
|
|
||||||
|
|
||||||
class SupplierList(generic.ListView):
|
class SupplierList(generic.ListView):
|
||||||
model = models.Supplier
|
model = models.Supplier
|
||||||
template_name = 'supplier_list.html'
|
template_name = 'supplier_list.html'
|
||||||
|
|||||||
@@ -35,5 +35,6 @@
|
|||||||
{# % endif % #}
|
{# % endif % #}
|
||||||
{% if perms.assets.view_asset %}
|
{% if perms.assets.view_asset %}
|
||||||
<li><a href="{% url 'asset_activity_table' %}">Recent Changes</a></li>
|
<li><a href="{% url 'asset_activity_table' %}">Recent Changes</a></li>
|
||||||
|
<li><a href="{% url 'asset_audit_list' %}">Audit</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user