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

631 lines
26 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}Inventory Items - Inventory Management{% endblock %}
{% block css %}
<link href="{% static 'plugins/datatables.net-bs5/css/dataTables.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/datatables.net-responsive-bs5/css/responsive.bootstrap5.min.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet" />
{% 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 'inventory:dashboard' %}">Inventory</a></li>
<li class="breadcrumb-item active">Items</li>
</ol>
<!-- END breadcrumb -->
<!-- BEGIN page-header -->
<h1 class="page-header">
Inventory Items
<small>Medical Supplies & Equipment Catalog</small>
</h1>
<!-- END page-header -->
<!-- BEGIN panel -->
<div class="panel panel-inverse">
<div class="panel-heading">
<h4 class="panel-title">Inventory Items Management</h4>
<div class="panel-heading-btn">
<a href="{% url 'inventory:item_create' %}" class="btn btn-xs btn-success me-2">
<i class="fa fa-plus"></i> Add Item
</a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>
</div>
</div>
<div class="panel-body">
<!-- Filters and Search -->
<div class="row mb-3">
<div class="col-md-3">
<label class="form-label">Search Items</label>
<div class="input-group">
<input type="text" class="form-control" id="search-input" placeholder="Item name, code, or description...">
<button class="btn btn-outline-secondary" type="button" id="search-btn">
<i class="fa fa-search"></i>
</button>
</div>
</div>
<div class="col-md-2">
<label class="form-label">Category</label>
<select class="form-select" id="category-filter">
<option value="">All Categories</option>
<option value="MEDICAL_SUPPLIES">Medical Supplies</option>
<option value="PHARMACEUTICALS">Pharmaceuticals</option>
<option value="SURGICAL_INSTRUMENTS">Surgical Instruments</option>
<option value="DIAGNOSTIC_EQUIPMENT">Diagnostic Equipment</option>
<option value="PATIENT_CARE">Patient Care Equipment</option>
<option value="LABORATORY_SUPPLIES">Laboratory Supplies</option>
<option value="RADIOLOGY_SUPPLIES">Radiology Supplies</option>
<option value="OTHER">Other</option>
</select>
</div>
<div class="col-md-2">
<label class="form-label">Item Type</label>
<select class="form-select" id="type-filter">
<option value="">All Types</option>
<option value="CONSUMABLE">Consumable</option>
<option value="REUSABLE">Reusable</option>
<option value="EQUIPMENT">Equipment</option>
<option value="MEDICATION">Medication</option>
<option value="DEVICE">Medical Device</option>
</select>
</div>
<div class="col-md-2">
<label class="form-label">Stock Status</label>
<select class="form-select" id="stock-filter">
<option value="">All Items</option>
<option value="in_stock">In Stock</option>
<option value="low_stock">Low Stock</option>
<option value="out_of_stock">Out of Stock</option>
<option value="needs_reorder">Needs Reorder</option>
</select>
</div>
<div class="col-md-3">
<label class="form-label">Manufacturer</label>
<select class="form-select select2" id="manufacturer-filter">
<option value="">All Manufacturers</option>
<!-- Options will be loaded dynamically -->
</select>
</div>
</div>
<!-- Quick Stats -->
<div class="row mb-4">
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 bg-primary text-white">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-fill">
<div class="fs-10px text-white-transparent-5 mb-1">TOTAL ITEMS</div>
<div class="fs-18px fw-900 text-white" id="total-items">-</div>
</div>
<div class="w-50px h-50px bg-white-transparent-2 rounded-circle d-flex align-items-center justify-content-center">
<i class="fa fa-boxes fa-lg text-white"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 bg-warning text-white">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-fill">
<div class="fs-10px text-white-transparent-5 mb-1">LOW STOCK ITEMS</div>
<div class="fs-18px fw-900 text-white" id="low-stock-count">-</div>
</div>
<div class="w-50px h-50px bg-white-transparent-2 rounded-circle d-flex align-items-center justify-content-center">
<i class="fa fa-exclamation-triangle fa-lg text-white"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 bg-danger text-white">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-fill">
<div class="fs-10px text-white-transparent-5 mb-1">OUT OF STOCK</div>
<div class="fs-18px fw-900 text-white" id="out-of-stock-count">-</div>
</div>
<div class="w-50px h-50px bg-white-transparent-2 rounded-circle d-flex align-items-center justify-content-center">
<i class="fa fa-times-circle fa-lg text-white"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-3 col-md-6 mb-3">
<div class="card border-0 bg-success text-white">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-fill">
<div class="fs-10px text-white-transparent-5 mb-1">TOTAL VALUE</div>
<div class="fs-18px fw-900 text-white" id="total-value">-</div>
</div>
<div class="w-50px h-50px bg-white-transparent-2 rounded-circle d-flex align-items-center justify-content-center">
<i class="fa fa-dollar-sign fa-lg text-white"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Action Buttons -->
<div class="d-flex justify-content-between align-items-center mb-3">
<div class="btn-group">
<button type="button" class="btn btn-outline-secondary" id="bulk-actions-btn" disabled>
<i class="fa fa-tasks me-2"></i>Bulk Actions
</button>
<button type="button" class="btn btn-outline-secondary dropdown-toggle dropdown-toggle-split"
data-bs-toggle="dropdown" id="bulk-dropdown" disabled>
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="bulkUpdateCategory()">
<i class="fa fa-tag me-2"></i>Update Category
</a></li>
<li><a class="dropdown-item" href="#" onclick="bulkUpdateSupplier()">
<i class="fa fa-truck me-2"></i>Update Supplier
</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="bulkExport()">
<i class="fa fa-download me-2"></i>Export Selected
</a></li>
<li><a class="dropdown-item text-danger" href="#" onclick="bulkDeactivate()">
<i class="fa fa-ban me-2"></i>Deactivate Selected
</a></li>
</ul>
</div>
<div class="btn-group">
<button type="button" class="btn btn-outline-secondary" onclick="refreshTable()">
<i class="fa fa-refresh me-2"></i>Refresh
</button>
<button type="button" class="btn btn-outline-secondary" onclick="showStockAlerts()">
<i class="fa fa-bell me-2"></i>Stock Alerts
</button>
<button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
<i class="fa fa-download me-2"></i>Export
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="exportData('csv')">
<i class="fa fa-file-csv me-2"></i>Export as CSV
</a></li>
<li><a class="dropdown-item" href="#" onclick="exportData('excel')">
<i class="fa fa-file-excel me-2"></i>Export as Excel
</a></li>
<li><a class="dropdown-item" href="#" onclick="exportData('pdf')">
<i class="fa fa-file-pdf me-2"></i>Export as PDF
</a></li>
</ul>
</div>
</div>
<!-- Items Table -->
<div class="table-responsive">
<table id="items-table" class="table table-striped table-bordered align-middle">
<thead>
<tr>
<th width="30">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="select-all">
</div>
</th>
<th>Item Code</th>
<th>Item Name</th>
<th>Category</th>
<th>Type</th>
<th>Manufacturer</th>
<th>Current Stock</th>
<th>Unit Cost</th>
<th>Total Value</th>
<th>Status</th>
<th width="120">Actions</th>
</tr>
</thead>
<tbody>
<!-- Data will be loaded via AJAX -->
</tbody>
</table>
</div>
</div>
</div>
<!-- END panel -->
<!-- Stock Alerts Modal -->
<div class="modal fade" id="stockAlertsModal" tabindex="-1">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Stock Level Alerts</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="row mb-3">
<div class="col-md-4">
<select class="form-select" id="alert-type-filter">
<option value="">All Alerts</option>
<option value="low_stock">Low Stock</option>
<option value="out_of_stock">Out of Stock</option>
<option value="needs_reorder">Needs Reorder</option>
<option value="expiring_soon">Expiring Soon</option>
</select>
</div>
<div class="col-md-4">
<select class="form-select" id="alert-priority-filter">
<option value="">All Priorities</option>
<option value="critical">Critical</option>
<option value="high">High</option>
<option value="medium">Medium</option>
<option value="low">Low</option>
</select>
</div>
<div class="col-md-4">
<button class="btn btn-primary" onclick="generateReorderReport()">
<i class="fa fa-shopping-cart me-2"></i>Generate Reorder Report
</button>
</div>
</div>
<div id="stock-alerts-content">
<!-- Content will be loaded dynamically -->
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" onclick="createPurchaseOrders()">Create Purchase Orders</button>
</div>
</div>
</div>
</div>
<!-- Quick View Modal -->
<div class="modal fade" id="itemQuickViewModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Item Quick View</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="item-quick-view-content">
<!-- Content will be loaded dynamically -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<a href="#" class="btn btn-primary" id="view-full-item">View Full Details</a>
</div>
</div>
</div>
</div>
{% endblock %}
{% block js %}
<script src="{% static 'plugins/datatables.net/js/dataTables.min.js' %}"></script>
<script src="{% static 'plugins/datatables.net-bs5/js/dataTables.bootstrap5.min.js' %}"></script>
<script src="{% static 'plugins/datatables.net-responsive/js/dataTables.responsive.min.js' %}"></script>
<script src="{% static 'plugins/datatables.net-responsive-bs5/js/responsive.bootstrap5.min.js' %}"></script>
<script src="{% static 'plugins/select2/dist/js/select2.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js' %}"></script>
<script>
$(document).ready(function() {
var table;
// Initialize DataTable
table = $('#items-table').DataTable({
processing: true,
serverSide: true,
ajax: {
url: '',
data: function(d) {
d.search_value = $('#search-input').val();
d.category = $('#category-filter').val();
d.item_type = $('#type-filter').val();
d.stock_status = $('#stock-filter').val();
d.manufacturer = $('#manufacturer-filter').val();
}
},
columns: [
{
data: 'id',
orderable: false,
render: function(data, type, row) {
return '<div class="form-check"><input class="form-check-input item-checkbox" type="checkbox" value="' + data + '"></div>';
}
},
{
data: 'item_code',
render: function(data, type, row) {
return '<div class="fw-bold">' + data + '</div>';
}
},
{
data: 'item_name',
render: function(data, type, row) {
return '<div>' +
'<div class="fw-bold">' + data + '</div>' +
(row.description ? '<div class="text-muted small">' + row.description.substring(0, 50) + '...</div>' : '') +
'</div>';
}
},
{
data: 'category',
render: function(data, type, row) {
return '<span class="badge bg-secondary">' + row.category_display + '</span>';
}
},
{
data: 'item_type',
render: function(data, type, row) {
var colors = {
'CONSUMABLE': 'primary',
'REUSABLE': 'success',
'EQUIPMENT': 'info',
'MEDICATION': 'warning',
'DEVICE': 'danger'
};
return '<span class="badge bg-' + (colors[data] || 'secondary') + '">' + row.item_type_display + '</span>';
}
},
{
data: 'manufacturer',
render: function(data, type, row) {
return data || '<span class="text-muted">-</span>';
}
},
{
data: 'current_stock',
render: function(data, type, row) {
var className = '';
if (data <= 0) {
className = 'text-danger fw-bold';
} else if (data <= row.reorder_point) {
className = 'text-warning fw-bold';
}
return '<span class="' + className + '">' + data + ' ' + row.unit_of_measure_display + '</span>';
}
},
{
data: 'unit_cost',
render: function(data, type, row) {
return '$' + parseFloat(data).toFixed(2);
}
},
{
data: 'total_value',
render: function(data, type, row) {
return '$' + parseFloat(data).toFixed(2);
}
},
{
data: 'stock_status',
render: function(data, type, row) {
var badges = {
'in_stock': 'bg-success',
'low_stock': 'bg-warning',
'out_of_stock': 'bg-danger',
'needs_reorder': 'bg-info'
};
return '<span class="badge ' + (badges[data] || 'bg-secondary') + '">' + row.stock_status_display + '</span>';
}
},
{
data: 'id',
orderable: false,
render: function(data, type, row) {
return '<div class="btn-group btn-group-sm">' +
'<button class="btn btn-outline-primary" onclick="viewItem(' + data + ')" title="View">' +
'<i class="fa fa-eye"></i></button>' +
'<button class="btn btn-outline-secondary" onclick="editItem(' + data + ')" title="Edit">' +
'<i class="fa fa-edit"></i></button>' +
'<button class="btn btn-outline-info" onclick="viewStock(' + data + ')" title="Stock">' +
'<i class="fa fa-boxes"></i></button>' +
'</div>';
}
}
],
order: [[1, 'asc']],
pageLength: 25,
responsive: true,
language: {
processing: '<div class="d-flex justify-content-center"><div class="spinner-border" role="status"></div></div>'
}
});
// Initialize Select2
$('.select2').select2({
theme: 'bootstrap-5',
width: '100%'
});
// Load filter options and stats
loadFilterOptions();
loadStats();
// Filter event handlers
$('#search-input, #category-filter, #type-filter, #stock-filter, #manufacturer-filter').on('change keyup', function() {
table.draw();
});
// Select all checkbox
$('#select-all').on('change', function() {
$('.item-checkbox').prop('checked', this.checked);
updateBulkActions();
});
// Individual checkbox change
$(document).on('change', '.item-checkbox', function() {
updateBulkActions();
});
// Auto-refresh every 5 minutes
setInterval(function() {
table.ajax.reload(null, false);
loadStats();
}, 300000);
});
function loadFilterOptions() {
// Load manufacturer options
$.ajax({
url: '',
success: function(data) {
var select = $('#manufacturer-filter');
data.forEach(function(manufacturer) {
select.append('<option value="' + manufacturer + '">' + manufacturer + '</option>');
});
}
});
}
function loadStats() {
$.ajax({
url: '{% url "inventory:inventory_stats" %}',
success: function(data) {
$('#total-items').text(data.total_items);
$('#low-stock-count').text(data.low_stock_count);
$('#out-of-stock-count').text(data.out_of_stock_count);
$('#total-value').text('$' + parseFloat(data.total_value).toLocaleString());
}
});
}
function updateBulkActions() {
var checkedCount = $('.item-checkbox:checked').length;
$('#bulk-actions-btn, #bulk-dropdown').prop('disabled', checkedCount === 0);
}
function viewItem(itemId) {
window.location.href = '{% url "inventory:item_detail" 0 %}'.replace('0', itemId);
}
function editItem(itemId) {
window.location.href = '{% url "inventory:item_update" 0 %}'.replace('0', itemId);
}
function viewStock(itemId) {
window.location.href = '{% url "inventory:stock_list" %}?item=' + itemId;
}
function bulkUpdateCategory() {
var selectedIds = $('.item-checkbox:checked').map(function() {
return this.value;
}).get();
if (selectedIds.length === 0) {
toastr.warning('Please select items to update');
return;
}
// Implementation for bulk category update
toastr.info('Bulk category update functionality will be implemented');
}
function bulkUpdateSupplier() {
var selectedIds = $('.item-checkbox:checked').map(function() {
return this.value;
}).get();
if (selectedIds.length === 0) {
toastr.warning('Please select items to update');
return;
}
// Implementation for bulk supplier update
toastr.info('Bulk supplier update functionality will be implemented');
}
{#function bulkExport() {#}
{# var selectedIds = $('.item-checkbox:checked').map(function() {#}
{# return this.value;#}
{# }).get();#}
{# #}
{# if (selectedIds.length === 0) {#}
{# toastr.warning('Please select items to export');#}
{# return;#}
{# }#}
{# #}
{# window.location.href = '{% url "inventory:item_export" %}?ids=' + selectedIds.join(',');#}
{# }#}
function bulkDeactivate() {
var selectedIds = $('.item-checkbox:checked').map(function() {
return this.value;
}).get();
if (selectedIds.length === 0) {
toastr.warning('Please select items to deactivate');
return;
}
if (confirm('Are you sure you want to deactivate ' + selectedIds.length + ' item(s)?')) {
$.ajax({
url: '',
method: 'POST',
data: {
'item_ids': selectedIds,
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
toastr.success(response.message);
$('#items-table').DataTable().ajax.reload();
$('#select-all').prop('checked', false);
updateBulkActions();
} else {
toastr.error('Failed to deactivate items');
}
},
error: function() {
toastr.error('An error occurred during bulk deactivation');
}
});
}
}
{#function exportData(format) {#}
{# window.location.href = '{% url "inventory:item_export" %}?format=' + format;#}
{# }#}
function refreshTable() {
$('#items-table').DataTable().ajax.reload();
loadStats();
toastr.info('Table refreshed');
}
function showStockAlerts() {
$('#stockAlertsModal').modal('show');
loadStockAlerts();
}
{#function loadStockAlerts() {#}
{# $.ajax({#}
{# url: '{% url "inventory:stock_alerts_api" %}',#}
{# success: function(data) {#}
{# $('#stock-alerts-content').html(data.html);#}
{# },#}
{# error: function() {#}
{# toastr.error('Failed to load stock alerts');#}
{# }#}
{# });#}
{# } #}
{#function generateReorderReport() {#}
{# window.open('{% url "inventory:reorder_report" %}', '_blank');#}
{# }#}
function createPurchaseOrders() {
// Implementation for creating purchase orders from alerts
toastr.info('Purchase order creation functionality will be implemented');
}
</script>
{% endblock %}