669 lines
31 KiB
HTML
669 lines
31 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ location.name }} - Location Details{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="content">
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h4>Location Details</h4>
|
|
<h6>{{ location.location_code }} - {{ location.name }}</h6>
|
|
</div>
|
|
<div class="page-btn">
|
|
<div class="btn-group">
|
|
<a href="{% url 'inventory:location_list' %}" class="btn btn-secondary">
|
|
<i class="fas fa-arrow-left me-1"></i>Back to List
|
|
</a>
|
|
<a href="{% url 'inventory:location_update' location.id %}" class="btn btn-primary">
|
|
<i class="fas fa-edit me-1"></i>Edit Location
|
|
</a>
|
|
<button type="button" class="btn btn-info" onclick="printLocationDetails()">
|
|
<i class="fas fa-print me-1"></i>Print
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Location Overview -->
|
|
<div class="row">
|
|
<div class="col-lg-8 col-sm-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Location Information</h5>
|
|
<div class="card-tools">
|
|
{% if location.is_active %}
|
|
<span class="badge bg-success">Active</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">Inactive</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="info-group">
|
|
<h6>Basic Information</h6>
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td><strong>Location Code:</strong></td>
|
|
<td>{{ location.location_code }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Name:</strong></td>
|
|
<td>{{ location.name }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Type:</strong></td>
|
|
<td>
|
|
<span class="badge bg-primary">{{ location.get_location_type_display }}</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Parent Location:</strong></td>
|
|
<td>
|
|
{% if location.parent_location %}
|
|
<a href="{% url 'inventory:location_detail' location.parent_location.id %}">
|
|
{{ location.parent_location.location_code }} - {{ location.parent_location.name }}
|
|
</a>
|
|
{% else %}
|
|
None
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Manager:</strong></td>
|
|
<td>{{ location.location_manager.get_full_name|default:"Not assigned" }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="info-group">
|
|
<h6>Physical Location</h6>
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td><strong>Building:</strong></td>
|
|
<td>{{ location.building|default:"-" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Floor:</strong></td>
|
|
<td>{{ location.floor|default:"-" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Room:</strong></td>
|
|
<td>{{ location.room|default:"-" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Zone:</strong></td>
|
|
<td>{{ location.zone|default:"-" }}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Full Address:</strong></td>
|
|
<td>{{ location.full_address|default:"Not specified" }}</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if location.description %}
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="info-group">
|
|
<h6>Description</h6>
|
|
<p>{{ location.description }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Environmental Controls -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Environmental Controls & Capacity</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="info-group">
|
|
<h6>Temperature Control</h6>
|
|
{% if location.temperature_controlled %}
|
|
<div class="alert alert-info">
|
|
<i class="fa fa-thermometer-half"></i>
|
|
<strong>Temperature Controlled</strong><br>
|
|
Range: {{ location.temperature_min|default:"N/A" }}°C to {{ location.temperature_max|default:"N/A" }}°C
|
|
</div>
|
|
{% else %}
|
|
<div class="alert alert-secondary">
|
|
<i class="fa fa-times"></i>
|
|
No temperature control
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="info-group">
|
|
<h6>Humidity Control</h6>
|
|
{% if location.humidity_controlled %}
|
|
<div class="alert alert-info">
|
|
<i class="fa fa-tint"></i>
|
|
<strong>Humidity Controlled</strong><br>
|
|
Range: {{ location.humidity_min|default:"N/A" }}% to {{ location.humidity_max|default:"N/A" }}%
|
|
</div>
|
|
{% else %}
|
|
<div class="alert alert-secondary">
|
|
<i class="fa fa-times"></i>
|
|
No humidity control
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="info-group">
|
|
<h6>Capacity Information</h6>
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td><strong>Capacity:</strong></td>
|
|
<td>{{ location.capacity_cubic_feet|default:"Not specified" }} cubic feet</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Max Weight:</strong></td>
|
|
<td>{{ location.max_weight_pounds|default:"Not specified" }} pounds</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="info-group">
|
|
<h6>Security & Access</h6>
|
|
<table class="table table-borderless">
|
|
<tr>
|
|
<td><strong>Access Control:</strong></td>
|
|
<td>
|
|
{% if location.access_control == 'OPEN' %}
|
|
<span class="badge bg-secondary">{{ location.get_access_control_display }}</span>
|
|
{% elif location.access_control == 'BADGE' %}
|
|
<span class="badge bg-primary">{{ location.get_access_control_display }}</span>
|
|
{% elif location.access_control == 'BIOMETRIC' %}
|
|
<span class="badge bg-danger">{{ location.get_access_control_display }}</span>
|
|
{% else %}
|
|
<span class="badge bg-warning">{{ location.get_access_control_display }}</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><strong>Secure Location:</strong></td>
|
|
<td>
|
|
{% if location.secure_location %}
|
|
<span class="badge bg-danger">Yes</span>
|
|
{% else %}
|
|
<span class="badge bg-success">No</span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Child Locations -->
|
|
{% if child_locations %}
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Child Locations</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table">
|
|
<thead>
|
|
<tr>
|
|
<th>Code</th>
|
|
<th>Name</th>
|
|
<th>Type</th>
|
|
<th>Items</th>
|
|
<th>Status</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for child in child_locations %}
|
|
<tr>
|
|
<td>{{ child.location_code }}</td>
|
|
<td>{{ child.name }}</td>
|
|
<td><span class="badge bg-info">{{ child.get_location_type_display }}</span></td>
|
|
<td>{{ child.total_items|default:0 }}</td>
|
|
<td>
|
|
{% if child.is_active %}
|
|
<span class="badge bg-success">Active</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">Inactive</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<a href="{% url 'inventory:location_detail' child.id %}" class="btn btn-sm btn-outline-primary">
|
|
<i class="fa fa-eye"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Statistics Sidebar -->
|
|
<div class="col-lg-4 col-sm-12">
|
|
<!-- Quick Stats -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Location Statistics</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="stats-list">
|
|
<div class="stats-item">
|
|
<div class="stats-icon bg-primary">
|
|
<i class="fa fa-boxes"></i>
|
|
</div>
|
|
<div class="stats-content">
|
|
<h4>{{ location.total_items|default:0 }}</h4>
|
|
<p>Total Items</p>
|
|
</div>
|
|
</div>
|
|
<div class="stats-item">
|
|
<div class="stats-icon bg-success">
|
|
<i class="fa fa-cubes"></i>
|
|
</div>
|
|
<div class="stats-content">
|
|
<h4>{{ location.total_quantity|default:0 }}</h4>
|
|
<p>Total Quantity</p>
|
|
</div>
|
|
</div>
|
|
<div class="stats-item">
|
|
<div class="stats-icon bg-warning">
|
|
<i class="fa fa-dollar-sign"></i>
|
|
</div>
|
|
<div class="stats-content">
|
|
<h4>${{ location.total_value|default:0|floatformat:2 }}</h4>
|
|
<p>Total Value</p>
|
|
</div>
|
|
</div>
|
|
<div class="stats-item">
|
|
<div class="stats-icon bg-info">
|
|
<i class="fa fa-sitemap"></i>
|
|
</div>
|
|
<div class="stats-content">
|
|
<h4>{{ child_locations|length }}</h4>
|
|
<p>Child Locations</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Activity -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Recent Activity</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="activity-list">
|
|
<div class="activity-item">
|
|
<div class="activity-icon bg-success">
|
|
<i class="fa fa-plus"></i>
|
|
</div>
|
|
<div class="activity-content">
|
|
<p><strong>Stock Added</strong></p>
|
|
<small>Surgical Gloves - 500 units</small>
|
|
<small class="text-muted d-block">2 hours ago</small>
|
|
</div>
|
|
</div>
|
|
<div class="activity-item">
|
|
<div class="activity-icon bg-warning">
|
|
<i class="fa fa-minus"></i>
|
|
</div>
|
|
<div class="activity-content">
|
|
<p><strong>Stock Removed</strong></p>
|
|
<small>Bandages - 50 units</small>
|
|
<small class="text-muted d-block">4 hours ago</small>
|
|
</div>
|
|
</div>
|
|
<div class="activity-item">
|
|
<div class="activity-icon bg-info">
|
|
<i class="fa fa-exchange-alt"></i>
|
|
</div>
|
|
<div class="activity-content">
|
|
<p><strong>Stock Transfer</strong></p>
|
|
<small>Syringes moved to OR-001</small>
|
|
<small class="text-muted d-block">6 hours ago</small>
|
|
</div>
|
|
</div>
|
|
<div class="activity-item">
|
|
<div class="activity-icon bg-primary">
|
|
<i class="fa fa-edit"></i>
|
|
</div>
|
|
<div class="activity-content">
|
|
<p><strong>Location Updated</strong></p>
|
|
<small>Temperature range modified</small>
|
|
<small class="text-muted d-block">1 day ago</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Quick Actions</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="quick-actions">
|
|
<a href="{% url 'inventory:stock_list' %}?location={{ location.id }}" class="btn btn-outline-primary w-100 mb-2">
|
|
<i class="fa fa-list"></i> View Stock Items
|
|
</a>
|
|
<button type="button" class="btn btn-outline-success w-100 mb-2" onclick="addStock()">
|
|
<i class="fa fa-plus"></i> Add Stock
|
|
</button>
|
|
<button type="button" class="btn btn-outline-warning w-100 mb-2" onclick="transferStock()">
|
|
<i class="fa fa-exchange-alt"></i> Transfer Stock
|
|
</button>
|
|
<button type="button" class="btn btn-outline-info w-100 mb-2" onclick="generateReport()">
|
|
<i class="fa fa-chart-bar"></i> Generate Report
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary w-100" onclick="auditLocation()">
|
|
<i class="fa fa-clipboard-check"></i> Audit Location
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stock Items in Location -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Stock Items in Location</h5>
|
|
<div class="card-tools">
|
|
<button type="button" class="btn btn-sm btn-primary" onclick="refreshStockList()">
|
|
<i class="fa fa-sync"></i> Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table" id="stockTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Item Code</th>
|
|
<th>Item Name</th>
|
|
<th>Lot Number</th>
|
|
<th>Quantity</th>
|
|
<th>Available</th>
|
|
<th>Unit Cost</th>
|
|
<th>Total Value</th>
|
|
<th>Expiration</th>
|
|
<th>Status</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for stock in stock_items %}
|
|
<tr>
|
|
<td>{{ stock.inventory_item.item_code }}</td>
|
|
<td>
|
|
<div class="productimgname">
|
|
<a href="{% url 'inventory:stock_detail' stock.id %}" class="product-img">
|
|
<i class="fas fa-box"></i>
|
|
</a>
|
|
<a href="{% url 'inventory:stock_detail' stock.id %}">{{ stock.inventory_item.item_name }}</a>
|
|
</div>
|
|
</td>
|
|
<td>{{ stock.lot_number|default:"-" }}</td>
|
|
<td>{{ stock.quantity_on_hand }}</td>
|
|
<td>{{ stock.quantity_available }}</td>
|
|
<td>${{ stock.unit_cost|floatformat:2 }}</td>
|
|
<td>${{ stock.total_cost|floatformat:2 }}</td>
|
|
<td>
|
|
{% if stock.expiration_date %}
|
|
{% if stock.is_expired %}
|
|
<span class="badge bg-danger">{{ stock.expiration_date }}</span>
|
|
{% elif stock.days_to_expiry < 30 %}
|
|
<span class="badge bg-warning">{{ stock.expiration_date }}</span>
|
|
{% else %}
|
|
<span class="badge bg-success">{{ stock.expiration_date }}</span>
|
|
{% endif %}
|
|
{% else %}
|
|
<span class="badge bg-secondary">No expiry</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
{% if stock.quality_status == 'GOOD' %}
|
|
<span class="badge bg-success">{{ stock.get_quality_status_display }}</span>
|
|
{% elif stock.quality_status == 'QUARANTINE' %}
|
|
<span class="badge bg-warning">{{ stock.get_quality_status_display }}</span>
|
|
{% else %}
|
|
<span class="badge bg-danger">{{ stock.get_quality_status_display }}</span>
|
|
{% endif %}
|
|
</td>
|
|
<td>
|
|
<a class="btn btn-xs btn-outline-primary me-1" href="{% url 'inventory:stock_detail' stock.id %}">
|
|
<i class="fas fa-eye"></i>
|
|
</a>
|
|
<a class="btn btn-xs btn-outline-secondary" href="{% url 'inventory:stock_update' stock.id %}">
|
|
<i class="fas fa-edit"></i>
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="10" class="text-center">No stock items in this location</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Notes Section -->
|
|
{% if location.notes %}
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Notes</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<p>{{ location.notes|linebreaks }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function printLocationDetails() {
|
|
console.log('Printing location details...');
|
|
window.print();
|
|
}
|
|
|
|
function addStock() {
|
|
console.log('Adding stock to location...');
|
|
alert('Add stock functionality would redirect to stock creation form with this location pre-selected.');
|
|
}
|
|
|
|
function transferStock() {
|
|
console.log('Transferring stock from location...');
|
|
alert('Stock transfer functionality would open transfer dialog.');
|
|
}
|
|
|
|
function generateReport() {
|
|
const reportType = prompt('Generate report:\n1. Location Summary\n2. Stock Valuation\n3. Activity Report\n4. Audit Report\n\nEnter number:');
|
|
|
|
if (reportType && ['1', '2', '3', '4'].includes(reportType)) {
|
|
const reportTypes = {
|
|
'1': 'Location Summary',
|
|
'2': 'Stock Valuation',
|
|
'3': 'Activity Report',
|
|
'4': 'Audit Report'
|
|
};
|
|
|
|
console.log(`Generating report: ${reportTypes[reportType]}`);
|
|
alert(`${reportTypes[reportType]} is being generated. You will receive an email when ready.`);
|
|
}
|
|
}
|
|
|
|
function auditLocation() {
|
|
console.log('Starting location audit...');
|
|
alert('Location audit functionality would open audit checklist and tracking interface.');
|
|
}
|
|
|
|
function refreshStockList() {
|
|
console.log('Refreshing stock list...');
|
|
location.reload();
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.info-group {
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.info-group h6 {
|
|
color: #6c757d;
|
|
font-weight: 600;
|
|
margin-bottom: 1rem;
|
|
border-bottom: 1px solid #dee2e6;
|
|
padding-bottom: 0.5rem;
|
|
}
|
|
|
|
.stats-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.stats-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 1rem;
|
|
background-color: #f8f9fa;
|
|
border-radius: 8px;
|
|
border: 1px solid #dee2e6;
|
|
}
|
|
|
|
.stats-icon {
|
|
width: 50px;
|
|
height: 50px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
margin-right: 1rem;
|
|
}
|
|
|
|
.stats-content h4 {
|
|
margin: 0;
|
|
font-size: 1.5rem;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.stats-content p {
|
|
margin: 0;
|
|
color: #6c757d;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.activity-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.activity-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
padding: 0.75rem;
|
|
background-color: #f8f9fa;
|
|
border-radius: 6px;
|
|
border-left: 3px solid #007bff;
|
|
}
|
|
|
|
.activity-icon {
|
|
width: 35px;
|
|
height: 35px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
color: white;
|
|
margin-right: 0.75rem;
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.activity-content p {
|
|
margin: 0 0 0.25rem 0;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.activity-content small {
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.quick-actions .btn {
|
|
text-align: left;
|
|
}
|
|
|
|
.quick-actions .btn i {
|
|
width: 20px;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.stats-item {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
}
|
|
|
|
.stats-icon {
|
|
margin-right: 0;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.activity-item {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
}
|
|
|
|
.activity-icon {
|
|
margin-right: 0;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|