2025-08-12 13:33:25 +03:00

637 lines
29 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{{ audit.title }} - Quality Audits{% endblock %}
{% block content %}
<!-- BEGIN breadcrumb -->
<ol class="breadcrumb float-xl-end">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'quality:dashboard' %}">Quality</a></li>
<li class="breadcrumb-item"><a href="{% url 'quality:audit_list' %}">Audits</a></li>
<li class="breadcrumb-item active">{{ audit.title|truncatechars:30 }}</li>
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">
{{ audit.title }}
<small>Quality audit details and findings</small>
</h1>
<!-- END page-header -->
<!-- BEGIN row -->
<div class="row">
<div class="col-xl-8">
<!-- Audit Information -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Audit Information</h4>
<div class="panel-heading-btn">
{% if audit.status == 'planned' %}
<button type="button" class="btn btn-success btn-sm me-2" onclick="startAudit()">
<i class="fa fa-play me-1"></i>Start Audit
</button>
{% elif audit.status == 'in_progress' %}
<button type="button" class="btn btn-warning btn-sm me-2" onclick="completeAudit()">
<i class="fa fa-check me-1"></i>Complete Audit
</button>
{% endif %}
<a href="{% url 'quality:audit_edit' audit.pk %}" class="btn btn-primary btn-sm me-2">
<i class="fa fa-edit me-1"></i>Edit
</a>
<button type="button" class="btn btn-secondary btn-sm" onclick="printAudit()">
<i class="fa fa-print me-1"></i>Print
</button>
</div>
</div>
<div class="panel-body">
<div class="row">
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td width="120"><strong>Audit ID:</strong></td>
<td>{{ audit.audit_number|default:audit.id }}</td>
</tr>
<tr>
<td><strong>Type:</strong></td>
<td>
{% if audit.audit_type == 'internal' %}
<span class="badge bg-primary">Internal</span>
{% elif audit.audit_type == 'external' %}
<span class="badge bg-info">External</span>
{% elif audit.audit_type == 'regulatory' %}
<span class="badge bg-warning text-dark">Regulatory</span>
{% elif audit.audit_type == 'accreditation' %}
<span class="badge bg-success">Accreditation</span>
{% endif %}
</td>
</tr>
<tr>
<td><strong>Status:</strong></td>
<td>
{% if audit.status == 'planned' %}
<span class="badge bg-secondary">Planned</span>
{% elif audit.status == 'in_progress' %}
<span class="badge bg-info">In Progress</span>
{% elif audit.status == 'completed' %}
<span class="badge bg-success">Completed</span>
{% elif audit.status == 'cancelled' %}
<span class="badge bg-danger">Cancelled</span>
{% endif %}
</td>
</tr>
<tr>
<td><strong>Department:</strong></td>
<td>{{ audit.department.name|default:"All Departments" }}</td>
</tr>
<tr>
<td><strong>Lead Auditor:</strong></td>
<td>
{% if audit.lead_auditor %}
{{ audit.lead_auditor.get_full_name }}
<br><small class="text-muted">{{ audit.lead_auditor.email }}</small>
{% else %}
<span class="text-muted">Not assigned</span>
{% endif %}
</td>
</tr>
</table>
</div>
<div class="col-md-6">
<table class="table table-borderless">
<tr>
<td width="120"><strong>Scheduled:</strong></td>
<td>
{% if audit.scheduled_date %}
{{ audit.scheduled_date|date:"F d, Y" }}
{% if audit.is_overdue %}
<br><small class="text-danger">Overdue</small>
{% endif %}
{% else %}
<span class="text-muted">Not scheduled</span>
{% endif %}
</td>
</tr>
<tr>
<td><strong>Started:</strong></td>
<td>
{% if audit.start_date %}
{{ audit.start_date|date:"F d, Y g:i A" }}
{% else %}
<span class="text-muted">Not started</span>
{% endif %}
</td>
</tr>
<tr>
<td><strong>Completed:</strong></td>
<td>
{% if audit.completion_date %}
{{ audit.completion_date|date:"F d, Y g:i A" }}
{% else %}
<span class="text-muted">Not completed</span>
{% endif %}
</td>
</tr>
<tr>
<td><strong>Findings:</strong></td>
<td>
{% if audit.findings_count > 0 %}
<span class="badge bg-orange">{{ audit.findings_count }} finding{{ audit.findings_count|pluralize }}</span>
{% else %}
<span class="badge bg-success">No findings</span>
{% endif %}
</td>
</tr>
<tr>
<td><strong>Created:</strong></td>
<td>
{{ audit.created_at|date:"F d, Y g:i A" }}
<br><small class="text-muted">by {{ audit.created_by.get_full_name|default:audit.created_by.username }}</small>
</td>
</tr>
</table>
</div>
</div>
{% if audit.description %}
<div class="mt-3">
<h6>Description:</h6>
<p class="text-muted">{{ audit.description }}</p>
</div>
{% endif %}
{% if audit.scope %}
<div class="mt-3">
<h6>Audit Scope:</h6>
<p class="text-muted">{{ audit.scope }}</p>
</div>
{% endif %}
</div>
</div>
<!-- Audit Findings -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Audit Findings</h4>
<div class="panel-heading-btn">
{% if audit.status == 'in_progress' %}
<button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addFindingModal">
<i class="fa fa-plus me-1"></i>Add Finding
</button>
{% endif %}
</div>
</div>
<div class="panel-body">
{% if audit.findings.exists %}
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>Finding</th>
<th>Severity</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for finding in audit.findings.all %}
<tr>
<td>
<strong>{{ finding.title }}</strong>
<br><small class="text-muted">{{ finding.description|truncatechars:100 }}</small>
</td>
<td>
{% if finding.severity == 'critical' %}
<span class="badge bg-danger">Critical</span>
{% elif finding.severity == 'major' %}
<span class="badge bg-warning text-dark">Major</span>
{% elif finding.severity == 'minor' %}
<span class="badge bg-info">Minor</span>
{% elif finding.severity == 'observation' %}
<span class="badge bg-secondary">Observation</span>
{% endif %}
</td>
<td>
{% if finding.status == 'open' %}
<span class="badge bg-danger">Open</span>
{% elif finding.status == 'in_progress' %}
<span class="badge bg-warning text-dark">In Progress</span>
{% elif finding.status == 'resolved' %}
<span class="badge bg-success">Resolved</span>
{% elif finding.status == 'closed' %}
<span class="badge bg-secondary">Closed</span>
{% endif %}
</td>
<td>
<div class="btn-group">
<button type="button" class="btn btn-outline-primary btn-sm" onclick="viewFinding('{{ finding.id }}')">
<i class="fa fa-eye"></i>
</button>
{% if audit.status == 'in_progress' %}
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="editFinding('{{ finding.id }}')">
<i class="fa fa-edit"></i>
</button>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="text-center text-muted py-4">
<i class="fa fa-search fa-3x mb-3"></i>
<br>No findings recorded
{% if audit.status == 'in_progress' %}
<br><small>Add findings as you conduct the audit</small>
{% endif %}
</div>
{% endif %}
</div>
</div>
<!-- Audit Timeline -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Audit Timeline</h4>
</div>
<div class="panel-body">
<div class="timeline">
<div class="timeline-item">
<div class="timeline-time">{{ audit.created_at|date:"M d, Y g:i A" }}</div>
<div class="timeline-icon bg-primary"><i class="fa fa-plus"></i></div>
<div class="timeline-body">
<div class="timeline-header">Audit Created</div>
<div class="timeline-content">
Audit created by {{ audit.created_by.get_full_name|default:audit.created_by.username }}
</div>
</div>
</div>
{% if audit.start_date %}
<div class="timeline-item">
<div class="timeline-time">{{ audit.start_date|date:"M d, Y g:i A" }}</div>
<div class="timeline-icon bg-success"><i class="fa fa-play"></i></div>
<div class="timeline-body">
<div class="timeline-header">Audit Started</div>
<div class="timeline-content">
Audit commenced by {{ audit.lead_auditor.get_full_name|default:"System" }}
</div>
</div>
</div>
{% endif %}
{% for finding in audit.findings.all %}
<div class="timeline-item">
<div class="timeline-time">{{ finding.created_at|date:"M d, Y g:i A" }}</div>
<div class="timeline-icon bg-warning"><i class="fa fa-exclamation-triangle"></i></div>
<div class="timeline-body">
<div class="timeline-header">Finding Added: {{ finding.title }}</div>
<div class="timeline-content">
{{ finding.description|truncatechars:100 }}
</div>
</div>
</div>
{% endfor %}
{% if audit.completion_date %}
<div class="timeline-item">
<div class="timeline-time">{{ audit.completion_date|date:"M d, Y g:i A" }}</div>
<div class="timeline-icon bg-info"><i class="fa fa-check"></i></div>
<div class="timeline-body">
<div class="timeline-header">Audit Completed</div>
<div class="timeline-content">
Audit completed with {{ audit.findings_count }} finding{{ audit.findings_count|pluralize }}
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="col-xl-4">
<!-- Quick Actions -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Quick Actions</h4>
</div>
<div class="panel-body">
<div class="d-grid gap-2">
{% if audit.status == 'planned' %}
<button type="button" class="btn btn-success" onclick="startAudit()">
<i class="fa fa-play me-2"></i>Start Audit
</button>
{% elif audit.status == 'in_progress' %}
<button type="button" class="btn btn-warning" onclick="completeAudit()">
<i class="fa fa-check me-2"></i>Complete Audit
</button>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addFindingModal">
<i class="fa fa-plus me-2"></i>Add Finding
</button>
{% endif %}
<a href="{% url 'quality:audit_edit' audit.pk %}" class="btn btn-outline-primary">
<i class="fa fa-edit me-2"></i>Edit Audit
</a>
{% if audit.status == 'completed' %}
<button type="button" class="btn btn-outline-success" onclick="generateReport()">
<i class="fa fa-file-pdf me-2"></i>Generate Report
</button>
{% endif %}
<button type="button" class="btn btn-outline-secondary" onclick="printAudit()">
<i class="fa fa-print me-2"></i>Print Audit
</button>
{% if audit.status != 'completed' %}
<button type="button" class="btn btn-outline-danger" onclick="cancelAudit()">
<i class="fa fa-times me-2"></i>Cancel Audit
</button>
{% endif %}
</div>
</div>
</div>
<!-- Audit Team -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Audit Team</h4>
</div>
<div class="panel-body">
{% if audit.lead_auditor %}
<div class="d-flex align-items-center mb-3">
<div class="flex-fill">
<div class="fw-bold">{{ audit.lead_auditor.get_full_name }}</div>
<div class="text-muted small">Lead Auditor</div>
<div class="text-muted small">{{ audit.lead_auditor.email }}</div>
</div>
<div>
<span class="badge bg-primary">Lead</span>
</div>
</div>
{% endif %}
{% for team_member in audit.team_members.all %}
<div class="d-flex align-items-center mb-3">
<div class="flex-fill">
<div class="fw-bold">{{ team_member.get_full_name }}</div>
<div class="text-muted small">Team Member</div>
<div class="text-muted small">{{ team_member.email }}</div>
</div>
<div>
<span class="badge bg-secondary">Member</span>
</div>
</div>
{% endfor %}
{% if not audit.lead_auditor and not audit.team_members.exists %}
<div class="text-center text-muted">
<i class="fa fa-users fa-2x mb-2"></i>
<br>No team members assigned
</div>
{% endif %}
</div>
</div>
<!-- Audit Statistics -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Statistics</h4>
</div>
<div class="panel-body">
<div class="row text-center">
<div class="col-6">
<div class="h3 mb-0">{{ audit.findings_count|default:0 }}</div>
<div class="text-muted small">Total Findings</div>
</div>
<div class="col-6">
<div class="h3 mb-0">{{ audit.critical_findings_count|default:0 }}</div>
<div class="text-muted small">Critical</div>
</div>
</div>
<hr>
<div class="row text-center">
<div class="col-6">
<div class="h3 mb-0">{{ audit.open_findings_count|default:0 }}</div>
<div class="text-muted small">Open</div>
</div>
<div class="col-6">
<div class="h3 mb-0">{{ audit.resolved_findings_count|default:0 }}</div>
<div class="text-muted small">Resolved</div>
</div>
</div>
{% if audit.duration %}
<hr>
<div class="text-center">
<div class="h3 mb-0">{{ audit.duration }} days</div>
<div class="text-muted small">Duration</div>
</div>
{% endif %}
</div>
</div>
</div>
</div>
<!-- END row -->
<!-- Add Finding Modal -->
<div class="modal fade" id="addFindingModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="fa fa-plus me-2"></i>Add Audit Finding
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="addFindingForm">
{% csrf_token %}
<input type="hidden" id="auditId" value="{{ audit.id }}">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="findingTitle" required>
<label for="findingTitle">Finding Title *</label>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="findingDescription" style="height: 120px;" required></textarea>
<label for="findingDescription">Description *</label>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-floating mb-3">
<select class="form-select" id="findingSeverity" required>
<option value="">Select Severity</option>
<option value="critical">Critical</option>
<option value="major">Major</option>
<option value="minor">Minor</option>
<option value="observation">Observation</option>
</select>
<label for="findingSeverity">Severity *</label>
</div>
</div>
<div class="col-md-6">
<div class="form-floating mb-3">
<select class="form-select" id="findingCategory">
<option value="">Select Category</option>
<option value="documentation">Documentation</option>
<option value="process">Process</option>
<option value="compliance">Compliance</option>
<option value="safety">Safety</option>
<option value="quality">Quality</option>
</select>
<label for="findingCategory">Category</label>
</div>
</div>
</div>
<div class="form-floating mb-3">
<textarea class="form-control" id="findingRecommendation" style="height: 100px;"></textarea>
<label for="findingRecommendation">Recommended Action</label>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" onclick="addFinding()">
<i class="fa fa-plus me-2"></i>Add Finding
</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script>
function startAudit() {
if (confirm('Start this audit? This will change the status to "In Progress".')) {
$.ajax({
url: '{% url "quality:start_audit" %}',
method: 'POST',
data: {
'audit_id': '{{ audit.id }}',
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
toastr.success('Audit started successfully');
location.reload();
} else {
toastr.error(response.message || 'Failed to start audit');
}
},
error: function() {
toastr.error('Failed to start audit');
}
});
}
}
function completeAudit() {
if (confirm('Complete this audit? This will finalize the audit and generate the report.')) {
$.ajax({
url: '{% url "quality:complete_audit" %}',
method: 'POST',
data: {
'audit_id': '{{ audit.id }}',
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
toastr.success('Audit completed successfully');
location.reload();
} else {
toastr.error(response.message || 'Failed to complete audit');
}
},
error: function() {
toastr.error('Failed to complete audit');
}
});
}
}
function cancelAudit() {
if (confirm('Cancel this audit? This action cannot be undone.')) {
$.ajax({
url: '{% url "quality:cancel_audit" %}',
method: 'POST',
data: {
'audit_id': '{{ audit.id }}',
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
toastr.success('Audit cancelled successfully');
location.reload();
} else {
toastr.error(response.message || 'Failed to cancel audit');
}
},
error: function() {
toastr.error('Failed to cancel audit');
}
});
}
}
function addFinding() {
const formData = {
'audit_id': $('#auditId').val(),
'title': $('#findingTitle').val(),
'description': $('#findingDescription').val(),
'severity': $('#findingSeverity').val(),
'category': $('#findingCategory').val(),
'recommendation': $('#findingRecommendation').val(),
'csrfmiddlewaretoken': '{{ csrf_token }}'
};
$.ajax({
url: '{% url "quality:add_audit_finding" %}',
method: 'POST',
data: formData,
success: function(response) {
if (response.success) {
toastr.success('Finding added successfully');
$('#addFindingModal').modal('hide');
location.reload();
} else {
toastr.error(response.message || 'Failed to add finding');
}
},
error: function() {
toastr.error('Failed to add finding');
}
});
}
function viewFinding(findingId) {
// Open finding detail modal or redirect to finding detail page
window.open('{% url "quality:audit_finding_detail" 0 %}'.replace('0', findingId), '_blank');
}
function editFinding(findingId) {
// Open finding edit modal or redirect to finding edit page
window.location.href = '{% url "quality:audit_finding_edit" 0 %}'.replace('0', findingId);
}
function generateReport() {
window.open('{% url "quality:audit_report" audit.pk %}', '_blank');
toastr.info('Generating audit report...');
}
function printAudit() {
window.print();
}
</script>
{% endblock %}