471 lines
24 KiB
HTML
471 lines
24 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}{{ object.medication.name }} Inventory - Pharmacy{% 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 'pharmacy:dashboard' %}">Pharmacy</a></li>
|
|
<li class="breadcrumb-item"><a href="{% url 'pharmacy:inventory_list' %}">Inventory</a></li>
|
|
<li class="breadcrumb-item active">{{ object.medication.name }}</li>
|
|
</ol>
|
|
<!-- END breadcrumb -->
|
|
|
|
<!-- BEGIN page-header -->
|
|
<h1 class="page-header">
|
|
Inventory Detail
|
|
<small>{{ object.medication.name }} - {{ object.location.name }}</small>
|
|
</h1>
|
|
<!-- END page-header -->
|
|
|
|
<div class="row">
|
|
<div class="col-xl-8">
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Inventory Information</h4>
|
|
<div class="panel-heading-btn">
|
|
<button type="button" class="btn btn-xs btn-primary me-2" data-bs-toggle="modal" data-bs-target="#adjustStockModal">
|
|
<i class="fa fa-plus-minus"></i> Adjust Stock
|
|
</button>
|
|
<button type="button" class="btn btn-xs btn-success me-2" data-bs-toggle="modal" data-bs-target="#addStockModal">
|
|
<i class="fa fa-plus"></i> Add Stock
|
|
</button>
|
|
<a href="{% url 'pharmacy:inventory_update' object.pk %}" class="btn btn-xs btn-warning me-2">
|
|
<i class="fa fa-edit"></i> Edit
|
|
</a>
|
|
<a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>
|
|
</div>
|
|
</div>
|
|
<div class="panel-body">
|
|
<!-- Medication Information -->
|
|
<div class="card border-primary mb-4">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fa fa-pills me-2"></i>Medication Details
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-2"><strong>Generic Name:</strong> {{ object.medication.generic_name }}</div>
|
|
<div class="mb-2"><strong>Brand Name:</strong> {{ object.medication.brand_name|default:"Not specified" }}</div>
|
|
<div class="mb-2"><strong>Strength:</strong> {{ object.medication.strength }}</div>
|
|
<div class="mb-2"><strong>Dosage Form:</strong> {{ object.medication.get_dosage_form_display }}</div>
|
|
<div class="mb-2"><strong>Route:</strong> {{ object.medication.get_route_display }}</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-2"><strong>Manufacturer:</strong> {{ object.medication.manufacturer|default:"Not specified" }}</div>
|
|
<div class="mb-2"><strong>NDC Number:</strong> {{ object.medication.ndc_number|default:"Not specified" }}</div>
|
|
<div class="mb-2"><strong>Category:</strong> {{ object.medication.get_category_display }}</div>
|
|
<div class="mb-2">
|
|
{% if object.medication.is_controlled %}
|
|
<span class="badge bg-warning text-dark">
|
|
<i class="fa fa-shield me-1"></i>Controlled - Schedule {{ object.medication.controlled_schedule }}
|
|
</span>
|
|
{% endif %}
|
|
{% if object.medication.is_high_alert %}
|
|
<span class="badge bg-danger">
|
|
<i class="fa fa-exclamation-triangle me-1"></i>High Alert
|
|
</span>
|
|
{% endif %}
|
|
{% if object.medication.requires_refrigeration %}
|
|
<span class="badge bg-info">
|
|
<i class="fa fa-snowflake me-1"></i>Refrigeration Required
|
|
</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stock Information -->
|
|
<div class="card border-success mb-4">
|
|
<div class="card-header bg-success text-white">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fa fa-boxes me-2"></i>Current Stock Status
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<div class="display-6 {% if object.current_stock <= object.reorder_level %}text-danger{% elif object.current_stock <= object.minimum_stock %}text-warning{% else %}text-success{% endif %}">
|
|
{{ object.current_stock }}
|
|
</div>
|
|
<div class="small text-muted">Current Stock</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<div class="display-6 text-info">{{ object.minimum_stock }}</div>
|
|
<div class="small text-muted">Minimum Stock</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<div class="display-6 text-warning">{{ object.reorder_level }}</div>
|
|
<div class="small text-muted">Reorder Level</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="text-center">
|
|
<div class="display-6 text-primary">{{ object.maximum_stock }}</div>
|
|
<div class="small text-muted">Maximum Stock</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="mb-2"><strong>Location:</strong> {{ object.location.name }}</div>
|
|
<div class="mb-2"><strong>Bin/Shelf:</strong> {{ object.bin_location|default:"Not specified" }}</div>
|
|
<div class="mb-2"><strong>Unit Cost:</strong> ${{ object.unit_cost|floatformat:2 }}</div>
|
|
<div class="mb-2"><strong>Total Value:</strong> ${{ object.total_value|floatformat:2 }}</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="mb-2"><strong>Last Updated:</strong> {{ object.last_updated|date:"M d, Y H:i" }}</div>
|
|
<div class="mb-2"><strong>Last Count:</strong> {{ object.last_count_date|date:"M d, Y"|default:"Never" }}</div>
|
|
<div class="mb-2"><strong>Expiration Date:</strong>
|
|
{% if object.expiration_date %}
|
|
<span class="{% if object.is_expired %}text-danger{% elif object.expires_soon %}text-warning{% else %}text-success{% endif %}">
|
|
{{ object.expiration_date|date:"M d, Y" }}
|
|
{% if object.is_expired %}(Expired){% elif object.expires_soon %}(Expires Soon){% endif %}
|
|
</span>
|
|
{% else %}
|
|
Not specified
|
|
{% endif %}
|
|
</div>
|
|
<div class="mb-2"><strong>Lot Number:</strong> {{ object.lot_number|default:"Not specified" }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stock Alerts -->
|
|
{% if object.current_stock <= object.reorder_level or object.is_expired or object.expires_soon %}
|
|
<div class="card border-warning mb-4">
|
|
<div class="card-header bg-warning text-dark">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fa fa-exclamation-triangle me-2"></i>Stock Alerts
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if object.current_stock <= 0 %}
|
|
<div class="alert alert-danger">
|
|
<i class="fa fa-times-circle me-2"></i>
|
|
<strong>Out of Stock:</strong> This medication is currently out of stock.
|
|
</div>
|
|
{% elif object.current_stock <= object.reorder_level %}
|
|
<div class="alert alert-warning">
|
|
<i class="fa fa-exclamation-triangle me-2"></i>
|
|
<strong>Reorder Alert:</strong> Stock level is at or below reorder point ({{ object.reorder_level }}).
|
|
</div>
|
|
{% elif object.current_stock <= object.minimum_stock %}
|
|
<div class="alert alert-info">
|
|
<i class="fa fa-info-circle me-2"></i>
|
|
<strong>Low Stock:</strong> Stock level is below minimum threshold ({{ object.minimum_stock }}).
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if object.is_expired %}
|
|
<div class="alert alert-danger">
|
|
<i class="fa fa-calendar-times me-2"></i>
|
|
<strong>Expired:</strong> This medication expired on {{ object.expiration_date|date:"M d, Y" }}.
|
|
</div>
|
|
{% elif object.expires_soon %}
|
|
<div class="alert alert-warning">
|
|
<i class="fa fa-calendar-exclamation me-2"></i>
|
|
<strong>Expires Soon:</strong> This medication expires on {{ object.expiration_date|date:"M d, Y" }}.
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Recent Transactions -->
|
|
<div class="card border-info mb-4">
|
|
<div class="card-header bg-info text-white">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fa fa-history me-2"></i>Recent Transactions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if object.transactions.exists %}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>Date</th>
|
|
<th>Type</th>
|
|
<th>Quantity</th>
|
|
<th>Balance</th>
|
|
<th>User</th>
|
|
<th>Notes</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for transaction in object.transactions.all|slice:":10" %}
|
|
<tr>
|
|
<td>{{ transaction.created_at|date:"M d, H:i" }}</td>
|
|
<td>
|
|
<span class="badge bg-{% if transaction.transaction_type == 'IN' %}success{% elif transaction.transaction_type == 'OUT' %}danger{% else %}warning{% endif %}">
|
|
{{ transaction.get_transaction_type_display }}
|
|
</span>
|
|
</td>
|
|
<td class="{% if transaction.transaction_type == 'IN' %}text-success{% elif transaction.transaction_type == 'OUT' %}text-danger{% endif %}">
|
|
{% if transaction.transaction_type == 'IN' %}+{% elif transaction.transaction_type == 'OUT' %}-{% endif %}{{ transaction.quantity }}
|
|
</td>
|
|
<td>{{ transaction.balance_after }}</td>
|
|
<td>{{ transaction.user.get_full_name|default:transaction.user.username }}</td>
|
|
<td>{{ transaction.notes|truncatechars:30|default:"-" }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% if object.transactions.count > 10 %}
|
|
<div class="text-center mt-3">
|
|
<a href="{% url 'pharmacy:inventory_transactions' object.pk %}" class="btn btn-outline-info btn-sm">
|
|
View All {{ object.transactions.count }} Transactions
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-center text-muted">
|
|
<i class="fa fa-info-circle fa-2x mb-2"></i>
|
|
<p>No transactions recorded yet.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Storage Information -->
|
|
{% if object.medication.storage_conditions or object.medication.special_handling %}
|
|
<div class="card border-secondary mb-4">
|
|
<div class="card-header bg-secondary text-white">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fa fa-warehouse me-2"></i>Storage Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if object.medication.storage_conditions %}
|
|
<div class="mb-3">
|
|
<strong>Storage Conditions:</strong>
|
|
<p class="mb-0">{{ object.medication.storage_conditions }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if object.medication.special_handling %}
|
|
<div class="mb-3">
|
|
<strong>Special Handling:</strong>
|
|
<p class="mb-0">{{ object.medication.special_handling }}</p>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if object.medication.storage_temperature %}
|
|
<div class="mb-3">
|
|
<strong>Storage Temperature:</strong> {{ object.medication.storage_temperature }}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
|
|
<div class="col-xl-4">
|
|
<!-- BEGIN panel -->
|
|
<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">
|
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#adjustStockModal">
|
|
<i class="fa fa-plus-minus me-2"></i>Adjust Stock
|
|
</button>
|
|
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#addStockModal">
|
|
<i class="fa fa-plus me-2"></i>Add Stock
|
|
</button>
|
|
<button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#transferStockModal">
|
|
<i class="fa fa-exchange-alt me-2"></i>Transfer Stock
|
|
</button>
|
|
<button type="button" class="btn btn-info" onclick="performStockCount()">
|
|
<i class="fa fa-clipboard-check me-2"></i>Stock Count
|
|
</button>
|
|
<a href="{% url 'pharmacy:inventory_update' object.pk %}" class="btn btn-outline-secondary">
|
|
<i class="fa fa-edit me-2"></i>Edit Details
|
|
</a>
|
|
<button type="button" class="btn btn-outline-primary" onclick="printLabel()">
|
|
<i class="fa fa-print me-2"></i>Print Label
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Stock Statistics</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
<div class="row text-center">
|
|
<div class="col-6">
|
|
<div class="mb-3">
|
|
<div class="h4 text-success">{{ object.total_received|default:0 }}</div>
|
|
<div class="small text-muted">Total Received</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="mb-3">
|
|
<div class="h4 text-danger">{{ object.total_dispensed|default:0 }}</div>
|
|
<div class="small text-muted">Total Dispensed</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="mb-3">
|
|
<div class="h4 text-warning">{{ object.total_adjusted|default:0 }}</div>
|
|
<div class="small text-muted">Total Adjusted</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-6">
|
|
<div class="mb-3">
|
|
<div class="h4 text-info">{{ object.turnover_rate|floatformat:1|default:0 }}</div>
|
|
<div class="small text-muted">Turnover Rate</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Reorder Information</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
{% if object.current_stock <= object.reorder_level %}
|
|
<div class="alert alert-warning">
|
|
<i class="fa fa-exclamation-triangle me-2"></i>
|
|
<strong>Reorder Needed</strong>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="mb-2"><strong>Suggested Order Quantity:</strong> {{ object.suggested_order_quantity }}</div>
|
|
<div class="mb-2"><strong>Lead Time:</strong> {{ object.lead_time_days|default:"Not specified" }} days</div>
|
|
<div class="mb-2"><strong>Preferred Supplier:</strong> {{ object.preferred_supplier|default:"Not specified" }}</div>
|
|
|
|
{% if object.current_stock <= object.reorder_level %}
|
|
<div class="d-grid mt-3">
|
|
<button type="button" class="btn btn-warning" onclick="createPurchaseOrder()">
|
|
<i class="fa fa-shopping-cart me-2"></i>Create Purchase Order
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
|
|
<!-- BEGIN panel -->
|
|
<div class="panel panel-inverse">
|
|
<div class="panel-heading">
|
|
<h4 class="panel-title">Expiration Tracking</h4>
|
|
</div>
|
|
<div class="panel-body">
|
|
{% if object.expiration_date %}
|
|
<div class="mb-2"><strong>Expiration Date:</strong>
|
|
<span class="{% if object.is_expired %}text-danger{% elif object.expires_soon %}text-warning{% else %}text-success{% endif %}">
|
|
{{ object.expiration_date|date:"M d, Y" }}
|
|
</span>
|
|
</div>
|
|
<div class="mb-2"><strong>Days Until Expiration:</strong>
|
|
<span class="{% if object.days_until_expiration <= 0 %}text-danger{% elif object.days_until_expiration <= 30 %}text-warning{% else %}text-success{% endif %}">
|
|
{{ object.days_until_expiration }} days
|
|
</span>
|
|
</div>
|
|
{% if object.is_expired or object.expires_soon %}
|
|
<div class="d-grid mt-3">
|
|
<button type="button" class="btn btn-danger btn-sm" onclick="markExpired()">
|
|
<i class="fa fa-calendar-times me-2"></i>Mark as Expired
|
|
</button>
|
|
</div>
|
|
{% endif %}
|
|
{% else %}
|
|
<div class="text-muted">No expiration date specified</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<!-- END panel -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modals -->
|
|
{% include 'pharmacy/partials/adjust_stock_modal.html' %}
|
|
{% include 'pharmacy/partials/add_stock_modal.html' %}
|
|
{% include 'pharmacy/partials/transfer_stock_modal.html' %}
|
|
{% endblock %}
|
|
|
|
{% block js %}
|
|
<script>
|
|
function performStockCount() {
|
|
// Implementation for stock count
|
|
toastr.info('Stock count feature coming soon');
|
|
}
|
|
|
|
function printLabel() {
|
|
// Implementation for printing labels
|
|
window.print();
|
|
}
|
|
|
|
{#function createPurchaseOrder() {#}
|
|
{# // Implementation for creating purchase orders#}
|
|
{# window.location.href = '{% url "pharmacy:create_purchase_order" %}?medication={{ object.medication.pk }}&quantity={{ object.suggested_order_quantity }}';#}
|
|
{# }#}
|
|
|
|
{#function markExpired() {#}
|
|
{# if (confirm('Are you sure you want to mark this inventory as expired?')) {#}
|
|
{# $.ajax({#}
|
|
{# url: '{% url "pharmacy:mark_inventory_expired" object.pk %}',#}
|
|
{# method: 'POST',#}
|
|
{# data: {#}
|
|
{# 'csrfmiddlewaretoken': '{{ csrf_token }}'#}
|
|
{# },#}
|
|
{# success: function(response) {#}
|
|
{# toastr.success('Inventory marked as expired');#}
|
|
{# location.reload();#}
|
|
{# },#}
|
|
{# error: function() {#}
|
|
{# toastr.error('Failed to mark inventory as expired');#}
|
|
{# }#}
|
|
{# });#}
|
|
{# }#}
|
|
{# }#}
|
|
|
|
// Auto-refresh for real-time updates
|
|
{#setInterval(function() {#}
|
|
{# // Refresh stock level display#}
|
|
{# $.ajax({#}
|
|
{# url: '{% url "pharmacy:inventory_status" object.pk %}',#}
|
|
{# method: 'GET',#}
|
|
{# success: function(response) {#}
|
|
{# // Update stock display if changed#}
|
|
{# if (response.current_stock !== {{ object.current_stock }}) {#}
|
|
{# location.reload();#}
|
|
{# }#}
|
|
{# }#}
|
|
{# });#}
|
|
{# }, 30000); #}
|
|
</script>
|
|
{% endblock %}
|
|
|